From 27d2c95b5b82645bf6c0372979f05236c83a10f6 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Mon, 10 Oct 2022 21:47:57 +0530 Subject: [PATCH] fix: dark mode not working in editors (#1773) --- .../src/Domain/Feature/FeatureIdentifier.ts | 2 +- packages/features/src/Domain/Lists/Themes.ts | 8 +-- .../src/Domain/Files/FileService.spec.ts | 2 +- packages/snjs/lib/Application/Application.ts | 4 +- .../lib/Services/Features/FeaturesService.ts | 26 ++++++- packages/snjs/mocha/application.test.js | 15 ++-- packages/snjs/mocha/auth-fringe-cases.test.js | 13 ++-- packages/snjs/mocha/auth.test.js | 9 ++- packages/snjs/mocha/backups.test.js | 5 +- packages/snjs/mocha/features.test.js | 19 +++-- packages/snjs/mocha/lib/Applications.js | 4 ++ .../snjs/mocha/migrations/migration.test.js | 5 +- .../snjs/mocha/model_tests/appmodels.test.js | 4 +- .../snjs/mocha/model_tests/importing.test.js | 30 ++++---- packages/snjs/mocha/model_tests/items.test.js | 5 +- .../snjs/mocha/model_tests/mapping.test.js | 5 +- .../snjs/mocha/model_tests/notes_tags.test.js | 5 +- packages/snjs/mocha/session.test.js | 5 +- packages/snjs/mocha/singletons.test.js | 6 +- packages/snjs/mocha/storage.test.js | 10 +-- .../snjs/mocha/sync_tests/conflicting.test.js | 6 +- .../snjs/mocha/sync_tests/integrity.test.js | 5 +- .../snjs/mocha/sync_tests/offline.test.js | 5 +- packages/snjs/mocha/sync_tests/online.test.js | 10 +-- .../ui-services/src/Theme/ThemeManager.ts | 13 +--- .../Preferences/Panes/Appearance.tsx | 5 -- .../QuickSettingsMenu/QuickSettingsMenu.tsx | 14 ---- .../web/src/javascripts/Utils/SortThemes.ts | 5 ++ packages/web/src/stylesheets/_dark-mode.scss | 71 ------------------- packages/web/src/stylesheets/index.css.scss | 1 - 30 files changed, 127 insertions(+), 190 deletions(-) delete mode 100644 packages/web/src/stylesheets/_dark-mode.scss diff --git a/packages/features/src/Domain/Feature/FeatureIdentifier.ts b/packages/features/src/Domain/Feature/FeatureIdentifier.ts index 78467bd80..0452e326a 100644 --- a/packages/features/src/Domain/Feature/FeatureIdentifier.ts +++ b/packages/features/src/Domain/Feature/FeatureIdentifier.ts @@ -21,7 +21,7 @@ export enum FeatureIdentifier { AutobiographyTheme = 'org.standardnotes.theme-autobiography', DynamicTheme = 'org.standardnotes.theme-dynamic', - FocusedTheme = 'org.standardnotes.theme-focus', + DarkTheme = 'org.standardnotes.theme-focus', FocusMode = 'org.standardnotes.focus-mode', FuturaTheme = 'org.standardnotes.theme-futura', MidnightTheme = 'org.standardnotes.theme-midnight', diff --git a/packages/features/src/Domain/Lists/Themes.ts b/packages/features/src/Domain/Lists/Themes.ts index df586b802..cf0cd4deb 100644 --- a/packages/features/src/Domain/Lists/Themes.ts +++ b/packages/features/src/Domain/Lists/Themes.ts @@ -68,10 +68,10 @@ export function themes(): ThemeFeatureDescription[] { }, }) - const focus: ThemeFeatureDescription = FillThemeComponentDefaults({ + const dark: ThemeFeatureDescription = FillThemeComponentDefaults({ availableInSubscriptions: [SubscriptionName.PlusPlan, SubscriptionName.ProPlan], - name: 'Focus', - identifier: FeatureIdentifier.FocusedTheme, + name: 'Dark', + identifier: FeatureIdentifier.DarkTheme, permission_name: PermissionName.FocusedTheme, description: 'For when you need to go in.', thumbnail_url: 'https://s3.amazonaws.com/standard-notes/screenshots/models/themes/focus-with-mobile.jpg', @@ -109,5 +109,5 @@ export function themes(): ThemeFeatureDescription[] { description: 'A smart theme that minimizes the tags and notes panels when they are not in use.', }) - return [midnight, futura, solarizedDark, autobiography, focus, titanium, dynamic] + return [midnight, futura, solarizedDark, autobiography, dark, titanium, dynamic] } diff --git a/packages/services/src/Domain/Files/FileService.spec.ts b/packages/services/src/Domain/Files/FileService.spec.ts index 87f9cdedb..e320de71b 100644 --- a/packages/services/src/Domain/Files/FileService.spec.ts +++ b/packages/services/src/Domain/Files/FileService.spec.ts @@ -77,7 +77,7 @@ describe('fileService', () => { crypto.xchacha20StreamEncryptorPush = jest.fn().mockReturnValue(new Uint8Array()) }) - it.only('should cache file after download', async () => { + it('should cache file after download', async () => { const file = { uuid: '1', decryptedSize: 100_000, diff --git a/packages/snjs/lib/Application/Application.ts b/packages/snjs/lib/Application/Application.ts index c3518ef88..8580d715c 100644 --- a/packages/snjs/lib/Application/Application.ts +++ b/packages/snjs/lib/Application/Application.ts @@ -1497,7 +1497,10 @@ export class SNApplication const syncEventCallback = async (eventName: ExternalServices.SyncEvent) => { const appEvent = applicationEventForSyncEvent(eventName) if (appEvent) { + await this.protocolService.onSyncEvent(eventName) + await this.notifyEvent(appEvent) + if (appEvent === ApplicationEvent.CompletedFullSync) { if (!this.handledFullSyncStage) { this.handledFullSyncStage = true @@ -1505,7 +1508,6 @@ export class SNApplication } } } - await this.protocolService.onSyncEvent(eventName) } const uninstall = this.syncService.addEventObserver(syncEventCallback) this.serviceObservers.push(uninstall) diff --git a/packages/snjs/lib/Services/Features/FeaturesService.ts b/packages/snjs/lib/Services/Features/FeaturesService.ts index 0851538d7..cafe0b50f 100644 --- a/packages/snjs/lib/Services/Features/FeaturesService.ts +++ b/packages/snjs/lib/Services/Features/FeaturesService.ts @@ -44,6 +44,7 @@ import { SetOfflineFeaturesFunctionResponse, StorageKey, } from '@standardnotes/services' +import { FeatureIdentifier } from '@standardnotes/features' type GetOfflineSubscriptionDetailsResponse = OfflineSubscriptionEntitlements | ClientDisplayableError @@ -145,7 +146,10 @@ export class SNFeaturesService override async handleApplicationStage(stage: ApplicationStage): Promise { await super.handleApplicationStage(stage) + if (stage === ApplicationStage.FullSyncCompleted_13) { + void this.addDarkTheme() + if (!this.hasOnlineSubscription()) { const offlineRepo = this.getOfflineRepo() if (offlineRepo) { @@ -155,6 +159,14 @@ export class SNFeaturesService } } + private async addDarkTheme() { + const darkThemeFeature = FeaturesImports.FindNativeFeature(FeatureIdentifier.DarkTheme) + + if (darkThemeFeature) { + await this.mapRemoteNativeFeaturesToItems([darkThemeFeature]) + } + } + public enableExperimentalFeature(identifier: FeaturesImports.FeatureIdentifier): void { const feature = this.getUserFeature(identifier) if (!feature) { @@ -366,7 +378,7 @@ export class SNFeaturesService if (!arraysEqual(this.roles, roles)) { void this.notifyEvent(FeaturesEvent.UserRolesChanged) } - await this.storageService.setValue(StorageKey.UserRoles, this.roles) + this.storageService.setValue(StorageKey.UserRoles, this.roles) } public async didDownloadFeatures(features: FeaturesImports.FeatureDescription[]): Promise { @@ -448,7 +460,15 @@ export class SNFeaturesService return FeaturesImports.FindNativeFeature(featureId)?.deprecated === true } + public isFreeFeature(featureId: FeaturesImports.FeatureIdentifier) { + return [FeatureIdentifier.DarkTheme].includes(featureId) + } + public getFeatureStatus(featureId: FeaturesImports.FeatureIdentifier): FeatureStatus { + if (this.isFreeFeature(featureId)) { + return FeatureStatus.Entitled + } + const isDeprecated = this.isFeatureDeprecated(featureId) if (isDeprecated) { if (this.hasPaidOnlineOrOfflineSubscription()) { @@ -548,7 +568,9 @@ export class SNFeaturesService let hasChanges = false const now = new Date() - const expired = new Date(feature.expires_at || 0).getTime() < now.getTime() + const expired = this.isFreeFeature(feature.identifier) + ? false + : new Date(feature.expires_at || 0).getTime() < now.getTime() const existingItem = currentItems.find((item) => { if (item.content.package_info) { diff --git a/packages/snjs/mocha/application.test.js b/packages/snjs/mocha/application.test.js index 4709e4311..e05569e92 100644 --- a/packages/snjs/mocha/application.test.js +++ b/packages/snjs/mocha/application.test.js @@ -1,12 +1,11 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('application instances', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, @@ -27,8 +26,8 @@ describe('application instances', () => { expect(app1.payloadManager).to.not.equal(app2.payloadManager) await Factory.createMappedNote(app1) - expect(app1.itemManager.items.length).length.to.equal(BASE_ITEM_COUNT + 1) - expect(app2.itemManager.items.length).to.equal(BASE_ITEM_COUNT) + expect(app1.itemManager.items.length).length.to.equal(BaseItemCounts.DefaultItems + 1) + expect(app2.itemManager.items.length).to.equal(BaseItemCounts.DefaultItems) await Factory.safeDeinit(app1) await Factory.safeDeinit(app2) }) @@ -40,14 +39,14 @@ describe('application instances', () => { await Factory.createMappedNote(app1) await app1.syncService.sync(syncOptions) - expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1) - expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT) + expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1) + expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems) await Factory.createMappedNote(app2) await app2.syncService.sync(syncOptions) - expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1) - expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BASE_ITEM_COUNT + 1) + expect((await app1.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1) + expect((await app2.diskStorageService.getAllRawPayloads()).length).length.to.equal(BaseItemCounts.DefaultItems + 1) await Factory.safeDeinit(app1) await Factory.safeDeinit(app2) }) diff --git a/packages/snjs/mocha/auth-fringe-cases.test.js b/packages/snjs/mocha/auth-fringe-cases.test.js index 0b2898042..820e3f69e 100644 --- a/packages/snjs/mocha/auth-fringe-cases.test.js +++ b/packages/snjs/mocha/auth-fringe-cases.test.js @@ -1,16 +1,15 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('auth fringe cases', () => { - const BASE_ITEM_COUNT = ['default items key', 'user prefs'].length - const createContext = async () => { const application = await Factory.createInitAppWithFakeCrypto() return { - expectedItemCount: BASE_ITEM_COUNT, + expectedItemCount: BaseItemCounts.DefaultItems, application: application, email: UuidGenerator.GenerateUuid(), password: UuidGenerator.GenerateUuid(), @@ -100,9 +99,13 @@ describe('auth fringe cases', () => { expect(newApplication.itemManager.getDisplayableNotes().length).to.equal(2) - expect(newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid === firstVersionOfNote.uuid).text).to.equal(staleText) + expect( + newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid === firstVersionOfNote.uuid).text, + ).to.equal(staleText) - const conflictedCopy = newApplication.itemManager.getDisplayableNotes().find((n) => n.uuid !== firstVersionOfNote.uuid) + const conflictedCopy = newApplication.itemManager + .getDisplayableNotes() + .find((n) => n.uuid !== firstVersionOfNote.uuid) expect(conflictedCopy.text).to.equal(serverText) expect(conflictedCopy.duplicate_of).to.equal(firstVersionOfNote.uuid) await Factory.safeDeinit(newApplication) diff --git a/packages/snjs/mocha/auth.test.js b/packages/snjs/mocha/auth.test.js index 00019476d..d91e4fcbf 100644 --- a/packages/snjs/mocha/auth.test.js +++ b/packages/snjs/mocha/auth.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect @@ -7,8 +8,6 @@ const expect = chai.expect describe('basic auth', function () { this.timeout(Factory.TenSecondTimeout) - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, @@ -16,7 +15,7 @@ describe('basic auth', function () { beforeEach(async function () { localStorage.clear() - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() this.email = UuidGenerator.GenerateUuid() this.password = UuidGenerator.GenerateUuid() @@ -62,7 +61,7 @@ describe('basic auth', function () { expect(this.application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.RootKeyNone) const rawPayloads = await this.application.diskStorageService.getAllRawPayloads() - expect(rawPayloads.length).to.equal(BASE_ITEM_COUNT) + expect(rawPayloads.length).to.equal(BaseItemCounts.DefaultItems) }) it('successfully signs in to registered account', async function () { @@ -408,7 +407,7 @@ describe('basic auth', function () { this.application = await Factory.signOutApplicationAndReturnNew(this.application) - expect(this.application.itemManager.items.length).to.equal(BASE_ITEM_COUNT) + expect(this.application.itemManager.items.length).to.equal(BaseItemCounts.DefaultItems) expect(this.application.payloadManager.invalidPayloads.length).to.equal(0) /** Should login with new password */ diff --git a/packages/snjs/mocha/backups.test.js b/packages/snjs/mocha/backups.test.js index 7b4775f19..a6320370a 100644 --- a/packages/snjs/mocha/backups.test.js +++ b/packages/snjs/mocha/backups.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect @@ -24,8 +25,8 @@ describe('backups', function () { this.application = null }) - const BASE_ITEM_COUNT_ENCRYPTED = ['ItemsKey', 'UserPreferences'].length - const BASE_ITEM_COUNT_DECRYPTED = ['UserPreferences'].length + const BASE_ITEM_COUNT_ENCRYPTED = BaseItemCounts.DefaultItems + const BASE_ITEM_COUNT_DECRYPTED = ['UserPreferences', 'DarkTheme'].length it('backup file should have a version number', async function () { let data = await this.application.createDecryptedBackupFile() diff --git a/packages/snjs/mocha/features.test.js b/packages/snjs/mocha/features.test.js index 26487343a..7b624dc5f 100644 --- a/packages/snjs/mocha/features.test.js +++ b/packages/snjs/mocha/features.test.js @@ -83,11 +83,11 @@ describe('features', () => { it('should fetch user features and create items for features with content type', async () => { expect(application.apiService.getUserFeatures.callCount).to.equal(1) expect(application.itemManager.createItem.callCount).to.equal(2) + const themeItems = application.items.getItems(ContentType.Theme) - const editorItems = application.items.getItems(ContentType.Component) - expect(themeItems).to.have.lengthOf(1) - expect(editorItems).to.have.lengthOf(1) - expect(themeItems[0].content).to.containSubset( + const systemThemeCount = 1 + expect(themeItems).to.have.lengthOf(1 + systemThemeCount) + expect(themeItems[1].content).to.containSubset( JSON.parse( JSON.stringify({ name: midnightThemeFeature.name, @@ -96,6 +96,9 @@ describe('features', () => { }), ), ) + + const editorItems = application.items.getItems(ContentType.Component) + expect(editorItems).to.have.lengthOf(1) expect(editorItems[0].content).to.containSubset( JSON.parse( JSON.stringify({ @@ -157,7 +160,9 @@ describe('features', () => { }) }) - const themeItem = application.items.getItems(ContentType.Theme)[0] + const themeItem = application.items + .getItems(ContentType.Theme) + .find((theme) => theme.identifier === midnightThemeFeature.identifier) // Wipe roles from initial sync await application.featuresService.setRoles([]) @@ -171,7 +176,9 @@ describe('features', () => { true, ) - const noTheme = application.items.getItems(ContentType.Theme)[0] + const noTheme = application.items + .getItems(ContentType.Theme) + .find((theme) => theme.identifier === midnightThemeFeature.identifier) expect(noTheme).to.not.be.ok }) }) diff --git a/packages/snjs/mocha/lib/Applications.js b/packages/snjs/mocha/lib/Applications.js index d0d4fac53..a606bc692 100644 --- a/packages/snjs/mocha/lib/Applications.js +++ b/packages/snjs/mocha/lib/Applications.js @@ -2,6 +2,10 @@ import WebDeviceInterface from './web_device_interface.js' import FakeWebCrypto from './fake_web_crypto.js' import * as Defaults from './Defaults.js' +export const BaseItemCounts = { + DefaultItems: ['ItemsKey', 'UserPreferences', 'DarkTheme'].length, +} + export function createApplicationWithOptions({ identifier, environment, platform, host, crypto, device }) { if (!device) { device = new WebDeviceInterface() diff --git a/packages/snjs/mocha/migrations/migration.test.js b/packages/snjs/mocha/migrations/migration.test.js index 7bc47092f..ae3066986 100644 --- a/packages/snjs/mocha/migrations/migration.test.js +++ b/packages/snjs/mocha/migrations/migration.test.js @@ -161,14 +161,15 @@ describe('migrations', () => { await application.mutator.insertItem(noDistractionItem) await application.sync.sync() - expect(application.items.getItems(ContentType.Theme).length).to.equal(1) + const systemThemeCount = 1 + expect(application.items.getItems(ContentType.Theme).length).to.equal(1 + systemThemeCount) /** Run migration */ const migration = new Migration2_42_0(application.migrationService.services) await migration.handleStage(ApplicationStage.FullSyncCompleted_13) await application.sync.sync() - expect(application.items.getItems(ContentType.Theme).length).to.equal(0) + expect(application.items.getItems(ContentType.Theme).length).to.equal(systemThemeCount) await Factory.safeDeinit(application) }) diff --git a/packages/snjs/mocha/model_tests/appmodels.test.js b/packages/snjs/mocha/model_tests/appmodels.test.js index a09b0714f..ca7e2660f 100644 --- a/packages/snjs/mocha/model_tests/appmodels.test.js +++ b/packages/snjs/mocha/model_tests/appmodels.test.js @@ -1,12 +1,12 @@ /* eslint-disable camelcase */ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('app models', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ const sharedApplication = Factory.createApplicationWithFakeCrypto() before(async function () { @@ -20,7 +20,7 @@ describe('app models', () => { }) beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() }) diff --git a/packages/snjs/mocha/model_tests/importing.test.js b/packages/snjs/mocha/model_tests/importing.test.js index 813e760cb..94e5d163c 100644 --- a/packages/snjs/mocha/model_tests/importing.test.js +++ b/packages/snjs/mocha/model_tests/importing.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' import { createRelatedNoteTagPairPayload } from '../lib/Items.js' chai.use(chaiAsPromised) @@ -7,7 +8,6 @@ const expect = chai.expect describe('importing', function () { this.timeout(Factory.TenSecondTimeout) - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ let expectedItemCount let application @@ -19,7 +19,7 @@ describe('importing', function () { }) const setup = async ({ fakeCrypto }) => { - expectedItemCount = BASE_ITEM_COUNT + expectedItemCount = BaseItemCounts.DefaultItems if (fakeCrypto) { application = await Factory.createInitAppWithFakeCrypto() } else { @@ -599,12 +599,11 @@ describe('importing', function () { application = await Factory.createInitAppWithFakeCrypto() application.setLaunchCallback({ receiveChallenge: (challenge) => { - const values = challenge.prompts.map( - (prompt) => - CreateChallengeValue( - prompt, - prompt.validation === ChallengeValidation.None ? 'incorrect password' : password, - ), + const values = challenge.prompts.map((prompt) => + CreateChallengeValue( + prompt, + prompt.validation === ChallengeValidation.None ? 'incorrect password' : password, + ), ) application.submitValuesForChallenge(challenge, values) }, @@ -782,14 +781,13 @@ describe('importing', function () { if (challenge.prompts.length === 2) { application.submitValuesForChallenge( challenge, - challenge.prompts.map( - (prompt) => - CreateChallengeValue( - prompt, - prompt.validation !== ChallengeValidation.ProtectionSessionDuration - ? password - : UnprotectedAccessSecondsDuration.OneMinute, - ), + challenge.prompts.map((prompt) => + CreateChallengeValue( + prompt, + prompt.validation !== ChallengeValidation.ProtectionSessionDuration + ? password + : UnprotectedAccessSecondsDuration.OneMinute, + ), ), ) } else { diff --git a/packages/snjs/mocha/model_tests/items.test.js b/packages/snjs/mocha/model_tests/items.test.js index e12388ef6..c79619ba9 100644 --- a/packages/snjs/mocha/model_tests/items.test.js +++ b/packages/snjs/mocha/model_tests/items.test.js @@ -1,19 +1,18 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('items', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, } beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() }) diff --git a/packages/snjs/mocha/model_tests/mapping.test.js b/packages/snjs/mocha/model_tests/mapping.test.js index 0e25ee494..0a26f880d 100644 --- a/packages/snjs/mocha/model_tests/mapping.test.js +++ b/packages/snjs/mocha/model_tests/mapping.test.js @@ -1,15 +1,14 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' import { createNoteParams } from '../lib/Items.js' chai.use(chaiAsPromised) const expect = chai.expect describe('model manager mapping', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() }) diff --git a/packages/snjs/mocha/model_tests/notes_tags.test.js b/packages/snjs/mocha/model_tests/notes_tags.test.js index 1aa0a927d..e057d81ce 100644 --- a/packages/snjs/mocha/model_tests/notes_tags.test.js +++ b/packages/snjs/mocha/model_tests/notes_tags.test.js @@ -2,19 +2,18 @@ import * as Factory from '../lib/factory.js' import * as Utils from '../lib/Utils.js' import { createRelatedNoteTagPairPayload } from '../lib/Items.js' +import { BaseItemCounts } from '../lib/Applications.js' chai.use(chaiAsPromised) const expect = chai.expect describe('notes and tags', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, } beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() }) diff --git a/packages/snjs/mocha/session.test.js b/packages/snjs/mocha/session.test.js index f3c3d3e85..63193b335 100644 --- a/packages/snjs/mocha/session.test.js +++ b/packages/snjs/mocha/session.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' import WebDeviceInterface from './lib/web_device_interface.js' chai.use(chaiAsPromised) @@ -8,8 +9,6 @@ const expect = chai.expect describe('server session', function () { this.timeout(Factory.TenSecondTimeout) - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, @@ -17,7 +16,7 @@ describe('server session', function () { beforeEach(async function () { localStorage.clear() - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() this.email = UuidGenerator.GenerateUuid() this.password = UuidGenerator.GenerateUuid() diff --git a/packages/snjs/mocha/singletons.test.js b/packages/snjs/mocha/singletons.test.js index e5a88f37a..e8fc0b7b3 100644 --- a/packages/snjs/mocha/singletons.test.js +++ b/packages/snjs/mocha/singletons.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' import WebDeviceInterface from './lib/web_device_interface.js' chai.use(chaiAsPromised) @@ -11,7 +12,6 @@ describe('singletons', function () { const syncOptions = { checkIntegrity: true, } - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ function createPrefsPayload() { const params = { @@ -30,7 +30,7 @@ describe('singletons', function () { beforeEach(async function () { localStorage.clear() - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() this.email = UuidGenerator.GenerateUuid() this.password = UuidGenerator.GenerateUuid() @@ -83,7 +83,7 @@ describe('singletons', function () { localStorage.clear() }) - it(`only resolves to ${BASE_ITEM_COUNT} items`, async function () { + it(`only resolves to ${BaseItemCounts.DefaultItems} items`, async function () { /** Preferences are an item we know to always return true for isSingleton */ const prefs1 = createPrefsPayload() const prefs2 = createPrefsPayload() diff --git a/packages/snjs/mocha/storage.test.js b/packages/snjs/mocha/storage.test.js index fab85a1e1..a80a9885f 100644 --- a/packages/snjs/mocha/storage.test.js +++ b/packages/snjs/mocha/storage.test.js @@ -1,5 +1,6 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from './lib/Applications.js' import * as Factory from './lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect @@ -11,7 +12,6 @@ describe('storage manager', function () { * Base keys are `storage`, `snjs_version`, and `keychain` */ const BASE_KEY_COUNT = 3 - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ beforeEach(async function () { localStorage.clear() @@ -37,7 +37,7 @@ describe('storage manager', function () { const payload = Factory.createNotePayload() await this.application.diskStorageService.savePayload(payload) const payloads = await this.application.diskStorageService.getAllRawPayloads() - expect(payloads.length).to.equal(BASE_ITEM_COUNT + 1) + expect(payloads.length).to.equal(BaseItemCounts.DefaultItems + 1) }) it('should clear values', async function () { @@ -71,7 +71,7 @@ describe('storage manager', function () { const value = 'bar' await this.application.diskStorageService.setValue(key, value) /** Items are stored in local storage */ - expect(Object.keys(localStorage).length).to.equal(this.expectedKeyCount + BASE_ITEM_COUNT) + expect(Object.keys(localStorage).length).to.equal(this.expectedKeyCount + BaseItemCounts.DefaultItems) const retrievedValue = await this.application.diskStorageService.getValue(key) expect(retrievedValue).to.equal(value) }) @@ -297,8 +297,8 @@ describe('storage manager', function () { }) await Factory.createSyncedNote(this.application) - expect(await Factory.storagePayloadCount(this.application)).to.equal(BASE_ITEM_COUNT + 1) + expect(await Factory.storagePayloadCount(this.application)).to.equal(BaseItemCounts.DefaultItems + 1) this.application = await Factory.signOutApplicationAndReturnNew(this.application) - expect(await Factory.storagePayloadCount(this.application)).to.equal(BASE_ITEM_COUNT) + expect(await Factory.storagePayloadCount(this.application)).to.equal(BaseItemCounts.DefaultItems) }) }) diff --git a/packages/snjs/mocha/sync_tests/conflicting.test.js b/packages/snjs/mocha/sync_tests/conflicting.test.js index f94156d3c..9da947311 100644 --- a/packages/snjs/mocha/sync_tests/conflicting.test.js +++ b/packages/snjs/mocha/sync_tests/conflicting.test.js @@ -1,13 +1,13 @@ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' -import { createItemParams, createSyncedNoteWithTag } from '../lib/Items.js' +import { createSyncedNoteWithTag } from '../lib/Items.js' import * as Utils from '../lib/Utils.js' chai.use(chaiAsPromised) const expect = chai.expect describe('online conflict handling', function () { this.timeout(Factory.TenSecondTimeout) - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ const syncOptions = { checkIntegrity: true, @@ -16,7 +16,7 @@ describe('online conflict handling', function () { beforeEach(async function () { localStorage.clear() - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.context = await Factory.createAppContextWithFakeCrypto('AppA') await this.context.launch() diff --git a/packages/snjs/mocha/sync_tests/integrity.test.js b/packages/snjs/mocha/sync_tests/integrity.test.js index c43ad5ffb..d91339f96 100644 --- a/packages/snjs/mocha/sync_tests/integrity.test.js +++ b/packages/snjs/mocha/sync_tests/integrity.test.js @@ -1,12 +1,11 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('sync integrity', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - before(function () { localStorage.clear() }) @@ -16,7 +15,7 @@ describe('sync integrity', () => { }) beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() this.email = UuidGenerator.GenerateUuid() this.password = UuidGenerator.GenerateUuid() diff --git a/packages/snjs/mocha/sync_tests/offline.test.js b/packages/snjs/mocha/sync_tests/offline.test.js index e5cabe7b9..e029a2702 100644 --- a/packages/snjs/mocha/sync_tests/offline.test.js +++ b/packages/snjs/mocha/sync_tests/offline.test.js @@ -1,19 +1,18 @@ /* eslint-disable no-unused-expressions */ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' chai.use(chaiAsPromised) const expect = chai.expect describe('offline syncing', () => { - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ - const syncOptions = { checkIntegrity: true, awaitAll: true, } beforeEach(async function () { - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.application = await Factory.createInitAppWithFakeCrypto() }) diff --git a/packages/snjs/mocha/sync_tests/online.test.js b/packages/snjs/mocha/sync_tests/online.test.js index 371990676..2621a059c 100644 --- a/packages/snjs/mocha/sync_tests/online.test.js +++ b/packages/snjs/mocha/sync_tests/online.test.js @@ -1,4 +1,5 @@ /* eslint-disable no-undef */ +import { BaseItemCounts } from '../lib/Applications.js' import * as Factory from '../lib/factory.js' import * as Utils from '../lib/Utils.js' chai.use(chaiAsPromised) @@ -6,7 +7,6 @@ const expect = chai.expect describe('online syncing', function () { this.timeout(Factory.TenSecondTimeout) - const BASE_ITEM_COUNT = 2 /** Default items key, user preferences */ const syncOptions = { checkIntegrity: true, @@ -15,7 +15,7 @@ describe('online syncing', function () { beforeEach(async function () { localStorage.clear() - this.expectedItemCount = BASE_ITEM_COUNT + this.expectedItemCount = BaseItemCounts.DefaultItems this.context = await Factory.createAppContext() await this.context.launch() @@ -97,7 +97,7 @@ describe('online syncing', function () { this.application = await Factory.signOutApplicationAndReturnNew(this.application) - expect(this.application.itemManager.items.length).to.equal(BASE_ITEM_COUNT) + expect(this.application.itemManager.items.length).to.equal(BaseItemCounts.DefaultItems) const promise = Factory.loginToApplication({ application: this.application, @@ -244,7 +244,7 @@ describe('online syncing', function () { // set item to be merged for when sign in occurs await this.application.syncService.markAllItemsAsNeedingSyncAndPersist() expect(this.application.syncService.isOutOfSync()).to.equal(false) - expect(this.application.itemManager.getDirtyItems().length).to.equal(BASE_ITEM_COUNT + 1) + expect(this.application.itemManager.getDirtyItems().length).to.equal(BaseItemCounts.DefaultItems + 1) // Sign back in for next tests await Factory.loginToApplication({ @@ -686,7 +686,7 @@ describe('online syncing', function () { this.application = await Factory.signOutApplicationAndReturnNew(this.application) const rawPayloads = await this.application.diskStorageService.getAllRawPayloads() - expect(rawPayloads.length).to.equal(BASE_ITEM_COUNT) + expect(rawPayloads.length).to.equal(BaseItemCounts.DefaultItems) await this.application.signIn(this.email, this.password, undefined, undefined, undefined, true) diff --git a/packages/ui-services/src/Theme/ThemeManager.ts b/packages/ui-services/src/Theme/ThemeManager.ts index ab29ab6eb..423a86327 100644 --- a/packages/ui-services/src/Theme/ThemeManager.ts +++ b/packages/ui-services/src/Theme/ThemeManager.ts @@ -16,11 +16,11 @@ import { StorageValueModes, FeatureStatus, } from '@standardnotes/services' +import { FeatureIdentifier } from '@standardnotes/snjs' const CachedThemesKey = 'cachedThemes' const TimeBeforeApplyingColorScheme = 5 const DefaultThemeIdentifier = 'Default' -const DarkThemeIdentifier = 'Dark' export class ThemeManager extends AbstractService { private activeThemes: Uuid[] = [] @@ -200,18 +200,14 @@ export class ThemeManager extends AbstractService { private setThemeAsPerColorScheme(prefersDarkColorScheme: boolean) { const preference = prefersDarkColorScheme ? PrefKey.AutoDarkThemeIdentifier : PrefKey.AutoLightThemeIdentifier const preferenceDefault = - preference === PrefKey.AutoDarkThemeIdentifier ? DarkThemeIdentifier : DefaultThemeIdentifier + preference === PrefKey.AutoDarkThemeIdentifier ? FeatureIdentifier.DarkTheme : DefaultThemeIdentifier const themes = this.application.items .getDisplayableComponents() .filter((component) => component.isTheme()) as SNTheme[] const activeTheme = themes.find((theme) => theme.active && !theme.isLayerable()) - const activeThemeIdentifier = activeTheme - ? activeTheme.identifier - : this.application.getPreference(PrefKey.DarkMode, false) - ? DarkThemeIdentifier - : DefaultThemeIdentifier + const activeThemeIdentifier = activeTheme ? activeTheme.identifier : DefaultThemeIdentifier const themeIdentifier = this.application.getPreference(preference, preferenceDefault) as string @@ -225,9 +221,6 @@ export class ThemeManager extends AbstractService { if (themeIdentifier === DefaultThemeIdentifier) { toggleActiveTheme() void this.application.setPreference(PrefKey.DarkMode, false) - } else if (themeIdentifier === DarkThemeIdentifier) { - toggleActiveTheme() - void this.application.setPreference(PrefKey.DarkMode, true) } else { const theme = themes.find((theme) => theme.package_info.identifier === themeIdentifier) if (theme && !theme.active) { diff --git a/packages/web/src/javascripts/Components/Preferences/Panes/Appearance.tsx b/packages/web/src/javascripts/Components/Preferences/Panes/Appearance.tsx index b9c84758a..fb4e6e4a4 100644 --- a/packages/web/src/javascripts/Components/Preferences/Panes/Appearance.tsx +++ b/packages/web/src/javascripts/Components/Preferences/Panes/Appearance.tsx @@ -58,11 +58,6 @@ const Appearance: FunctionComponent = ({ application }) => { } }) - themesAsItems.unshift({ - label: 'Dark', - value: 'Dark', - }) - themesAsItems.unshift({ label: 'Default', value: 'Default', diff --git a/packages/web/src/javascripts/Components/QuickSettingsMenu/QuickSettingsMenu.tsx b/packages/web/src/javascripts/Components/QuickSettingsMenu/QuickSettingsMenu.tsx index d45e0f1d3..d4212cc5b 100644 --- a/packages/web/src/javascripts/Components/QuickSettingsMenu/QuickSettingsMenu.tsx +++ b/packages/web/src/javascripts/Components/QuickSettingsMenu/QuickSettingsMenu.tsx @@ -166,13 +166,6 @@ const QuickSettingsMenu: FunctionComponent = ({ application, quickSet application.setPreference(PrefKey.DarkMode, false) }, [application, deactivateAnyNonLayerableTheme]) - const toggleDarkMode = useCallback(() => { - if (!isDarkModeOn) { - deactivateAnyNonLayerableTheme() - application.setPreference(PrefKey.DarkMode, true) - } - }, [application, isDarkModeOn, deactivateAnyNonLayerableTheme]) - return (
{toggleableComponents.length > 0 && ( @@ -205,13 +198,6 @@ const QuickSettingsMenu: FunctionComponent = ({ application, quickSet Default - {themes.map((theme) => ( ))} diff --git a/packages/web/src/javascripts/Utils/SortThemes.ts b/packages/web/src/javascripts/Utils/SortThemes.ts index 7dc7ea01e..855f66e19 100644 --- a/packages/web/src/javascripts/Utils/SortThemes.ts +++ b/packages/web/src/javascripts/Utils/SortThemes.ts @@ -1,4 +1,7 @@ import { ThemeItem } from '@/Components/QuickSettingsMenu/ThemeItem' +import { FeatureIdentifier } from '@standardnotes/snjs' + +const isDarkModeTheme = (theme: ThemeItem) => theme.identifier === FeatureIdentifier.DarkTheme export const sortThemes = (a: ThemeItem, b: ThemeItem) => { const aIsLayerable = a.component?.isLayerable() @@ -8,6 +11,8 @@ export const sortThemes = (a: ThemeItem, b: ThemeItem) => { return 1 } else if (!aIsLayerable && bIsLayerable) { return -1 + } else if (!isDarkModeTheme(a) && isDarkModeTheme(b)) { + return 1 } else { return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1 } diff --git a/packages/web/src/stylesheets/_dark-mode.scss b/packages/web/src/stylesheets/_dark-mode.scss deleted file mode 100644 index a12c7e155..000000000 --- a/packages/web/src/stylesheets/_dark-mode.scss +++ /dev/null @@ -1,71 +0,0 @@ -.dark-mode { - --foreground-color: #eeeeee; - --background-color: #0f1011; - --highlight-color: #a464c2; - --border-color: #0f1011; - - --sn-component-foreground-color: var(--foreground-color); - --sn-component-background-color: transparent; - --sn-component-foreground-highlight-color: var(--highlight-color); - --sn-component-outer-border-color: transparent; - --sn-component-inner-border-color: var(--foreground-color); - - // StyleKit Vars - - --sn-stylekit-passive-color-0: #999999; - --sn-stylekit-passive-color-3: #28292b; - --sn-stylekit-passive-color-4: #1c1d1e; - --sn-stylekit-passive-color-5: #1d1f20; - - --sn-stylekit-shadow-color: #000000; - - --sn-stylekit-info-color: var(--highlight-color); - --sn-stylekit-info-contrast-color: var(--foreground-color); - - --sn-stylekit-neutral-color: #7c7c7c; - --sn-stylekit-neutral-contrast-color: #ffffff; - - --sn-stylekit-success-color: #2b9612; - --sn-stylekit-success-contrast-color: #ffffff; - - --sn-stylekit-warning-color: #f6a200; - --sn-stylekit-warning-contrast-color: #ffffff; - - --sn-stylekit-danger-color: #f80324; - --sn-stylekit-danger-contrast-color: #ffffff; - - --sn-stylekit-editor-background-color: var(--sn-stylekit-background-color); - --sn-stylekit-editor-foreground-color: var(--sn-stylekit-foreground-color); - - --sn-stylekit-background-color: var(--background-color); - --sn-stylekit-foreground-color: var(--foreground-color); - --sn-stylekit-border-color: #000000; - - --sn-stylekit-contrast-background-color: #000000; - --sn-stylekit-contrast-foreground-color: #ffffff; - --sn-stylekit-contrast-border-color: #000000; - - --sn-stylekit-secondary-background-color: var(--sn-stylekit-passive-color-4); - --sn-stylekit-secondary-foreground-color: #ffffff; - --sn-stylekit-secondary-border-color: #000000; - - --sn-stylekit-secondary-contrast-background-color: #000000; - --sn-stylekit-secondary-contrast-foreground-color: #ffffff; - --sn-stylekit-secondary-contrast-border-color: #ffffff; - - --sn-stylekit-paragraph-text-color: #ffffff; - - --sn-desktop-titlebar-bg-color: var(--background-color); - --sn-desktop-titlebar-border-color: var(--border-color); - --sn-desktop-titlebar-ui-color: var(--foreground-color); - --sn-desktop-titlebar-ui-hover-color: var(--highlight-color); - - --sn-stylekit-scrollbar-track-border-color: var(--border-color); - --sn-stylekit-scrollbar-thumb-color: var(--sn-stylekit-info-color); - - --sn-stylekit-menu-border: 1px solid #424242; - - // Theme - - --navigation-item-selected-background-color: var(--background-color); -} diff --git a/packages/web/src/stylesheets/index.css.scss b/packages/web/src/stylesheets/index.css.scss index 74be63612..8a814ced6 100644 --- a/packages/web/src/stylesheets/index.css.scss +++ b/packages/web/src/stylesheets/index.css.scss @@ -3,7 +3,6 @@ @import '../../../styles/src/Styles/_scrollbar.scss'; @import '../../../styles/src/Styles/utils/_animation.scss'; @import 'theme'; -@import 'dark-mode'; @import 'main'; @import 'ui'; @import 'footer';