feat: privacy prefs (#935)
* chore: move all components into Components dir with pascal case * feat: privacy prefs * chore: upgrade deps
This commit is contained in:
@@ -123,13 +123,13 @@ export class ChallengeModal extends PureComponent<Props, State> {
|
||||
})
|
||||
) {
|
||||
this.dismiss();
|
||||
this.application.signOut();
|
||||
this.application.user.signOut();
|
||||
}
|
||||
};
|
||||
|
||||
cancel = () => {
|
||||
if (this.props.challenge.cancelable) {
|
||||
this.application!.cancelChallenge(this.props.challenge);
|
||||
this.application.cancelChallenge(this.props.challenge);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export const ConfirmSignoutModal = observer(
|
||||
if (deleteLocalBackups) {
|
||||
application.signOutAndDeleteLocalBackups();
|
||||
} else {
|
||||
application.signOut();
|
||||
application.user.signOut();
|
||||
}
|
||||
closeDialog();
|
||||
}}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { AppState } from '@/ui_models/app_state';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { PreferencesPane } from '../components';
|
||||
import { Encryption, PasscodeLock, Protections } from './security-segments';
|
||||
import { Privacy } from './security-segments/Privacy';
|
||||
import { TwoFactorAuthWrapper } from './two-factor-auth';
|
||||
import { MfaProps } from './two-factor-auth/MfaProps';
|
||||
|
||||
@@ -20,5 +21,8 @@ export const Security: FunctionComponent<SecurityProps> = (props) => (
|
||||
userProvider={props.userProvider}
|
||||
/>
|
||||
<PasscodeLock appState={props.appState} application={props.application} />
|
||||
{props.appState.enableUnfinishedFeatures && (
|
||||
<Privacy application={props.application} />
|
||||
)}
|
||||
</PreferencesPane>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,11 @@ import {
|
||||
Text,
|
||||
Title,
|
||||
} from '../../components';
|
||||
import { EmailBackupFrequency, SettingName } from '@standardnotes/settings';
|
||||
import {
|
||||
EmailBackupFrequency,
|
||||
MuteFailedBackupsEmailsOption,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings';
|
||||
import { Dropdown, DropdownItem } from '@/components/Dropdown';
|
||||
import { Switch } from '@/components/Switch';
|
||||
import { HorizontalSeparator } from '@/components/Shared/HorizontalSeparator';
|
||||
@@ -44,14 +48,19 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const userSettings = await application.listSettings();
|
||||
const userSettings = await application.settings.listSettings();
|
||||
setEmailFrequency(
|
||||
(userSettings.EMAIL_BACKUP_FREQUENCY ||
|
||||
EmailBackupFrequency.Disabled) as EmailBackupFrequency
|
||||
userSettings.getSettingValue<EmailBackupFrequency>(
|
||||
SettingName.EmailBackupFrequency,
|
||||
EmailBackupFrequency.Disabled
|
||||
)
|
||||
);
|
||||
setIsFailedBackupEmailMuted(
|
||||
convertStringifiedBooleanToBoolean(
|
||||
userSettings[SettingName.MuteFailedBackupsEmails] as string
|
||||
userSettings.getSettingValue<MuteFailedBackupsEmailsOption>(
|
||||
SettingName.MuteFailedBackupsEmails,
|
||||
MuteFailedBackupsEmailsOption.NotMuted
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -75,7 +84,10 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
EmailBackupFrequency[frequency as keyof typeof EmailBackupFrequency];
|
||||
frequencyOptions.push({
|
||||
value: frequencyValue,
|
||||
label: application.getEmailBackupFrequencyOptionLabel(frequencyValue),
|
||||
label:
|
||||
application.settings.getEmailBackupFrequencyOptionLabel(
|
||||
frequencyValue
|
||||
),
|
||||
});
|
||||
}
|
||||
setEmailFrequencyOptions(frequencyOptions);
|
||||
@@ -88,7 +100,7 @@ export const EmailBackups = observer(({ application }: Props) => {
|
||||
payload: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
await application.updateSetting(settingName, payload);
|
||||
await application.settings.updateSetting(settingName, payload, false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
application.alertService.alert(STRING_FAILED_TO_UPDATE_USER_SETTING);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
import { ButtonType, SettingName } from '@standardnotes/snjs';
|
||||
import {
|
||||
@@ -27,7 +26,9 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
}) => {
|
||||
const [authBegan, setAuthBegan] = useState(false);
|
||||
const [successfullyInstalled, setSuccessfullyInstalled] = useState(false);
|
||||
const [backupFrequency, setBackupFrequency] = useState<string | null>(null);
|
||||
const [backupFrequency, setBackupFrequency] = useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [confirmation, setConfirmation] = useState('');
|
||||
|
||||
const disable = async (event: Event) => {
|
||||
@@ -42,10 +43,10 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
'Cancel'
|
||||
);
|
||||
if (shouldDisable) {
|
||||
await application.deleteSetting(backupFrequencySettingName);
|
||||
await application.deleteSetting(backupTokenSettingName);
|
||||
await application.settings.deleteSetting(backupFrequencySettingName);
|
||||
await application.settings.deleteSetting(backupTokenSettingName);
|
||||
|
||||
setBackupFrequency(null);
|
||||
setBackupFrequency(undefined);
|
||||
}
|
||||
} catch (error) {
|
||||
application.alertService.alert(error as string);
|
||||
@@ -66,7 +67,7 @@ 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.updateSetting(
|
||||
await application.settings.updateSetting(
|
||||
backupFrequencySettingName,
|
||||
backupFrequency as string
|
||||
);
|
||||
@@ -134,11 +135,11 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
if (!cloudProviderToken) {
|
||||
throw new Error();
|
||||
}
|
||||
await application.updateSetting(
|
||||
await application.settings.updateSetting(
|
||||
backupTokenSettingName,
|
||||
cloudProviderToken
|
||||
);
|
||||
await application.updateSetting(
|
||||
await application.settings.updateSetting(
|
||||
backupFrequencySettingName,
|
||||
defaultBackupFrequency
|
||||
);
|
||||
@@ -166,7 +167,9 @@ export const CloudBackupProvider: FunctionComponent<Props> = ({
|
||||
if (!application.getUser()) {
|
||||
return;
|
||||
}
|
||||
const frequency = await application.getSetting(backupFrequencySettingName);
|
||||
const frequency = await application.settings.getSetting(
|
||||
backupFrequencySettingName
|
||||
);
|
||||
setBackupFrequency(frequency);
|
||||
}, [application, backupFrequencySettingName]);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import { CloudBackupProvider } from './CloudBackupProvider';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
@@ -13,21 +12,19 @@ import { HorizontalSeparator } from '@/components/Shared/HorizontalSeparator';
|
||||
import { FeatureIdentifier } from '@standardnotes/features';
|
||||
import { FeatureStatus } from '@standardnotes/snjs';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { CloudProvider, SettingName } from '@standardnotes/settings';
|
||||
import {
|
||||
CloudProvider,
|
||||
MuteFailedCloudBackupsEmailsOption,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings';
|
||||
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,
|
||||
},
|
||||
{ name: CloudProvider.Dropbox },
|
||||
{ name: CloudProvider.Google },
|
||||
{ name: CloudProvider.OneDrive },
|
||||
];
|
||||
|
||||
type Props = {
|
||||
@@ -51,10 +48,13 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const userSettings = await application.listSettings();
|
||||
const userSettings = await application.settings.listSettings();
|
||||
setIsFailedCloudBackupEmailMuted(
|
||||
convertStringifiedBooleanToBoolean(
|
||||
userSettings[SettingName.MuteFailedCloudBackupsEmails] as string
|
||||
userSettings.getSettingValue(
|
||||
SettingName.MuteFailedCloudBackupsEmails,
|
||||
MuteFailedCloudBackupsEmailsOption.NotMuted
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -89,7 +89,7 @@ export const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
||||
payload: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
await application.updateSetting(settingName, payload);
|
||||
await application.settings.updateSetting(settingName, payload);
|
||||
return true;
|
||||
} catch (e) {
|
||||
application.alertService.alert(STRING_FAILED_TO_UPDATE_USER_SETTING);
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
import { HorizontalSeparator } from '@/components/Shared/HorizontalSeparator';
|
||||
import { Switch } from '@/components/Switch';
|
||||
import {
|
||||
PreferencesGroup,
|
||||
PreferencesSegment,
|
||||
Subtitle,
|
||||
Text,
|
||||
Title,
|
||||
} from '@/components/Preferences/components';
|
||||
import { WebApplication } from '@/ui_models/application';
|
||||
import {
|
||||
MuteSignInEmailsOption,
|
||||
LogSessionUserAgentOption,
|
||||
SettingName,
|
||||
} from '@standardnotes/settings';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { FunctionalComponent } from 'preact';
|
||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||
import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/strings';
|
||||
|
||||
type Props = {
|
||||
application: WebApplication;
|
||||
};
|
||||
|
||||
export const Privacy: FunctionalComponent<Props> = observer(
|
||||
({ application }: Props) => {
|
||||
const [signInEmailsMutedValue, setSignInEmailsMutedValue] =
|
||||
useState<MuteSignInEmailsOption>(MuteSignInEmailsOption.NotMuted);
|
||||
const [sessionUaLoggingValue, setSessionUaLoggingValue] =
|
||||
useState<LogSessionUserAgentOption>(LogSessionUserAgentOption.Enabled);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const updateSetting = async (
|
||||
settingName: SettingName,
|
||||
payload: string
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
await application.settings.updateSetting(settingName, payload, false);
|
||||
return true;
|
||||
} catch (e) {
|
||||
application.alertService.alert(STRING_FAILED_TO_UPDATE_USER_SETTING);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadSettings = useCallback(async () => {
|
||||
if (!application.getUser()) {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const userSettings = await application.settings.listSettings();
|
||||
setSignInEmailsMutedValue(
|
||||
userSettings.getSettingValue<MuteSignInEmailsOption>(
|
||||
SettingName.MuteSignInEmails,
|
||||
MuteSignInEmailsOption.NotMuted
|
||||
)
|
||||
);
|
||||
setSessionUaLoggingValue(
|
||||
userSettings.getSettingValue<LogSessionUserAgentOption>(
|
||||
SettingName.LogSessionUserAgent,
|
||||
LogSessionUserAgentOption.Enabled
|
||||
)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [application]);
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
}, [loadSettings]);
|
||||
|
||||
const toggleMuteSignInEmails = async () => {
|
||||
const previousValue = signInEmailsMutedValue;
|
||||
const newValue =
|
||||
previousValue === MuteSignInEmailsOption.Muted
|
||||
? MuteSignInEmailsOption.NotMuted
|
||||
: MuteSignInEmailsOption.Muted;
|
||||
setSignInEmailsMutedValue(newValue);
|
||||
|
||||
const updateResult = await updateSetting(
|
||||
SettingName.MuteSignInEmails,
|
||||
newValue
|
||||
);
|
||||
|
||||
if (!updateResult) {
|
||||
setSignInEmailsMutedValue(previousValue);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSessionLogging = async () => {
|
||||
const previousValue = sessionUaLoggingValue;
|
||||
const newValue =
|
||||
previousValue === LogSessionUserAgentOption.Enabled
|
||||
? LogSessionUserAgentOption.Disabled
|
||||
: LogSessionUserAgentOption.Enabled;
|
||||
setSessionUaLoggingValue(newValue);
|
||||
|
||||
const updateResult = await updateSetting(
|
||||
SettingName.LogSessionUserAgent,
|
||||
newValue
|
||||
);
|
||||
|
||||
if (!updateResult) {
|
||||
setSessionUaLoggingValue(previousValue);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
<Title>Privacy</Title>
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<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).
|
||||
</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className={'sk-spinner info small'} />
|
||||
) : (
|
||||
<Switch
|
||||
onChange={toggleMuteSignInEmails}
|
||||
checked={
|
||||
signInEmailsMutedValue === MuteSignInEmailsOption.Muted
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<HorizontalSeparator classes="mt-5 mb-3" />
|
||||
<div className="flex items-center justify-between">
|
||||
<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.
|
||||
</Text>
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className={'sk-spinner info small'} />
|
||||
) : (
|
||||
<Switch
|
||||
onChange={toggleSessionLogging}
|
||||
checked={
|
||||
sessionUaLoggingValue === LogSessionUserAgentOption.Enabled
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user