Merge branch 'release/10.3.0'
This commit is contained in:
@@ -70,16 +70,19 @@ export const GeneralAccountMenu: FunctionComponent<Props> = observer(
|
|||||||
<div className="my-0.5 font-bold">{user.email}</div>
|
<div className="my-0.5 font-bold">{user.email}</div>
|
||||||
<span className="color-neutral">{application.getHost()}</span>
|
<span className="color-neutral">{application.getHost()}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between px-3 mb-2">
|
<div className="flex items-start justify-between px-3 mb-2">
|
||||||
{isSyncingInProgress ? (
|
{isSyncingInProgress ? (
|
||||||
<div className="flex items-center color-info font-semibold">
|
<div className="flex items-center color-info font-semibold">
|
||||||
<div className="sk-spinner w-5 h-5 mr-2 spinner-info"></div>
|
<div className="sk-spinner w-5 h-5 mr-2 spinner-info"></div>
|
||||||
Syncing...
|
Syncing...
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center success font-semibold">
|
<div className="flex items-start">
|
||||||
<Icon type="check-circle" className="mr-2" />
|
<Icon type="check-circle" className="mr-2 success" />
|
||||||
Last synced: {lastSyncDate}
|
<div>
|
||||||
|
<div class="font-semibold success">Last synced:</div>
|
||||||
|
<div class="color-text">{lastSyncDate}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (emailInputRef?.current) {
|
if (emailInputRef?.current) {
|
||||||
emailInputRef.current!.focus();
|
emailInputRef.current?.focus();
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
|||||||
|
|
||||||
const signIn = () => {
|
const signIn = () => {
|
||||||
setIsSigningIn(true);
|
setIsSigningIn(true);
|
||||||
emailInputRef?.current!.blur();
|
emailInputRef?.current?.blur();
|
||||||
passwordInputRef?.current!.blur();
|
passwordInputRef?.current?.blur();
|
||||||
|
|
||||||
application
|
application
|
||||||
.signIn(email, password, isStrictSignin, isEphemeral, shouldMergeLocal)
|
.signIn(email, password, isStrictSignin, isEphemeral, shouldMergeLocal)
|
||||||
@@ -92,7 +92,7 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
|||||||
application.alertService.alert(err);
|
application.alertService.alert(err);
|
||||||
}
|
}
|
||||||
setPassword('');
|
setPassword('');
|
||||||
passwordInputRef?.current!.blur();
|
passwordInputRef?.current?.blur();
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsSigningIn(false);
|
setIsSigningIn(false);
|
||||||
@@ -109,12 +109,12 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!email || email.length === 0) {
|
if (!email || email.length === 0) {
|
||||||
emailInputRef?.current!.focus();
|
emailInputRef?.current?.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password || password.length === 0) {
|
if (!password || password.length === 0) {
|
||||||
passwordInputRef?.current!.focus();
|
passwordInputRef?.current?.focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,69 +134,67 @@ export const SignInPane: FunctionComponent<Props> = observer(
|
|||||||
/>
|
/>
|
||||||
<div className="sn-account-menu-headline">Sign in</div>
|
<div className="sn-account-menu-headline">Sign in</div>
|
||||||
</div>
|
</div>
|
||||||
<form onSubmit={handleSignInFormSubmit}>
|
<div className="px-3 mb-1">
|
||||||
<div className="px-3 mb-1">
|
<InputWithIcon
|
||||||
<InputWithIcon
|
className={`mb-2 ${isInvalid ? 'border-dark-red' : null}`}
|
||||||
className={`mb-2 ${isInvalid ? 'border-dark-red' : null}`}
|
icon="email"
|
||||||
icon="email"
|
inputType="email"
|
||||||
inputType="email"
|
placeholder="Email"
|
||||||
placeholder="Email"
|
value={email}
|
||||||
value={email}
|
onChange={handleEmailChange}
|
||||||
onChange={handleEmailChange}
|
onFocus={resetInvalid}
|
||||||
onFocus={resetInvalid}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
disabled={isSigningIn}
|
||||||
disabled={isSigningIn}
|
ref={emailInputRef}
|
||||||
ref={emailInputRef}
|
/>
|
||||||
/>
|
<InputWithIcon
|
||||||
<InputWithIcon
|
className={`mb-2 ${isInvalid ? 'border-dark-red' : null}`}
|
||||||
className={`mb-2 ${isInvalid ? 'border-dark-red' : null}`}
|
icon="password"
|
||||||
icon="password"
|
inputType={showPassword ? 'text' : 'password'}
|
||||||
inputType={showPassword ? 'text' : 'password'}
|
placeholder="Password"
|
||||||
placeholder="Password"
|
value={password}
|
||||||
value={password}
|
onChange={handlePasswordChange}
|
||||||
onChange={handlePasswordChange}
|
onFocus={resetInvalid}
|
||||||
onFocus={resetInvalid}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
disabled={isSigningIn}
|
||||||
disabled={isSigningIn}
|
toggle={{
|
||||||
toggle={{
|
toggleOnIcon: 'eye-off',
|
||||||
toggleOnIcon: 'eye-off',
|
toggleOffIcon: 'eye',
|
||||||
toggleOffIcon: 'eye',
|
title: 'Show password',
|
||||||
title: 'Show password',
|
toggled: showPassword,
|
||||||
toggled: showPassword,
|
onClick: setShowPassword,
|
||||||
onClick: setShowPassword,
|
}}
|
||||||
}}
|
ref={passwordInputRef}
|
||||||
ref={passwordInputRef}
|
/>
|
||||||
/>
|
{isInvalid ? (
|
||||||
{isInvalid ? (
|
<div className="color-dark-red my-2">
|
||||||
<div className="color-dark-red my-2">
|
Invalid email or password.
|
||||||
Invalid email or password.
|
</div>
|
||||||
</div>
|
) : null}
|
||||||
) : null}
|
<Button
|
||||||
<Button
|
className="btn-w-full mt-1 mb-3"
|
||||||
className="btn-w-full mt-1 mb-3"
|
label={isSigningIn ? 'Signing in...' : 'Sign in'}
|
||||||
label={isSigningIn ? 'Signing in...' : 'Sign in'}
|
type="primary"
|
||||||
type="primary"
|
onClick={handleSignInFormSubmit}
|
||||||
onClick={handleSignInFormSubmit}
|
disabled={isSigningIn}
|
||||||
disabled={isSigningIn}
|
/>
|
||||||
/>
|
<Checkbox
|
||||||
|
name="is-ephemeral"
|
||||||
|
label="Stay signed in"
|
||||||
|
checked={!isEphemeral}
|
||||||
|
disabled={isSigningIn}
|
||||||
|
onChange={handleEphemeralChange}
|
||||||
|
/>
|
||||||
|
{notesAndTagsCount > 0 ? (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name="is-ephemeral"
|
name="should-merge-local"
|
||||||
label="Stay signed in"
|
label={`Merge local data (${notesAndTagsCount} notes and tags)`}
|
||||||
checked={!isEphemeral}
|
checked={shouldMergeLocal}
|
||||||
disabled={isSigningIn}
|
disabled={isSigningIn}
|
||||||
onChange={handleEphemeralChange}
|
onChange={handleShouldMergeChange}
|
||||||
/>
|
/>
|
||||||
{notesAndTagsCount > 0 ? (
|
) : null}
|
||||||
<Checkbox
|
</div>
|
||||||
name="should-merge-local"
|
|
||||||
label={`Merge local data (${notesAndTagsCount} notes and tags)`}
|
|
||||||
checked={shouldMergeLocal}
|
|
||||||
disabled={isSigningIn}
|
|
||||||
onChange={handleShouldMergeChange}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div className="h-1px my-2 bg-border"></div>
|
<div className="h-1px my-2 bg-border"></div>
|
||||||
<AdvancedOptions
|
<AdvancedOptions
|
||||||
appState={appState}
|
appState={appState}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { WebApplication } from '@/ui_models/application';
|
|||||||
import { CollectionSort, PrefKey } from '@standardnotes/snjs';
|
import { CollectionSort, PrefKey } from '@standardnotes/snjs';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import { FunctionComponent } from 'preact';
|
import { FunctionComponent } from 'preact';
|
||||||
import { useState } from 'preact/hooks';
|
import { useRef, useState } from 'preact/hooks';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { Menu } from './menu/Menu';
|
import { Menu } from './menu/Menu';
|
||||||
import { MenuItem, MenuItemSeparator, MenuItemType } from './menu/MenuItem';
|
import { MenuItem, MenuItemSeparator, MenuItemType } from './menu/MenuItem';
|
||||||
import { toDirective } from './utils';
|
import { toDirective, useCloseOnClickOutside } from './utils';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication;
|
application: WebApplication;
|
||||||
@@ -108,8 +108,16 @@ flex flex-col py-2 bottom-0 left-2 absolute';
|
|||||||
application.setPreference(PrefKey.NotesHideProtected, !hideProtected);
|
application.setPreference(PrefKey.NotesHideProtected, !hideProtected);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useCloseOnClickOutside(menuRef as any, (open: boolean) => {
|
||||||
|
if (!open) {
|
||||||
|
setShowMenuFalse();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={menuClassName}>
|
<div ref={menuRef} className={menuClassName}>
|
||||||
<Menu a11yLabel="Sort by" closeMenu={setShowMenuFalse}>
|
<Menu a11yLabel="Sort by" closeMenu={setShowMenuFalse}>
|
||||||
<div className="px-3 my-1 text-xs font-semibold color-text uppercase">
|
<div className="px-3 my-1 text-xs font-semibold color-text uppercase">
|
||||||
Sort by
|
Sort by
|
||||||
|
|||||||
@@ -76,55 +76,67 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
|
|||||||
}, [focusModeEnabled]);
|
}, [focusModeEnabled]);
|
||||||
|
|
||||||
const reloadThemes = useCallback(() => {
|
const reloadThemes = useCallback(() => {
|
||||||
application.streamItems(ContentType.Theme, () => {
|
const themes = application.getDisplayableItems(
|
||||||
const themes = application.getDisplayableItems(
|
ContentType.Theme
|
||||||
ContentType.Theme
|
) as SNTheme[];
|
||||||
) as SNTheme[];
|
setThemes(
|
||||||
setThemes(
|
themes.sort((a, b) => {
|
||||||
themes.sort((a, b) => {
|
const aIsLayerable = a.isLayerable();
|
||||||
const aIsLayerable = a.isLayerable();
|
const bIsLayerable = b.isLayerable();
|
||||||
const bIsLayerable = b.isLayerable();
|
|
||||||
|
|
||||||
if (aIsLayerable && !bIsLayerable) {
|
if (aIsLayerable && !bIsLayerable) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (!aIsLayerable && bIsLayerable) {
|
} else if (!aIsLayerable && bIsLayerable) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
return a.package_info.name.toLowerCase() <
|
return a.package_info.name.toLowerCase() <
|
||||||
b.package_info.name.toLowerCase()
|
b.package_info.name.toLowerCase()
|
||||||
? -1
|
? -1
|
||||||
: 1;
|
: 1;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
setDefaultThemeOn(
|
setDefaultThemeOn(
|
||||||
!themes.find((theme) => theme.active && !theme.isLayerable())
|
!themes.find((theme) => theme.active && !theme.isLayerable())
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}, [application]);
|
}, [application]);
|
||||||
|
|
||||||
const reloadToggleableComponents = useCallback(() => {
|
const reloadToggleableComponents = useCallback(() => {
|
||||||
application.streamItems(ContentType.Component, () => {
|
const toggleableComponents = (
|
||||||
const toggleableComponents = (
|
application.getDisplayableItems(ContentType.Component) as SNComponent[]
|
||||||
application.getDisplayableItems(
|
).filter((component) =>
|
||||||
ContentType.Component
|
[ComponentArea.EditorStack, ComponentArea.TagsList].includes(
|
||||||
) as SNComponent[]
|
component.area
|
||||||
).filter((component) =>
|
)
|
||||||
[ComponentArea.EditorStack, ComponentArea.TagsList].includes(
|
);
|
||||||
component.area
|
setToggleableComponents(toggleableComponents);
|
||||||
)
|
|
||||||
);
|
|
||||||
setToggleableComponents(toggleableComponents);
|
|
||||||
});
|
|
||||||
}, [application]);
|
}, [application]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reloadThemes();
|
const cleanupItemStream = application.streamItems(
|
||||||
}, [reloadThemes]);
|
ContentType.Theme,
|
||||||
|
() => {
|
||||||
|
reloadThemes();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cleanupItemStream();
|
||||||
|
};
|
||||||
|
}, [application, reloadThemes]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reloadToggleableComponents();
|
const cleanupItemStream = application.streamItems(
|
||||||
}, [reloadToggleableComponents]);
|
ContentType.Component,
|
||||||
|
() => {
|
||||||
|
reloadToggleableComponents();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cleanupItemStream();
|
||||||
|
};
|
||||||
|
}, [application, reloadToggleableComponents]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (themesMenuOpen) {
|
if (themesMenuOpen) {
|
||||||
@@ -274,10 +286,9 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
|
|||||||
</Disclosure>
|
</Disclosure>
|
||||||
) : null}
|
) : null}
|
||||||
{toggleableComponents.map((component) => (
|
{toggleableComponents.map((component) => (
|
||||||
<Switch
|
<button
|
||||||
className="sn-dropdown-item focus:bg-info-backdrop focus:shadow-none"
|
className="sn-dropdown-item justify-between focus:bg-info-backdrop focus:shadow-none"
|
||||||
checked={component.active}
|
onClick={() => {
|
||||||
onChange={() => {
|
|
||||||
toggleComponent(component);
|
toggleComponent(component);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -285,7 +296,8 @@ const QuickSettingsMenu: FunctionComponent<MenuProps> = observer(
|
|||||||
<Icon type="window" className="color-neutral mr-2" />
|
<Icon type="window" className="color-neutral mr-2" />
|
||||||
{component.name}
|
{component.name}
|
||||||
</div>
|
</div>
|
||||||
</Switch>
|
<Switch checked={component.active} className="px-0" />
|
||||||
|
</button>
|
||||||
))}
|
))}
|
||||||
<FocusModeSwitch
|
<FocusModeSwitch
|
||||||
application={application}
|
application={application}
|
||||||
|
|||||||
@@ -17,112 +17,124 @@ interface IProps {
|
|||||||
appState: AppState;
|
appState: AppState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OfflineSubscription: FunctionalComponent<IProps> = observer(({ application, appState }) => {
|
export const OfflineSubscription: FunctionalComponent<IProps> = observer(
|
||||||
const [activationCode, setActivationCode] = useState('');
|
({ application, appState }) => {
|
||||||
const [isSuccessfullyActivated, setIsSuccessfullyActivated] = useState(false);
|
const [activationCode, setActivationCode] = useState('');
|
||||||
const [isSuccessfullyRemoved, setIsSuccessfullyRemoved] = useState(false);
|
const [isSuccessfullyActivated, setIsSuccessfullyActivated] =
|
||||||
const [hasUserPreviouslyStoredCode, setHasUserPreviouslyStoredCode] = useState(false);
|
useState(false);
|
||||||
|
const [isSuccessfullyRemoved, setIsSuccessfullyRemoved] = useState(false);
|
||||||
|
const [hasUserPreviouslyStoredCode, setHasUserPreviouslyStoredCode] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (application.hasOfflineRepo()) {
|
if (application.hasOfflineRepo()) {
|
||||||
setHasUserPreviouslyStoredCode(true);
|
setHasUserPreviouslyStoredCode(true);
|
||||||
|
}
|
||||||
|
}, [application]);
|
||||||
|
|
||||||
|
const shouldShowOfflineSubscription = () => {
|
||||||
|
return !application.hasAccount() || application.isThirdPartyHostUsed();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubscriptionCodeSubmit = async (
|
||||||
|
event: TargetedEvent<HTMLFormElement, Event>
|
||||||
|
) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const result = await application.setOfflineFeaturesCode(activationCode);
|
||||||
|
|
||||||
|
if (result?.error) {
|
||||||
|
await application.alertService.alert(result.error);
|
||||||
|
} else {
|
||||||
|
setIsSuccessfullyActivated(true);
|
||||||
|
setHasUserPreviouslyStoredCode(true);
|
||||||
|
setIsSuccessfullyRemoved(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveOfflineKey = async () => {
|
||||||
|
await application.deleteOfflineFeatureRepo();
|
||||||
|
|
||||||
|
setIsSuccessfullyActivated(false);
|
||||||
|
setHasUserPreviouslyStoredCode(false);
|
||||||
|
setActivationCode('');
|
||||||
|
setIsSuccessfullyRemoved(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveClick = async () => {
|
||||||
|
application.alertService
|
||||||
|
.confirm(
|
||||||
|
STRING_REMOVE_OFFLINE_KEY_CONFIRMATION,
|
||||||
|
'Remove offline key?',
|
||||||
|
'Remove Offline Key',
|
||||||
|
ButtonType.Danger,
|
||||||
|
'Cancel'
|
||||||
|
)
|
||||||
|
.then(async (shouldRemove: boolean) => {
|
||||||
|
if (shouldRemove) {
|
||||||
|
await handleRemoveOfflineKey();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: string) => {
|
||||||
|
application.alertService.alert(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!shouldShowOfflineSubscription()) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}, [application]);
|
|
||||||
|
|
||||||
const shouldShowOfflineSubscription = () => {
|
return (
|
||||||
return !application.hasAccount() || application.isCustomServerHostUsed();
|
<>
|
||||||
};
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex flex-col mt-3 w-full">
|
||||||
const handleSubscriptionCodeSubmit = async (event: TargetedEvent<HTMLFormElement, Event>) => {
|
<Subtitle>
|
||||||
event.preventDefault();
|
{!hasUserPreviouslyStoredCode && 'Activate'} Offline Subscription
|
||||||
|
</Subtitle>
|
||||||
const result = await application.setOfflineFeaturesCode(activationCode);
|
<form onSubmit={handleSubscriptionCodeSubmit}>
|
||||||
|
<div className={'mt-2'}>
|
||||||
if (result?.error) {
|
{!hasUserPreviouslyStoredCode && (
|
||||||
await application.alertService.alert(result.error);
|
<DecoratedInput
|
||||||
} else {
|
onChange={(code) => setActivationCode(code)}
|
||||||
setIsSuccessfullyActivated(true);
|
placeholder={'Offline Subscription Code'}
|
||||||
setHasUserPreviouslyStoredCode(true);
|
text={activationCode}
|
||||||
setIsSuccessfullyRemoved(false);
|
disabled={isSuccessfullyActivated}
|
||||||
}
|
className={'mb-3'}
|
||||||
};
|
/>
|
||||||
|
)}
|
||||||
const handleRemoveOfflineKey = async () => {
|
</div>
|
||||||
await application.deleteOfflineFeatureRepo();
|
{(isSuccessfullyActivated || isSuccessfullyRemoved) && (
|
||||||
|
<div className={'mt-3 mb-3 info'}>
|
||||||
setIsSuccessfullyActivated(false);
|
Your offline subscription code has been successfully{' '}
|
||||||
setHasUserPreviouslyStoredCode(false);
|
{isSuccessfullyActivated ? 'activated' : 'removed'}.
|
||||||
setActivationCode('');
|
</div>
|
||||||
setIsSuccessfullyRemoved(true);
|
)}
|
||||||
};
|
{hasUserPreviouslyStoredCode && (
|
||||||
|
<Button
|
||||||
const handleRemoveClick = async () => {
|
type="danger"
|
||||||
application.alertService.confirm(
|
label="Remove offline key"
|
||||||
STRING_REMOVE_OFFLINE_KEY_CONFIRMATION,
|
onClick={() => {
|
||||||
'Remove offline key?',
|
handleRemoveClick();
|
||||||
'Remove Offline Key',
|
}}
|
||||||
ButtonType.Danger,
|
|
||||||
'Cancel'
|
|
||||||
)
|
|
||||||
.then(async (shouldRemove: boolean) => {
|
|
||||||
if (shouldRemove) {
|
|
||||||
await handleRemoveOfflineKey();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err: string) => {
|
|
||||||
application.alertService.alert(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!shouldShowOfflineSubscription()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<div className='flex flex-col mt-3 w-full'>
|
|
||||||
<Subtitle>{!hasUserPreviouslyStoredCode && 'Activate'} Offline Subscription</Subtitle>
|
|
||||||
<form onSubmit={handleSubscriptionCodeSubmit}>
|
|
||||||
<div className={'mt-2'}>
|
|
||||||
{!hasUserPreviouslyStoredCode && (
|
|
||||||
<DecoratedInput
|
|
||||||
onChange={(code) => setActivationCode(code)}
|
|
||||||
placeholder={'Offline Subscription Code'}
|
|
||||||
text={activationCode}
|
|
||||||
disabled={isSuccessfullyActivated}
|
|
||||||
className={'mb-3'}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
|
||||||
{(isSuccessfullyActivated || isSuccessfullyRemoved) && (
|
<Button
|
||||||
<div className={'mt-3 mb-3 info'}>
|
label={'Submit'}
|
||||||
Your offline subscription code has been successfully {isSuccessfullyActivated ? 'activated' : 'removed'}.
|
type="primary"
|
||||||
</div>
|
disabled={activationCode === ''}
|
||||||
)}
|
onClick={(event) =>
|
||||||
{hasUserPreviouslyStoredCode && (
|
handleSubscriptionCodeSubmit(
|
||||||
<Button
|
event as TargetedEvent<HTMLFormElement>
|
||||||
type='danger'
|
)
|
||||||
label='Remove offline key'
|
}
|
||||||
onClick={() => {
|
/>
|
||||||
handleRemoveClick();
|
)}
|
||||||
}}
|
</form>
|
||||||
/>
|
</div>
|
||||||
)}
|
|
||||||
{!hasUserPreviouslyStoredCode && !isSuccessfullyActivated && (
|
|
||||||
<Button
|
|
||||||
label={'Submit'}
|
|
||||||
type='primary'
|
|
||||||
disabled={activationCode === ''}
|
|
||||||
onClick={(event) =>
|
|
||||||
handleSubscriptionCodeSubmit(event as TargetedEvent<HTMLFormElement>)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<HorizontalSeparator classes="mt-8 mb-5" />
|
||||||
<HorizontalSeparator classes='mt-8 mb-5' />
|
</>
|
||||||
</>
|
);
|
||||||
);
|
}
|
||||||
});
|
);
|
||||||
|
|||||||
@@ -256,6 +256,10 @@
|
|||||||
margin-right: 0.75rem;
|
margin-right: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mr-4 {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mr-12 {
|
.mr-12 {
|
||||||
margin-right: 3rem;
|
margin-right: 3rem;
|
||||||
}
|
}
|
||||||
@@ -283,6 +287,10 @@
|
|||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-w-1\/2 {
|
||||||
|
max-width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.max-w-3\/4 {
|
.max-w-3\/4 {
|
||||||
max-width: 75%;
|
max-width: 75%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "standard-notes-web",
|
"name": "standard-notes-web",
|
||||||
"version": "3.9.8",
|
"version": "3.9.10",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
"@reach/listbox": "^0.16.2",
|
"@reach/listbox": "^0.16.2",
|
||||||
"@standardnotes/features": "1.10.2",
|
"@standardnotes/features": "1.10.2",
|
||||||
"@standardnotes/sncrypto-web": "1.5.3",
|
"@standardnotes/sncrypto-web": "1.5.3",
|
||||||
"@standardnotes/snjs": "2.20.1",
|
"@standardnotes/snjs": "2.20.3",
|
||||||
"mobx": "^6.3.5",
|
"mobx": "^6.3.5",
|
||||||
"mobx-react-lite": "^3.2.2",
|
"mobx-react-lite": "^3.2.2",
|
||||||
"preact": "^10.5.15",
|
"preact": "^10.5.15",
|
||||||
|
|||||||
@@ -2614,10 +2614,10 @@
|
|||||||
buffer "^6.0.3"
|
buffer "^6.0.3"
|
||||||
libsodium-wrappers "^0.7.9"
|
libsodium-wrappers "^0.7.9"
|
||||||
|
|
||||||
"@standardnotes/snjs@2.20.1":
|
"@standardnotes/snjs@2.20.3":
|
||||||
version "2.20.1"
|
version "2.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.20.1.tgz#4813adbfd16a1c373357bd4c7ece3085abf142b4"
|
resolved "https://registry.yarnpkg.com/@standardnotes/snjs/-/snjs-2.20.3.tgz#11fe962dfb017be459e856b9fbc6311c5046a0b0"
|
||||||
integrity sha512-wJILt7YerLFaZTKoIZaCkqSuvpVWoVKqCVjP0KNHrMknnbgMHZygHqrb9sr57JL1AcNCkAULEFS/kev2GYTG3Q==
|
integrity sha512-FHog3p3SuMvTXsEl76UenbjY8PS6VL/b6MjaC9whJR5ZwsvmLwE0G16dWZ+z+izm+CsRaHMWU8R2cy7RG7HjZg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@standardnotes/auth" "^3.8.1"
|
"@standardnotes/auth" "^3.8.1"
|
||||||
"@standardnotes/common" "^1.2.1"
|
"@standardnotes/common" "^1.2.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user