refactor(cc-dark-theme): migrate to the new DI architecture
Bug: twpowertools:226
Change-Id: I735013393d1d99cadee48399bba53a22fe59e27c
diff --git a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
index 794a681..4f01b82 100644
--- a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
+++ b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
@@ -1,6 +1,6 @@
import ExtraInfo from '../../../features/extraInfo/core';
import AutoRefresh from '../../../features/autoRefresh/core/autoRefresh';
-import OptionsProvider from '../../options/OptionsProvider';
+import OptionsProviderAdapter from '../../options/OptionsProvider';
import WorkflowsImport from '../../../features/workflows/core/communityConsole/import';
import Workflows from '../../../features/workflows/core/communityConsole/workflows';
import StartupDataStorage from '../../../contentScripts/communityConsole/utils/StartupDataStorage';
@@ -17,7 +17,7 @@
export const DependenciesToClass = {
[AutoRefreshDependency]: AutoRefresh,
[ExtraInfoDependency]: ExtraInfo,
- [OptionsProviderDependency]: OptionsProvider,
+ [OptionsProviderDependency]: OptionsProviderAdapter,
[ReportDialogColorThemeFixDependency]: ReportDialogColorThemeFix,
[StartupDataStorageDependency]: StartupDataStorage,
[WorkflowsDependency]: Workflows,
diff --git a/src/common/architecture/scripts/stylesheet/StylesheetScript.ts b/src/common/architecture/scripts/stylesheet/StylesheetScript.ts
index 991afa5..2ed9025 100644
--- a/src/common/architecture/scripts/stylesheet/StylesheetScript.ts
+++ b/src/common/architecture/scripts/stylesheet/StylesheetScript.ts
@@ -1,6 +1,6 @@
import StylesheetManager from '../../../StylesheetManager';
import { StylesheetAttributes } from '../../../contentScriptsUtils';
-import OptionsProvider from '../../../options/OptionsProvider';
+import OptionsProviderAdapter from '../../../options/OptionsProvider';
import DependenciesProviderSingleton, {
OptionsProviderDependency,
} from '../../dependenciesProvider/DependenciesProvider';
@@ -23,11 +23,13 @@
*/
readonly attributes: StylesheetAttributes = {};
- protected optionsProvider: OptionsProvider;
+ protected optionsProvider: OptionsProviderAdapter;
private stylesheetManager: StylesheetManager;
constructor() {
super();
+
+ // TODO(https://iavm.xyz/b/226): Retrieve this via constructor injection.
const dependenciesProvider = DependenciesProviderSingleton.getInstance();
this.optionsProvider = dependenciesProvider.getDependency(
OptionsProviderDependency,
diff --git a/src/common/options/OptionsProvider.ts b/src/common/options/OptionsProvider.ts
index 138da07..94a43de 100644
--- a/src/common/options/OptionsProvider.ts
+++ b/src/common/options/OptionsProvider.ts
@@ -3,6 +3,10 @@
import { getOptions } from './optionsUtils';
import { OptionCodename, OptionsValues } from './optionsPrototype';
import { OptionsConfiguration } from './OptionsConfiguration';
+import {
+ OptionsChangeListener,
+ OptionsProviderPort,
+} from '../../services/options/OptionsProvider';
// Prioritize reads before writes.
const kReadPriority = 10;
@@ -11,12 +15,46 @@
/**
* Class which provides option values and a way to listen to option changes.
*/
-export default class OptionsProvider {
+export default class OptionsProviderAdapter implements OptionsProviderPort {
private optionsConfiguration: OptionsConfiguration;
private mutex: MutexInterface = withTimeout(new Mutex(), 60 * 1000);
private listeners: Set<OptionsChangeListener> = new Set();
+ private isSetUp = false;
- constructor() {
+ async getOptionValue<O extends OptionCodename>(
+ option: O,
+ ): Promise<OptionsValues[O]> {
+ this.setUp();
+ return this.mutex.runExclusive(
+ () => this.optionsConfiguration.getOptionValue(option),
+ kReadPriority,
+ );
+ }
+
+ async isEnabled(option: OptionCodename): Promise<boolean> {
+ this.setUp();
+ return this.mutex.runExclusive(
+ () => this.optionsConfiguration.isEnabled(option),
+ kReadPriority,
+ );
+ }
+
+ async getOptionsValues(): Promise<OptionsValues> {
+ this.setUp();
+ return this.mutex.runExclusive(
+ () => this.optionsConfiguration.optionsValues,
+ kReadPriority,
+ );
+ }
+
+ addListener(listener: OptionsChangeListener) {
+ this.setUp();
+ this.listeners.add(listener);
+ }
+
+ private setUp() {
+ if (this.isSetUp) return;
+
this.listenForStorageChanges();
this.updateValues();
}
@@ -66,45 +104,4 @@
listener(previousOptionsConfiguration, this.optionsConfiguration);
}
}
-
- /**
- * Returns the value of option |option|.
- */
- async getOptionValue<O extends OptionCodename>(
- option: O,
- ): Promise<OptionsValues[O]> {
- return this.mutex.runExclusive(
- () => this.optionsConfiguration.getOptionValue(option),
- kReadPriority,
- );
- }
-
- /**
- * Returns whether |feature| is enabled.
- */
- async isEnabled(option: OptionCodename): Promise<boolean> {
- return this.mutex.runExclusive(
- () => this.optionsConfiguration.isEnabled(option),
- kReadPriority,
- );
- }
-
- async getOptionsValues(): Promise<OptionsValues> {
- return this.mutex.runExclusive(
- () => this.optionsConfiguration.optionsValues,
- kReadPriority,
- );
- }
-
- /**
- * Adds a listener for changes in the options configuration.
- */
- addListener(listener: OptionsChangeListener) {
- this.listeners.add(listener);
- }
}
-
-export type OptionsChangeListener = (
- previousOptionValues: OptionsConfiguration,
- currentOptionValues: OptionsConfiguration,
-) => void;
diff --git a/src/entryPoints/communityConsole/contentScripts/main.ts b/src/entryPoints/communityConsole/contentScripts/main.ts
index ff70c11..fb6fa17 100644
--- a/src/entryPoints/communityConsole/contentScripts/main.ts
+++ b/src/entryPoints/communityConsole/contentScripts/main.ts
@@ -1,5 +1,6 @@
import DependenciesProviderSingleton, {
AutoRefreshDependency,
+ OptionsProviderDependency,
} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
import { Context } from '../../../common/architecture/entrypoint/Context';
import {
@@ -21,6 +22,10 @@
// Run legacy Javascript entry point
import '../../../contentScripts/communityConsole/main';
+import CCDarkThemeEcAppHandler from '../../../features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler';
+import CCDarkThemeReportDialogHandler from '../../../features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler';
+import CCDarkThemeUnifiedProfilesIframeHandler from '../../../features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler';
+import ReportDialogColorThemeFix from '../../../features/ccDarkTheme/core/logic/reportDialog';
const scriptRunner = createScriptRunner();
scriptRunner.run();
@@ -28,6 +33,9 @@
function createScriptRunner() {
const dependenciesProvider = DependenciesProviderSingleton.getInstance();
const autoRefresh = dependenciesProvider.getDependency(AutoRefreshDependency);
+ const optionsProvider = dependenciesProvider.getDependency(
+ OptionsProviderDependency,
+ );
const context: Context = {
page: ScriptPage.CommunityConsole,
@@ -50,6 +58,18 @@
'autoRefreshThreadListHide',
new AutoRefreshThreadListHideHandler(autoRefresh),
],
+ ['ccDarkThemeEcApp', new CCDarkThemeEcAppHandler(optionsProvider)],
+ [
+ 'ccDarkThemeReportDialog',
+ new CCDarkThemeReportDialogHandler(
+ optionsProvider,
+ new ReportDialogColorThemeFix(),
+ ),
+ ],
+ [
+ 'ccDarkThemeUnifiedProfilesIframe',
+ new CCDarkThemeUnifiedProfilesIframeHandler(optionsProvider),
+ ],
]),
),
diff --git a/src/entryPoints/communityConsole/contentScripts/start.ts b/src/entryPoints/communityConsole/contentScripts/start.ts
index 5e11a87..e9be92c 100644
--- a/src/entryPoints/communityConsole/contentScripts/start.ts
+++ b/src/entryPoints/communityConsole/contentScripts/start.ts
@@ -16,6 +16,8 @@
// Run legacy Javascript entry point
import '../../../contentScripts/communityConsole/start';
+import CCDarkThemeInjectAutoDarkTheme from '../../../features/ccDarkTheme/scripts/injectAutoDarkTheme.script';
+import CCDarkThemeInjectForcedDarkTheme from '../../../features/ccDarkTheme/scripts/injectForcedDarkTheme.script';
const scriptRunner = createScriptRunner();
scriptRunner.run();
@@ -35,6 +37,8 @@
[
// Individual feature scripts
new AutoRefreshSetUpScript(autoRefresh),
+ new CCDarkThemeInjectAutoDarkTheme(),
+ new CCDarkThemeInjectForcedDarkTheme(),
// Non-DI scripts (legacy, should be migrated to use a DI approach)
...new Features().getScripts(context),
diff --git a/src/features/Features.ts b/src/features/Features.ts
index bc09eee..6bd74b0 100644
--- a/src/features/Features.ts
+++ b/src/features/Features.ts
@@ -3,7 +3,6 @@
import ScriptFilterListProvider from '../common/architecture/scripts/ScriptFilterListProvider';
import ExtraInfoFeature from './extraInfo/extraInfo.feature';
import WorkflowsFeature from './workflows/workflows.feature';
-import CCDarkThemeFeature from './ccDarkTheme/ccDarkTheme.feature';
import LoadDraftsFeature from './loadDrafts/loadDrafts.feature';
import InteropThreadPageFeature from './interopThreadPage/interopThreadPage.feature';
@@ -11,7 +10,6 @@
export default class Features extends ScriptFilterListProvider {
private features: ConcreteFeatureClass[] = [
- CCDarkThemeFeature,
ExtraInfoFeature,
InfiniteScrollFeature,
InteropThreadPageFeature,
diff --git a/src/features/ccDarkTheme/ccDarkTheme.feature.ts b/src/features/ccDarkTheme/ccDarkTheme.feature.ts
deleted file mode 100644
index 74efbf2..0000000
--- a/src/features/ccDarkTheme/ccDarkTheme.feature.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import Feature from '../../common/architecture/features/Feature';
-import { ConcreteScript } from '../../common/architecture/scripts/Script';
-import { OptionCodename } from '../../common/options/optionsPrototype';
-import InjectAutoDarkTheme from './scripts/injectAutoDarkTheme.script';
-import InjectForcedDarkTheme from './scripts/injectForcedDarkTheme.script';
-import CCDarkThemeNodeWatcherScript from './scripts/nodeWatcher.script';
-
-export default class CCDarkThemeFeature extends Feature {
- public readonly scripts: ConcreteScript[] = [
- InjectAutoDarkTheme,
- InjectForcedDarkTheme,
- CCDarkThemeNodeWatcherScript,
- ];
-
- readonly codename = 'darkTheme';
- readonly relatedOptions: OptionCodename[] = [
- 'ccdarktheme',
- 'ccdarktheme_mode',
- 'ccdarktheme_switch_status',
- ];
-}
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts
index fee1ffd..355de3f 100644
--- a/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts
@@ -1,20 +1,23 @@
-import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import CssSelectorNodeWatcherHandler from '../../../infrastructure/presentation/nodeWatcher/handlers/CssSelectorHandler.adapter';
import { NodeMutation } from '../../../presentation/nodeWatcher/NodeWatcherHandler';
+import { OptionsProviderPort } from '../../../services/options/OptionsProvider';
import { injectDarkThemeButton } from '../core/logic/darkTheme';
-import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
/**
* Injects the dark theme button.
*/
-export default class CCDarkThemeEcAppHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+export default class CCDarkThemeEcAppHandler extends CssSelectorNodeWatcherHandler {
cssSelector = 'ec-app';
+ constructor(private optionsProvider: OptionsProviderPort) {
+ super();
+ }
+
async onMutatedNode(mutation: NodeMutation) {
if (!(mutation.node instanceof Element)) return;
- const optionsProvider = this.options.optionsProvider;
- const isEnabled = await optionsProvider.isEnabled('ccdarktheme');
- const mode = await optionsProvider.getOptionValue('ccdarktheme_mode');
+ const isEnabled = await this.optionsProvider.isEnabled('ccdarktheme');
+ const mode = await this.optionsProvider.getOptionValue('ccdarktheme_mode');
// TODO(avm99963): make this feature dynamic.
if (isEnabled && mode === 'switch') {
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts
index 42c7a59..56699e9 100644
--- a/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts
@@ -1,17 +1,25 @@
-import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import CssSelectorNodeWatcherHandler from '../../../infrastructure/presentation/nodeWatcher/handlers/CssSelectorHandler.adapter';
import { NodeMutation } from '../../../presentation/nodeWatcher/NodeWatcherHandler';
-import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+import { OptionsProviderPort } from '../../../services/options/OptionsProvider';
+import ReportDialogColorThemeFix from '../core/logic/reportDialog';
/**
* Sets the report dialog iframe's theme to the appropriate theme.
*/
-export default class CCDarkThemeReportDialogHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+export default class CCDarkThemeReportDialogHandler extends CssSelectorNodeWatcherHandler {
cssSelector = 'iframe';
+ constructor(
+ private optionsProvider: OptionsProviderPort,
+ private reportDialogColorThemeFix: ReportDialogColorThemeFix,
+ ) {
+ super();
+ }
+
onMutatedNode(mutation: NodeMutation) {
- this.options.reportDialogColorThemeFix.fixThemeIfReportDialogIframeAndApplicable(
+ this.reportDialogColorThemeFix.fixThemeIfReportDialogIframeAndApplicable(
mutation.node,
- this.options.optionsProvider,
+ this.optionsProvider,
);
}
}
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts
index 6343834..28b9753 100644
--- a/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts
@@ -1,17 +1,21 @@
-import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import CssSelectorNodeWatcherHandler from '../../../infrastructure/presentation/nodeWatcher/handlers/CssSelectorHandler.adapter';
import { NodeMutation } from '../../../presentation/nodeWatcher/NodeWatcherHandler';
+import { OptionsProviderPort } from '../../../services/options/OptionsProvider';
import { isDarkThemeOn } from '../core/logic/darkTheme';
import { unifiedProfilesFix } from '../core/logic/unifiedProfiles';
-import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
/**
* Redirect unified profile iframe to dark version if applicable
*/
-export default class CCDarkThemeUnifiedProfilesIframeHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+export default class CCDarkThemeUnifiedProfilesIframeHandler extends CssSelectorNodeWatcherHandler {
cssSelector = 'iframe';
+ constructor(private optionsProvider: OptionsProviderPort) {
+ super();
+ }
+
async onMutatedNode(mutation: NodeMutation) {
- const optionsValues = await this.options.optionsProvider.getOptionsValues();
+ const optionsValues = await this.optionsProvider.getOptionsValues();
if (
isDarkThemeOn(optionsValues) &&
diff --git a/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts b/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts
index c462a03..c1bf0a1 100644
--- a/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts
+++ b/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts
@@ -1,7 +1,7 @@
import { ScriptPage } from '../../../common/architecture/scripts/Script';
import StylesheetScript from '../../../common/architecture/scripts/stylesheet/StylesheetScript';
-export default class InjectAutoDarkTheme extends StylesheetScript {
+export default class CCDarkThemeInjectAutoDarkTheme extends StylesheetScript {
stylesheet = 'ccDarkTheme.bundle.css';
attributes = { media: '(prefers-color-scheme: dark)' };
page = ScriptPage.CommunityConsole;
diff --git a/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts b/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts
index ebd221b..04c624a 100644
--- a/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts
+++ b/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts
@@ -1,7 +1,7 @@
import { ScriptPage } from '../../../common/architecture/scripts/Script';
import StylesheetScript from '../../../common/architecture/scripts/stylesheet/StylesheetScript';
-export default class InjectForcedDarkTheme extends StylesheetScript {
+export default class CCDarkThemeInjectForcedDarkTheme extends StylesheetScript {
stylesheet = 'ccDarkTheme.bundle.css';
page = ScriptPage.CommunityConsole;
diff --git a/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts b/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts
deleted file mode 100644
index 83e343e..0000000
--- a/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import DependenciesProviderSingleton, {
- OptionsProviderDependency,
- ReportDialogColorThemeFixDependency,
-} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
-import {
- ScriptEnvironment,
- ScriptPage,
- ScriptRunPhase,
-} from '../../../common/architecture/scripts/Script';
-import LegacyNodeWatcherScript from '../../../common/architecture/scripts/nodeWatcher/LegacyNodeWatcherScript';
-import OptionsProvider from '../../../common/options/OptionsProvider';
-import ReportDialogColorThemeFix from '../core/logic/reportDialog';
-import CCDarkThemeEcAppHandler from '../nodeWatcherHandlers/ecApp.handler';
-import CCDarkThemeReportDialogHandler from '../nodeWatcherHandlers/reportDialog.handler';
-import CCDarkThemeUnifiedProfilesIframeHandler from '../nodeWatcherHandlers/unifiedProfilesIframe.handler';
-
-export interface CCDarkThemeNodeWatcherDependencies {
- reportDialogColorThemeFix: ReportDialogColorThemeFix;
- optionsProvider: OptionsProvider;
-}
-
-export default class CCDarkThemeNodeWatcherScript extends LegacyNodeWatcherScript<CCDarkThemeNodeWatcherDependencies> {
- public page = ScriptPage.CommunityConsole;
- public environment = ScriptEnvironment.ContentScript;
- public runPhase = ScriptRunPhase.Main;
- public handlers = new Map([
- ['cc-dark-theme-ec-app', CCDarkThemeEcAppHandler],
- ['cc-dark-theme-report-dialog', CCDarkThemeReportDialogHandler],
- [
- 'cc-dark-theme-unified-profiles-iframe',
- CCDarkThemeUnifiedProfilesIframeHandler,
- ],
- ]);
-
- protected optionsFactory(): CCDarkThemeNodeWatcherDependencies {
- const dependenciesProvider = DependenciesProviderSingleton.getInstance();
- return {
- reportDialogColorThemeFix: dependenciesProvider.getDependency(
- ReportDialogColorThemeFixDependency,
- ),
- optionsProvider: dependenciesProvider.getDependency(
- OptionsProviderDependency,
- ),
- };
- }
-}
diff --git a/src/services/options/OptionsProvider.ts b/src/services/options/OptionsProvider.ts
new file mode 100644
index 0000000..2e64156
--- /dev/null
+++ b/src/services/options/OptionsProvider.ts
@@ -0,0 +1,31 @@
+import { OptionsConfiguration } from '../../common/options/OptionsConfiguration';
+import {
+ OptionCodename,
+ OptionsValues,
+} from '../../common/options/optionsPrototype';
+
+export interface OptionsProviderPort {
+ /**
+ * Returns the value of option |option|.
+ */
+ getOptionValue<O extends OptionCodename>(
+ option: O,
+ ): Promise<OptionsValues[O]>;
+
+ /**
+ * Returns whether |feature| is enabled.
+ */
+ isEnabled(option: OptionCodename): Promise<boolean>;
+
+ getOptionsValues(): Promise<OptionsValues>;
+
+ /**
+ * Adds a listener for changes in the options configuration.
+ */
+ addListener(listener: OptionsChangeListener): void;
+}
+
+export type OptionsChangeListener = (
+ previousOptionValues: OptionsConfiguration,
+ currentOptionValues: OptionsConfiguration,
+) => void;