chore: prettier files
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
import { FunctionalComponent } from 'preact'
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { OfflineSubscription } from '@/Components/Preferences/Panes/Account/OfflineSubscription'
|
||||
import { WebApplication } from '@/UIModels/Application'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
@@ -17,25 +14,23 @@ interface IProps {
|
||||
extensionsLatestVersions: ExtensionsLatestVersions
|
||||
}
|
||||
|
||||
export const Advanced: FunctionalComponent<IProps> = observer(
|
||||
({ application, appState, extensionsLatestVersions }) => {
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<AccordionItem title={'Advanced Settings'}>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<OfflineSubscription application={application} appState={appState} />
|
||||
<Extensions
|
||||
className={'mt-3'}
|
||||
application={application}
|
||||
extensionsLatestVersions={extensionsLatestVersions}
|
||||
/>
|
||||
</div>
|
||||
export const Advanced: FunctionalComponent<IProps> = observer(({ application, appState, extensionsLatestVersions }) => {
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<AccordionItem title={'Advanced Settings'}>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<OfflineSubscription application={application} appState={appState} />
|
||||
<Extensions
|
||||
className={'mt-3'}
|
||||
application={application}
|
||||
extensionsLatestVersions={extensionsLatestVersions}
|
||||
/>
|
||||
</div>
|
||||
</AccordionItem>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)
|
||||
},
|
||||
)
|
||||
</div>
|
||||
</AccordionItem>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { AccountMenuPane } from '@/Components/AccountMenu'
|
||||
import { Button } from '@/Components/Button/Button'
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Text,
|
||||
Title,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { WebApplication } from '@/UIModels/Application'
|
||||
import { AppState } from '@/UIModels/AppState'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
@@ -35,21 +30,12 @@ export const Authentication: FunctionComponent<{
|
||||
<AccountIllustration className="mb-3" />
|
||||
<Title>You're not signed in</Title>
|
||||
<Text className="text-center mb-3">
|
||||
Sign in to sync your notes and preferences across all your devices and enable end-to-end
|
||||
encryption.
|
||||
Sign in to sync your notes and preferences across all your devices and enable end-to-end encryption.
|
||||
</Text>
|
||||
<Button
|
||||
variant="primary"
|
||||
label="Create free account"
|
||||
onClick={clickRegister}
|
||||
className="mb-3"
|
||||
/>
|
||||
<Button variant="primary" label="Create free account" onClick={clickRegister} className="mb-3" />
|
||||
<div className="text-input">
|
||||
Already have an account?{' '}
|
||||
<button
|
||||
className="border-0 p-0 bg-default color-info underline cursor-pointer"
|
||||
onClick={clickSignIn}
|
||||
>
|
||||
<button className="border-0 p-0 bg-default color-info underline cursor-pointer" onClick={clickSignIn}>
|
||||
Sign in
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -10,10 +10,7 @@ const labelClassName = 'block mb-1'
|
||||
|
||||
const inputClassName = 'sk-input contrast'
|
||||
|
||||
export const ChangeEmailForm: FunctionalComponent<Props> = ({
|
||||
setNewEmail,
|
||||
setCurrentPassword,
|
||||
}) => {
|
||||
export const ChangeEmailForm: FunctionalComponent<Props> = ({ setNewEmail, setCurrentPassword }) => {
|
||||
return (
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="mt-2 mb-3">
|
||||
|
||||
@@ -5,8 +5,8 @@ export const ChangeEmailSuccess: FunctionalComponent = () => {
|
||||
<div>
|
||||
<div className={'sk-label sk-bold info mt-2'}>Your email has been successfully changed.</div>
|
||||
<p className={'sk-p'}>
|
||||
Please ensure you are running the latest version of Standard Notes on all platforms to
|
||||
ensure maximum compatibility.
|
||||
Please ensure you are running the latest version of Standard Notes on all platforms to ensure maximum
|
||||
compatibility.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -63,9 +63,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({ onCloseDialog, applica
|
||||
const validateNewEmail = async () => {
|
||||
if (!isEmailValid(newEmail)) {
|
||||
applicationAlertService
|
||||
.alert(
|
||||
'The email you entered has an invalid format. Please review your input and try again.',
|
||||
)
|
||||
.alert('The email you entered has an invalid format. Please review your input and try again.')
|
||||
.catch(console.error)
|
||||
|
||||
return false
|
||||
@@ -95,9 +93,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({ onCloseDialog, applica
|
||||
|
||||
const dismiss = () => {
|
||||
if (lockContinue) {
|
||||
applicationAlertService
|
||||
.alert('Cannot close window until pending tasks are complete.')
|
||||
.catch(console.error)
|
||||
applicationAlertService.alert('Cannot close window until pending tasks are complete.').catch(console.error)
|
||||
} else {
|
||||
onCloseDialog()
|
||||
}
|
||||
@@ -139,9 +135,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({ onCloseDialog, applica
|
||||
|
||||
const handleDialogClose = () => {
|
||||
if (lockContinue) {
|
||||
applicationAlertService
|
||||
.alert('Cannot close window until pending tasks are complete.')
|
||||
.catch(console.error)
|
||||
applicationAlertService.alert('Cannot close window until pending tasks are complete.').catch(console.error)
|
||||
} else {
|
||||
onCloseDialog()
|
||||
}
|
||||
@@ -158,12 +152,7 @@ export const ChangeEmail: FunctionalComponent<Props> = ({ onCloseDialog, applica
|
||||
{currentStep === Steps.FinishStep && <ChangeEmailSuccess />}
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons className="px-4.5">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="primary"
|
||||
label={submitButtonTitle}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
<Button className="min-w-20" variant="primary" label={submitButtonTitle} onClick={handleSubmit} />
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
</div>
|
||||
|
||||
@@ -30,10 +30,7 @@ export const Credentials: FunctionComponent<Props> = observer(({ application }:
|
||||
const passwordCreatedOn = dateToLocalizedString(passwordCreatedAtTimestamp)
|
||||
|
||||
const presentPasswordWizard = useCallback(() => {
|
||||
render(
|
||||
<PasswordWizard application={application} />,
|
||||
document.body.appendChild(document.createElement('div')),
|
||||
)
|
||||
render(<PasswordWizard application={application} />, document.body.appendChild(document.createElement('div')))
|
||||
}, [application])
|
||||
|
||||
return (
|
||||
@@ -57,17 +54,9 @@ export const Credentials: FunctionComponent<Props> = observer(({ application }:
|
||||
<Text>
|
||||
Current password was set on <span className="font-bold">{passwordCreatedOn}</span>
|
||||
</Text>
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
variant="normal"
|
||||
label="Change password"
|
||||
onClick={presentPasswordWizard}
|
||||
/>
|
||||
<Button className="min-w-20 mt-3" variant="normal" label="Change password" onClick={presentPasswordWizard} />
|
||||
{isChangeEmailDialogOpen && (
|
||||
<ChangeEmail
|
||||
onCloseDialog={() => setIsChangeEmailDialogOpen(false)}
|
||||
application={application}
|
||||
/>
|
||||
<ChangeEmail onCloseDialog={() => setIsChangeEmailDialogOpen(false)} application={application} />
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
|
||||
@@ -28,9 +28,7 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(({ appl
|
||||
}, [application])
|
||||
|
||||
const shouldShowOfflineSubscription = () => {
|
||||
return (
|
||||
!application.hasAccount() || application.isThirdPartyHostUsed() || hasUserPreviouslyStoredCode
|
||||
)
|
||||
return !application.hasAccount() || application.isThirdPartyHostUsed() || hasUserPreviouslyStoredCode
|
||||
}
|
||||
|
||||
const handleSubscriptionCodeSubmit = async (event: Event) => {
|
||||
@@ -98,8 +96,8 @@ export const OfflineSubscription: FunctionalComponent<IProps> = observer(({ appl
|
||||
</div>
|
||||
{(isSuccessfullyActivated || isSuccessfullyRemoved) && (
|
||||
<div className={'mt-3 mb-3 info'}>
|
||||
Your offline subscription code has been successfully{' '}
|
||||
{isSuccessfullyActivated ? 'activated' : 'removed'}.
|
||||
Your offline subscription code has been successfully {isSuccessfullyActivated ? 'activated' : 'removed'}
|
||||
.
|
||||
</div>
|
||||
)}
|
||||
{hasUserPreviouslyStoredCode && (
|
||||
|
||||
@@ -33,11 +33,7 @@ const SignOutView: FunctionComponent<{
|
||||
appState.accountMenu.setOtherSessionsSignOut(true)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="normal"
|
||||
label="Manage sessions"
|
||||
onClick={() => appState.openSessionsModal()}
|
||||
/>
|
||||
<Button variant="normal" label="Manage sessions" onClick={() => appState.openSessionsModal()} />
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
<PreferencesSegment>
|
||||
|
||||
@@ -12,8 +12,7 @@ export const NoSubscription: FunctionalComponent<{
|
||||
const [purchaseFlowError, setPurchaseFlowError] = useState<string | undefined>(undefined)
|
||||
|
||||
const onPurchaseClick = async () => {
|
||||
const errorMessage =
|
||||
'There was an error when attempting to redirect you to the subscription page.'
|
||||
const errorMessage = 'There was an error when attempting to redirect you to the subscription page.'
|
||||
setIsLoadingPurchaseFlow(true)
|
||||
try {
|
||||
if (!(await loadPurchaseFlowUrl(application))) {
|
||||
@@ -32,18 +31,9 @@ export const NoSubscription: FunctionalComponent<{
|
||||
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
|
||||
{purchaseFlowError && <Text className="color-danger">{purchaseFlowError}</Text>}
|
||||
<div className="flex">
|
||||
<LinkButton
|
||||
className="min-w-20 mt-3 mr-3"
|
||||
label="Learn More"
|
||||
link={window.plansUrl as string}
|
||||
/>
|
||||
<LinkButton className="min-w-20 mt-3 mr-3" label="Learn More" link={window.plansUrl as string} />
|
||||
{application.hasAccount() && (
|
||||
<Button
|
||||
className="min-w-20 mt-3"
|
||||
variant="primary"
|
||||
label="Subscribe"
|
||||
onClick={onPurchaseClick}
|
||||
/>
|
||||
<Button className="min-w-20 mt-3" variant="primary" label="Subscribe" onClick={onPurchaseClick} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Title,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment, Title } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { WebApplication } from '@/UIModels/Application'
|
||||
import { SubscriptionInformation } from './SubscriptionInformation'
|
||||
import { NoSubscription } from './NoSubscription'
|
||||
@@ -15,31 +11,26 @@ type Props = {
|
||||
appState: AppState
|
||||
}
|
||||
|
||||
export const Subscription: FunctionComponent<Props> = observer(
|
||||
({ application, appState }: Props) => {
|
||||
const subscriptionState = appState.subscription
|
||||
const { userSubscription } = subscriptionState
|
||||
export const Subscription: FunctionComponent<Props> = observer(({ application, appState }: Props) => {
|
||||
const subscriptionState = appState.subscription
|
||||
const { userSubscription } = subscriptionState
|
||||
|
||||
const now = new Date().getTime()
|
||||
const now = new Date().getTime()
|
||||
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<Title>Subscription</Title>
|
||||
{userSubscription && userSubscription.endsAt > now ? (
|
||||
<SubscriptionInformation
|
||||
subscriptionState={subscriptionState}
|
||||
application={application}
|
||||
/>
|
||||
) : (
|
||||
<NoSubscription application={application} />
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<Title>Subscription</Title>
|
||||
{userSubscription && userSubscription.endsAt > now ? (
|
||||
<SubscriptionInformation subscriptionState={subscriptionState} application={application} />
|
||||
) : (
|
||||
<NoSubscription application={application} />
|
||||
)}
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)
|
||||
},
|
||||
)
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -10,49 +10,16 @@ type Props = {
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
const StatusText = observer(
|
||||
({ subscriptionState }: { subscriptionState: Props['subscriptionState'] }) => {
|
||||
const {
|
||||
userSubscriptionName,
|
||||
userSubscriptionExpirationDate,
|
||||
isUserSubscriptionExpired,
|
||||
isUserSubscriptionCanceled,
|
||||
} = subscriptionState
|
||||
const expirationDateString = userSubscriptionExpirationDate?.toLocaleString()
|
||||
|
||||
if (isUserSubscriptionCanceled) {
|
||||
return (
|
||||
<Text className="mt-1">
|
||||
Your{' '}
|
||||
<span className="font-bold">
|
||||
Standard Notes{userSubscriptionName ? ' ' : ''}
|
||||
{userSubscriptionName}
|
||||
</span>{' '}
|
||||
subscription has been canceled{' '}
|
||||
{isUserSubscriptionExpired ? (
|
||||
<span className="font-bold">and expired on {expirationDateString}</span>
|
||||
) : (
|
||||
<span className="font-bold">but will remain valid until {expirationDateString}</span>
|
||||
)}
|
||||
. You may resubscribe below if you wish.
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
if (isUserSubscriptionExpired) {
|
||||
return (
|
||||
<Text className="mt-1">
|
||||
Your{' '}
|
||||
<span className="font-bold">
|
||||
Standard Notes{userSubscriptionName ? ' ' : ''}
|
||||
{userSubscriptionName}
|
||||
</span>{' '}
|
||||
subscription <span className="font-bold">expired on {expirationDateString}</span>. You may
|
||||
resubscribe below if you wish.
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
const StatusText = observer(({ subscriptionState }: { subscriptionState: Props['subscriptionState'] }) => {
|
||||
const {
|
||||
userSubscriptionName,
|
||||
userSubscriptionExpirationDate,
|
||||
isUserSubscriptionExpired,
|
||||
isUserSubscriptionCanceled,
|
||||
} = subscriptionState
|
||||
const expirationDateString = userSubscriptionExpirationDate?.toLocaleString()
|
||||
|
||||
if (isUserSubscriptionCanceled) {
|
||||
return (
|
||||
<Text className="mt-1">
|
||||
Your{' '}
|
||||
@@ -60,11 +27,42 @@ const StatusText = observer(
|
||||
Standard Notes{userSubscriptionName ? ' ' : ''}
|
||||
{userSubscriptionName}
|
||||
</span>{' '}
|
||||
subscription will be <span className="font-bold">renewed on {expirationDateString}</span>.
|
||||
subscription has been canceled{' '}
|
||||
{isUserSubscriptionExpired ? (
|
||||
<span className="font-bold">and expired on {expirationDateString}</span>
|
||||
) : (
|
||||
<span className="font-bold">but will remain valid until {expirationDateString}</span>
|
||||
)}
|
||||
. You may resubscribe below if you wish.
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (isUserSubscriptionExpired) {
|
||||
return (
|
||||
<Text className="mt-1">
|
||||
Your{' '}
|
||||
<span className="font-bold">
|
||||
Standard Notes{userSubscriptionName ? ' ' : ''}
|
||||
{userSubscriptionName}
|
||||
</span>{' '}
|
||||
subscription <span className="font-bold">expired on {expirationDateString}</span>. You may resubscribe below if
|
||||
you wish.
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Text className="mt-1">
|
||||
Your{' '}
|
||||
<span className="font-bold">
|
||||
Standard Notes{userSubscriptionName ? ' ' : ''}
|
||||
{userSubscriptionName}
|
||||
</span>{' '}
|
||||
subscription will be <span className="font-bold">renewed on {expirationDateString}</span>.
|
||||
</Text>
|
||||
)
|
||||
})
|
||||
|
||||
export const SubscriptionInformation = observer(({ subscriptionState, application }: Props) => {
|
||||
const manageSubscription = async () => {
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Text,
|
||||
Title,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Button } from '@/Components/Button/Button'
|
||||
import { SyncQueueStrategy, dateToLocalizedString } from '@standardnotes/snjs'
|
||||
import { STRING_GENERIC_SYNC_ERROR } from '@/Strings'
|
||||
@@ -22,9 +17,7 @@ export const formatLastSyncDate = (lastUpdatedDate: Date) => {
|
||||
|
||||
export const Sync: FunctionComponent<Props> = observer(({ application }: Props) => {
|
||||
const [isSyncingInProgress, setIsSyncingInProgress] = useState(false)
|
||||
const [lastSyncDate, setLastSyncDate] = useState(
|
||||
formatLastSyncDate(application.sync.getLastSyncDate() as Date),
|
||||
)
|
||||
const [lastSyncDate, setLastSyncDate] = useState(formatLastSyncDate(application.sync.getLastSyncDate() as Date))
|
||||
|
||||
const doSynchronization = async () => {
|
||||
setIsSyncingInProgress(true)
|
||||
|
||||
@@ -3,14 +3,7 @@ import { usePremiumModal } from '@/Hooks/usePremiumModal'
|
||||
import { HorizontalSeparator } from '@/Components/Shared/HorizontalSeparator'
|
||||
import { Switch } from '@/Components/Switch'
|
||||
import { WebApplication } from '@/UIModels/Application'
|
||||
import {
|
||||
ContentType,
|
||||
FeatureIdentifier,
|
||||
FeatureStatus,
|
||||
PrefKey,
|
||||
GetFeatures,
|
||||
SNTheme,
|
||||
} from '@standardnotes/snjs'
|
||||
import { ContentType, FeatureIdentifier, FeatureStatus, PrefKey, GetFeatures, SNTheme } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { FunctionComponent } from 'preact'
|
||||
import { useEffect, useState } from 'preact/hooks'
|
||||
@@ -31,8 +24,7 @@ type Props = {
|
||||
export const Appearance: FunctionComponent<Props> = observer(({ application }) => {
|
||||
const premiumModal = usePremiumModal()
|
||||
const isEntitledToMidnightTheme =
|
||||
application.features.getFeatureStatus(FeatureIdentifier.MidnightTheme) ===
|
||||
FeatureStatus.Entitled
|
||||
application.features.getFeatureStatus(FeatureIdentifier.MidnightTheme) === FeatureStatus.Entitled
|
||||
|
||||
const [themeItems, setThemeItems] = useState<DropdownItem[]>([])
|
||||
const [autoLightTheme, setAutoLightTheme] = useState<string>(
|
||||
@@ -100,9 +92,7 @@ export const Appearance: FunctionComponent<Props> = observer(({ application }) =
|
||||
if (item.icon === 'premium-feature') {
|
||||
premiumModal.activate(`${item.label} theme`)
|
||||
} else {
|
||||
application
|
||||
.setPreference(PrefKey.AutoLightThemeIdentifier, value as FeatureIdentifier)
|
||||
.catch(console.error)
|
||||
application.setPreference(PrefKey.AutoLightThemeIdentifier, value as FeatureIdentifier).catch(console.error)
|
||||
setAutoLightTheme(value)
|
||||
}
|
||||
}
|
||||
@@ -111,9 +101,7 @@ export const Appearance: FunctionComponent<Props> = observer(({ application }) =
|
||||
if (item.icon === 'premium-feature') {
|
||||
premiumModal.activate(`${item.label} theme`)
|
||||
} else {
|
||||
application
|
||||
.setPreference(PrefKey.AutoDarkThemeIdentifier, value as FeatureIdentifier)
|
||||
.catch(console.error)
|
||||
application.setPreference(PrefKey.AutoDarkThemeIdentifier, value as FeatureIdentifier).catch(console.error)
|
||||
setAutoDarkTheme(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,18 +66,13 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
const performBackupNow = async () => {
|
||||
// A backup is performed anytime the setting is updated with the integration token, so just update it here
|
||||
try {
|
||||
await application.settings.updateSetting(
|
||||
backupFrequencySettingName,
|
||||
backupFrequency as string,
|
||||
)
|
||||
await application.settings.updateSetting(backupFrequencySettingName, backupFrequency as string)
|
||||
void application.alertService.alert(
|
||||
'A backup has been triggered for this provider. Please allow a couple minutes for your backup to be processed.',
|
||||
)
|
||||
} catch (err) {
|
||||
application.alertService
|
||||
.alert(
|
||||
'There was an error while trying to trigger a backup for this provider. Please try again.',
|
||||
)
|
||||
.alert('There was an error while trying to trigger a backup for this provider. Please try again.')
|
||||
.catch(console.error)
|
||||
}
|
||||
}
|
||||
@@ -184,8 +179,8 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
{authBegan && (
|
||||
<div>
|
||||
<p className="sk-panel-row">
|
||||
Complete authentication from the newly opened window. Upon completion, a confirmation
|
||||
code will be displayed. Enter this code below:
|
||||
Complete authentication from the newly opened window. Upon completion, a confirmation code will be
|
||||
displayed. Enter this code below:
|
||||
</p>
|
||||
<div className={'mt-1'}>
|
||||
<input
|
||||
|
||||
@@ -22,11 +22,7 @@ import { Switch } from '@/Components/Switch'
|
||||
import { convertStringifiedBooleanToBoolean } from '@/Utils'
|
||||
import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/Strings'
|
||||
|
||||
const providerData = [
|
||||
{ name: CloudProvider.Dropbox },
|
||||
{ name: CloudProvider.Google },
|
||||
{ name: CloudProvider.OneDrive },
|
||||
]
|
||||
const providerData = [{ name: CloudProvider.Dropbox }, { name: CloudProvider.Google }, { name: CloudProvider.OneDrive }]
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -62,20 +58,12 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
||||
}, [application])
|
||||
|
||||
useEffect(() => {
|
||||
const dailyDropboxBackupStatus = application.features.getFeatureStatus(
|
||||
FeatureIdentifier.DailyDropboxBackup,
|
||||
const dailyDropboxBackupStatus = application.features.getFeatureStatus(FeatureIdentifier.DailyDropboxBackup)
|
||||
const dailyGdriveBackupStatus = application.features.getFeatureStatus(FeatureIdentifier.DailyGDriveBackup)
|
||||
const dailyOneDriveBackupStatus = application.features.getFeatureStatus(FeatureIdentifier.DailyOneDriveBackup)
|
||||
const isCloudBackupsAllowed = [dailyDropboxBackupStatus, dailyGdriveBackupStatus, dailyOneDriveBackupStatus].every(
|
||||
(status) => status === FeatureStatus.Entitled,
|
||||
)
|
||||
const dailyGdriveBackupStatus = application.features.getFeatureStatus(
|
||||
FeatureIdentifier.DailyGDriveBackup,
|
||||
)
|
||||
const dailyOneDriveBackupStatus = application.features.getFeatureStatus(
|
||||
FeatureIdentifier.DailyOneDriveBackup,
|
||||
)
|
||||
const isCloudBackupsAllowed = [
|
||||
dailyDropboxBackupStatus,
|
||||
dailyGdriveBackupStatus,
|
||||
dailyOneDriveBackupStatus,
|
||||
].every((status) => status === FeatureStatus.Entitled)
|
||||
|
||||
setIsEntitledToCloudBackups(isCloudBackupsAllowed)
|
||||
loadIsFailedCloudBackupEmailMutedSetting().catch(console.error)
|
||||
@@ -114,9 +102,8 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
||||
{!isEntitledToCloudBackups && (
|
||||
<>
|
||||
<Text>
|
||||
A <span className={'font-bold'}>Plus</span> or{' '}
|
||||
<span className={'font-bold'}>Pro</span> subscription plan is required to enable Cloud
|
||||
Backups.{' '}
|
||||
A <span className={'font-bold'}>Plus</span> or <span className={'font-bold'}>Pro</span> subscription plan
|
||||
is required to enable Cloud Backups.{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/features">
|
||||
Learn more
|
||||
</a>
|
||||
@@ -127,8 +114,8 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
||||
)}
|
||||
<div>
|
||||
<Text className={additionalClass}>
|
||||
Configure the integrations below to enable automatic daily backups of your encrypted
|
||||
data set to your third-party cloud provider.
|
||||
Configure the integrations below to enable automatic daily backups of your encrypted data set to your
|
||||
third-party cloud provider.
|
||||
</Text>
|
||||
<div>
|
||||
<HorizontalSeparator classes={`mt-3 mb-3 ${additionalClass}`} />
|
||||
|
||||
@@ -160,8 +160,7 @@ export const DataBackups = observer(({ application, appState }: Props) => {
|
||||
|
||||
{!isDesktopApplication() && (
|
||||
<Text className="mb-3">
|
||||
Backups are automatically created on desktop and can be managed via the "Backups"
|
||||
top-level menu.
|
||||
Backups are automatically created on desktop and can be managed via the "Backups" top-level menu.
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -171,43 +170,25 @@ export const DataBackups = observer(({ application, appState }: Props) => {
|
||||
<form className="sk-panel-form sk-panel-row">
|
||||
<div className="sk-input-group">
|
||||
<label className="sk-horizontal-group tight">
|
||||
<input
|
||||
type="radio"
|
||||
onChange={() => setIsBackupEncrypted(true)}
|
||||
checked={isBackupEncrypted}
|
||||
/>
|
||||
<input type="radio" onChange={() => setIsBackupEncrypted(true)} checked={isBackupEncrypted} />
|
||||
<Subtitle>Encrypted</Subtitle>
|
||||
</label>
|
||||
<label className="sk-horizontal-group tight">
|
||||
<input
|
||||
type="radio"
|
||||
onChange={() => setIsBackupEncrypted(false)}
|
||||
checked={!isBackupEncrypted}
|
||||
/>
|
||||
<input type="radio" onChange={() => setIsBackupEncrypted(false)} checked={!isBackupEncrypted} />
|
||||
<Subtitle>Decrypted</Subtitle>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant="normal"
|
||||
onClick={downloadDataArchive}
|
||||
label="Download backup"
|
||||
className="mt-2"
|
||||
/>
|
||||
<Button variant="normal" onClick={downloadDataArchive} label="Download backup" className="mt-2" />
|
||||
</PreferencesSegment>
|
||||
<PreferencesSegment>
|
||||
<Subtitle>Import a previously saved backup file</Subtitle>
|
||||
|
||||
<div class="flex flex-row items-center mt-3">
|
||||
<Button variant="normal" label="Import backup" onClick={handleImportFile} />
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
onChange={importFileSelected}
|
||||
className="hidden"
|
||||
/>
|
||||
<input type="file" ref={fileInputRef} onChange={importFileSelected} className="hidden" />
|
||||
{isImportDataLoading && <div className="sk-spinner normal info ml-4" />}
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
|
||||
@@ -27,9 +27,7 @@ type Props = {
|
||||
|
||||
export const EmailBackups = observer(({ application }: Props) => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [emailFrequency, setEmailFrequency] = useState<EmailBackupFrequency>(
|
||||
EmailBackupFrequency.Disabled,
|
||||
)
|
||||
const [emailFrequency, setEmailFrequency] = useState<EmailBackupFrequency>(EmailBackupFrequency.Disabled)
|
||||
const [emailFrequencyOptions, setEmailFrequencyOptions] = useState<DropdownItem[]>([])
|
||||
const [isFailedBackupEmailMuted, setIsFailedBackupEmailMuted] = useState(true)
|
||||
const [isEntitledToEmailBackups, setIsEntitledToEmailBackups] = useState(false)
|
||||
@@ -64,9 +62,7 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
}, [application])
|
||||
|
||||
useEffect(() => {
|
||||
const emailBackupsFeatureStatus = application.features.getFeatureStatus(
|
||||
FeatureIdentifier.DailyEmailBackup,
|
||||
)
|
||||
const emailBackupsFeatureStatus = application.features.getFeatureStatus(FeatureIdentifier.DailyEmailBackup)
|
||||
setIsEntitledToEmailBackups(emailBackupsFeatureStatus === FeatureStatus.Entitled)
|
||||
|
||||
const frequencyOptions = []
|
||||
@@ -109,10 +105,7 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
const previousValue = isFailedBackupEmailMuted
|
||||
setIsFailedBackupEmailMuted(!isFailedBackupEmailMuted)
|
||||
|
||||
const updateResult = await updateSetting(
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
`${!isFailedBackupEmailMuted}`,
|
||||
)
|
||||
const updateResult = await updateSetting(SettingName.MuteFailedBackupsEmails, `${!isFailedBackupEmailMuted}`)
|
||||
if (!updateResult) {
|
||||
setIsFailedBackupEmailMuted(previousValue)
|
||||
}
|
||||
@@ -132,9 +125,8 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
{!isEntitledToEmailBackups && (
|
||||
<>
|
||||
<Text>
|
||||
A <span className={'font-bold'}>Plus</span> or{' '}
|
||||
<span className={'font-bold'}>Pro</span> subscription plan is required to enable Email
|
||||
Backups.{' '}
|
||||
A <span className={'font-bold'}>Plus</span> or <span className={'font-bold'}>Pro</span> subscription plan
|
||||
is required to enable Email Backups.{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/features">
|
||||
Learn more
|
||||
</a>
|
||||
@@ -146,8 +138,7 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
<div className={isEntitledToEmailBackups ? '' : 'faded cursor-default pointer-events-none'}>
|
||||
{!isDesktopApplication() && (
|
||||
<Text className="mb-3">
|
||||
Daily encrypted email backups of your entire data set delivered directly to your
|
||||
inbox.
|
||||
Daily encrypted email backups of your entire data set delivered directly to your inbox.
|
||||
</Text>
|
||||
)}
|
||||
<Subtitle>Email frequency</Subtitle>
|
||||
|
||||
@@ -17,10 +17,10 @@ export const CloudLink: FunctionComponent = () => (
|
||||
<div className="h-2 w-full" />
|
||||
<Subtitle>Who can read my private notes?</Subtitle>
|
||||
<Text>
|
||||
Quite simply: no one but you. Not us, not your ISP, not a hacker, and not a government
|
||||
agency. As long as you keep your password safe, and your password is reasonably strong,
|
||||
then you are the only person in the world with the ability to decrypt your notes. For more
|
||||
on how we handle your privacy and security, check out our easy to read{' '}
|
||||
Quite simply: no one but you. Not us, not your ISP, not a hacker, and not a government agency. As long as you
|
||||
keep your password safe, and your password is reasonably strong, then you are the only person in the world
|
||||
with the ability to decrypt your notes. For more on how we handle your privacy and security, check out our
|
||||
easy to read{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/privacy">
|
||||
Privacy Manifesto.
|
||||
</a>
|
||||
@@ -29,21 +29,17 @@ export const CloudLink: FunctionComponent = () => (
|
||||
<PreferencesSegment>
|
||||
<Subtitle>Can I collaborate with others on a note?</Subtitle>
|
||||
<Text>
|
||||
Because of our encrypted architecture, Standard Notes does not currently provide a
|
||||
real-time collaboration solution. Multiple users can share the same account however, but
|
||||
editing at the same time may result in sync conflicts, which may result in the duplication
|
||||
of notes.
|
||||
Because of our encrypted architecture, Standard Notes does not currently provide a real-time collaboration
|
||||
solution. Multiple users can share the same account however, but editing at the same time may result in sync
|
||||
conflicts, which may result in the duplication of notes.
|
||||
</Text>
|
||||
</PreferencesSegment>
|
||||
<PreferencesSegment>
|
||||
<Subtitle>Can I use Standard Notes totally offline?</Subtitle>
|
||||
<Text>
|
||||
Standard Notes can be used totally offline without an account, and without an internet
|
||||
connection. You can find{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://standardnotes.com/help/59/can-i-use-standard-notes-totally-offline"
|
||||
>
|
||||
Standard Notes can be used totally offline without an account, and without an internet connection. You can
|
||||
find{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/help/59/can-i-use-standard-notes-totally-offline">
|
||||
more details here.
|
||||
</a>
|
||||
</Text>
|
||||
@@ -57,38 +53,25 @@ export const CloudLink: FunctionComponent = () => (
|
||||
<PreferencesSegment>
|
||||
<Title>Community forum</Title>
|
||||
<Text>
|
||||
If you have an issue, found a bug or want to suggest a feature, you can browse or post to
|
||||
the forum. It’s recommended for non-account related issues. Please read our{' '}
|
||||
If you have an issue, found a bug or want to suggest a feature, you can browse or post to the forum. It’s
|
||||
recommended for non-account related issues. Please read our{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/longevity/">
|
||||
Longevity statement
|
||||
</a>{' '}
|
||||
before advocating for a feature request.
|
||||
</Text>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
label="Go to the forum"
|
||||
link="https://forum.standardnotes.org/"
|
||||
/>
|
||||
<LinkButton className="mt-3" label="Go to the forum" link="https://forum.standardnotes.org/" />
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Community groups</Title>
|
||||
<Text>
|
||||
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your
|
||||
feedback with us? Join the Standard Notes community groups for discussions on security,
|
||||
themes, editors and more.
|
||||
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your feedback with us? Join
|
||||
the Standard Notes community groups for discussions on security, themes, editors and more.
|
||||
</Text>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
link="https://standardnotes.com/slack"
|
||||
label="Join our Slack"
|
||||
/>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
link="https://standardnotes.com/discord"
|
||||
label="Join our Discord"
|
||||
/>
|
||||
<LinkButton className="mt-3" link="https://standardnotes.com/slack" label="Join our Slack" />
|
||||
<LinkButton className="mt-3" link="https://standardnotes.com/discord" label="Join our Discord" />
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
<PreferencesGroup>
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { DisplayStringForContentType, SNComponent } from '@standardnotes/snjs'
|
||||
import { Button } from '@/Components/Button/Button'
|
||||
import { FunctionComponent } from 'preact'
|
||||
import {
|
||||
Title,
|
||||
Text,
|
||||
Subtitle,
|
||||
PreferencesSegment,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Title, Text, Subtitle, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
|
||||
|
||||
export const ConfirmCustomExtension: FunctionComponent<{
|
||||
component: SNComponent
|
||||
@@ -59,21 +54,11 @@ export const ConfirmCustomExtension: FunctionComponent<{
|
||||
<div className="min-h-3" />
|
||||
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Cancel"
|
||||
onClick={() => callback(false)}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Cancel" onClick={() => callback(false)} />
|
||||
|
||||
<div className="min-w-3" />
|
||||
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Install"
|
||||
onClick={() => callback(true)}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Install" onClick={() => callback(true)} />
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { FunctionComponent } from 'preact'
|
||||
import { SNComponent } from '@standardnotes/snjs'
|
||||
import {
|
||||
PreferencesSegment,
|
||||
SubtitleLight,
|
||||
Title,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesSegment, SubtitleLight, Title } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Switch } from '@/Components/Switch'
|
||||
import { WebApplication } from '@/UIModels/Application'
|
||||
import { useState } from 'preact/hooks'
|
||||
@@ -30,12 +26,7 @@ export interface ExtensionItemProps {
|
||||
toggleActivate?: (extension: SNComponent) => void
|
||||
}
|
||||
|
||||
export const ExtensionItem: FunctionComponent<ExtensionItemProps> = ({
|
||||
application,
|
||||
extension,
|
||||
first,
|
||||
uninstall,
|
||||
}) => {
|
||||
export const ExtensionItem: FunctionComponent<ExtensionItemProps> = ({ application, extension, first, uninstall }) => {
|
||||
const [offlineOnly, setOfflineOnly] = useState(extension.offlineOnly ?? false)
|
||||
const [extensionName, setExtensionName] = useState(extension.name)
|
||||
|
||||
@@ -95,12 +86,7 @@ export const ExtensionItem: FunctionComponent<ExtensionItemProps> = ({
|
||||
<>
|
||||
<div className="min-h-2" />
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Uninstall"
|
||||
onClick={() => uninstall(extension)}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Uninstall" onClick={() => uninstall(extension)} />
|
||||
</div>
|
||||
</>
|
||||
</PreferencesSegment>
|
||||
|
||||
@@ -29,10 +29,7 @@ export class ExtensionsLatestVersions {
|
||||
}
|
||||
}
|
||||
|
||||
function collectFeatures(
|
||||
features: FeatureDescription[] | undefined,
|
||||
versionMap: Map<string, string>,
|
||||
) {
|
||||
function collectFeatures(features: FeatureDescription[] | undefined, versionMap: Map<string, string>) {
|
||||
if (features == undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,11 +11,7 @@ import { ExtensionItem } from './ExtensionItem'
|
||||
import { ConfirmCustomExtension } from './ConfirmCustomExtension'
|
||||
|
||||
const loadExtensions = (application: WebApplication) =>
|
||||
application.items.getItems([
|
||||
ContentType.ActionsExtension,
|
||||
ContentType.Component,
|
||||
ContentType.Theme,
|
||||
]) as SNComponent[]
|
||||
application.items.getItems([ContentType.ActionsExtension, ContentType.Component, ContentType.Theme]) as SNComponent[]
|
||||
|
||||
export const Extensions: FunctionComponent<{
|
||||
application: WebApplication
|
||||
@@ -23,9 +19,7 @@ export const Extensions: FunctionComponent<{
|
||||
className?: string
|
||||
}> = observer(({ application, extensionsLatestVersions, className = '' }) => {
|
||||
const [customUrl, setCustomUrl] = useState('')
|
||||
const [confirmableExtension, setConfirmableExtension] = useState<SNComponent | undefined>(
|
||||
undefined,
|
||||
)
|
||||
const [confirmableExtension, setConfirmableExtension] = useState<SNComponent | undefined>(undefined)
|
||||
const [extensions, setExtensions] = useState(loadExtensions(application))
|
||||
|
||||
const confirmableEnd = useRef<HTMLDivElement>(null)
|
||||
@@ -122,10 +116,7 @@ export const Extensions: FunctionComponent<{
|
||||
)}
|
||||
{confirmableExtension && (
|
||||
<PreferencesSegment>
|
||||
<ConfirmCustomExtension
|
||||
component={confirmableExtension}
|
||||
callback={handleConfirmExtensionSubmit}
|
||||
/>
|
||||
<ConfirmCustomExtension component={confirmableExtension} callback={handleConfirmExtensionSubmit} />
|
||||
<div ref={confirmableEnd} />
|
||||
</PreferencesSegment>
|
||||
)}
|
||||
|
||||
@@ -29,19 +29,16 @@ export const LabsPane: FunctionComponent<Props> = ({ application }) => {
|
||||
const [experimentalFeatures, setExperimentalFeatures] = useState<ExperimentalFeatureItem[]>([])
|
||||
|
||||
const reloadExperimentalFeatures = useCallback(() => {
|
||||
const experimentalFeatures = application.features
|
||||
.getExperimentalFeatures()
|
||||
.map((featureIdentifier) => {
|
||||
const feature = FindNativeFeature(featureIdentifier)
|
||||
return {
|
||||
identifier: featureIdentifier,
|
||||
name: feature?.name ?? featureIdentifier,
|
||||
description: feature?.description ?? '',
|
||||
isEnabled: application.features.isExperimentalFeatureEnabled(featureIdentifier),
|
||||
isEntitled:
|
||||
application.features.getFeatureStatus(featureIdentifier) === FeatureStatus.Entitled,
|
||||
}
|
||||
})
|
||||
const experimentalFeatures = application.features.getExperimentalFeatures().map((featureIdentifier) => {
|
||||
const feature = FindNativeFeature(featureIdentifier)
|
||||
return {
|
||||
identifier: featureIdentifier,
|
||||
name: feature?.name ?? featureIdentifier,
|
||||
description: feature?.description ?? '',
|
||||
isEnabled: application.features.isExperimentalFeatureEnabled(featureIdentifier),
|
||||
isEntitled: application.features.getFeatureStatus(featureIdentifier) === FeatureStatus.Entitled,
|
||||
}
|
||||
})
|
||||
setExperimentalFeatures(experimentalFeatures)
|
||||
}, [application.features])
|
||||
|
||||
@@ -56,35 +53,32 @@ export const LabsPane: FunctionComponent<Props> = ({ application }) => {
|
||||
<PreferencesSegment>
|
||||
<Title>Labs</Title>
|
||||
<div>
|
||||
{experimentalFeatures.map(
|
||||
({ identifier, name, description, isEnabled, isEntitled }, index: number) => {
|
||||
const toggleFeature = () => {
|
||||
if (!isEntitled) {
|
||||
premiumModal.activate(name)
|
||||
return
|
||||
}
|
||||
|
||||
application.features.toggleExperimentalFeature(identifier)
|
||||
reloadExperimentalFeatures()
|
||||
{experimentalFeatures.map(({ identifier, name, description, isEnabled, isEntitled }, index: number) => {
|
||||
const toggleFeature = () => {
|
||||
if (!isEntitled) {
|
||||
premiumModal.activate(name)
|
||||
return
|
||||
}
|
||||
|
||||
const showHorizontalSeparator =
|
||||
experimentalFeatures.length > 1 && index !== experimentalFeatures.length - 1
|
||||
application.features.toggleExperimentalFeature(identifier)
|
||||
reloadExperimentalFeatures()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>{name}</Subtitle>
|
||||
<Text>{description}</Text>
|
||||
</div>
|
||||
<Switch onChange={toggleFeature} checked={isEnabled} />
|
||||
const showHorizontalSeparator = experimentalFeatures.length > 1 && index !== experimentalFeatures.length - 1
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>{name}</Subtitle>
|
||||
<Text>{description}</Text>
|
||||
</div>
|
||||
{showHorizontalSeparator && <HorizontalSeparator classes="mt-5 mb-3" />}
|
||||
</>
|
||||
)
|
||||
},
|
||||
)}
|
||||
<Switch onChange={toggleFeature} checked={isEnabled} />
|
||||
</div>
|
||||
{showHorizontalSeparator && <HorizontalSeparator classes="mt-5 mb-3" />}
|
||||
</>
|
||||
)
|
||||
})}
|
||||
{experimentalFeatures.length === 0 && (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col">
|
||||
|
||||
@@ -21,11 +21,7 @@ export const General: FunctionComponent<GeneralProps> = observer(
|
||||
<Tools application={application} />
|
||||
<Defaults application={application} />
|
||||
<LabsPane application={application} />
|
||||
<Advanced
|
||||
application={application}
|
||||
appState={appState}
|
||||
extensionsLatestVersions={extensionsLatestVersions}
|
||||
/>
|
||||
<Advanced application={application} appState={appState} extensionsLatestVersions={extensionsLatestVersions} />
|
||||
</PreferencesPane>
|
||||
),
|
||||
)
|
||||
|
||||
@@ -17,10 +17,10 @@ export const HelpAndFeedback: FunctionComponent = () => (
|
||||
<div className="h-2 w-full" />
|
||||
<Subtitle>Who can read my private notes?</Subtitle>
|
||||
<Text>
|
||||
Quite simply: no one but you. Not us, not your ISP, not a hacker, and not a government
|
||||
agency. As long as you keep your password safe, and your password is reasonably strong,
|
||||
then you are the only person in the world with the ability to decrypt your notes. For more
|
||||
on how we handle your privacy and security, check out our easy to read{' '}
|
||||
Quite simply: no one but you. Not us, not your ISP, not a hacker, and not a government agency. As long as you
|
||||
keep your password safe, and your password is reasonably strong, then you are the only person in the world
|
||||
with the ability to decrypt your notes. For more on how we handle your privacy and security, check out our
|
||||
easy to read{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/privacy">
|
||||
Privacy Manifesto.
|
||||
</a>
|
||||
@@ -29,21 +29,17 @@ export const HelpAndFeedback: FunctionComponent = () => (
|
||||
<PreferencesSegment>
|
||||
<Subtitle>Can I collaborate with others on a note?</Subtitle>
|
||||
<Text>
|
||||
Because of our encrypted architecture, Standard Notes does not currently provide a
|
||||
real-time collaboration solution. Multiple users can share the same account however, but
|
||||
editing at the same time may result in sync conflicts, which may result in the duplication
|
||||
of notes.
|
||||
Because of our encrypted architecture, Standard Notes does not currently provide a real-time collaboration
|
||||
solution. Multiple users can share the same account however, but editing at the same time may result in sync
|
||||
conflicts, which may result in the duplication of notes.
|
||||
</Text>
|
||||
</PreferencesSegment>
|
||||
<PreferencesSegment>
|
||||
<Subtitle>Can I use Standard Notes totally offline?</Subtitle>
|
||||
<Text>
|
||||
Standard Notes can be used totally offline without an account, and without an internet
|
||||
connection. You can find{' '}
|
||||
<a
|
||||
target="_blank"
|
||||
href="https://standardnotes.com/help/59/can-i-use-standard-notes-totally-offline"
|
||||
>
|
||||
Standard Notes can be used totally offline without an account, and without an internet connection. You can
|
||||
find{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/help/59/can-i-use-standard-notes-totally-offline">
|
||||
more details here.
|
||||
</a>
|
||||
</Text>
|
||||
@@ -57,38 +53,25 @@ export const HelpAndFeedback: FunctionComponent = () => (
|
||||
<PreferencesSegment>
|
||||
<Title>Community forum</Title>
|
||||
<Text>
|
||||
If you have an issue, found a bug or want to suggest a feature, you can browse or post to
|
||||
the forum. It’s recommended for non-account related issues. Please read our{' '}
|
||||
If you have an issue, found a bug or want to suggest a feature, you can browse or post to the forum. It’s
|
||||
recommended for non-account related issues. Please read our{' '}
|
||||
<a target="_blank" href="https://standardnotes.com/longevity/">
|
||||
Longevity statement
|
||||
</a>{' '}
|
||||
before advocating for a feature request.
|
||||
</Text>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
label="Go to the forum"
|
||||
link="https://forum.standardnotes.org/"
|
||||
/>
|
||||
<LinkButton className="mt-3" label="Go to the forum" link="https://forum.standardnotes.org/" />
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Community groups</Title>
|
||||
<Text>
|
||||
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your
|
||||
feedback with us? Join the Standard Notes community groups for discussions on security,
|
||||
themes, editors and more.
|
||||
Want to meet other passionate note-takers and privacy enthusiasts? Want to share your feedback with us? Join
|
||||
the Standard Notes community groups for discussions on security, themes, editors and more.
|
||||
</Text>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
link="https://standardnotes.com/slack"
|
||||
label="Join our Slack"
|
||||
/>
|
||||
<LinkButton
|
||||
className="mt-3"
|
||||
link="https://standardnotes.com/discord"
|
||||
label="Join our Discord"
|
||||
/>
|
||||
<LinkButton className="mt-3" link="https://standardnotes.com/slack" label="Join our Slack" />
|
||||
<LinkButton className="mt-3" link="https://standardnotes.com/discord" label="Join our Discord" />
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
<PreferencesGroup>
|
||||
|
||||
@@ -11,11 +11,7 @@ type Props = {
|
||||
application: WebApplication
|
||||
}
|
||||
|
||||
export const ListedAccountItem: FunctionalComponent<Props> = ({
|
||||
account,
|
||||
showSeparator,
|
||||
application,
|
||||
}) => {
|
||||
export const ListedAccountItem: FunctionalComponent<Props> = ({ account, showSeparator, application }) => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [accountInfo, setAccountInfo] = useState<ListedAccountInfo>()
|
||||
|
||||
|
||||
@@ -84,8 +84,8 @@ export const Listed = observer(({ application }: Props) => {
|
||||
<div className="h-2 w-full" />
|
||||
<Subtitle>What is Listed?</Subtitle>
|
||||
<Text>
|
||||
Listed is a free blogging platform that allows you to create a public journal published
|
||||
directly from your notes.{' '}
|
||||
Listed is a free blogging platform that allows you to create a public journal published directly from your
|
||||
notes.{' '}
|
||||
<a target="_blank" href="https://listed.to" rel="noreferrer noopener">
|
||||
Learn more
|
||||
</a>
|
||||
|
||||
@@ -3,12 +3,7 @@ import { STRING_E2E_ENABLED, STRING_ENC_NOT_ENABLED, STRING_LOCAL_ENC_ENABLED }
|
||||
import { AppState } from '@/UIModels/AppState'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { ComponentChild, FunctionComponent } from 'preact'
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Text,
|
||||
Title,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment, Text, Title } from '@/Components/Preferences/PreferencesComponents'
|
||||
|
||||
const formatCount = (count: number, itemType: string) => `${count} / ${count} ${itemType}`
|
||||
|
||||
|
||||
@@ -18,12 +18,7 @@ import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { AppState } from '@/UIModels/AppState'
|
||||
import {
|
||||
PreferencesSegment,
|
||||
Title,
|
||||
Text,
|
||||
PreferencesGroup,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesSegment, Title, Text, PreferencesGroup } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Button } from '@/Components/Button/Button'
|
||||
|
||||
type Props = {
|
||||
@@ -35,8 +30,7 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
const keyStorageInfo = StringUtils.keyStorageInfo(application)
|
||||
const passcodeAutoLockOptions = application.getAutolockService().getAutoLockIntervalOptions()
|
||||
|
||||
const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } =
|
||||
appState.accountMenu
|
||||
const { setIsEncryptionEnabled, setIsBackupEncrypted, setEncryptionStatusString } = appState.accountMenu
|
||||
|
||||
const passcodeInputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
@@ -109,9 +103,7 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
setPasscodeConfirmation(value)
|
||||
}
|
||||
|
||||
const submitPasscodeForm = async (
|
||||
event: TargetedEvent<HTMLFormElement> | TargetedMouseEvent<HTMLButtonElement>,
|
||||
) => {
|
||||
const submitPasscodeForm = async (event: TargetedEvent<HTMLFormElement> | TargetedMouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault()
|
||||
|
||||
if (!passcode || passcode.length === 0) {
|
||||
@@ -183,22 +175,18 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
|
||||
{!hasPasscode && canAddPasscode && (
|
||||
<>
|
||||
<Text className="mb-3">
|
||||
Add a passcode to lock the application and encrypt on-device key storage.
|
||||
</Text>
|
||||
<Text className="mb-3">Add a passcode to lock the application and encrypt on-device key storage.</Text>
|
||||
|
||||
{keyStorageInfo && <Text className="mb-3">{keyStorageInfo}</Text>}
|
||||
|
||||
{!showPasscodeForm && (
|
||||
<Button label="Add passcode" onClick={handleAddPassCode} variant="primary" />
|
||||
)}
|
||||
{!showPasscodeForm && <Button label="Add passcode" onClick={handleAddPassCode} variant="primary" />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{!hasPasscode && !canAddPasscode && (
|
||||
<Text>
|
||||
Adding a passcode is not supported in temporary sessions. Please sign out, then sign
|
||||
back in with the "Stay signed in" option checked.
|
||||
Adding a passcode is not supported in temporary sessions. Please sign out, then sign back in with the
|
||||
"Stay signed in" option checked.
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -221,12 +209,7 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
placeholder="Confirm Passcode"
|
||||
/>
|
||||
<div className="min-h-2" />
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={submitPasscodeForm}
|
||||
label="Set Passcode"
|
||||
className="mr-3"
|
||||
/>
|
||||
<Button variant="primary" onClick={submitPasscodeForm} label="Set Passcode" className="mr-3" />
|
||||
<Button variant="normal" onClick={() => setShowPasscodeForm(false)} label="Cancel" />
|
||||
</form>
|
||||
)}
|
||||
@@ -235,17 +218,8 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
<>
|
||||
<Text>Passcode lock is enabled.</Text>
|
||||
<div className="flex flex-row mt-3">
|
||||
<Button
|
||||
variant="normal"
|
||||
label="Change Passcode"
|
||||
onClick={changePasscodePressed}
|
||||
className="mr-3"
|
||||
/>
|
||||
<Button
|
||||
dangerStyle={true}
|
||||
label="Remove Passcode"
|
||||
onClick={removePasscodePressed}
|
||||
/>
|
||||
<Button variant="normal" label="Change Passcode" onClick={changePasscodePressed} className="mr-3" />
|
||||
<Button dangerStyle={true} label="Remove Passcode" onClick={removePasscodePressed} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -258,16 +232,12 @@ export const PasscodeLock = observer(({ application, appState }: Props) => {
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Autolock</Title>
|
||||
<Text className="mb-3">
|
||||
The autolock timer begins when the window or tab loses focus.
|
||||
</Text>
|
||||
<Text className="mb-3">The autolock timer begins when the window or tab loses focus.</Text>
|
||||
<div className="flex flex-row items-center">
|
||||
{passcodeAutoLockOptions.map((option) => {
|
||||
return (
|
||||
<a
|
||||
className={`sk-a info mr-3 ${
|
||||
option.value === selectedAutoLockInterval ? 'boxed' : ''
|
||||
}`}
|
||||
className={`sk-a info mr-3 ${option.value === selectedAutoLockInterval ? 'boxed' : ''}`}
|
||||
onClick={() => selectAutoLockInterval(option.value)}
|
||||
>
|
||||
{option.label}
|
||||
|
||||
@@ -71,9 +71,7 @@ export const Privacy: FunctionalComponent<Props> = observer(({ application }: Pr
|
||||
const toggleMuteSignInEmails = async () => {
|
||||
const previousValue = signInEmailsMutedValue
|
||||
const newValue =
|
||||
previousValue === MuteSignInEmailsOption.Muted
|
||||
? MuteSignInEmailsOption.NotMuted
|
||||
: MuteSignInEmailsOption.Muted
|
||||
previousValue === MuteSignInEmailsOption.Muted ? MuteSignInEmailsOption.NotMuted : MuteSignInEmailsOption.Muted
|
||||
setSignInEmailsMutedValue(newValue)
|
||||
|
||||
const updateResult = await updateSetting(SettingName.MuteSignInEmails, newValue)
|
||||
@@ -107,8 +105,8 @@ export const Privacy: FunctionalComponent<Props> = observer(({ application }: Pr
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>Disable sign-in notification emails</Subtitle>
|
||||
<Text>
|
||||
Disables email notifications when a new sign-in occurs on your account. (Email
|
||||
notifications are available to paid subscribers).
|
||||
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
|
||||
available to paid subscribers).
|
||||
</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
@@ -125,9 +123,9 @@ export const Privacy: FunctionalComponent<Props> = observer(({ application }: Pr
|
||||
<div className="flex flex-col">
|
||||
<Subtitle>Session user agent logging</Subtitle>
|
||||
<Text>
|
||||
User agent logging allows you to identify the devices or browsers signed into your
|
||||
account. For increased privacy, you can disable this feature, which will remove all
|
||||
saved user agent values from our server, and disable future logging of this value.
|
||||
User agent logging allows you to identify the devices or browsers signed into your account. For
|
||||
increased privacy, you can disable this feature, which will remove all saved user agent values from our
|
||||
server, and disable future logging of this value.
|
||||
</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
|
||||
@@ -3,12 +3,7 @@ import { FunctionalComponent } from 'preact'
|
||||
import { useCallback, useState, useEffect } from 'preact/hooks'
|
||||
import { ApplicationEvent } from '@standardnotes/snjs'
|
||||
import { isSameDay } from '@/Utils'
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Title,
|
||||
Text,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { PreferencesGroup, PreferencesSegment, Title, Text } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Button } from '@/Components/Button/Button'
|
||||
|
||||
type Props = {
|
||||
@@ -47,9 +42,7 @@ export const Protections: FunctionalComponent<Props> = ({ application }) => {
|
||||
return null
|
||||
}, [application])
|
||||
|
||||
const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(
|
||||
getProtectionsDisabledUntil(),
|
||||
)
|
||||
const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(getProtectionsDisabledUntil())
|
||||
|
||||
useEffect(() => {
|
||||
const removeUnprotectedSessionBeginObserver = application.addEventObserver(async () => {
|
||||
@@ -85,17 +78,11 @@ export const Protections: FunctionalComponent<Props> = ({ application }) => {
|
||||
<Text className="info">Protections are enabled.</Text>
|
||||
)}
|
||||
<Text className="mt-2">
|
||||
Actions like viewing or searching protected notes, exporting decrypted backups, or
|
||||
revoking an active session require additional authentication such as entering your account
|
||||
password or application passcode.
|
||||
Actions like viewing or searching protected notes, exporting decrypted backups, or revoking an active session
|
||||
require additional authentication such as entering your account password or application passcode.
|
||||
</Text>
|
||||
{protectionsDisabledUntil && (
|
||||
<Button
|
||||
className="mt-3"
|
||||
variant="primary"
|
||||
label="End Unprotected Access"
|
||||
onClick={enableProtections}
|
||||
/>
|
||||
<Button className="mt-3" variant="primary" label="End Unprotected Access" onClick={enableProtections} />
|
||||
)}
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
|
||||
@@ -14,9 +14,7 @@ const DisclosureIconButton: FunctionComponent<{
|
||||
<DisclosureButton
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
className={`no-border cursor-pointer bg-transparent hover:brightness-130 p-0 ${
|
||||
className ?? ''
|
||||
}`}
|
||||
className={`no-border cursor-pointer bg-transparent hover:brightness-130 p-0 ${className ?? ''}`}
|
||||
>
|
||||
<Icon type={icon} />
|
||||
</DisclosureButton>
|
||||
@@ -56,8 +54,8 @@ export const AuthAppInfoTooltip: FunctionComponent = () => {
|
||||
className={`bg-inverted-default color-inverted-default text-center rounded shadow-overlay
|
||||
py-1.5 px-2 absolute w-103 -top-10 -left-51`}
|
||||
>
|
||||
Some apps, like Google Authenticator, do not back up and restore your secret keys if you
|
||||
lose your device or get a new one.
|
||||
Some apps, like Google Authenticator, do not back up and restore your secret keys if you lose your device or
|
||||
get a new one.
|
||||
</div>
|
||||
</DisclosurePanel>
|
||||
</div>
|
||||
|
||||
@@ -76,18 +76,8 @@ export const SaveSecretKey: FunctionComponent<{
|
||||
</div>
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Back"
|
||||
onClick={() => act.openScanQRCode()}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="primary"
|
||||
label="Next"
|
||||
onClick={() => act.openVerification()}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Back" onClick={() => act.openScanQRCode()} />
|
||||
<Button className="min-w-20" variant="primary" label="Next" onClick={() => act.openVerification()} />
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
)
|
||||
|
||||
@@ -19,16 +19,10 @@ export const ScanQRCode: FunctionComponent<{
|
||||
}> = observer(({ activation: act }) => {
|
||||
return (
|
||||
<ModalDialog>
|
||||
<ModalDialogLabel closeDialog={act.cancelActivation}>
|
||||
Step 1 of 3 - Scan QR code
|
||||
</ModalDialogLabel>
|
||||
<ModalDialogLabel closeDialog={act.cancelActivation}>Step 1 of 3 - Scan QR code</ModalDialogLabel>
|
||||
<ModalDialogDescription className="h-33">
|
||||
<div className="w-25 h-25 flex items-center justify-center bg-info">
|
||||
<QRCode
|
||||
className="border-neutral-contrast-bg border-solid border-2"
|
||||
value={act.qrCode}
|
||||
size={100}
|
||||
/>
|
||||
<QRCode className="border-neutral-contrast-bg border-solid border-2" value={act.qrCode} size={100} />
|
||||
</div>
|
||||
<div className="min-w-5" />
|
||||
<div className="flex-grow flex flex-col">
|
||||
@@ -59,18 +53,8 @@ export const ScanQRCode: FunctionComponent<{
|
||||
</div>
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Cancel"
|
||||
onClick={() => act.cancelActivation()}
|
||||
/>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="primary"
|
||||
label="Next"
|
||||
onClick={() => act.openSaveSecretKey()}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Cancel" onClick={() => act.cancelActivation()} />
|
||||
<Button className="min-w-20" variant="primary" label="Next" onClick={() => act.openSaveSecretKey()} />
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
)
|
||||
|
||||
@@ -25,12 +25,7 @@ export class TwoFactorActivation {
|
||||
|
||||
makeAutoObservable<
|
||||
TwoFactorActivation,
|
||||
| '_secretKey'
|
||||
| '_authCode'
|
||||
| '_step'
|
||||
| '_enable2FAVerification'
|
||||
| 'inputOtpToken'
|
||||
| 'inputSecretKey'
|
||||
'_secretKey' | '_authCode' | '_step' | '_enable2FAVerification' | 'inputOtpToken' | 'inputSecretKey'
|
||||
>(
|
||||
this,
|
||||
{
|
||||
|
||||
@@ -10,23 +10,16 @@ export const is2FADisabled = (status: TwoFactorStatus): status is 'two-factor-di
|
||||
export const is2FAActivation = (status: TwoFactorStatus): status is TwoFactorActivation =>
|
||||
(status as TwoFactorActivation)?.type === 'two-factor-activation'
|
||||
|
||||
export const is2FAEnabled = (status: TwoFactorStatus): status is 'two-factor-enabled' =>
|
||||
status === 'two-factor-enabled'
|
||||
export const is2FAEnabled = (status: TwoFactorStatus): status is 'two-factor-enabled' => status === 'two-factor-enabled'
|
||||
|
||||
export class TwoFactorAuth {
|
||||
private _status: TwoFactorStatus | 'fetching' = 'fetching'
|
||||
private _errorMessage: string | null
|
||||
|
||||
constructor(
|
||||
private readonly mfaProvider: MfaProvider,
|
||||
private readonly userProvider: UserProvider,
|
||||
) {
|
||||
constructor(private readonly mfaProvider: MfaProvider, private readonly userProvider: UserProvider) {
|
||||
this._errorMessage = null
|
||||
|
||||
makeAutoObservable<
|
||||
TwoFactorAuth,
|
||||
'_status' | '_errorMessage' | 'deactivateMfa' | 'startActivation'
|
||||
>(
|
||||
makeAutoObservable<TwoFactorAuth, '_status' | '_errorMessage' | 'deactivateMfa' | 'startActivation'>(
|
||||
this,
|
||||
{
|
||||
_status: observable,
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { FunctionComponent } from 'preact'
|
||||
import {
|
||||
Title,
|
||||
Text,
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
} from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Title, Text, PreferencesGroup, PreferencesSegment } from '@/Components/Preferences/PreferencesComponents'
|
||||
import { Switch } from '@/Components/Switch'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { is2FAActivation, is2FADisabled, TwoFactorAuth } from './TwoFactorAuth'
|
||||
|
||||
@@ -16,18 +16,11 @@ export const TwoFactorSuccess: FunctionComponent<{
|
||||
<ModalDialogLabel closeDialog={act.finishActivation}>Successfully Enabled</ModalDialogLabel>
|
||||
<ModalDialogDescription>
|
||||
<div className="flex flex-row items-center justify-center pt-2">
|
||||
<Subtitle>
|
||||
Two-factor authentication has been successfully enabled for your account.
|
||||
</Subtitle>
|
||||
<Subtitle>Two-factor authentication has been successfully enabled for your account.</Subtitle>
|
||||
</div>
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="primary"
|
||||
label="Finish"
|
||||
onClick={act.finishActivation}
|
||||
/>
|
||||
<Button className="min-w-20" variant="primary" label="Finish" onClick={act.finishActivation} />
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
))
|
||||
|
||||
@@ -18,9 +18,7 @@ export const Verification: FunctionComponent<{
|
||||
const authTokenClass = act.verificationStatus === 'invalid-auth-code' ? 'border-danger' : ''
|
||||
return (
|
||||
<ModalDialog>
|
||||
<ModalDialogLabel closeDialog={act.cancelActivation}>
|
||||
Step 3 of 3 - Verification
|
||||
</ModalDialogLabel>
|
||||
<ModalDialogLabel closeDialog={act.cancelActivation}>Step 3 of 3 - Verification</ModalDialogLabel>
|
||||
<ModalDialogDescription className="h-33">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<div className="flex flex-row items-center mb-4">
|
||||
@@ -45,21 +43,12 @@ export const Verification: FunctionComponent<{
|
||||
</ModalDialogDescription>
|
||||
<ModalDialogButtons>
|
||||
{act.verificationStatus === 'invalid-auth-code' && (
|
||||
<div className="text-sm color-danger flex-grow">
|
||||
Incorrect authentication code, please try again.
|
||||
</div>
|
||||
<div className="text-sm color-danger flex-grow">Incorrect authentication code, please try again.</div>
|
||||
)}
|
||||
{act.verificationStatus === 'invalid-secret' && (
|
||||
<div className="text-sm color-danger flex-grow">
|
||||
Incorrect secret key, please try again.
|
||||
</div>
|
||||
<div className="text-sm color-danger flex-grow">Incorrect secret key, please try again.</div>
|
||||
)}
|
||||
<Button
|
||||
className="min-w-20"
|
||||
variant="normal"
|
||||
label="Back"
|
||||
onClick={act.openSaveSecretKey}
|
||||
/>
|
||||
<Button className="min-w-20" variant="normal" label="Back" onClick={act.openSaveSecretKey} />
|
||||
<Button className="min-w-20" variant="primary" label="Next" onClick={act.enable2FA} />
|
||||
</ModalDialogButtons>
|
||||
</ModalDialog>
|
||||
|
||||
@@ -7,15 +7,13 @@ export const Title: FunctionComponent = ({ children }) => (
|
||||
</>
|
||||
)
|
||||
|
||||
export const Subtitle: FunctionComponent<{ className?: string }> = ({
|
||||
children,
|
||||
className = '',
|
||||
}) => <h4 className={`font-medium text-sm m-0 mb-1 ${className}`}>{children}</h4>
|
||||
export const Subtitle: FunctionComponent<{ className?: string }> = ({ children, className = '' }) => (
|
||||
<h4 className={`font-medium text-sm m-0 mb-1 ${className}`}>{children}</h4>
|
||||
)
|
||||
|
||||
export const SubtitleLight: FunctionComponent<{ className?: string }> = ({
|
||||
children,
|
||||
className = '',
|
||||
}) => <h4 className={`font-normal text-sm m-0 mb-1 ${className}`}>{children}</h4>
|
||||
export const SubtitleLight: FunctionComponent<{ className?: string }> = ({ children, className = '' }) => (
|
||||
<h4 className={`font-normal text-sm m-0 mb-1 ${className}`}>{children}</h4>
|
||||
)
|
||||
|
||||
export const Text: FunctionComponent<{ className?: string }> = ({ children, className = '' }) => (
|
||||
<p className={`${className} text-xs`}>{children}</p>
|
||||
|
||||
@@ -10,13 +10,7 @@ interface Props {
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
export const MenuItem: FunctionComponent<Props> = ({
|
||||
iconType,
|
||||
label,
|
||||
selected,
|
||||
onClick,
|
||||
hasBubble,
|
||||
}) => (
|
||||
export const MenuItem: FunctionComponent<Props> = ({ iconType, label, selected, onClick, hasBubble }) => (
|
||||
<div
|
||||
className={`preferences-menu-item select-none ${selected ? 'selected' : ''}`}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -4,9 +4,7 @@ export const PreferencesPane: FunctionComponent = ({ children }) => (
|
||||
<div className="color-foreground flex-grow flex flex-row overflow-y-auto min-h-0">
|
||||
<div className="flex-grow flex flex-col py-6 items-center">
|
||||
<div className="w-125 max-w-125 flex flex-col">
|
||||
{children != undefined && Array.isArray(children)
|
||||
? children.filter((child) => child != undefined)
|
||||
: children}
|
||||
{children != undefined && Array.isArray(children) ? children.filter((child) => child != undefined) : children}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-basis-55 flex-shrink" />
|
||||
|
||||
@@ -59,27 +59,16 @@ const READY_PREFERENCES_MENU_ITEMS: PreferencesMenuItem[] = [
|
||||
export class PreferencesMenu {
|
||||
private _selectedPane: PreferenceId = 'account'
|
||||
private _menu: PreferencesMenuItem[]
|
||||
private _extensionLatestVersions: ExtensionsLatestVersions = new ExtensionsLatestVersions(
|
||||
new Map(),
|
||||
)
|
||||
private _extensionLatestVersions: ExtensionsLatestVersions = new ExtensionsLatestVersions(new Map())
|
||||
|
||||
constructor(
|
||||
private application: WebApplication,
|
||||
private readonly _enableUnfinishedFeatures: boolean,
|
||||
) {
|
||||
this._menu = this._enableUnfinishedFeatures
|
||||
? PREFERENCES_MENU_ITEMS
|
||||
: READY_PREFERENCES_MENU_ITEMS
|
||||
constructor(private application: WebApplication, private readonly _enableUnfinishedFeatures: boolean) {
|
||||
this._menu = this._enableUnfinishedFeatures ? PREFERENCES_MENU_ITEMS : READY_PREFERENCES_MENU_ITEMS
|
||||
|
||||
this.loadLatestVersions()
|
||||
|
||||
makeAutoObservable<
|
||||
PreferencesMenu,
|
||||
| '_selectedPane'
|
||||
| '_twoFactorAuth'
|
||||
| '_extensionPanes'
|
||||
| '_extensionLatestVersions'
|
||||
| 'loadLatestVersions'
|
||||
'_selectedPane' | '_twoFactorAuth' | '_extensionPanes' | '_extensionLatestVersions' | 'loadLatestVersions'
|
||||
>(this, {
|
||||
_twoFactorAuth: observable,
|
||||
_selectedPane: observable,
|
||||
|
||||
@@ -67,14 +67,12 @@ const PaneSelector: FunctionComponent<PreferencesProps & { menu: PreferencesMenu
|
||||
},
|
||||
)
|
||||
|
||||
const PreferencesCanvas: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = observer(
|
||||
(props) => (
|
||||
<div className="flex flex-row flex-grow min-h-0 justify-between">
|
||||
<PreferencesMenuView menu={props.menu} />
|
||||
<PaneSelector {...props} />
|
||||
</div>
|
||||
),
|
||||
)
|
||||
const PreferencesCanvas: FunctionComponent<PreferencesProps & { menu: PreferencesMenu }> = observer((props) => (
|
||||
<div className="flex flex-row flex-grow min-h-0 justify-between">
|
||||
<PreferencesMenuView menu={props.menu} />
|
||||
<PaneSelector {...props} />
|
||||
</div>
|
||||
))
|
||||
|
||||
export const PreferencesView: FunctionComponent<PreferencesProps> = observer((props) => {
|
||||
const menu = useMemo(
|
||||
|
||||
Reference in New Issue
Block a user