diff --git a/packages/core/services/PersistentConfigService/index.ts b/packages/core/services/PersistentConfigService/index.ts index fac029820..541b320fd 100644 --- a/packages/core/services/PersistentConfigService/index.ts +++ b/packages/core/services/PersistentConfigService/index.ts @@ -1,9 +1,12 @@ +import { AnnotationResponse } from "../AnnotationService"; + /** * Keys for the data saved by this service */ export enum PersistedConfigKeys { AllenMountPoint = "ALLEN_MOUNT_POINT", CsvColumns = "CSV_COLUMNS", + DisplayAnnotations = "DISPLAY_ANNOTATIONS", ImageJExecutable = "IMAGE_J_EXECUTABLE", // Deprecated HasUsedApplicationBefore = "HAS_USED_APPLICATION_BEFORE", UserSelectedApplications = "USER_SELECTED_APPLICATIONS", @@ -16,6 +19,7 @@ export interface UserSelectedApplication { export interface PersistedConfig { [PersistedConfigKeys.CsvColumns]?: string[]; + [PersistedConfigKeys.DisplayAnnotations]?: AnnotationResponse[]; [PersistedConfigKeys.ImageJExecutable]?: string; // Deprecated [PersistedConfigKeys.HasUsedApplicationBefore]?: boolean; [PersistedConfigKeys.UserSelectedApplications]?: UserSelectedApplication[]; diff --git a/packages/core/state/index.ts b/packages/core/state/index.ts index f12904f05..cb4e02e15 100644 --- a/packages/core/state/index.ts +++ b/packages/core/state/index.ts @@ -8,6 +8,7 @@ import { PersistedConfig, PersistedConfigKeys } from "../services/PersistentConf import interaction, { InteractionStateBranch } from "./interaction"; import metadata, { MetadataStateBranch } from "./metadata"; import selection, { SelectionStateBranch } from "./selection"; +import Annotation from "../entity/Annotation"; export { interaction, metadata, selection }; @@ -63,6 +64,13 @@ export function createReduxStore(options: CreateStoreOptions = {}) { userSelectedApplications: persistedConfig && persistedConfig[PersistedConfigKeys.UserSelectedApplications], }, + selection: { + displayAnnotations: + persistedConfig && + persistedConfig[PersistedConfigKeys.DisplayAnnotations]?.map( + (annotation) => new Annotation(annotation) + ), + }, }); return configureStore({ middleware: [...(options.middleware || []), ...(middleware || [])], diff --git a/packages/core/state/metadata/logics.ts b/packages/core/state/metadata/logics.ts index 2cb3707df..f12ef8f4e 100644 --- a/packages/core/state/metadata/logics.ts +++ b/packages/core/state/metadata/logics.ts @@ -10,6 +10,7 @@ import { } from "./actions"; import { AnnotationName, TOP_LEVEL_FILE_ANNOTATIONS } from "../../constants"; import AnnotationService from "../../services/AnnotationService"; +import Annotation from "../../entity/Annotation"; /** * Interceptor responsible for turning REQUEST_ANNOTATIONS action into a network call for available annotations. Outputs @@ -20,14 +21,25 @@ const requestAnnotations = createLogic({ const { getState, httpClient } = deps; const applicationVersion = interaction.selectors.getApplicationVersion(getState()); const baseUrl = interaction.selectors.getFileExplorerServiceBaseUrl(getState()); + const displayAnnotations = selection.selectors.getAnnotationsToDisplay(getState()); const annotationService = new AnnotationService({ applicationVersion, baseUrl, httpClient, }); + let annotations: Annotation[] = []; + try { - const annotations = await annotationService.fetchAnnotations(); + annotations = await annotationService.fetchAnnotations(); + dispatch(receiveAnnotations(annotations)); + } catch (err) { + console.error("Failed to fetch annotations", err); + done(); + return; + } + + if (!displayAnnotations.length) { const defaultDisplayAnnotations = compact([ find( TOP_LEVEL_FILE_ANNOTATIONS, @@ -40,14 +52,9 @@ const requestAnnotations = createLogic({ (annotation) => annotation.name === AnnotationName.FILE_SIZE ), ]); - - dispatch(receiveAnnotations(annotations)); dispatch(selection.actions.selectDisplayAnnotation(defaultDisplayAnnotations, true)); - } catch (err) { - console.error("Failed to fetch annotations", err); - } finally { - done(); } + done(); }, type: REQUEST_ANNOTATIONS, }); diff --git a/packages/core/state/selection/actions.ts b/packages/core/state/selection/actions.ts index d30ff0a62..24dbdef3c 100644 --- a/packages/core/state/selection/actions.ts +++ b/packages/core/state/selection/actions.ts @@ -157,7 +157,9 @@ export const DESELECT_DISPLAY_ANNOTATION = makeConstant( ); export interface DeselectDisplayAnnotationAction { - payload: Annotation | Annotation[]; + payload: { + annotation: Annotation | Annotation[]; + }; type: string; } @@ -165,7 +167,9 @@ export function deselectDisplayAnnotation( annotation: Annotation | Annotation[] ): DeselectDisplayAnnotationAction { return { - payload: annotation, + payload: { + annotation, + }, type: DESELECT_DISPLAY_ANNOTATION, }; } diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index 0148d01a2..7ff01b00e 100644 --- a/packages/core/state/selection/reducer.ts +++ b/packages/core/state/selection/reducer.ts @@ -1,5 +1,5 @@ import { makeReducer } from "@aics/redux-utils"; -import { castArray, difference, omit, without } from "lodash"; +import { castArray, difference, omit } from "lodash"; import interaction from "../interaction"; import { AnnotationName, PAST_YEAR_FILTER } from "../../constants"; @@ -123,9 +123,9 @@ export default makeReducer( sortColumn: action.payload, }), [DESELECT_DISPLAY_ANNOTATION]: (state, action) => { - const displayAnnotations = without( - state.displayAnnotations, - ...castArray(action.payload) + // remove deselected annotation from state.displayAnnotations + const displayAnnotations = state.displayAnnotations.filter( + (annotation) => annotation.name !== action.payload.annotation.name ); const columnWidthsToPrune = difference( diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx index adae2098f..b8c63908e 100644 --- a/packages/desktop/src/renderer/index.tsx +++ b/packages/desktop/src/renderer/index.tsx @@ -10,7 +10,7 @@ import { Provider } from "react-redux"; import FmsFileExplorer from "../../../core/App"; import { PersistedConfigKeys } from "../../../core/services"; -import { createReduxStore, interaction } from "../../../core/state"; +import { createReduxStore, interaction, selection } from "../../../core/state"; import ApplicationInfoServiceElectron from "../services/ApplicationInfoServiceElectron"; import ExecutionEnvServiceElectron from "../services/ExecutionEnvServiceElectron"; @@ -73,19 +73,20 @@ const store = createReduxStore({ store.subscribe(() => { const state = store.getState(); const csvColumns = interaction.selectors.getCsvColumns(state); + const displayAnnotations = selection.selectors.getAnnotationsToDisplay(state); const userSelectedApplications = interaction.selectors.getUserSelectedApplications(state); - const hasUsedApplicationBefore = interaction.selectors.hasUsedApplicationBefore(state); const appState = { [PersistedConfigKeys.CsvColumns]: csvColumns, + [PersistedConfigKeys.DisplayAnnotations]: displayAnnotations.map((annotation) => ({ + annotationDisplayName: annotation.displayName, + annotationName: annotation.name, + description: annotation.description, + type: annotation.type, + })), [PersistedConfigKeys.UserSelectedApplications]: userSelectedApplications, + [PersistedConfigKeys.HasUsedApplicationBefore]: true, }; - if (JSON.stringify(appState) !== JSON.stringify(persistentConfigService.getAll())) { - persistentConfigService.persist({ - [PersistedConfigKeys.CsvColumns]: csvColumns, - [PersistedConfigKeys.UserSelectedApplications]: userSelectedApplications, - [PersistedConfigKeys.HasUsedApplicationBefore]: hasUsedApplicationBefore, - }); - } + persistentConfigService.persist(appState); }); function renderFmsFileExplorer() { diff --git a/packages/desktop/src/services/PersistentConfigServiceElectron.ts b/packages/desktop/src/services/PersistentConfigServiceElectron.ts index 17815a6cd..ee3ca6216 100644 --- a/packages/desktop/src/services/PersistentConfigServiceElectron.ts +++ b/packages/desktop/src/services/PersistentConfigServiceElectron.ts @@ -22,6 +22,26 @@ const OPTIONS: Options> = { type: "string", }, }, + [PersistedConfigKeys.DisplayAnnotations]: { + type: "array", + items: { + type: "object", + properties: { + annotationDisplayName: { + type: "string", + }, + annotationName: { + type: "string", + }, + description: { + type: "string", + }, + type: { + type: "string", + }, + }, + }, + }, // ImageJExecutable is Deprecated [PersistedConfigKeys.ImageJExecutable]: { type: "string", diff --git a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts index 5d05ef1b0..ff2d1667f 100644 --- a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts +++ b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts @@ -45,6 +45,17 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { name: "ZEN", }, ]; + + const expectedDisplayAnnotations = [ + { + annotationDisplayName: "Foo", + annotationName: "foo", + description: "foo-long", + type: "string", + units: "string", + }, + ]; + service.persist(PersistedConfigKeys.AllenMountPoint, expectedAllenMountPoint); service.persist(PersistedConfigKeys.CsvColumns, expectedCsvColumns); service.persist(PersistedConfigKeys.ImageJExecutable, expectedImageJExecutable); @@ -53,12 +64,15 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { expectedHasUsedApplicationBefore ); service.persist(PersistedConfigKeys.UserSelectedApplications, expectedUserSelectedApps); + service.persist(PersistedConfigKeys.DisplayAnnotations, expectedDisplayAnnotations); + const expectedConfig = { [PersistedConfigKeys.AllenMountPoint]: expectedAllenMountPoint, [PersistedConfigKeys.CsvColumns]: expectedCsvColumns, [PersistedConfigKeys.ImageJExecutable]: expectedImageJExecutable, [PersistedConfigKeys.HasUsedApplicationBefore]: expectedHasUsedApplicationBefore, [PersistedConfigKeys.UserSelectedApplications]: expectedUserSelectedApps, + [PersistedConfigKeys.DisplayAnnotations]: expectedDisplayAnnotations, }; // Act @@ -85,6 +99,15 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { name: "ImageJ/Fiji", }, ], + [PersistedConfigKeys.DisplayAnnotations]: [ + { + annotationDisplayName: "Foo", + annotationName: "foo", + description: "foo-long", + type: "string", + units: "string", + }, + ], }; // Act