Skip to content

Commit

Permalink
feat: Themes and appeareance settings are now local to your device an…
Browse files Browse the repository at this point in the history
…d not synced (#2847)
  • Loading branch information
amanharwara committed Feb 17, 2024
1 parent 3d1a038 commit bfbf9ab
Show file tree
Hide file tree
Showing 18 changed files with 250 additions and 117 deletions.
10 changes: 5 additions & 5 deletions packages/models/src/Domain/Syncable/UserPrefs/PrefDefaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export const PrefDefaults = {
[PrefKey.NotesHideDate]: false,
[PrefKey.NotesHideTags]: false,
[PrefKey.NotesHideEditorIcon]: false,
[PrefKey.UseSystemColorScheme]: false,
[PrefKey.UseTranslucentUI]: true,
[PrefKey.AutoLightThemeIdentifier]: 'Default',
[PrefKey.AutoDarkThemeIdentifier]: NativeFeatureIdentifier.TYPES.DarkTheme,
[PrefKey.DEPRECATED_UseSystemColorScheme]: false,
[PrefKey.DEPRECATED_UseTranslucentUI]: true,
[PrefKey.DEPRECATED_AutoLightThemeIdentifier]: 'Default',
[PrefKey.DEPRECATED_AutoDarkThemeIdentifier]: NativeFeatureIdentifier.TYPES.DarkTheme,
[PrefKey.NoteAddToParentFolders]: true,
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat.CurrentDateAndTime,
[PrefKey.CustomNoteTitleFormat]: 'YYYY-MM-DD [at] hh:mm A',
Expand All @@ -46,7 +46,7 @@ export const PrefDefaults = {
[PrefKey.SystemViewPreferences]: {},
[PrefKey.AuthenticatorNames]: '',
[PrefKey.ComponentPreferences]: {},
[PrefKey.ActiveThemes]: [],
[PrefKey.DEPRECATED_ActiveThemes]: [],
[PrefKey.ActiveComponents]: [],
[PrefKey.AlwaysShowSuperToolbar]: true,
[PrefKey.AddImportsToTag]: true,
Expand Down
20 changes: 10 additions & 10 deletions packages/models/src/Domain/Syncable/UserPrefs/PrefKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ export enum PrefKey {
NotesHideDate = 'hideDate',
NotesHideTags = 'hideTags',
NotesHideEditorIcon = 'hideEditorIcon',
UseSystemColorScheme = 'useSystemColorScheme',
UseTranslucentUI = 'useTranslucentUI',
AutoLightThemeIdentifier = 'autoLightThemeIdentifier',
AutoDarkThemeIdentifier = 'autoDarkThemeIdentifier',
NoteAddToParentFolders = 'noteAddToParentFolders',
NewNoteTitleFormat = 'newNoteTitleFormat',
CustomNoteTitleFormat = 'customNoteTitleFormat',
Expand All @@ -47,12 +43,16 @@ export enum PrefKey {
AuthenticatorNames = 'authenticatorNames',
PaneGesturesEnabled = 'paneGesturesEnabled',
ComponentPreferences = 'componentPreferences',
ActiveThemes = 'activeThemes',
ActiveComponents = 'activeComponents',
AlwaysShowSuperToolbar = 'alwaysShowSuperToolbar',
AddImportsToTag = 'addImportsToTag',
AlwaysCreateNewTagForImports = 'alwaysCreateNewTagForImports',
ExistingTagForImports = 'existingTagForImports',
DEPRECATED_ActiveThemes = 'activeThemes',
DEPRECATED_UseSystemColorScheme = 'useSystemColorScheme',
DEPRECATED_UseTranslucentUI = 'useTranslucentUI',
DEPRECATED_AutoLightThemeIdentifier = 'autoLightThemeIdentifier',
DEPRECATED_AutoDarkThemeIdentifier = 'autoDarkThemeIdentifier',
}

export type PrefValue = {
Expand All @@ -73,10 +73,11 @@ export type PrefValue = {
[PrefKey.NotesHideDate]: boolean
[PrefKey.NotesHideTags]: boolean
[PrefKey.NotesHideEditorIcon]: boolean
[PrefKey.UseSystemColorScheme]: boolean
[PrefKey.UseTranslucentUI]: boolean
[PrefKey.AutoLightThemeIdentifier]: string
[PrefKey.AutoDarkThemeIdentifier]: string
[PrefKey.DEPRECATED_ActiveThemes]: string[]
[PrefKey.DEPRECATED_UseSystemColorScheme]: boolean
[PrefKey.DEPRECATED_UseTranslucentUI]: boolean
[PrefKey.DEPRECATED_AutoLightThemeIdentifier]: string
[PrefKey.DEPRECATED_AutoDarkThemeIdentifier]: string
[PrefKey.NoteAddToParentFolders]: boolean
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat
[PrefKey.CustomNoteTitleFormat]: string
Expand All @@ -95,7 +96,6 @@ export type PrefValue = {
[PrefKey.AuthenticatorNames]: string
[PrefKey.PaneGesturesEnabled]: boolean
[PrefKey.ComponentPreferences]: AllComponentPreferences
[PrefKey.ActiveThemes]: string[]
[PrefKey.ActiveComponents]: string[]
[PrefKey.AlwaysShowSuperToolbar]: boolean
[PrefKey.AddImportsToTag]: boolean
Expand Down
1 change: 1 addition & 0 deletions packages/services/src/Domain/Event/ApplicationEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export enum ApplicationEvent {
/** When StorageService is ready (but NOT yet decrypted) to start servicing read/write requests */
StorageReady = 'Application:StorageReady',
PreferencesChanged = 'Application:PreferencesChanged',
LocalPreferencesChanged = 'Application:LocalPreferencesChanged',
UnprotectedSessionBegan = 'Application:UnprotectedSessionBegan',
UserRolesChanged = 'Application:UserRolesChanged',
FeaturesAvailabilityChanged = 'Application:FeaturesAvailabilityChanged',
Expand Down
15 changes: 15 additions & 0 deletions packages/services/src/Domain/Preferences/LocalPrefKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export enum LocalPrefKey {
ActiveThemes = 'activeThemes',
UseSystemColorScheme = 'useSystemColorScheme',
UseTranslucentUI = 'useTranslucentUI',
AutoLightThemeIdentifier = 'autoLightThemeIdentifier',
AutoDarkThemeIdentifier = 'autoDarkThemeIdentifier',
}

export type LocalPrefValue = {
[LocalPrefKey.ActiveThemes]: string[]
[LocalPrefKey.UseSystemColorScheme]: boolean
[LocalPrefKey.UseTranslucentUI]: boolean
[LocalPrefKey.AutoLightThemeIdentifier]: string
[LocalPrefKey.AutoDarkThemeIdentifier]: string
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { PrefKey, PrefValue } from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'
import { LocalPrefKey, LocalPrefValue } from './LocalPrefKey'

export enum PreferencesServiceEvent {
LocalPreferencesChanged = 'LocalPreferencesChanged',
PreferencesChanged = 'PreferencesChanged',
}

Expand All @@ -10,7 +12,15 @@ export interface PreferenceServiceInterface extends AbstractService<PreferencesS
getValue<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K] | undefined): PrefValue[K] | undefined

getLocalValue<K extends LocalPrefKey>(key: K, defaultValue: LocalPrefValue[K]): LocalPrefValue[K]
getLocalValue<K extends LocalPrefKey>(key: K, defaultValue?: LocalPrefValue[K]): LocalPrefValue[K] | undefined
getLocalValue<K extends LocalPrefKey>(
key: K,
defaultValue: LocalPrefValue[K] | undefined,
): LocalPrefValue[K] | undefined

setValue<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void>
/** Set value without triggering sync or event notifications */
setValueDetached<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void>
setLocalValue<K extends LocalPrefKey>(key: K, value: LocalPrefValue[K]): void
}
1 change: 1 addition & 0 deletions packages/services/src/Domain/Storage/StorageKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export enum StorageKey {
FileBackupsLocation = 'file_backups_location',
VaultSelectionOptions = 'vault_selection_options',
Subscription = 'subscription',
LocalPreferences = 'local_preferences',
}

export enum NonwrappedStorageKey {
Expand Down
1 change: 1 addition & 0 deletions packages/services/src/Domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ export * from './KeySystem/KeySystemKeyManager'
export * from './Mfa/MfaServiceInterface'
export * from './Mutator/MutatorClientInterface'
export * from './Payloads/PayloadManagerInterface'
export * from './Preferences/LocalPrefKey'
export * from './Preferences/PreferenceId'
export * from './Preferences/PreferenceServiceInterface'
export * from './Protection/MobileUnlockTiming'
Expand Down
9 changes: 7 additions & 2 deletions packages/snjs/lib/Application/Application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
CreateDecryptedBackupFile,
CreateEncryptedBackupFile,
WebSocketsService,
PreferencesServiceEvent,
} from '@standardnotes/services'
import {
SNNote,
Expand Down Expand Up @@ -326,8 +327,12 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli

const preferencesService = this.dependencies.get<PreferencesService>(TYPES.PreferencesService)
this.serviceObservers.push(
preferencesService.addEventObserver(() => {
void this.notifyEvent(ApplicationEvent.PreferencesChanged)
preferencesService.addEventObserver((event) => {
if (event === PreferencesServiceEvent.PreferencesChanged) {
void this.notifyEvent(ApplicationEvent.PreferencesChanged)
} else if (event === PreferencesServiceEvent.LocalPreferencesChanged) {
void this.notifyEvent(ApplicationEvent.LocalPreferencesChanged)
}
}),
)

Expand Down
2 changes: 2 additions & 0 deletions packages/snjs/lib/Application/Dependencies/Dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ import {
DesignateSurvivor,
SyncBackoffService,
SyncBackoffServiceInterface,
StorageServiceInterface,
} from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
Expand Down Expand Up @@ -1282,6 +1283,7 @@ export class Dependencies {
this.get<ItemManager>(TYPES.ItemManager),
this.get<MutatorService>(TYPES.MutatorService),
this.get<SyncService>(TYPES.SyncService),
this.get<StorageServiceInterface>(TYPES.DiskStorageService),
this.get<InternalEventBus>(TYPES.InternalEventBus),
)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/snjs/lib/Migrations/Versions/2_202_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class Migration2_202_1 extends Migration {
const activeThemes = allActiveitems.filter((component) => component.isTheme())
const activeComponents = allActiveitems.filter((component) => !component.isTheme())

await this.services.preferences.setValueDetached(PrefKey.ActiveThemes, Uuids(activeThemes))
await this.services.preferences.setValueDetached(PrefKey.DEPRECATED_ActiveThemes, Uuids(activeThemes))
await this.services.preferences.setValueDetached(PrefKey.ActiveComponents, Uuids(activeComponents))
}
}
55 changes: 55 additions & 0 deletions packages/snjs/lib/Migrations/Versions/2_208_0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { LocalPrefKey, ApplicationStage } from '@standardnotes/services'
import { Migration } from '@Lib/Migrations/Migration'
import { PrefDefaults, PrefKey } from '@standardnotes/models'

export class Migration2_208_0 extends Migration {
static override version(): string {
return '2.208.0'
}

protected registerStageHandlers(): void {
this.registerStageHandler(ApplicationStage.FullSyncCompleted_13, async () => {
await this.migrateSyncedPreferencesToLocal()

this.markDone()
})
}

private async migrateSyncedPreferencesToLocal(): Promise<void> {
this.services.preferences.setLocalValue(
LocalPrefKey.ActiveThemes,
this.services.preferences.getValue(
PrefKey.DEPRECATED_ActiveThemes,
PrefDefaults[PrefKey.DEPRECATED_ActiveThemes],
),
)
this.services.preferences.setLocalValue(
LocalPrefKey.UseSystemColorScheme,
this.services.preferences.getValue(
PrefKey.DEPRECATED_UseSystemColorScheme,
PrefDefaults[PrefKey.DEPRECATED_UseSystemColorScheme],
),
)
this.services.preferences.setLocalValue(
LocalPrefKey.AutoLightThemeIdentifier,
this.services.preferences.getValue(
PrefKey.DEPRECATED_AutoLightThemeIdentifier,
PrefDefaults[PrefKey.DEPRECATED_AutoLightThemeIdentifier],
),
)
this.services.preferences.setLocalValue(
LocalPrefKey.AutoDarkThemeIdentifier,
this.services.preferences.getValue(
PrefKey.DEPRECATED_AutoDarkThemeIdentifier,
PrefDefaults[PrefKey.DEPRECATED_AutoDarkThemeIdentifier],
),
)
this.services.preferences.setLocalValue(
LocalPrefKey.UseTranslucentUI,
this.services.preferences.getValue(
PrefKey.DEPRECATED_UseTranslucentUI,
PrefDefaults[PrefKey.DEPRECATED_UseTranslucentUI],
),
)
}
}
3 changes: 3 additions & 0 deletions packages/snjs/lib/Migrations/Versions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Migration2_42_0 } from './2_42_0'
import { Migration2_167_6 } from './2_167_6'
import { Migration2_168_6 } from './2_168_6'
import { Migration2_202_1 } from './2_202_1'
import { Migration2_208_0 } from './2_208_0'

export const MigrationClasses = [
Migration2_0_15,
Expand All @@ -16,6 +17,7 @@ export const MigrationClasses = [
Migration2_167_6,
Migration2_168_6,
Migration2_202_1,
Migration2_208_0,
]

export {
Expand All @@ -27,4 +29,5 @@ export {
Migration2_167_6,
Migration2_168_6,
Migration2_202_1,
Migration2_208_0,
}
29 changes: 15 additions & 14 deletions packages/snjs/lib/Services/ComponentManager/ComponentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
ItemManagerInterface,
SyncServiceInterface,
FeatureStatus,
LocalPrefKey,
} from '@standardnotes/services'
import { GetFeatureUrl } from './UseCase/GetFeatureUrl'
import { ComponentManagerEventData } from './ComponentManagerEventData'
Expand Down Expand Up @@ -393,7 +394,7 @@ export class ComponentManager
this.logger.info('Toggling theme', uiFeature.uniqueIdentifier)

if (this.isThemeActive(uiFeature)) {
await this.removeActiveTheme(uiFeature)
this.removeActiveTheme(uiFeature)
return
}

Expand All @@ -403,7 +404,7 @@ export class ComponentManager
}

/* Activate current before deactivating others, so as not to flicker */
await this.addActiveTheme(uiFeature)
this.addActiveTheme(uiFeature)

/* Deactive currently active theme(s) if new theme is not layerable */
if (!uiFeature.layerable) {
Expand All @@ -416,7 +417,7 @@ export class ComponentManager
}

if (!candidate.layerable) {
await this.removeActiveTheme(candidate)
this.removeActiveTheme(candidate)
}
}
}
Expand Down Expand Up @@ -453,7 +454,7 @@ export class ComponentManager
const features: NativeFeatureIdentifier[] = []
const uuids: Uuid[] = []

const strings = this.preferences.getValue(PrefKey.ActiveThemes, undefined) ?? []
const strings = this.preferences.getLocalValue(LocalPrefKey.ActiveThemes, [])
for (const string of strings) {
const nativeIdentifier = NativeFeatureIdentifier.create(string)
if (!nativeIdentifier.isFailed()) {
Expand Down Expand Up @@ -534,38 +535,38 @@ export class ComponentManager
return preferences[preferencesLookupKey]
}

async addActiveTheme(theme: UIFeature<ThemeFeatureDescription>): Promise<void> {
const activeThemes = (this.preferences.getValue(PrefKey.ActiveThemes, undefined) ?? []).slice()
addActiveTheme(theme: UIFeature<ThemeFeatureDescription>) {
const activeThemes = this.preferences.getLocalValue(LocalPrefKey.ActiveThemes, []).slice()

activeThemes.push(theme.uniqueIdentifier.value)

await this.preferences.setValue(PrefKey.ActiveThemes, activeThemes)
this.preferences.setLocalValue(LocalPrefKey.ActiveThemes, activeThemes)
}

async replaceActiveTheme(theme: UIFeature<ThemeFeatureDescription>): Promise<void> {
await this.preferences.setValue(PrefKey.ActiveThemes, [theme.uniqueIdentifier.value])
replaceActiveTheme(theme: UIFeature<ThemeFeatureDescription>) {
this.preferences.setLocalValue(LocalPrefKey.ActiveThemes, [theme.uniqueIdentifier.value])
}

async removeActiveTheme(theme: UIFeature<ThemeFeatureDescription>): Promise<void> {
const activeThemes = this.preferences.getValue(PrefKey.ActiveThemes, undefined) ?? []
removeActiveTheme(theme: UIFeature<ThemeFeatureDescription>) {
const activeThemes = this.preferences.getLocalValue(LocalPrefKey.ActiveThemes, [])

const filteredThemes = activeThemes.filter((activeTheme) => activeTheme !== theme.uniqueIdentifier.value)

await this.preferences.setValue(PrefKey.ActiveThemes, filteredThemes)
this.preferences.setLocalValue(LocalPrefKey.ActiveThemes, filteredThemes)
}

isThemeActive(theme: UIFeature<ThemeFeatureDescription>): boolean {
if (this.features.getFeatureStatus(theme.uniqueIdentifier) !== FeatureStatus.Entitled) {
return false
}

const activeThemes = this.preferences.getValue(PrefKey.ActiveThemes, undefined) ?? []
const activeThemes = this.preferences.getLocalValue(LocalPrefKey.ActiveThemes, [])

return activeThemes.includes(theme.uniqueIdentifier.value)
}

async addActiveComponent(component: ComponentInterface): Promise<void> {
const activeComponents = (this.preferences.getValue(PrefKey.ActiveComponents, undefined) ?? []).slice()
const activeComponents = this.preferences.getValue(PrefKey.ActiveComponents, []).slice()

activeComponents.push(component.uuid)

Expand Down

0 comments on commit bfbf9ab

Please sign in to comment.