chore(web): put sign-in email notifications setting under paywall (#2249)
* chore: upgrade setting names to value objects * chore(web): put sign-in email notifications setting under paywall * chore: fix using setting name value objects in mocha tests * chore: fix wording on email notifications titles
This commit is contained in:
Binary file not shown.
@@ -8,7 +8,9 @@ export class GetRecoveryCodes implements UseCaseInterface<string> {
|
|||||||
constructor(private authClient: AuthClientInterface, private settingsClient: SettingsClientInterface) {}
|
constructor(private authClient: AuthClientInterface, private settingsClient: SettingsClientInterface) {}
|
||||||
|
|
||||||
async execute(): Promise<Result<string>> {
|
async execute(): Promise<Result<string>> {
|
||||||
const existingRecoveryCodes = await this.settingsClient.getSetting(SettingName.RecoveryCodes)
|
const existingRecoveryCodes = await this.settingsClient.getSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.RecoveryCodes).getValue(),
|
||||||
|
)
|
||||||
if (existingRecoveryCodes !== undefined) {
|
if (existingRecoveryCodes !== undefined) {
|
||||||
return Result.ok(existingRecoveryCodes)
|
return Result.ok(existingRecoveryCodes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { FeatureDescription } from '@standardnotes/features'
|
import { FeatureDescription } from '@standardnotes/features'
|
||||||
import { joinPaths } from '@standardnotes/utils'
|
import { joinPaths } from '@standardnotes/utils'
|
||||||
import { SettingName, SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
import {
|
import {
|
||||||
AbstractService,
|
AbstractService,
|
||||||
ApiServiceInterface,
|
ApiServiceInterface,
|
||||||
@@ -568,31 +567,25 @@ export class SNApiService
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSetting(userUuid: UuidString, settingName: SettingName): Promise<HttpResponse<GetSettingResponse>> {
|
async getSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<GetSettingResponse>> {
|
||||||
return await this.tokenRefreshableRequest<GetSettingResponse>({
|
return await this.tokenRefreshableRequest<GetSettingResponse>({
|
||||||
verb: HttpVerb.Get,
|
verb: HttpVerb.Get,
|
||||||
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName.toLowerCase() as SettingName)),
|
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName.toLowerCase())),
|
||||||
authentication: this.getSessionAccessToken(),
|
authentication: this.getSessionAccessToken(),
|
||||||
fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
|
fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscriptionSetting(
|
async getSubscriptionSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<GetSettingResponse>> {
|
||||||
userUuid: UuidString,
|
|
||||||
settingName: SubscriptionSettingName,
|
|
||||||
): Promise<HttpResponse<GetSettingResponse>> {
|
|
||||||
return await this.tokenRefreshableRequest<GetSettingResponse>({
|
return await this.tokenRefreshableRequest<GetSettingResponse>({
|
||||||
verb: HttpVerb.Get,
|
verb: HttpVerb.Get,
|
||||||
url: joinPaths(
|
url: joinPaths(this.host, Paths.v1.subscriptionSetting(userUuid, settingName.toLowerCase())),
|
||||||
this.host,
|
|
||||||
Paths.v1.subscriptionSetting(userUuid, settingName.toLowerCase() as SubscriptionSettingName),
|
|
||||||
),
|
|
||||||
authentication: this.getSessionAccessToken(),
|
authentication: this.getSessionAccessToken(),
|
||||||
fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
|
fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSetting(userUuid: UuidString, settingName: SettingName): Promise<HttpResponse<DeleteSettingResponse>> {
|
async deleteSetting(userUuid: UuidString, settingName: string): Promise<HttpResponse<DeleteSettingResponse>> {
|
||||||
return this.tokenRefreshableRequest<DeleteSettingResponse>({
|
return this.tokenRefreshableRequest<DeleteSettingResponse>({
|
||||||
verb: HttpVerb.Delete,
|
verb: HttpVerb.Delete,
|
||||||
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName)),
|
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName)),
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { SettingName, SubscriptionSettingName } from '@standardnotes/settings'
|
|
||||||
|
|
||||||
const FilesPaths = {
|
const FilesPaths = {
|
||||||
closeUploadSession: '/v1/files/upload/close-session',
|
closeUploadSession: '/v1/files/upload/close-session',
|
||||||
createFileValetToken: '/v1/files/valet-tokens',
|
createFileValetToken: '/v1/files/valet-tokens',
|
||||||
@@ -31,8 +29,8 @@ const ItemsPaths = {
|
|||||||
|
|
||||||
const SettingsPaths = {
|
const SettingsPaths = {
|
||||||
settings: (userUuid: string) => `/v1/users/${userUuid}/settings`,
|
settings: (userUuid: string) => `/v1/users/${userUuid}/settings`,
|
||||||
setting: (userUuid: string, settingName: SettingName) => `/v1/users/${userUuid}/settings/${settingName}`,
|
setting: (userUuid: string, settingName: string) => `/v1/users/${userUuid}/settings/${settingName}`,
|
||||||
subscriptionSetting: (userUuid: string, settingName: SubscriptionSettingName) =>
|
subscriptionSetting: (userUuid: string, settingName: string) =>
|
||||||
`/v1/users/${userUuid}/subscription-settings/${settingName}`,
|
`/v1/users/${userUuid}/subscription-settings/${settingName}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -725,7 +725,7 @@ describe('featuresService', () => {
|
|||||||
|
|
||||||
const featuresService = createService()
|
const featuresService = createService()
|
||||||
await featuresService.migrateFeatureRepoToUserSetting([extensionRepoItem])
|
await featuresService.migrateFeatureRepoToUserSetting([extensionRepoItem])
|
||||||
expect(settingsService.updateSetting).toHaveBeenCalledWith(SettingName.ExtensionKey, extensionKey, true)
|
expect(settingsService.updateSetting).toHaveBeenCalledWith(SettingName.create(SettingName.NAMES.ExtensionKey).getValue(), extensionKey, true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -341,7 +341,11 @@ export class SNFeaturesService
|
|||||||
const userKeyMatch = repoUrl.match(/\w{32,64}/)
|
const userKeyMatch = repoUrl.match(/\w{32,64}/)
|
||||||
if (userKeyMatch && userKeyMatch.length > 0) {
|
if (userKeyMatch && userKeyMatch.length > 0) {
|
||||||
const userKey = userKeyMatch[0]
|
const userKey = userKeyMatch[0]
|
||||||
await this.settingsService.updateSetting(SettingName.ExtensionKey, userKey, true)
|
await this.settingsService.updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.ExtensionKey).getValue(),
|
||||||
|
userKey,
|
||||||
|
true,
|
||||||
|
)
|
||||||
await this.itemManager.changeFeatureRepo(item, (m) => {
|
await this.itemManager.changeFeatureRepo(item, (m) => {
|
||||||
m.migratedToUserSetting = true
|
m.migratedToUserSetting = true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ export class ListedService extends AbstractService implements ListedClientInterf
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getSettingsBasedListedAccounts(): Promise<ListedAccount[]> {
|
private async getSettingsBasedListedAccounts(): Promise<ListedAccount[]> {
|
||||||
const response = await this.settingsService.getSetting(SettingName.ListedAuthorSecrets)
|
const response = await this.settingsService.getSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.ListedAuthorSecrets).getValue(),
|
||||||
|
)
|
||||||
if (!response) {
|
if (!response) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ export class SNMfaService extends AbstractService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async saveMfaSetting(secret: string): Promise<void> {
|
private async saveMfaSetting(secret: string): Promise<void> {
|
||||||
return await this.settingsService.updateSetting(SettingName.MfaSecret, secret, true)
|
return await this.settingsService.updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
|
||||||
|
secret,
|
||||||
|
true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async isMfaActivated(): Promise<boolean> {
|
async isMfaActivated(): Promise<boolean> {
|
||||||
const mfaSetting = await this.settingsService.getDoesSensitiveSettingExist(SettingName.MfaSecret)
|
const mfaSetting = await this.settingsService.getDoesSensitiveSettingExist(
|
||||||
|
SettingName.create(SettingName.NAMES.MfaSecret).getValue(),
|
||||||
|
)
|
||||||
return mfaSetting != false
|
return mfaSetting != false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +49,7 @@ export class SNMfaService extends AbstractService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async disableMfa(): Promise<void> {
|
async disableMfa(): Promise<void> {
|
||||||
return await this.settingsService.deleteSetting(SettingName.MfaSecret)
|
return await this.settingsService.deleteSetting(SettingName.create(SettingName.NAMES.MfaSecret).getValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
override deinit(): void {
|
override deinit(): void {
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
import { SNApiService } from '../Api/ApiService'
|
import { SNApiService } from '../Api/ApiService'
|
||||||
import { SettingsGateway } from './SettingsGateway'
|
import { SettingsGateway } from './SettingsGateway'
|
||||||
import { SNSessionManager } from '../Session/SessionManager'
|
import { SNSessionManager } from '../Session/SessionManager'
|
||||||
import {
|
import { CloudProvider, EmailBackupFrequency, SettingName } from '@standardnotes/settings'
|
||||||
CloudProvider,
|
|
||||||
EmailBackupFrequency,
|
|
||||||
SettingName,
|
|
||||||
SensitiveSettingName,
|
|
||||||
SubscriptionSettingName,
|
|
||||||
} from '@standardnotes/settings'
|
|
||||||
import { ExtensionsServerURL } from '@Lib/Hosts'
|
import { ExtensionsServerURL } from '@Lib/Hosts'
|
||||||
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
|
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services'
|
||||||
import { SettingsClientInterface } from './SettingsClientInterface'
|
import { SettingsClientInterface } from './SettingsClientInterface'
|
||||||
@@ -46,7 +40,7 @@ export class SNSettingsService extends AbstractService implements SettingsClient
|
|||||||
return this.provider.getSetting(name)
|
return this.provider.getSetting(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscriptionSetting(name: SubscriptionSettingName) {
|
async getSubscriptionSetting(name: SettingName) {
|
||||||
return this.provider.getSubscriptionSetting(name)
|
return this.provider.getSubscriptionSetting(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +48,7 @@ export class SNSettingsService extends AbstractService implements SettingsClient
|
|||||||
return this.provider.updateSetting(name, payload, sensitive)
|
return this.provider.updateSetting(name, payload, sensitive)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDoesSensitiveSettingExist(name: SensitiveSettingName) {
|
async getDoesSensitiveSettingExist(name: SettingName) {
|
||||||
return this.provider.getDoesSensitiveSettingExist(name)
|
return this.provider.getDoesSensitiveSettingExist(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SettingName, SensitiveSettingName, EmailBackupFrequency } from '@standardnotes/settings'
|
import { SettingName, EmailBackupFrequency } from '@standardnotes/settings'
|
||||||
import { SettingsList } from './SettingsList'
|
import { SettingsList } from './SettingsList'
|
||||||
|
|
||||||
export interface SettingsClientInterface {
|
export interface SettingsClientInterface {
|
||||||
@@ -6,7 +6,7 @@ export interface SettingsClientInterface {
|
|||||||
|
|
||||||
getSetting(name: SettingName): Promise<string | undefined>
|
getSetting(name: SettingName): Promise<string | undefined>
|
||||||
|
|
||||||
getDoesSensitiveSettingExist(name: SensitiveSettingName): Promise<boolean>
|
getDoesSensitiveSettingExist(name: SettingName): Promise<boolean>
|
||||||
|
|
||||||
updateSetting(name: SettingName, payload: string, sensitive?: boolean): Promise<void>
|
updateSetting(name: SettingName, payload: string, sensitive?: boolean): Promise<void>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SettingsList } from './SettingsList'
|
import { SettingsList } from './SettingsList'
|
||||||
import { SettingName, SensitiveSettingName, SubscriptionSettingName } from '@standardnotes/settings'
|
import { SettingName } from '@standardnotes/settings'
|
||||||
import { API_MESSAGE_INVALID_SESSION } from '@standardnotes/services'
|
import { API_MESSAGE_INVALID_SESSION } from '@standardnotes/services'
|
||||||
import { HttpStatusCode, isErrorResponse, User } from '@standardnotes/responses'
|
import { HttpStatusCode, isErrorResponse, User } from '@standardnotes/responses'
|
||||||
import { SettingsServerInterface } from './SettingsServerInterface'
|
import { SettingsServerInterface } from './SettingsServerInterface'
|
||||||
@@ -46,7 +46,7 @@ export class SettingsGateway {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getSetting(name: SettingName): Promise<string | undefined> {
|
async getSetting(name: SettingName): Promise<string | undefined> {
|
||||||
const response = await this.settingsApi.getSetting(this.userUuid, name)
|
const response = await this.settingsApi.getSetting(this.userUuid, name.value)
|
||||||
|
|
||||||
if (response.status === HttpStatusCode.BadRequest) {
|
if (response.status === HttpStatusCode.BadRequest) {
|
||||||
return undefined
|
return undefined
|
||||||
@@ -59,8 +59,12 @@ export class SettingsGateway {
|
|||||||
return response?.data?.setting?.value ?? undefined
|
return response?.data?.setting?.value ?? undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSubscriptionSetting(name: SubscriptionSettingName): Promise<string | undefined> {
|
async getSubscriptionSetting(name: SettingName): Promise<string | undefined> {
|
||||||
const response = await this.settingsApi.getSubscriptionSetting(this.userUuid, name)
|
if (!name.isASubscriptionSetting()) {
|
||||||
|
throw new Error(`Setting ${name.value} is not a subscription setting`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.settingsApi.getSubscriptionSetting(this.userUuid, name.value)
|
||||||
|
|
||||||
if (response.status === HttpStatusCode.BadRequest) {
|
if (response.status === HttpStatusCode.BadRequest) {
|
||||||
return undefined
|
return undefined
|
||||||
@@ -73,8 +77,12 @@ export class SettingsGateway {
|
|||||||
return response?.data?.setting?.value ?? undefined
|
return response?.data?.setting?.value ?? undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDoesSensitiveSettingExist(name: SensitiveSettingName): Promise<boolean> {
|
async getDoesSensitiveSettingExist(name: SettingName): Promise<boolean> {
|
||||||
const response = await this.settingsApi.getSetting(this.userUuid, name)
|
if (!name.isSensitive()) {
|
||||||
|
throw new Error(`Setting ${name.value} is not sensitive`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.settingsApi.getSetting(this.userUuid, name.value)
|
||||||
|
|
||||||
if (response.status === HttpStatusCode.BadRequest) {
|
if (response.status === HttpStatusCode.BadRequest) {
|
||||||
return false
|
return false
|
||||||
@@ -88,14 +96,14 @@ export class SettingsGateway {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateSetting(name: SettingName, payload: string, sensitive: boolean): Promise<void> {
|
async updateSetting(name: SettingName, payload: string, sensitive: boolean): Promise<void> {
|
||||||
const response = await this.settingsApi.updateSetting(this.userUuid, name, payload, sensitive)
|
const response = await this.settingsApi.updateSetting(this.userUuid, name.value, payload, sensitive)
|
||||||
if (isErrorResponse(response)) {
|
if (isErrorResponse(response)) {
|
||||||
throw new Error(response.data?.error.message)
|
throw new Error(response.data?.error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteSetting(name: SettingName): Promise<void> {
|
async deleteSetting(name: SettingName): Promise<void> {
|
||||||
const response = await this.settingsApi.deleteSetting(this.userUuid, name)
|
const response = await this.settingsApi.deleteSetting(this.userUuid, name.value)
|
||||||
if (isErrorResponse(response)) {
|
if (isErrorResponse(response)) {
|
||||||
throw new Error(response.data?.error.message)
|
throw new Error(response.data?.error.message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,16 +28,16 @@ type SettingType =
|
|||||||
| OneDriveBackupFrequency
|
| OneDriveBackupFrequency
|
||||||
|
|
||||||
export class SettingsList {
|
export class SettingsList {
|
||||||
private map: Partial<Record<SettingName, SettingData>> = {}
|
private map: Partial<Record<string, SettingData>> = {}
|
||||||
|
|
||||||
constructor(settings: SettingData[]) {
|
constructor(settings: SettingData[]) {
|
||||||
for (const setting of settings) {
|
for (const setting of settings) {
|
||||||
this.map[setting.name as SettingName] = setting
|
this.map[setting.name] = setting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSettingValue<T = SettingType, D = SettingType>(setting: SettingName, defaultValue: D): T {
|
getSettingValue<T = SettingType, D = SettingType>(settingName: SettingName, defaultValue: D): T {
|
||||||
const settingData = this.map[setting]
|
const settingData = this.map[settingName.value]
|
||||||
return (settingData?.value as unknown as T) || (defaultValue as unknown as T)
|
return (settingData?.value as unknown as T) || (defaultValue as unknown as T)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ describe('features', () => {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(await application.settings.getDoesSensitiveSettingExist(SettingName.ExtensionKey)).to.equal(false)
|
expect(await application.settings.getDoesSensitiveSettingExist(SettingName.create(SettingName.NAMES.ExtensionKey).getValue())).to.equal(false)
|
||||||
|
|
||||||
const extensionKey = UuidGenerator.GenerateUuid().split('-').join('')
|
const extensionKey = UuidGenerator.GenerateUuid().split('-').join('')
|
||||||
|
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ describe('account recovery', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should get the same recovery code at each consecutive call', async () => {
|
it('should get the same recovery code at each consecutive call', async () => {
|
||||||
let recoveryCodesSetting = await application.settings.getSetting(SettingName.RecoveryCodes)
|
let recoveryCodesSetting = await application.settings.getSetting(SettingName.create(SettingName.NAMES.RecoveryCodes).getValue())
|
||||||
expect(recoveryCodesSetting).to.equal(undefined)
|
expect(recoveryCodesSetting).to.equal(undefined)
|
||||||
|
|
||||||
const generatedRecoveryCodesAfterFirstCall = await application.getRecoveryCodes.execute()
|
const generatedRecoveryCodesAfterFirstCall = await application.getRecoveryCodes.execute()
|
||||||
expect(generatedRecoveryCodesAfterFirstCall.getValue().length).to.equal(49)
|
expect(generatedRecoveryCodesAfterFirstCall.getValue().length).to.equal(49)
|
||||||
|
|
||||||
recoveryCodesSetting = await application.settings.getSetting(SettingName.RecoveryCodes)
|
recoveryCodesSetting = await application.settings.getSetting(SettingName.create(SettingName.NAMES.RecoveryCodes).getValue())
|
||||||
expect(recoveryCodesSetting).to.equal(generatedRecoveryCodesAfterFirstCall.getValue())
|
expect(recoveryCodesSetting).to.equal(generatedRecoveryCodesAfterFirstCall.getValue())
|
||||||
|
|
||||||
const fetchedRecoveryCodesOnTheSecondCall = await application.getRecoveryCodes.execute()
|
const fetchedRecoveryCodesOnTheSecondCall = await application.getRecoveryCodes.execute()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const expect = chai.expect
|
|||||||
describe('settings service', function () {
|
describe('settings service', function () {
|
||||||
this.timeout(Factory.ThirtySecondTimeout)
|
this.timeout(Factory.ThirtySecondTimeout)
|
||||||
|
|
||||||
const validSetting = SettingName.GoogleDriveBackupFrequency
|
const validSetting = SettingName.create(SettingName.NAMES.GoogleDriveBackupFrequency).getValue()
|
||||||
const fakePayload = 'Im so meta even this acronym'
|
const fakePayload = 'Im so meta even this acronym'
|
||||||
const updatedFakePayload = 'is meta'
|
const updatedFakePayload = 'is meta'
|
||||||
|
|
||||||
@@ -98,22 +98,22 @@ describe('settings service', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('reads a nonexistent sensitive setting', async () => {
|
it('reads a nonexistent sensitive setting', async () => {
|
||||||
const setting = await application.settings.getDoesSensitiveSettingExist(SettingName.MfaSecret)
|
const setting = await application.settings.getDoesSensitiveSettingExist(SettingName.create(SettingName.NAMES.MfaSecret).getValue())
|
||||||
expect(setting).to.equal(false)
|
expect(setting).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('creates and reads a sensitive setting', async () => {
|
it('creates and reads a sensitive setting', async () => {
|
||||||
await application.settings.updateSetting(SettingName.MfaSecret, 'fake_secret', true)
|
await application.settings.updateSetting(SettingName.create(SettingName.NAMES.MfaSecret).getValue(), 'fake_secret', true)
|
||||||
const setting = await application.settings.getDoesSensitiveSettingExist(SettingName.MfaSecret)
|
const setting = await application.settings.getDoesSensitiveSettingExist(SettingName.create(SettingName.NAMES.MfaSecret).getValue())
|
||||||
expect(setting).to.equal(true)
|
expect(setting).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('creates and lists a sensitive setting', async () => {
|
it('creates and lists a sensitive setting', async () => {
|
||||||
await application.settings.updateSetting(SettingName.MfaSecret, 'fake_secret', true)
|
await application.settings.updateSetting(SettingName.create(SettingName.NAMES.MfaSecret).getValue(), 'fake_secret', true)
|
||||||
await application.settings.updateSetting(SettingName.MuteFailedBackupsEmails, MuteFailedBackupsEmailsOption.Muted)
|
await application.settings.updateSetting(SettingName.create(SettingName.NAMES.MuteFailedBackupsEmails).getValue(), MuteFailedBackupsEmailsOption.Muted)
|
||||||
const settings = await application.settings.listSettings()
|
const settings = await application.settings.listSettings()
|
||||||
expect(settings.getSettingValue(SettingName.MuteFailedBackupsEmails)).to.eql(MuteFailedBackupsEmailsOption.Muted)
|
expect(settings.getSettingValue(SettingName.create(SettingName.NAMES.MuteFailedBackupsEmails).getValue())).to.eql(MuteFailedBackupsEmailsOption.Muted)
|
||||||
expect(settings.getSettingValue(SettingName.MfaSecret)).to.not.be.ok
|
expect(settings.getSettingValue(SettingName.create(SettingName.NAMES.MfaSecret).getValue())).to.not.be.ok
|
||||||
})
|
})
|
||||||
|
|
||||||
it('reads a subscription setting', async () => {
|
it('reads a subscription setting', async () => {
|
||||||
@@ -135,7 +135,7 @@ describe('settings service', function () {
|
|||||||
|
|
||||||
await Factory.sleep(2)
|
await Factory.sleep(2)
|
||||||
|
|
||||||
const setting = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
const setting = await application.settings.getSubscriptionSetting(SettingName.create(SettingName.NAMES.FileUploadBytesLimit).getValue())
|
||||||
expect(setting).to.be.a('string')
|
expect(setting).to.be.a('string')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -166,10 +166,10 @@ describe('settings service', function () {
|
|||||||
|
|
||||||
await Factory.sleep(1)
|
await Factory.sleep(1)
|
||||||
|
|
||||||
const limitSettingBefore = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
const limitSettingBefore = await application.settings.getSubscriptionSetting(SettingName.create(SettingName.NAMES.FileUploadBytesLimit).getValue())
|
||||||
expect(limitSettingBefore).to.equal('107374182400')
|
expect(limitSettingBefore).to.equal('107374182400')
|
||||||
|
|
||||||
const usedSettingBefore = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_USED')
|
const usedSettingBefore = await application.settings.getSubscriptionSetting(SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue())
|
||||||
expect(usedSettingBefore).to.equal('196')
|
expect(usedSettingBefore).to.equal('196')
|
||||||
|
|
||||||
await Factory.publishMockedEvent('SUBSCRIPTION_EXPIRED', {
|
await Factory.publishMockedEvent('SUBSCRIPTION_EXPIRED', {
|
||||||
@@ -202,10 +202,10 @@ describe('settings service', function () {
|
|||||||
})
|
})
|
||||||
await Factory.sleep(1)
|
await Factory.sleep(1)
|
||||||
|
|
||||||
const limitSettingAfter = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_LIMIT')
|
const limitSettingAfter = await application.settings.getSubscriptionSetting(SettingName.create(SettingName.NAMES.FileUploadBytesLimit).getValue())
|
||||||
expect(limitSettingAfter).to.equal(limitSettingBefore)
|
expect(limitSettingAfter).to.equal(limitSettingBefore)
|
||||||
|
|
||||||
const usedSettingAfter = await application.settings.getSubscriptionSetting('FILE_UPLOAD_BYTES_USED')
|
const usedSettingAfter = await application.settings.getSubscriptionSetting(SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue())
|
||||||
expect(usedSettingAfter).to.equal(usedSettingBefore)
|
expect(usedSettingAfter).to.equal(usedSettingBefore)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
"@standardnotes/responses": "workspace:*",
|
"@standardnotes/responses": "workspace:*",
|
||||||
"@standardnotes/security": "^1.7.6",
|
"@standardnotes/security": "^1.7.6",
|
||||||
"@standardnotes/services": "workspace:*",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/settings": "^1.19.1",
|
"@standardnotes/settings": "^1.20.0",
|
||||||
"@standardnotes/sncrypto-common": "workspace:*",
|
"@standardnotes/sncrypto-common": "workspace:*",
|
||||||
"@standardnotes/sncrypto-web": "workspace:*",
|
"@standardnotes/sncrypto-web": "workspace:*",
|
||||||
"@standardnotes/utils": "workspace:*",
|
"@standardnotes/utils": "workspace:*",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import SignOutWrapper from './SignOutView'
|
|||||||
import FilesSection from './Files'
|
import FilesSection from './Files'
|
||||||
import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
|
import PreferencesPane from '../../PreferencesComponents/PreferencesPane'
|
||||||
import SubscriptionSharing from './SubscriptionSharing/SubscriptionSharing'
|
import SubscriptionSharing from './SubscriptionSharing/SubscriptionSharing'
|
||||||
import Email from './Email'
|
import Email from './Email/Email'
|
||||||
import DeleteAccount from '@/Components/Preferences/Panes/Account/DeleteAccount'
|
import DeleteAccount from '@/Components/Preferences/Panes/Account/DeleteAccount'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { MuteMarketingEmailsOption, MuteSignInEmailsOption, SettingName } from '@standardnotes/snjs'
|
import {
|
||||||
|
FeatureIdentifier,
|
||||||
|
FeatureStatus,
|
||||||
|
MuteMarketingEmailsOption,
|
||||||
|
MuteSignInEmailsOption,
|
||||||
|
SettingName,
|
||||||
|
} from '@standardnotes/snjs'
|
||||||
import { observer } from 'mobx-react-lite'
|
import { observer } from 'mobx-react-lite'
|
||||||
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
import { FunctionComponent, useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
@@ -10,6 +16,7 @@ import { STRING_FAILED_TO_UPDATE_USER_SETTING } from '@/Constants/Strings'
|
|||||||
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/PreferencesGroup'
|
||||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||||
import Spinner from '@/Components/Spinner/Spinner'
|
import Spinner from '@/Components/Spinner/Spinner'
|
||||||
|
import NoProSubscription from '../NoProSubscription'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -20,6 +27,9 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
const [marketingEmailsMutedValue, setMarketingEmailsMutedValue] = useState(MuteMarketingEmailsOption.NotMuted)
|
const [marketingEmailsMutedValue, setMarketingEmailsMutedValue] = useState(MuteMarketingEmailsOption.NotMuted)
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
|
||||||
|
const isMuteSignInEmailsFeatureAvailable =
|
||||||
|
application.features.getFeatureStatus(FeatureIdentifier.SignInAlerts) === FeatureStatus.Entitled
|
||||||
|
|
||||||
const updateSetting = async (settingName: SettingName, payload: string): Promise<boolean> => {
|
const updateSetting = async (settingName: SettingName, payload: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
await application.settings.updateSetting(settingName, payload, false)
|
await application.settings.updateSetting(settingName, payload, false)
|
||||||
@@ -40,13 +50,13 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
const userSettings = await application.settings.listSettings()
|
const userSettings = await application.settings.listSettings()
|
||||||
setSignInEmailsMutedValue(
|
setSignInEmailsMutedValue(
|
||||||
userSettings.getSettingValue<MuteSignInEmailsOption>(
|
userSettings.getSettingValue<MuteSignInEmailsOption>(
|
||||||
SettingName.MuteSignInEmails,
|
SettingName.create(SettingName.NAMES.MuteSignInEmails).getValue(),
|
||||||
MuteSignInEmailsOption.NotMuted,
|
MuteSignInEmailsOption.NotMuted,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
setMarketingEmailsMutedValue(
|
setMarketingEmailsMutedValue(
|
||||||
userSettings.getSettingValue<MuteMarketingEmailsOption>(
|
userSettings.getSettingValue<MuteMarketingEmailsOption>(
|
||||||
SettingName.MuteMarketingEmails,
|
SettingName.create(SettingName.NAMES.MuteMarketingEmails).getValue(),
|
||||||
MuteMarketingEmailsOption.NotMuted,
|
MuteMarketingEmailsOption.NotMuted,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -67,7 +77,10 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
previousValue === MuteSignInEmailsOption.Muted ? MuteSignInEmailsOption.NotMuted : MuteSignInEmailsOption.Muted
|
previousValue === MuteSignInEmailsOption.Muted ? MuteSignInEmailsOption.NotMuted : MuteSignInEmailsOption.Muted
|
||||||
setSignInEmailsMutedValue(newValue)
|
setSignInEmailsMutedValue(newValue)
|
||||||
|
|
||||||
const updateResult = await updateSetting(SettingName.MuteSignInEmails, newValue)
|
const updateResult = await updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.MuteSignInEmails).getValue(),
|
||||||
|
newValue,
|
||||||
|
)
|
||||||
|
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
setSignInEmailsMutedValue(previousValue)
|
setSignInEmailsMutedValue(previousValue)
|
||||||
@@ -82,7 +95,10 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
: MuteMarketingEmailsOption.Muted
|
: MuteMarketingEmailsOption.Muted
|
||||||
setMarketingEmailsMutedValue(newValue)
|
setMarketingEmailsMutedValue(newValue)
|
||||||
|
|
||||||
const updateResult = await updateSetting(SettingName.MuteMarketingEmails, newValue)
|
const updateResult = await updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.MuteMarketingEmails).getValue(),
|
||||||
|
newValue,
|
||||||
|
)
|
||||||
|
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
setMarketingEmailsMutedValue(previousValue)
|
setMarketingEmailsMutedValue(previousValue)
|
||||||
@@ -96,25 +112,40 @@ const Email: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Subtitle>Disable sign-in notification emails</Subtitle>
|
<Subtitle>Sign-in notification emails</Subtitle>
|
||||||
<Text>
|
{isMuteSignInEmailsFeatureAvailable ? (
|
||||||
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
|
<Text>
|
||||||
available only to paid subscribers).
|
Disables email notifications when a new sign-in occurs on your account. (Email notifications are
|
||||||
</Text>
|
available only to paid subscribers).
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<NoProSubscription
|
||||||
|
application={application}
|
||||||
|
text={
|
||||||
|
<span>
|
||||||
|
Sign-in notification emails are available only on a{' '}
|
||||||
|
<span className="font-bold">subscription</span> plan. Please upgrade in order to enable sign-in
|
||||||
|
notifications.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Spinner className="ml-2 flex-shrink-0" />
|
<Spinner className="ml-2 flex-shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<Switch
|
isMuteSignInEmailsFeatureAvailable && (
|
||||||
onChange={toggleMuteSignInEmails}
|
<Switch
|
||||||
checked={signInEmailsMutedValue === MuteSignInEmailsOption.Muted}
|
onChange={toggleMuteSignInEmails}
|
||||||
/>
|
checked={signInEmailsMutedValue === MuteSignInEmailsOption.Muted}
|
||||||
|
/>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<HorizontalSeparator classes="my-4" />
|
<HorizontalSeparator classes="my-4" />
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<Subtitle>Disable marketing notification emails</Subtitle>
|
<Subtitle>Marketing notification emails</Subtitle>
|
||||||
<Text>Disables email notifications with special deals and promotions.</Text>
|
<Text>Disables email notifications with special deals and promotions.</Text>
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
import Spinner from '@/Components/Spinner/Spinner'
|
import Spinner from '@/Components/Spinner/Spinner'
|
||||||
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
import { formatSizeToReadableString } from '@standardnotes/filepicker'
|
||||||
import { SubscriptionSettingName } from '@standardnotes/snjs'
|
import { SettingName } from '@standardnotes/snjs'
|
||||||
import { FunctionComponent, useEffect, useState } from 'react'
|
import { FunctionComponent, useEffect, useState } from 'react'
|
||||||
import { Subtitle, Title } from '../../PreferencesComponents/Content'
|
import { Subtitle, Title } from '../../PreferencesComponents/Content'
|
||||||
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
import PreferencesGroup from '../../PreferencesComponents/PreferencesGroup'
|
||||||
@@ -19,10 +19,10 @@ const FilesSection: FunctionComponent<Props> = ({ application }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getFilesQuota = async () => {
|
const getFilesQuota = async () => {
|
||||||
const filesQuotaUsed = await application.settings.getSubscriptionSetting(
|
const filesQuotaUsed = await application.settings.getSubscriptionSetting(
|
||||||
SubscriptionSettingName.FileUploadBytesUsed,
|
SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(),
|
||||||
)
|
)
|
||||||
const filesQuotaTotal = await application.settings.getSubscriptionSetting(
|
const filesQuotaTotal = await application.settings.getSubscriptionSetting(
|
||||||
SubscriptionSettingName.FileUploadBytesLimit,
|
SettingName.create(SettingName.NAMES.FileUploadBytesLimit).getValue(),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (filesQuotaUsed) {
|
if (filesQuotaUsed) {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { FunctionComponent, useState } from 'react'
|
import { FunctionComponent, ReactNode, useState } from 'react'
|
||||||
import { LinkButton, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
import { LinkButton, Text } from '@/Components/Preferences/PreferencesComponents/Content'
|
||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
import { WebApplication } from '@/Application/Application'
|
import { WebApplication } from '@/Application/Application'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
|
text: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoProSubscription: FunctionComponent<Props> = ({ application }) => {
|
const NoProSubscription: FunctionComponent<Props> = ({ application, text }) => {
|
||||||
const [isLoadingPurchaseFlow, setIsLoadingPurchaseFlow] = useState(false)
|
const [isLoadingPurchaseFlow, setIsLoadingPurchaseFlow] = useState(false)
|
||||||
const [purchaseFlowError, setPurchaseFlowError] = useState<string | undefined>(undefined)
|
const [purchaseFlowError, setPurchaseFlowError] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
@@ -29,10 +30,7 @@ const NoProSubscription: FunctionComponent<Props> = ({ application }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Text>
|
<Text>{text}</Text>
|
||||||
Subscription sharing is available only on the <span className="font-bold">Professional</span> plan. Please
|
|
||||||
upgrade in order to share your subscription.
|
|
||||||
</Text>
|
|
||||||
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
|
{isLoadingPurchaseFlow && <Text>Redirecting you to the subscription page...</Text>}
|
||||||
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
|
{purchaseFlowError && <Text className="text-danger">{purchaseFlowError}</Text>}
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import PreferencesGroup from '@/Components/Preferences/PreferencesComponents/Pre
|
|||||||
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
import PreferencesSegment from '@/Components/Preferences/PreferencesComponents/PreferencesSegment'
|
||||||
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
import HorizontalSeparator from '@/Components/Shared/HorizontalSeparator'
|
||||||
|
|
||||||
import NoProSubscription from './NoProSubscription'
|
import NoProSubscription from '../NoProSubscription'
|
||||||
import InvitationsList from './InvitationsList'
|
import InvitationsList from './InvitationsList'
|
||||||
import Invite from './Invite/Invite'
|
import Invite from './Invite/Invite'
|
||||||
import Button from '@/Components/Button/Button'
|
import Button from '@/Components/Button/Button'
|
||||||
@@ -57,7 +57,15 @@ const SubscriptionSharing: FunctionComponent<Props> = ({ application, viewContro
|
|||||||
</ModalOverlay>
|
</ModalOverlay>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<NoProSubscription application={application} />
|
<NoProSubscription
|
||||||
|
application={application}
|
||||||
|
text={
|
||||||
|
<span>
|
||||||
|
Subscription sharing is available only on the <span className="font-bold">Professional</span> plan.
|
||||||
|
Please upgrade in order to share your subscription.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -82,18 +82,18 @@ const CloudBackupProvider: FunctionComponent<Props> = ({ application, providerNa
|
|||||||
|
|
||||||
const backupSettingsData = {
|
const backupSettingsData = {
|
||||||
[CloudProvider.Dropbox]: {
|
[CloudProvider.Dropbox]: {
|
||||||
backupTokenSettingName: SettingName.DropboxBackupToken,
|
backupTokenSettingName: SettingName.create(SettingName.NAMES.DropboxBackupToken).getValue(),
|
||||||
backupFrequencySettingName: SettingName.DropboxBackupFrequency,
|
backupFrequencySettingName: SettingName.create(SettingName.NAMES.DropboxBackupFrequency).getValue(),
|
||||||
defaultBackupFrequency: DropboxBackupFrequency.Daily,
|
defaultBackupFrequency: DropboxBackupFrequency.Daily,
|
||||||
},
|
},
|
||||||
[CloudProvider.Google]: {
|
[CloudProvider.Google]: {
|
||||||
backupTokenSettingName: SettingName.GoogleDriveBackupToken,
|
backupTokenSettingName: SettingName.create(SettingName.NAMES.GoogleDriveBackupToken).getValue(),
|
||||||
backupFrequencySettingName: SettingName.GoogleDriveBackupFrequency,
|
backupFrequencySettingName: SettingName.create(SettingName.NAMES.GoogleDriveBackupFrequency).getValue(),
|
||||||
defaultBackupFrequency: GoogleDriveBackupFrequency.Daily,
|
defaultBackupFrequency: GoogleDriveBackupFrequency.Daily,
|
||||||
},
|
},
|
||||||
[CloudProvider.OneDrive]: {
|
[CloudProvider.OneDrive]: {
|
||||||
backupTokenSettingName: SettingName.OneDriveBackupToken,
|
backupTokenSettingName: SettingName.create(SettingName.NAMES.OneDriveBackupToken).getValue(),
|
||||||
backupFrequencySettingName: SettingName.OneDriveBackupFrequency,
|
backupFrequencySettingName: SettingName.create(SettingName.NAMES.OneDriveBackupFrequency).getValue(),
|
||||||
defaultBackupFrequency: OneDriveBackupFrequency.Daily,
|
defaultBackupFrequency: OneDriveBackupFrequency.Daily,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
|||||||
setIsFailedCloudBackupEmailMuted(
|
setIsFailedCloudBackupEmailMuted(
|
||||||
convertStringifiedBooleanToBoolean(
|
convertStringifiedBooleanToBoolean(
|
||||||
userSettings.getSettingValue(
|
userSettings.getSettingValue(
|
||||||
SettingName.MuteFailedCloudBackupsEmails,
|
SettingName.create(SettingName.NAMES.MuteFailedCloudBackupsEmails).getValue(),
|
||||||
MuteFailedCloudBackupsEmailsOption.NotMuted,
|
MuteFailedCloudBackupsEmailsOption.NotMuted,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -83,7 +83,7 @@ const CloudLink: FunctionComponent<Props> = ({ application }) => {
|
|||||||
setIsFailedCloudBackupEmailMuted(!isFailedCloudBackupEmailMuted)
|
setIsFailedCloudBackupEmailMuted(!isFailedCloudBackupEmailMuted)
|
||||||
|
|
||||||
const updateResult = await updateSetting(
|
const updateResult = await updateSetting(
|
||||||
SettingName.MuteFailedCloudBackupsEmails,
|
SettingName.create(SettingName.NAMES.MuteFailedCloudBackupsEmails).getValue(),
|
||||||
`${!isFailedCloudBackupEmailMuted}`,
|
`${!isFailedCloudBackupEmailMuted}`,
|
||||||
)
|
)
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ const EmailBackups = ({ application }: Props) => {
|
|||||||
const userSettings = await application.settings.listSettings()
|
const userSettings = await application.settings.listSettings()
|
||||||
setEmailFrequency(
|
setEmailFrequency(
|
||||||
userSettings.getSettingValue<EmailBackupFrequency>(
|
userSettings.getSettingValue<EmailBackupFrequency>(
|
||||||
SettingName.EmailBackupFrequency,
|
SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue(),
|
||||||
EmailBackupFrequency.Disabled,
|
EmailBackupFrequency.Disabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
setIsFailedBackupEmailMuted(
|
setIsFailedBackupEmailMuted(
|
||||||
convertStringifiedBooleanToBoolean(
|
convertStringifiedBooleanToBoolean(
|
||||||
userSettings.getSettingValue<MuteFailedBackupsEmailsOption>(
|
userSettings.getSettingValue<MuteFailedBackupsEmailsOption>(
|
||||||
SettingName.MuteFailedBackupsEmails,
|
SettingName.create(SettingName.NAMES.MuteFailedBackupsEmails).getValue(),
|
||||||
MuteFailedBackupsEmailsOption.NotMuted,
|
MuteFailedBackupsEmailsOption.NotMuted,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -81,7 +81,10 @@ const EmailBackups = ({ application }: Props) => {
|
|||||||
const previousFrequency = emailFrequency
|
const previousFrequency = emailFrequency
|
||||||
setEmailFrequency(frequency)
|
setEmailFrequency(frequency)
|
||||||
|
|
||||||
const updateResult = await updateSetting(SettingName.EmailBackupFrequency, frequency)
|
const updateResult = await updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue(),
|
||||||
|
frequency,
|
||||||
|
)
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
setEmailFrequency(previousFrequency)
|
setEmailFrequency(previousFrequency)
|
||||||
}
|
}
|
||||||
@@ -91,7 +94,10 @@ const EmailBackups = ({ application }: Props) => {
|
|||||||
const previousValue = isFailedBackupEmailMuted
|
const previousValue = isFailedBackupEmailMuted
|
||||||
setIsFailedBackupEmailMuted(!isFailedBackupEmailMuted)
|
setIsFailedBackupEmailMuted(!isFailedBackupEmailMuted)
|
||||||
|
|
||||||
const updateResult = await updateSetting(SettingName.MuteFailedBackupsEmails, `${!isFailedBackupEmailMuted}`)
|
const updateResult = await updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.MuteFailedBackupsEmails).getValue(),
|
||||||
|
`${!isFailedBackupEmailMuted}`,
|
||||||
|
)
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
setIsFailedBackupEmailMuted(previousValue)
|
setIsFailedBackupEmailMuted(previousValue)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const Privacy: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
const userSettings = await application.settings.listSettings()
|
const userSettings = await application.settings.listSettings()
|
||||||
setSessionUaLoggingValue(
|
setSessionUaLoggingValue(
|
||||||
userSettings.getSettingValue<LogSessionUserAgentOption>(
|
userSettings.getSettingValue<LogSessionUserAgentOption>(
|
||||||
SettingName.LogSessionUserAgent,
|
SettingName.create(SettingName.NAMES.LogSessionUserAgent).getValue(),
|
||||||
LogSessionUserAgentOption.Enabled,
|
LogSessionUserAgentOption.Enabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -62,7 +62,10 @@ const Privacy: FunctionComponent<Props> = ({ application }: Props) => {
|
|||||||
: LogSessionUserAgentOption.Enabled
|
: LogSessionUserAgentOption.Enabled
|
||||||
setSessionUaLoggingValue(newValue)
|
setSessionUaLoggingValue(newValue)
|
||||||
|
|
||||||
const updateResult = await updateSetting(SettingName.LogSessionUserAgent, newValue)
|
const updateResult = await updateSetting(
|
||||||
|
SettingName.create(SettingName.NAMES.LogSessionUserAgent).getValue(),
|
||||||
|
newValue,
|
||||||
|
)
|
||||||
|
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
setSessionUaLoggingValue(previousValue)
|
setSessionUaLoggingValue(previousValue)
|
||||||
|
|||||||
11
yarn.lock
11
yarn.lock
@@ -5317,12 +5317,13 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@standardnotes/settings@npm:^1.19.1":
|
"@standardnotes/settings@npm:^1.20.0":
|
||||||
version: 1.19.1
|
version: 1.20.0
|
||||||
resolution: "@standardnotes/settings@npm:1.19.1"
|
resolution: "@standardnotes/settings@npm:1.20.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@standardnotes/domain-core": ^1.11.3
|
||||||
reflect-metadata: ^0.1.13
|
reflect-metadata: ^0.1.13
|
||||||
checksum: d99d49d4401ac8c973284d8637195c0441e6f73b7e01e9eb4ab14feb27c6d2e928a493d944ed67ea1d6f229bf5ea839b5dd667c373b917aed321bb32972daacb
|
checksum: 6e8c03107eec03e3d800bb9ced690dbade6713f7ef5e19364aaf06a86a92d2d58ac629d2e948775223ae9798be3ec60edfc5c24a8f7a4d57de959e5937727f44
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -5403,7 +5404,7 @@ __metadata:
|
|||||||
"@standardnotes/responses": "workspace:*"
|
"@standardnotes/responses": "workspace:*"
|
||||||
"@standardnotes/security": ^1.7.6
|
"@standardnotes/security": ^1.7.6
|
||||||
"@standardnotes/services": "workspace:*"
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/settings": ^1.19.1
|
"@standardnotes/settings": ^1.20.0
|
||||||
"@standardnotes/sncrypto-common": "workspace:*"
|
"@standardnotes/sncrypto-common": "workspace:*"
|
||||||
"@standardnotes/sncrypto-web": "workspace:*"
|
"@standardnotes/sncrypto-web": "workspace:*"
|
||||||
"@standardnotes/utils": "workspace:*"
|
"@standardnotes/utils": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user