From d9c5fd51292a3d8b9a2998ea60a6e1c4feb1cf87 Mon Sep 17 00:00:00 2001 From: Gorjan Petrovski Date: Wed, 21 Jul 2021 12:17:50 +0200 Subject: [PATCH] feat: two factor authentication segment in preferences with mocked state (#600) --- app/assets/icons/ic-copy.svg | 3 + app/assets/icons/ic-download.svg | 3 + app/assets/javascripts/app.ts | 2 +- .../javascripts/components/DecoratedInput.tsx | 42 +++++++ app/assets/javascripts/components/Icon.tsx | 6 +- .../javascripts/components/IconButton.tsx | 33 ++---- app/assets/javascripts/components/Input.tsx | 22 ++++ .../components/PreferencesMenuItem.tsx | 23 ---- .../components/RoundIconButton.tsx | 42 +++++++ app/assets/javascripts/components/Switch.tsx | 6 +- .../components/preferences/index.tsx | 22 ---- .../components/preferences/menu.tsx | 23 ---- .../components/preferences/pane.tsx | 33 ------ .../components/preferences/preferences.ts | 55 --------- .../components/preferences/view.tsx | 45 ------- .../preferences/PreferencesMenu.tsx | 19 +++ .../preferences/PreferencesView.tsx | 81 +++++++++++++ .../components/Content.tsx} | 12 +- .../preferences/components/MenuItem.tsx | 27 +++++ .../preferences/components/Pane.tsx | 35 ++++++ .../preferences/components/index.ts | 3 + app/assets/javascripts/preferences/index.ts | 9 ++ .../javascripts/preferences/models/index.ts | 2 + .../preferences/models/preferences.ts | 76 ++++++++++++ .../preferences/models/two-factor-auth.ts | 81 +++++++++++++ .../panes/HelpFeedback.tsx} | 17 ++- .../preferences/panes/Security.tsx | 13 +++ .../preferences/panes/TwoFactorAuth.tsx | 110 ++++++++++++++++++ .../javascripts/preferences/panes/index.ts | 2 + app/assets/javascripts/strings.ts | 8 +- package.json | 2 +- yarn.lock | 8 +- 32 files changed, 618 insertions(+), 247 deletions(-) create mode 100644 app/assets/icons/ic-copy.svg create mode 100644 app/assets/icons/ic-download.svg create mode 100644 app/assets/javascripts/components/DecoratedInput.tsx create mode 100644 app/assets/javascripts/components/Input.tsx delete mode 100644 app/assets/javascripts/components/PreferencesMenuItem.tsx create mode 100644 app/assets/javascripts/components/RoundIconButton.tsx delete mode 100644 app/assets/javascripts/components/preferences/index.tsx delete mode 100644 app/assets/javascripts/components/preferences/menu.tsx delete mode 100644 app/assets/javascripts/components/preferences/pane.tsx delete mode 100644 app/assets/javascripts/components/preferences/preferences.ts delete mode 100644 app/assets/javascripts/components/preferences/view.tsx create mode 100644 app/assets/javascripts/preferences/PreferencesMenu.tsx create mode 100644 app/assets/javascripts/preferences/PreferencesView.tsx rename app/assets/javascripts/{components/preferences/content.tsx => preferences/components/Content.tsx} (51%) create mode 100644 app/assets/javascripts/preferences/components/MenuItem.tsx create mode 100644 app/assets/javascripts/preferences/components/Pane.tsx create mode 100644 app/assets/javascripts/preferences/components/index.ts create mode 100644 app/assets/javascripts/preferences/index.ts create mode 100644 app/assets/javascripts/preferences/models/index.ts create mode 100644 app/assets/javascripts/preferences/models/preferences.ts create mode 100644 app/assets/javascripts/preferences/models/two-factor-auth.ts rename app/assets/javascripts/{components/preferences/help-feedback.tsx => preferences/panes/HelpFeedback.tsx} (91%) create mode 100644 app/assets/javascripts/preferences/panes/Security.tsx create mode 100644 app/assets/javascripts/preferences/panes/TwoFactorAuth.tsx create mode 100644 app/assets/javascripts/preferences/panes/index.ts diff --git a/app/assets/icons/ic-copy.svg b/app/assets/icons/ic-copy.svg new file mode 100644 index 000000000..9ad40e8f1 --- /dev/null +++ b/app/assets/icons/ic-copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/icons/ic-download.svg b/app/assets/icons/ic-download.svg new file mode 100644 index 000000000..de2c70fc2 --- /dev/null +++ b/app/assets/icons/ic-download.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index e7470bbe0..b493808ad 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -65,7 +65,7 @@ import { NotesContextMenuDirective } from './components/NotesContextMenu'; import { NotesOptionsPanelDirective } from './components/NotesOptionsPanel'; import { IconDirective } from './components/Icon'; import { NoteTagsContainerDirective } from './components/NoteTagsContainer'; -import { PreferencesDirective } from './components/preferences'; +import { PreferencesDirective } from './preferences'; function reloadHiddenFirefoxTab(): boolean { /** diff --git a/app/assets/javascripts/components/DecoratedInput.tsx b/app/assets/javascripts/components/DecoratedInput.tsx new file mode 100644 index 000000000..860b6b362 --- /dev/null +++ b/app/assets/javascripts/components/DecoratedInput.tsx @@ -0,0 +1,42 @@ +import { FunctionalComponent, ComponentChild } from 'preact'; + +interface Props { + className?: string; + disabled?: boolean; + left?: ComponentChild[]; + right?: ComponentChild[]; + text?: string; +} + +/** + * Input that can be decorated on the left and right side + */ +export const DecoratedInput: FunctionalComponent = ({ + className = '', + disabled = false, + left, + right, + text, +}) => { + const base = + 'rounded py-1.5 px-3 text-input my-1 h-8 flex flex-row items-center gap-4'; + const stateClasses = disabled + ? 'no-border bg-grey-5' + : 'border-solid border-1 border-gray-300'; + const classes = `${base} ${stateClasses} ${className}`; + + return ( +
+ {left} +
+ +
+ {right} +
+ ); +}; diff --git a/app/assets/javascripts/components/Icon.tsx b/app/assets/javascripts/components/Icon.tsx index e9c9a8b1e..c6f9f632c 100644 --- a/app/assets/javascripts/components/Icon.tsx +++ b/app/assets/javascripts/components/Icon.tsx @@ -23,6 +23,8 @@ import SettingsIcon from '../../icons/ic-settings.svg'; import StarIcon from '../../icons/ic-star.svg'; import ThemesIcon from '../../icons/ic-themes.svg'; import UserIcon from '../../icons/ic-user.svg'; +import CopyIcon from '../../icons/ic-copy.svg'; +import DownloadIcon from '../../icons/ic-download.svg'; import { toDirective } from './utils'; import { FunctionalComponent } from 'preact'; @@ -52,6 +54,8 @@ const ICONS = { star: StarIcon, themes: ThemesIcon, user: UserIcon, + copy: CopyIcon, + download: DownloadIcon, }; export type IconType = keyof typeof ICONS; @@ -61,7 +65,7 @@ type Props = { className?: string; }; -export const Icon: FunctionalComponent = ({ type, className }) => { +export const Icon: FunctionalComponent = ({ type, className = '' }) => { const IconComponent = ICONS[type]; return ; }; diff --git a/app/assets/javascripts/components/IconButton.tsx b/app/assets/javascripts/components/IconButton.tsx index 93d826c4e..8c0920c64 100644 --- a/app/assets/javascripts/components/IconButton.tsx +++ b/app/assets/javascripts/components/IconButton.tsx @@ -1,53 +1,38 @@ import { FunctionComponent } from 'preact'; import { Icon, IconType } from './Icon'; -const ICON_BUTTON_TYPES: { - [type: string]: { className: string }; -} = { - normal: { - className: '', - }, - primary: { - className: 'info', - }, -}; - -export type IconButtonType = keyof typeof ICON_BUTTON_TYPES; - -interface IconButtonProps { +interface Props { /** * onClick - preventDefault is handled within the component */ onClick: () => void; - type: IconButtonType; - className?: string; - iconType: IconType; + icon: IconType; } /** - * CircleButton component with an icon for SPA + * IconButton component with an icon * preventDefault is already handled within the component */ -export const IconButton: FunctionComponent = ({ +export const IconButton: FunctionComponent = ({ onClick, - type, className, - iconType, + icon, }) => { const click = (e: MouseEvent) => { e.preventDefault(); onClick(); }; - const typeProps = ICON_BUTTON_TYPES[type]; return ( ); }; diff --git a/app/assets/javascripts/components/Input.tsx b/app/assets/javascripts/components/Input.tsx new file mode 100644 index 000000000..0955b632c --- /dev/null +++ b/app/assets/javascripts/components/Input.tsx @@ -0,0 +1,22 @@ +import { FunctionalComponent } from 'preact'; + +interface Props { + text?: string; + disabled?: boolean; + className?: string; +} + +export const Input: FunctionalComponent = ({ + className = '', + disabled = false, + text, +}) => { + const base = `rounded py-1.5 px-3 text-input my-1 h-8`; + const stateClasses = disabled + ? 'no-border bg-grey-5' + : 'border-solid border-1 border-gray-300'; + const classes = `${base} ${stateClasses} ${className}`; + return ( + + ); +}; diff --git a/app/assets/javascripts/components/PreferencesMenuItem.tsx b/app/assets/javascripts/components/PreferencesMenuItem.tsx deleted file mode 100644 index eb872053a..000000000 --- a/app/assets/javascripts/components/PreferencesMenuItem.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Icon, IconType } from '@/components/Icon'; -import { FunctionComponent } from 'preact'; - -interface PreferencesMenuItemProps { - iconType: IconType; - label: string; - selected: boolean; - onClick: () => void; -} - -export const PreferencesMenuItem: FunctionComponent = - ({ iconType, label, selected, onClick }) => ( -
{ - e.preventDefault(); - onClick(); - }} - > - - {label} -
- ); diff --git a/app/assets/javascripts/components/RoundIconButton.tsx b/app/assets/javascripts/components/RoundIconButton.tsx new file mode 100644 index 000000000..d143fd4e2 --- /dev/null +++ b/app/assets/javascripts/components/RoundIconButton.tsx @@ -0,0 +1,42 @@ +import { FunctionComponent } from 'preact'; +import { Icon, IconType } from './Icon'; + +type ButtonType = 'normal' | 'primary'; + +interface Props { + /** + * onClick - preventDefault is handled within the component + */ + onClick: () => void; + + type: ButtonType; + + className?: string; + + icon: IconType; +} + +/** + * IconButton component with an icon + * preventDefault is already handled within the component + */ +export const RoundIconButton: FunctionComponent = ({ + onClick, + type, + className, + icon: iconType, +}) => { + const click = (e: MouseEvent) => { + e.preventDefault(); + onClick(); + }; + const classes = type === 'primary' ? 'info ' : ''; + return ( + + ); +}; diff --git a/app/assets/javascripts/components/Switch.tsx b/app/assets/javascripts/components/Switch.tsx index c504df77c..0a35c2a74 100644 --- a/app/assets/javascripts/components/Switch.tsx +++ b/app/assets/javascripts/components/Switch.tsx @@ -12,7 +12,7 @@ export type SwitchProps = HTMLProps & { checked?: boolean; onChange: (checked: boolean) => void; className?: string; - children: ComponentChildren; + children?: ComponentChildren; }; export const Switch: FunctionalComponent = ( @@ -22,7 +22,9 @@ export const Switch: FunctionalComponent = ( const checked = props.checked ?? checkedState; const className = props.className ?? ''; return ( -