diff --git a/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx b/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx index 1a75878df..a09335b36 100644 --- a/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx +++ b/app/assets/javascripts/components/AccountMenu/AdvancedOptions.tsx @@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application'; import { AppState } from '@/ui_models/app_state'; import { observer } from 'mobx-react-lite'; import { FunctionComponent } from 'preact'; -import { useState } from 'preact/hooks'; +import { useEffect, useState } from 'preact/hooks'; import { Checkbox } from '../Checkbox'; import { Icon } from '../Icon'; import { InputWithIcon } from '../InputWithIcon'; @@ -11,14 +11,70 @@ type Props = { application: WebApplication; appState: AppState; disabled?: boolean; + onVaultChange?: (isVault: boolean, vaultedEmail?: string) => void; + onStrictSignInChange?: (isStrictSignIn: boolean) => void; }; export const AdvancedOptions: FunctionComponent = observer( - ({ appState, application, disabled = false, children }) => { + ({ + appState, + application, + disabled = false, + onVaultChange, + onStrictSignInChange, + children, + }) => { const { server, setServer, enableServerOption, setEnableServerOption } = appState.accountMenu; const [showAdvanced, setShowAdvanced] = useState(false); + const [isVault, setIsVault] = useState(false); + const [vaultName, setVaultName] = useState(''); + const [vaultUserphrase, setVaultUserphrase] = useState(''); + + const [isStrictSignin, setIsStrictSignin] = useState(false); + + useEffect(() => { + const recomputeVaultedEmail = async () => { + const vaultedEmail = await application.vaultToEmail( + vaultName, + vaultUserphrase + ); + + if (!vaultedEmail) { + if (vaultName?.length > 0 && vaultUserphrase?.length > 0) { + application.alertService.alert('Unable to compute vault name.'); + } + return; + } + onVaultChange?.(true, vaultedEmail); + }; + + if (vaultName && vaultUserphrase) { + recomputeVaultedEmail(); + } + }, [vaultName, vaultUserphrase, application, onVaultChange]); + + useEffect(() => { + onVaultChange?.(isVault); + }, [isVault, onVaultChange]); + + const handleIsVaultChange = () => { + setIsVault(!isVault); + }; + + const handleVaultNameChange = (e: Event) => { + if (e.target instanceof HTMLInputElement) { + setVaultName(e.target.value); + } + }; + + const handleVaultUserphraseChange = (e: Event) => { + if (e.target instanceof HTMLInputElement) { + setVaultUserphrase(e.target.value); + } + }; + const handleServerOptionChange = (e: Event) => { if (e.target instanceof HTMLInputElement) { setEnableServerOption(e.target.checked); @@ -32,6 +88,12 @@ export const AdvancedOptions: FunctionComponent = observer( } }; + const handleStrictSigninChange = () => { + const newValue = !isStrictSignin; + setIsStrictSignin(newValue); + onStrictSignInChange?.(newValue); + }; + const toggleShowAdvanced = () => { setShowAdvanced(!showAdvanced); }; @@ -50,6 +112,70 @@ export const AdvancedOptions: FunctionComponent = observer( {showAdvanced ? (
{children} + + {appState.enableUnfinishedFeatures && ( +
+ + + + +
+ )} + + {appState.enableUnfinishedFeatures && isVault && ( + <> + + + + )} + + {onStrictSignInChange && ( +
+ + + + +
+ )} + = observer( /> ) : null} -
- ); } diff --git a/app/assets/javascripts/components/AccountMenu/CreateAccount.tsx b/app/assets/javascripts/components/AccountMenu/CreateAccount.tsx index 3388d6aa0..46d2f0621 100644 --- a/app/assets/javascripts/components/AccountMenu/CreateAccount.tsx +++ b/app/assets/javascripts/components/AccountMenu/CreateAccount.tsx @@ -33,6 +33,7 @@ export const CreateAccount: FunctionComponent = observer( const emailInputRef = useRef(null); const passwordInputRef = useRef(null); + const [isVault, setIsVault] = useState(false); useEffect(() => { if (emailInputRef.current) { @@ -82,6 +83,13 @@ export const CreateAccount: FunctionComponent = observer( setPassword(''); }; + const onVaultChange = (isVault: boolean, vaultedEmail?: string) => { + setIsVault(isVault); + if (isVault && vaultedEmail) { + setEmail(vaultedEmail); + } + }; + return ( <>
@@ -101,6 +109,7 @@ export const CreateAccount: FunctionComponent = observer( inputType="email" placeholder="Email" value={email} + disabled={isVault} onChange={handleEmailChange} onKeyDown={handleKeyDown} ref={emailInputRef} @@ -130,7 +139,11 @@ export const CreateAccount: FunctionComponent = observer( />
- + ); } diff --git a/app/assets/javascripts/components/AccountMenu/GeneralAccountMenu.tsx b/app/assets/javascripts/components/AccountMenu/GeneralAccountMenu.tsx index 5590741be..f27a910e5 100644 --- a/app/assets/javascripts/components/AccountMenu/GeneralAccountMenu.tsx +++ b/app/assets/javascripts/components/AccountMenu/GeneralAccountMenu.tsx @@ -66,7 +66,7 @@ export const GeneralAccountMenu: FunctionComponent = observer( <>
You're signed in as:
-
{user.email}
+
{user.email}
{application.getHost()}
diff --git a/app/assets/javascripts/components/AccountMenu/SignIn.tsx b/app/assets/javascripts/components/AccountMenu/SignIn.tsx index 7886ab769..74578be4f 100644 --- a/app/assets/javascripts/components/AccountMenu/SignIn.tsx +++ b/app/assets/javascripts/components/AccountMenu/SignIn.tsx @@ -3,7 +3,7 @@ import { AppState } from '@/ui_models/app_state'; import { isDev } from '@/utils'; import { observer } from 'mobx-react-lite'; import { FunctionComponent } from 'preact'; -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; import { AccountMenuPane } from '.'; import { Button } from '../Button'; import { Checkbox } from '../Checkbox'; @@ -25,10 +25,12 @@ export const SignInPane: FunctionComponent = observer( const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [isEphemeral, setIsEphemeral] = useState(false); + const [isStrictSignin, setIsStrictSignin] = useState(false); const [isSigningIn, setIsSigningIn] = useState(false); const [showPassword, setShowPassword] = useState(false); const [shouldMergeLocal, setShouldMergeLocal] = useState(true); + const [isVault, setIsVault] = useState(false); const emailInputRef = useRef(null); const passwordInputRef = useRef(null); @@ -106,6 +108,16 @@ export const SignInPane: FunctionComponent = observer( } }; + const onVaultChange = useCallback( + (newIsVault: boolean, vaultedEmail?: string) => { + setIsVault(newIsVault); + if (newIsVault && vaultedEmail) { + setEmail(vaultedEmail); + } + }, + [setEmail] + ); + const handleSignInFormSubmit = (e: Event) => { e.preventDefault(); @@ -145,7 +157,7 @@ export const SignInPane: FunctionComponent = observer( onChange={handleEmailChange} onFocus={resetInvalid} onKeyDown={handleKeyDown} - disabled={isSigningIn} + disabled={isSigningIn || isVault} ref={emailInputRef} /> = observer( appState={appState} application={application} disabled={isSigningIn} - > -
- - - - -
- + onVaultChange={onVaultChange} + onStrictSignInChange={handleStrictSigninChange} + /> ); } diff --git a/app/assets/javascripts/components/Icon.tsx b/app/assets/javascripts/components/Icon.tsx index b7c24f52b..6257fd188 100644 --- a/app/assets/javascripts/components/Icon.tsx +++ b/app/assets/javascripts/components/Icon.tsx @@ -27,6 +27,8 @@ import { EmailIcon, EyeIcon, EyeOffIcon, + FileIcon, + FolderIcon, FileDocIcon, FileImageIcon, FileMovIcon, @@ -139,6 +141,8 @@ export const ICONS = { editor: EditorIcon, email: EmailIcon, eye: EyeIcon, + file: FileIcon, + folder: FolderIcon, hashtag: HashtagIcon, help: HelpIcon, history: HistoryIcon, diff --git a/app/assets/javascripts/components/InputWithIcon.tsx b/app/assets/javascripts/components/InputWithIcon.tsx index 733a6ed3c..1f517c69f 100644 --- a/app/assets/javascripts/components/InputWithIcon.tsx +++ b/app/assets/javascripts/components/InputWithIcon.tsx @@ -68,6 +68,7 @@ export const InputWithIcon: FunctionComponent = forwardRef( className={`pr-2 w-full border-0 focus:shadow-none ${ disabled ? DISABLED_CLASSNAME : '' }`} + spellcheck={false} disabled={disabled} placeholder={placeholder} ref={ref} diff --git a/app/assets/javascripts/components/Preferences/panes/account/Credentials.tsx b/app/assets/javascripts/components/Preferences/panes/account/Credentials.tsx index 1b0e8b790..b15e863f9 100644 --- a/app/assets/javascripts/components/Preferences/panes/account/Credentials.tsx +++ b/app/assets/javascripts/components/Preferences/panes/account/Credentials.tsx @@ -45,7 +45,8 @@ export const Credentials: FunctionComponent = observer( Credentials Email - You're signed in as {user?.email} + You're signed in as{' '} + {user?.email}