feat: Themes and appeareance settings are now local to your device and not synced (#2847)
This commit is contained in:
@@ -81,6 +81,7 @@ import {
|
||||
CreateDecryptedBackupFile,
|
||||
CreateEncryptedBackupFile,
|
||||
WebSocketsService,
|
||||
PreferencesServiceEvent,
|
||||
} from '@standardnotes/services'
|
||||
import {
|
||||
SNNote,
|
||||
@@ -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)
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -147,6 +147,7 @@ import {
|
||||
DesignateSurvivor,
|
||||
SyncBackoffService,
|
||||
SyncBackoffServiceInterface,
|
||||
StorageServiceInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '../../Services/Items/ItemManager'
|
||||
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
|
||||
@@ -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),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -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
packages/snjs/lib/Migrations/Versions/2_208_0.ts
Normal file
55
packages/snjs/lib/Migrations/Versions/2_208_0.ts
Normal file
@@ -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],
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -16,6 +17,7 @@ export const MigrationClasses = [
|
||||
Migration2_167_6,
|
||||
Migration2_168_6,
|
||||
Migration2_202_1,
|
||||
Migration2_208_0,
|
||||
]
|
||||
|
||||
export {
|
||||
@@ -27,4 +29,5 @@ export {
|
||||
Migration2_167_6,
|
||||
Migration2_168_6,
|
||||
Migration2_202_1,
|
||||
Migration2_208_0,
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import {
|
||||
ItemManagerInterface,
|
||||
SyncServiceInterface,
|
||||
FeatureStatus,
|
||||
LocalPrefKey,
|
||||
} from '@standardnotes/services'
|
||||
import { GetFeatureUrl } from './UseCase/GetFeatureUrl'
|
||||
import { ComponentManagerEventData } from './ComponentManagerEventData'
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
@@ -416,7 +417,7 @@ export class ComponentManager
|
||||
}
|
||||
|
||||
if (!candidate.layerable) {
|
||||
await this.removeActiveTheme(candidate)
|
||||
this.removeActiveTheme(candidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()) {
|
||||
@@ -534,24 +535,24 @@ 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 {
|
||||
@@ -559,13 +560,13 @@ export class ComponentManager
|
||||
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)
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
InternalEventInterface,
|
||||
ApplicationEvent,
|
||||
ApplicationStageChangedEventPayload,
|
||||
StorageServiceInterface,
|
||||
StorageKey,
|
||||
LocalPrefKey,
|
||||
LocalPrefValue,
|
||||
} from '@standardnotes/services'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
@@ -24,6 +28,7 @@ export class PreferencesService
|
||||
private shouldReload = true
|
||||
private reloading = false
|
||||
private preferences?: SNUserPrefs
|
||||
private localPreferences: { [key in LocalPrefKey]?: LocalPrefValue[key] } = {}
|
||||
private removeItemObserver?: () => void
|
||||
private removeSyncObserver?: () => void
|
||||
|
||||
@@ -32,6 +37,7 @@ export class PreferencesService
|
||||
items: ItemManager,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncService,
|
||||
private storage: StorageServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
@@ -69,16 +75,36 @@ export class PreferencesService
|
||||
if (this.preferences) {
|
||||
void this.notifyEvent(PreferencesServiceEvent.PreferencesChanged)
|
||||
}
|
||||
} else if (stage === ApplicationStage.StorageDecrypted_09) {
|
||||
this.localPreferences = this.storage.getValue(StorageKey.LocalPreferences) ?? {}
|
||||
void this.notifyEvent(PreferencesServiceEvent.LocalPreferencesChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLocalValue<K extends LocalPrefKey>(
|
||||
key: K,
|
||||
defaultValue: LocalPrefValue[K] | undefined,
|
||||
): LocalPrefValue[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 {
|
||||
return this.localPreferences[key] ?? defaultValue
|
||||
}
|
||||
|
||||
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K] | undefined): PrefValue[K] | undefined
|
||||
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K]): PrefValue[K]
|
||||
getValue<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined {
|
||||
return this.preferences?.getPref(key) ?? defaultValue
|
||||
}
|
||||
|
||||
setLocalValue<K extends LocalPrefKey>(key: K, value: LocalPrefValue[K]): void {
|
||||
this.localPreferences[key] = value
|
||||
|
||||
this.storage.setValue(StorageKey.LocalPreferences, this.localPreferences)
|
||||
|
||||
void this.notifyEvent(PreferencesServiceEvent.LocalPreferencesChanged)
|
||||
}
|
||||
|
||||
async setValue<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void> {
|
||||
await this.setValueDetached(key, value)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user