From f3e41954c52cb3b3bf5bb2ca513a05a96e784b31 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Fri, 20 May 2022 12:38:52 +0300 Subject: [PATCH] Synchronize initial values of sync boxes when window starts Signed-off-by: Janne Savolainen --- ...cation-update-status-channel.injectable.ts | 2 +- .../discovered-update-version.injectable.ts | 3 + .../progress-of-update-download.injectable.ts | 3 + .../update-is-being-downloaded.injectable.ts | 3 + ...updates-are-being-discovered.injectable.ts | 3 + .../sync-box/create-sync-box.injectable.ts | 7 +- ...nc-box-initial-value-channel.injectable.ts | 21 +++ .../sync-box/sync-box-injection-token.ts | 16 +++ src/common/sync-box/sync-box.test.ts | 121 +++++++++++++----- ...itial-value-channel-listener.injectable.ts | 31 +++++ ...nitial-values-for-sync-boxes.injectable.ts | 40 ++++++ 11 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 src/common/sync-box/sync-box-initial-value-channel.injectable.ts create mode 100644 src/common/sync-box/sync-box-injection-token.ts create mode 100644 src/main/sync-box/sync-box-initial-value-channel-listener.injectable.ts create mode 100644 src/renderer/sync-box/provide-initial-values-for-sync-boxes.injectable.ts diff --git a/src/common/application-update/application-update-status-channel.injectable.ts b/src/common/application-update/application-update-status-channel.injectable.ts index 1f68ab12ae..552b6ec6fe 100644 --- a/src/common/application-update/application-update-status-channel.injectable.ts +++ b/src/common/application-update/application-update-status-channel.injectable.ts @@ -13,7 +13,7 @@ export type ApplicationUpdateStatusEventId = | "download-for-update-failed"; export interface ApplicationUpdateStatusChannelMessage { eventId: ApplicationUpdateStatusEventId; version?: string } -export type ApplicationUpdateStatusChannel = Channel; +export type ApplicationUpdateStatusChannel = Channel; const applicationUpdateStatusChannelInjectable = getInjectable({ id: "application-update-status-channel", diff --git a/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts b/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts index 218fe96c70..ade09c8960 100644 --- a/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts +++ b/src/common/application-update/discovered-update-version/discovered-update-version.injectable.ts @@ -5,6 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../sync-box/create-sync-box.injectable"; import type { UpdateChannel } from "../update-channels"; +import { syncBoxInjectionToken } from "../../sync-box/sync-box-injection-token"; const discoveredUpdateVersionInjectable = getInjectable({ id: "discovered-update-version", @@ -16,6 +17,8 @@ const discoveredUpdateVersionInjectable = getInjectable({ "discovered-update-version", ); }, + + injectionToken: syncBoxInjectionToken, }); export default discoveredUpdateVersionInjectable; diff --git a/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts b/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts index 9aa2f965d9..c615a08a0d 100644 --- a/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts +++ b/src/common/application-update/progress-of-update-download/progress-of-update-download.injectable.ts @@ -4,6 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../sync-box/create-sync-box.injectable"; +import { syncBoxInjectionToken } from "../../sync-box/sync-box-injection-token"; const progressOfUpdateDownloadInjectable = getInjectable({ id: "progress-of-update-download-state", @@ -13,6 +14,8 @@ const progressOfUpdateDownloadInjectable = getInjectable({ return createSyncBox("progress-of-update-download"); }, + + injectionToken: syncBoxInjectionToken, }); export default progressOfUpdateDownloadInjectable; diff --git a/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts b/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts index ab829cba7a..1cc2c18f64 100644 --- a/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts +++ b/src/common/application-update/update-is-being-downloaded/update-is-being-downloaded.injectable.ts @@ -4,6 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../sync-box/create-sync-box.injectable"; +import { syncBoxInjectionToken } from "../../sync-box/sync-box-injection-token"; const updateIsBeingDownloadedInjectable = getInjectable({ id: "update-is-being-downloaded", @@ -13,6 +14,8 @@ const updateIsBeingDownloadedInjectable = getInjectable({ return createSyncBox("update-is-being-downloaded"); }, + + injectionToken: syncBoxInjectionToken, }); export default updateIsBeingDownloadedInjectable; diff --git a/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts b/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts index 45d8b37369..8e18d105b6 100644 --- a/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts +++ b/src/common/application-update/updates-are-being-discovered/updates-are-being-discovered.injectable.ts @@ -4,6 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import createSyncBoxInjectable from "../../sync-box/create-sync-box.injectable"; +import { syncBoxInjectionToken } from "../../sync-box/sync-box-injection-token"; const updatesAreBeingDiscoveredInjectable = getInjectable({ id: "updates-are-being-discovered", @@ -13,6 +14,8 @@ const updatesAreBeingDiscoveredInjectable = getInjectable({ return createSyncBox("updates-are-being-discovered"); }, + + injectionToken: syncBoxInjectionToken, }); export default updatesAreBeingDiscoveredInjectable; diff --git a/src/common/sync-box/create-sync-box.injectable.ts b/src/common/sync-box/create-sync-box.injectable.ts index 914b1e2b41..82cf4b637a 100644 --- a/src/common/sync-box/create-sync-box.injectable.ts +++ b/src/common/sync-box/create-sync-box.injectable.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import type { IComputedValue } from "mobx"; import { computed } from "mobx"; import syncBoxChannelInjectable from "./sync-box-channel.injectable"; import { sendToAgnosticChannelInjectionToken } from "../channel/send-to-agnostic-channel-injection-token"; import syncBoxStateInjectable from "./sync-box-state.injectable"; +import type { SyncBox } from "./sync-box-injection-token"; const createSyncBoxInjectable = getInjectable({ id: "create-sync-box", @@ -37,8 +37,3 @@ const createSyncBoxInjectable = getInjectable({ export default createSyncBoxInjectable; -export interface SyncBox { - id: string; - value: IComputedValue; - set: (value: TValue) => void; -} diff --git a/src/common/sync-box/sync-box-initial-value-channel.injectable.ts b/src/common/sync-box/sync-box-initial-value-channel.injectable.ts new file mode 100644 index 0000000000..f3b1b4f1f2 --- /dev/null +++ b/src/common/sync-box/sync-box-initial-value-channel.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Channel } from "../channel/channel-injection-token"; +import { channelInjectionToken } from "../channel/channel-injection-token"; + +export type SyncBoxInitialValueChannel = Channel; + +const syncBoxInitialValueChannelInjectable = getInjectable({ + id: "sync-box-initial-value-channel", + + instantiate: (): SyncBoxInitialValueChannel => ({ + id: "sync-box-initial-value-channel", + }), + + injectionToken: channelInjectionToken, +}); + +export default syncBoxInitialValueChannelInjectable; diff --git a/src/common/sync-box/sync-box-injection-token.ts b/src/common/sync-box/sync-box-injection-token.ts new file mode 100644 index 0000000000..980b917d3d --- /dev/null +++ b/src/common/sync-box/sync-box-injection-token.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; + +export interface SyncBox { + id: string; + value: IComputedValue; + set: (value: TValue) => void; +} + +export const syncBoxInjectionToken = getInjectionToken>({ + id: "sync-box", +}); diff --git a/src/common/sync-box/sync-box.test.ts b/src/common/sync-box/sync-box.test.ts index ea97fbc967..db9b26a06b 100644 --- a/src/common/sync-box/sync-box.test.ts +++ b/src/common/sync-box/sync-box.test.ts @@ -6,47 +6,99 @@ import { getInjectable } from "@ogre-tools/injectable"; import { observe, runInAction } from "mobx"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { SyncBox } from "./create-sync-box.injectable"; import createSyncBoxInjectable from "./create-sync-box.injectable"; -import applicationWindowInjectable from "../../main/start-main-application/lens-window/application-window/application-window.injectable"; +import { flushPromises } from "../test-utils/flush-promises"; +import type { SyncBox } from "./sync-box-injection-token"; describe("sync-box", () => { - let syncBoxInMain: SyncBox; - let syncBoxInRenderer: SyncBox; let applicationBuilder: ApplicationBuilder; beforeEach(() => { applicationBuilder = getApplicationBuilder(); - const someInjectable = getInjectable({ - id: "some-injectable", - - instantiate: (di) => { - const createSyncBox = di.inject(createSyncBoxInjectable); - - return createSyncBox("some-state"); - }, - }); - applicationBuilder.dis.mainDi.register(someInjectable); applicationBuilder.dis.rendererDi.register(someInjectable); - - syncBoxInMain = applicationBuilder.dis.mainDi.inject(someInjectable); - syncBoxInRenderer = applicationBuilder.dis.rendererDi.inject(someInjectable); }); - describe("when application starts", () => { - beforeEach(() => { + describe("given application is started, when value is set in main", () => { + let valueInMain: string; + let syncBoxInMain: SyncBox; + beforeEach(async () => { + syncBoxInMain = applicationBuilder.dis.mainDi.inject(someInjectable); + // await applicationBuilder.start(); + + observe(syncBoxInMain.value, ({ newValue }) => { + valueInMain = newValue as string; + }, true); + + runInAction(() => { + syncBoxInMain.set("some-value-from-main"); + }); }); - it("", () => { - + it("knows value in main", () => { + expect(valueInMain).toBe("some-value-from-main"); }); describe("when window starts", () => { - it("", () => { + let valueInRenderer: string; + let syncBoxInRenderer: SyncBox; + beforeEach(() => { + // applicationBuilder. + + syncBoxInRenderer = applicationBuilder.dis.rendererDi.inject(someInjectable); + + observe(syncBoxInRenderer.value, ({ newValue }) => { + valueInRenderer = newValue as string; + }, true); + }); + + it("does not have the initial value yet", () => { + expect(valueInRenderer).toBe(undefined); + }); + + describe("when getting initial value resolves", () => { + beforeEach(async () => { + await flushPromises(); + }); + + it("has value in renderer", () => { + expect(valueInRenderer).toBe("some-value-from-main"); + }); + + describe("when value is set from renderer", () => { + beforeEach(() => { + runInAction(() => { + syncBoxInRenderer.set("some-value-from-renderer"); + }); + }); + + it("has value in main", () => { + expect(valueInMain).toBe("some-value-from-renderer"); + }); + + it("has value in renderer", () => { + expect(valueInRenderer).toBe("some-value-from-renderer"); + }); + }); + }); + + describe("when value is set from renderer before getting initial value from main resolves", () => { + beforeEach(() => { + runInAction(() => { + syncBoxInRenderer.set("some-value-from-renderer"); + }); + }); + + it("has value in main", () => { + expect(valueInMain).toBe("some-value-from-renderer"); + }); + + it("has value in renderer", () => { + expect(valueInRenderer).toBe("some-value-from-renderer"); + }); }); }); }); @@ -54,16 +106,15 @@ describe("sync-box", () => { describe("when application starts with a window", () => { let valueInRenderer: string; let valueInMain: string; + let syncBoxInMain: SyncBox; + let syncBoxInRenderer: SyncBox; beforeEach(async () => { + syncBoxInMain = applicationBuilder.dis.mainDi.inject(someInjectable); + syncBoxInRenderer = applicationBuilder.dis.rendererDi.inject(someInjectable); + await applicationBuilder.render(); - const applicationWindow = applicationBuilder.dis.mainDi.inject( - applicationWindowInjectable, - ); - - await applicationWindow.show(); - observe(syncBoxInRenderer.value, ({ newValue }) => { valueInRenderer = newValue as string; }, true); @@ -73,11 +124,11 @@ describe("sync-box", () => { }, true); }); - it("does not know default value in main", () => { + it("does not know initial value in main", () => { expect(valueInMain).toBeUndefined(); }); - it("does not know default value in renderer", () => { + it("does not know initial value in renderer", () => { expect(valueInRenderer).toBeUndefined(); }); @@ -114,3 +165,13 @@ describe("sync-box", () => { }); }); }); + +const someInjectable = getInjectable({ + id: "some-injectable", + + instantiate: (di) => { + const createSyncBox = di.inject(createSyncBoxInjectable); + + return createSyncBox("some-state"); + }, +}); diff --git a/src/main/sync-box/sync-box-initial-value-channel-listener.injectable.ts b/src/main/sync-box/sync-box-initial-value-channel-listener.injectable.ts new file mode 100644 index 0000000000..15b3f81d21 --- /dev/null +++ b/src/main/sync-box/sync-box-initial-value-channel-listener.injectable.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { channelListenerInjectionToken } from "../../common/channel/channel-listener-injection-token"; +import syncBoxInitialValueChannelInjectable from "../../common/sync-box/sync-box-initial-value-channel.injectable"; +import { syncBoxInjectionToken } from "../../common/sync-box/sync-box-injection-token"; + +const syncBoxInitialValueChannelListenerInjectable = getInjectable({ + id: "sync-box-initial-value-channel-listener", + + instantiate: (di) => { + const channel = di.inject(syncBoxInitialValueChannelInjectable); + const syncBoxes = di.injectMany(syncBoxInjectionToken); + + return { + channel, + + handler: () => + syncBoxes.map((box) => ({ + id: box.id, + value: box.value.get(), + })), + }; + }, + + injectionToken: channelListenerInjectionToken, +}); + +export default syncBoxInitialValueChannelListenerInjectable; diff --git a/src/renderer/sync-box/provide-initial-values-for-sync-boxes.injectable.ts b/src/renderer/sync-box/provide-initial-values-for-sync-boxes.injectable.ts new file mode 100644 index 0000000000..b8d585703b --- /dev/null +++ b/src/renderer/sync-box/provide-initial-values-for-sync-boxes.injectable.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token"; +import { syncBoxInjectionToken } from "../../common/sync-box/sync-box-injection-token"; +import getValueFromChannelInjectable from "../channel/get-value-from-channel.injectable"; +import syncBoxInitialValueChannelInjectable from "../../common/sync-box/sync-box-initial-value-channel.injectable"; +import assert from "assert"; + +const provideInitialValuesForSyncBoxesInjectable = getInjectable({ + id: "provide-initial-values-for-sync-boxes", + + instantiate: (di) => { + const syncBoxes = di.injectMany(syncBoxInjectionToken); + const getValueFromChannel = di.inject(getValueFromChannelInjectable); + const syncBoxInitialValueChannel = di.inject(syncBoxInitialValueChannelInjectable); + + return { + run: async () => { + const initialValues = await getValueFromChannel(syncBoxInitialValueChannel); + + assert(initialValues); + + initialValues.forEach(({ id, value }) => { + const targetBox = syncBoxes.find(box => box.id === id); + + if (targetBox) { + targetBox.set(value); + } + }); + }, + }; + }, + + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default provideInitialValuesForSyncBoxesInjectable;