From 57e62e197b542908e7309b25da89adef20493a5c Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Tue, 21 May 2024 16:25:06 -0700 Subject: [PATCH 1/8] feature/annotation-persistance --- .../components/AnnotationPicker/index.tsx | 78 ++++++++++++++----- .../FileDetails/FileAnnotationList.tsx | 4 +- .../components/ListPicker/ListRow.module.css | 7 ++ .../core/components/ListPicker/ListRow.tsx | 4 + packages/core/components/ListPicker/index.tsx | 3 +- .../ListPicker/test/ListPicker.test.tsx | 2 +- .../core/components/QueryPart/QueryFilter.tsx | 4 +- .../core/components/QueryPart/QueryGroup.tsx | 4 +- .../core/components/QueryPart/QuerySort.tsx | 4 +- .../core/components/QuerySidebar/Query.tsx | 4 +- .../services/PersistentConfigService/index.ts | 2 + packages/core/state/index.ts | 4 + packages/core/state/metadata/selectors.ts | 22 ------ packages/core/state/selection/reducer.ts | 13 +++- packages/core/state/selection/selectors.ts | 21 ++++- .../core/state/selection/test/reducer.test.ts | 22 +++--- packages/desktop/src/renderer/index.tsx | 2 + .../PersistentConfigServiceElectron.ts | 6 ++ .../PersistentConfigServiceElectron.test.ts | 4 + 19 files changed, 143 insertions(+), 67 deletions(-) diff --git a/packages/core/components/AnnotationPicker/index.tsx b/packages/core/components/AnnotationPicker/index.tsx index 48a5d6b44..2ea38ffce 100644 --- a/packages/core/components/AnnotationPicker/index.tsx +++ b/packages/core/components/AnnotationPicker/index.tsx @@ -4,8 +4,9 @@ import { useSelector } from "react-redux"; import ListPicker from "../ListPicker"; import { ListItem } from "../ListPicker/ListRow"; import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../constants"; -import Annotation from "../../entity/Annotation"; -import { metadata, selection } from "../../state"; +import Annotation, { AnnotationName } from "../../entity/Annotation"; +import { uniqBy } from "lodash"; +import { selection } from "../../state"; interface Props { id?: string; @@ -27,7 +28,11 @@ interface Props { * downloading a manifest. */ export default function AnnotationPicker(props: Props) { - const annotations = useSelector(metadata.selectors.getSortedAnnotations); + const annotations = useSelector(selection.selectors.getSortedAnnotations).filter( + (annotation) => + !props.disabledTopLevelAnnotations || + !TOP_LEVEL_FILE_ANNOTATION_NAMES.includes(annotation.name) + ); const unavailableAnnotations = useSelector( selection.selectors.getUnavailableAnnotationsForHierarchy ); @@ -35,23 +40,56 @@ export default function AnnotationPicker(props: Props) { selection.selectors.getAvailableAnnotationsForHierarchyLoading ); - const items = annotations - .filter( - (annotation) => - !props.disabledTopLevelAnnotations || - !TOP_LEVEL_FILE_ANNOTATION_NAMES.includes(annotation.name) - ) - .map((annotation) => ({ - selected: props.selections.some((selected) => selected.name === annotation.name), - disabled: - !props.enableAllAnnotations && - unavailableAnnotations.some((unavailable) => unavailable.name === annotation.name), - loading: !props.enableAllAnnotations && areAvailableAnnotationLoading, - description: annotation.description, - data: annotation, - value: annotation.name, - displayValue: annotation.displayName, - })); + const recentAnnotationNames = useSelector(selection.selectors.getRecentAnnotations); + const recentAnnotations = recentAnnotationNames.flatMap((name) => + annotations.filter((annotation) => annotation.name === name) + ); + + const fileNameAnnotation = annotations.filter( + (annotation) => + annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" + ); + + // Define buffer item + const bufferBar = { + selected: false, + disabled: false, + isBuffer: true, + value: "recent buffer", + displayValue: "", + }; + + // combine all annotation lists and buffer item objects + const rawItems = [...recentAnnotations, bufferBar, ...fileNameAnnotation, ...annotations]; + + const items = uniqBy( + rawItems.flatMap((annotation) => { + if (annotation instanceof Annotation) { + return { + selected: props.selections.some( + (selected) => selected.name === annotation.name + ), + disabled: + !props.enableAllAnnotations && + unavailableAnnotations.some( + (unavailable) => unavailable.name === annotation.name + ), + recent: + recentAnnotationNames.includes(annotation.name) && + !props.selections.some((selected) => selected.name === annotation.name), + loading: !props.enableAllAnnotations && areAvailableAnnotationLoading, + description: annotation.description, + data: annotation, + value: annotation.name, + displayValue: annotation.displayName, + }; + } else { + // This is reached if the 'annotation' is a spacer. + return annotation; + } + }), + "value" + ); const removeSelection = (item: ListItem) => { props.setSelections( diff --git a/packages/core/components/FileDetails/FileAnnotationList.tsx b/packages/core/components/FileDetails/FileAnnotationList.tsx index c0aca5145..9fe435640 100644 --- a/packages/core/components/FileDetails/FileAnnotationList.tsx +++ b/packages/core/components/FileDetails/FileAnnotationList.tsx @@ -5,7 +5,7 @@ import { useSelector } from "react-redux"; import FileAnnotationRow from "./FileAnnotationRow"; import Annotation, { AnnotationName } from "../../entity/Annotation"; import FileDetail from "../../entity/FileDetail"; -import { interaction, metadata } from "../../state"; +import { interaction, selection } from "../../state"; import styles from "./FileAnnotationList.module.css"; @@ -21,7 +21,7 @@ interface FileAnnotationListProps { */ export default function FileAnnotationList(props: FileAnnotationListProps) { const { className, fileDetails, isLoading } = props; - const annotations = useSelector(metadata.selectors.getSortedAnnotations); + const annotations = useSelector(selection.selectors.getSortedAnnotations); const { executionEnvService } = useSelector(interaction.selectors.getPlatformDependentServices); // The path to this file on the host this application is running on diff --git a/packages/core/components/ListPicker/ListRow.module.css b/packages/core/components/ListPicker/ListRow.module.css index b716bde40..748d5354e 100644 --- a/packages/core/components/ListPicker/ListRow.module.css +++ b/packages/core/components/ListPicker/ListRow.module.css @@ -31,6 +31,13 @@ .selected { background-color: var(--primary-background-color); color: var(--primary-text-color); + margin: calc(var(--spacing) / 4) 0 +} + +.isBuffer { + background-color: var(--secondary-text-color); + height: 3px; + pointer-events: none; } .item-container, .item-container label { diff --git a/packages/core/components/ListPicker/ListRow.tsx b/packages/core/components/ListPicker/ListRow.tsx index 85bf4c05e..23978016f 100644 --- a/packages/core/components/ListPicker/ListRow.tsx +++ b/packages/core/components/ListPicker/ListRow.tsx @@ -9,6 +9,8 @@ import styles from "./ListRow.module.css"; export interface ListItem { disabled?: boolean; loading?: boolean; + recent?: boolean; + isBuffer?: boolean; selected: boolean; displayValue: AnnotationValue; value: AnnotationValue; @@ -37,6 +39,7 @@ export default function ListRow(props: Props) { className={classNames(styles.itemContainer, { [styles.selected]: item.selected, [styles.disabled]: item.disabled, + [styles.isBuffer]: item.isBuffer, })} menuIconProps={{ iconName: props.subMenuRenderer ? "ChevronRight" : undefined, @@ -59,6 +62,7 @@ export default function ListRow(props: Props) {
{item.selected && }
{item.displayValue} + {item.recent && } {item.loading && } ); diff --git a/packages/core/components/ListPicker/index.tsx b/packages/core/components/ListPicker/index.tsx index 3798c81a5..e5bf274b8 100644 --- a/packages/core/components/ListPicker/index.tsx +++ b/packages/core/components/ListPicker/index.tsx @@ -162,7 +162,8 @@ export default function ListPicker(props: ListPickerProps) {
- Displaying {filteredItems.length} of {items.length} Options + {/* (item.lenght -1) to account for buffer in item list. */} + Displaying {filteredItems.length - 1} of {items.length - 1} Options
diff --git a/packages/core/components/ListPicker/test/ListPicker.test.tsx b/packages/core/components/ListPicker/test/ListPicker.test.tsx index 0e98187ba..757287195 100644 --- a/packages/core/components/ListPicker/test/ListPicker.test.tsx +++ b/packages/core/components/ListPicker/test/ListPicker.test.tsx @@ -201,6 +201,6 @@ describe("", () => { ); // Act / Assert - expect(getByText(`Displaying ${items.length} of ${items.length} Options`)).to.exist; + expect(getByText(`Displaying ${items.length - 1} of ${items.length - 1} Options`)).to.exist; }); }); diff --git a/packages/core/components/QueryPart/QueryFilter.tsx b/packages/core/components/QueryPart/QueryFilter.tsx index 8f2497154..6e0c06df6 100644 --- a/packages/core/components/QueryPart/QueryFilter.tsx +++ b/packages/core/components/QueryPart/QueryFilter.tsx @@ -7,7 +7,7 @@ import AnnotationPicker from "../AnnotationPicker"; import AnnotationFilterForm from "../AnnotationFilterForm"; import Tutorial from "../../entity/Tutorial"; import FileFilter from "../../entity/FileFilter"; -import { metadata, selection } from "../../state"; +import { selection } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { @@ -20,7 +20,7 @@ interface Props { export default function QueryFilter(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(metadata.selectors.getSortedAnnotations); + const annotations = useSelector(selection.selectors.getSortedAnnotations); const filtersGroupedByName = useSelector(selection.selectors.getGroupedByFilterName); return ( diff --git a/packages/core/components/QueryPart/QueryGroup.tsx b/packages/core/components/QueryPart/QueryGroup.tsx index ee393fdf1..6c6edbff3 100644 --- a/packages/core/components/QueryPart/QueryGroup.tsx +++ b/packages/core/components/QueryPart/QueryGroup.tsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; import Tutorial from "../../entity/Tutorial"; -import { metadata, selection } from "../../state"; +import { selection } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { @@ -17,7 +17,7 @@ interface Props { export default function QueryGroup(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(metadata.selectors.getSortedAnnotations); + const annotations = useSelector(selection.selectors.getSortedAnnotations); const selectedAnnotations = props.groups .map((annotationName) => diff --git a/packages/core/components/QueryPart/QuerySort.tsx b/packages/core/components/QueryPart/QuerySort.tsx index 0001c6ca0..7bfe1fe1a 100644 --- a/packages/core/components/QueryPart/QuerySort.tsx +++ b/packages/core/components/QueryPart/QuerySort.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; -import { metadata, selection } from "../../state"; +import { selection } from "../../state"; import FileSort, { SortOrder } from "../../entity/FileSort"; import Tutorial from "../../entity/Tutorial"; @@ -17,7 +17,7 @@ interface Props { export default function QuerySort(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(metadata.selectors.getSortedAnnotations); + const annotations = useSelector(selection.selectors.getSortedAnnotations); return ( new Annotation(annotation)) : []; + const recentAnnotations = persistedConfig?.[PersistedConfigKeys.RecentAnnotations]?.length + ? persistedConfig?.[PersistedConfigKeys.RecentAnnotations] + : []; const preloadedState: State = mergeState(initialState, { interaction: { isOnWeb: !!options.isOnWeb, @@ -107,6 +110,7 @@ export function createReduxStore(options: CreateStoreOptions = {}) { ), }, })), + recentAnnotations, }, }); return configureStore({ diff --git a/packages/core/state/metadata/selectors.ts b/packages/core/state/metadata/selectors.ts index fe2f4fe19..b0f0bf9e4 100644 --- a/packages/core/state/metadata/selectors.ts +++ b/packages/core/state/metadata/selectors.ts @@ -1,27 +1,5 @@ -import { createSelector } from "reselect"; - import { State } from "../"; -import Annotation, { AnnotationName } from "../../entity/Annotation"; // BASIC SELECTORS export const getAnnotations = (state: State) => state.metadata.annotations; export const getDataSources = (state: State) => state.metadata.dataSources; - -// COMPOSED SELECTORS -export const getSortedAnnotations = createSelector(getAnnotations, (annotations: Annotation[]) => { - // Sort annotations by file name first then everything else alphabetically - const fileNameAnnotationIndex = annotations.findIndex( - (annotation) => - annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" - ); - if (fileNameAnnotationIndex === -1) { - return Annotation.sort(annotations); - } - return [ - annotations[fileNameAnnotationIndex], - ...Annotation.sort([ - ...annotations.slice(0, fileNameAnnotationIndex), - ...annotations.slice(fileNameAnnotationIndex + 1), - ]), - ]; -}); diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index e43a187e0..30226bf86 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 { omit } from "lodash"; +import { castArray, omit, uniq } from "lodash"; import interaction from "../interaction"; import { THUMBNAIL_SIZE_TO_NUM_COLUMNS } from "../../constants"; @@ -51,6 +51,7 @@ export interface SelectionStateBranch { filters: FileFilter[]; isDarkTheme: boolean; openFileFolders: FileFolder[]; + recentAnnotations: string[]; selectedQuery?: string; shouldDisplaySmallFont: boolean; shouldDisplayThumbnailView: boolean; @@ -75,6 +76,7 @@ export const initialState = { fileSelection: new FileSelection(), filters: [], openFileFolders: [], + recentAnnotations: [], shouldDisplaySmallFont: false, queries: [], shouldDisplayThumbnailView: false, @@ -101,6 +103,10 @@ export default makeReducer( [SET_FILE_FILTERS]: (state, action) => ({ ...state, filters: action.payload, + recentAnnotations: uniq([ + ...action.payload.map((filter: any) => filter.annotationName), + ...state.recentAnnotations, + ]).slice(0, 5), // Reset file selections when file filters change fileSelection: new FileSelection(), @@ -155,6 +161,10 @@ export default makeReducer( }), [SET_SORT_COLUMN]: (state, action) => ({ ...state, + recentAnnotations: uniq([ + ...castArray(action.payload?.annotationName ?? []), + ...state.recentAnnotations, + ]).slice(0, 5), sortColumn: action.payload, }), [interaction.actions.REFRESH]: (state) => ({ @@ -185,6 +195,7 @@ export default makeReducer( ...state, annotationHierarchy: action.payload, availableAnnotationsForHierarchyLoading: true, + recentAnnotations: uniq([...action.payload, ...state.recentAnnotations]).slice(0, 5), // Reset file selections when annotation hierarchy changes fileSelection: new FileSelection(), diff --git a/packages/core/state/selection/selectors.ts b/packages/core/state/selection/selectors.ts index ec484a4d6..fc51ccb38 100644 --- a/packages/core/state/selection/selectors.ts +++ b/packages/core/state/selection/selectors.ts @@ -2,7 +2,7 @@ import { groupBy, keyBy, map } from "lodash"; import { createSelector } from "reselect"; import { State } from "../"; -import Annotation from "../../entity/Annotation"; +import Annotation, { AnnotationName } from "../../entity/Annotation"; import FileExplorerURL, { FileExplorerURLComponents } from "../../entity/FileExplorerURL"; import FileFilter from "../../entity/FileFilter"; import FileFolder from "../../entity/FileFolder"; @@ -25,6 +25,7 @@ export const getFileFilters = (state: State) => state.selection.filters; export const getFileSelection = (state: State) => state.selection.fileSelection; export const getIsDarkTheme = (state: State) => state.selection.isDarkTheme; export const getOpenFileFolders = (state: State) => state.selection.openFileFolders; +export const getRecentAnnotations = (state: State) => state.selection.recentAnnotations; export const getSelectedQuery = (state: State) => state.selection.selectedQuery; export const getShouldDisplaySmallFont = (state: State) => state.selection.shouldDisplaySmallFont; export const getShouldDisplayThumbnailView = (state: State) => @@ -85,3 +86,21 @@ export const getUnavailableAnnotationsForHierarchy = createSelector( allAnnotations.filter((annotation) => !availableAnnotations.includes(annotation.name)) ) ); + +export const getSortedAnnotations = createSelector(getAnnotations, (annotations: Annotation[]) => { + // Sort annotations by file name first then everything else alphabetically + const fileNameAnnotationIndex = annotations.findIndex( + (annotation) => + annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" + ); + if (fileNameAnnotationIndex === -1) { + return Annotation.sort(annotations); + } + return [ + annotations[fileNameAnnotationIndex], + ...Annotation.sort([ + ...annotations.slice(0, fileNameAnnotationIndex), + ...annotations.slice(fileNameAnnotationIndex + 1), + ]), + ]; +}); diff --git a/packages/core/state/selection/test/reducer.test.ts b/packages/core/state/selection/test/reducer.test.ts index 2e421c376..2f3ce0ce6 100644 --- a/packages/core/state/selection/test/reducer.test.ts +++ b/packages/core/state/selection/test/reducer.test.ts @@ -15,9 +15,15 @@ import { DataSource } from "../../../services/DataSourceService"; describe("Selection reducer", () => { [ - selection.actions.SET_ANNOTATION_HIERARCHY, - interaction.actions.SET_FILE_EXPLORER_SERVICE_BASE_URL, - ].forEach((actionConstant) => + { + actionConstant: selection.actions.SET_ANNOTATION_HIERARCHY, + expectedAction: selection.actions.setAnnotationHierarchy([]), + }, + { + actionConstant: interaction.actions.SET_FILE_EXPLORER_SERVICE_BASE_URL, + expectedAction: interaction.actions.setFileExplorerServiceBaseUrl("base"), + }, + ].forEach(({ actionConstant, expectedAction }) => it(`clears selected file state when ${actionConstant} is fired`, () => { // arrange const prevSelection = new FileSelection().select({ @@ -29,20 +35,14 @@ describe("Selection reducer", () => { ...selection.initialState, fileSelection: prevSelection, }; - - const action = { - type: actionConstant, - }; - // act - const nextSelectionState = selection.reducer(initialSelectionState, action); + const nextSelectionState = selection.reducer(initialSelectionState, expectedAction); const nextSelection = selection.selectors.getFileSelection({ ...initialState, selection: nextSelectionState, }); - // assert - expect(prevSelection.count()).to.equal(3); // sanity-check + expect(prevSelection.count()).to.equal(3); // consistency check expect(nextSelection.count()).to.equal(0); }) ); diff --git a/packages/desktop/src/renderer/index.tsx b/packages/desktop/src/renderer/index.tsx index 1785e9360..b561756f0 100644 --- a/packages/desktop/src/renderer/index.tsx +++ b/packages/desktop/src/renderer/index.tsx @@ -74,6 +74,7 @@ store.subscribe(() => { const csvColumns = interaction.selectors.getCsvColumns(state); const displayAnnotations = selection.selectors.getAnnotationsToDisplay(state); const hasUsedApplicationBefore = interaction.selectors.hasUsedApplicationBefore(state); + const recentAnnotations = selection.selectors.getRecentAnnotations(state); const userSelectedApplications = interaction.selectors.getUserSelectedApplications(state); const appState = { @@ -86,6 +87,7 @@ store.subscribe(() => { })), [PersistedConfigKeys.HasUsedApplicationBefore]: hasUsedApplicationBefore, [PersistedConfigKeys.Queries]: queries, + [PersistedConfigKeys.RecentAnnotations]: recentAnnotations, [PersistedConfigKeys.UserSelectedApplications]: userSelectedApplications, }; diff --git a/packages/desktop/src/services/PersistentConfigServiceElectron.ts b/packages/desktop/src/services/PersistentConfigServiceElectron.ts index e6e399c65..06b06429c 100644 --- a/packages/desktop/src/services/PersistentConfigServiceElectron.ts +++ b/packages/desktop/src/services/PersistentConfigServiceElectron.ts @@ -63,6 +63,12 @@ const OPTIONS: Options> = { [PersistedConfigKeys.HasUsedApplicationBefore]: { type: "boolean", }, + [PersistedConfigKeys.RecentAnnotations]: { + type: "array", + items: { + type: "string", + }, + }, [PersistedConfigKeys.UserSelectedApplications]: { type: "array", items: { diff --git a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts index f0608c11a..7d1c8efa3 100644 --- a/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts +++ b/packages/desktop/src/services/test/PersistentConfigServiceElectron.test.ts @@ -45,6 +45,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { name: "ZEN", }, ]; + const expectedRecentAnnotations = ["column"]; const expectedQueries = [ { name: "foo", @@ -71,6 +72,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { ); service.persist(PersistedConfigKeys.UserSelectedApplications, expectedUserSelectedApps); service.persist(PersistedConfigKeys.DisplayAnnotations, expectedDisplayAnnotations); + service.persist(PersistedConfigKeys.RecentAnnotations, expectedRecentAnnotations); const expectedConfig = { [PersistedConfigKeys.AllenMountPoint]: expectedAllenMountPoint, @@ -80,6 +82,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { [PersistedConfigKeys.HasUsedApplicationBefore]: expectedHasUsedApplicationBefore, [PersistedConfigKeys.UserSelectedApplications]: expectedUserSelectedApps, [PersistedConfigKeys.DisplayAnnotations]: expectedDisplayAnnotations, + [PersistedConfigKeys.RecentAnnotations]: expectedRecentAnnotations, }; // Act @@ -100,6 +103,7 @@ describe(`${RUN_IN_RENDERER} PersistentConfigServiceElectron`, () => { [PersistedConfigKeys.ImageJExecutable]: "/my/imagej", [PersistedConfigKeys.Queries]: [], [PersistedConfigKeys.HasUsedApplicationBefore]: undefined, + [PersistedConfigKeys.RecentAnnotations]: ["column"], [PersistedConfigKeys.UserSelectedApplications]: [ { filePath: "/some/path/to/ImageJ", From 0e672fc19dde1c87f4222efaff49c23af8b746ee Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Tue, 21 May 2024 16:47:09 -0700 Subject: [PATCH 2/8] remove buffer chevron bug --- packages/core/components/ListPicker/ListRow.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/components/ListPicker/ListRow.tsx b/packages/core/components/ListPicker/ListRow.tsx index 23978016f..d29d50222 100644 --- a/packages/core/components/ListPicker/ListRow.tsx +++ b/packages/core/components/ListPicker/ListRow.tsx @@ -42,7 +42,7 @@ export default function ListRow(props: Props) { [styles.isBuffer]: item.isBuffer, })} menuIconProps={{ - iconName: props.subMenuRenderer ? "ChevronRight" : undefined, + iconName: props.subMenuRenderer && !item.isBuffer ? "ChevronRight" : undefined, }} menuProps={ props.subMenuRenderer From b3d4b269afe5c582a286506f5a88ff52cfb6ed8b Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Tue, 21 May 2024 16:49:49 -0700 Subject: [PATCH 3/8] length typo --- packages/core/components/ListPicker/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/components/ListPicker/index.tsx b/packages/core/components/ListPicker/index.tsx index e5bf274b8..a621c9987 100644 --- a/packages/core/components/ListPicker/index.tsx +++ b/packages/core/components/ListPicker/index.tsx @@ -162,7 +162,7 @@ export default function ListPicker(props: ListPickerProps) {
- {/* (item.lenght -1) to account for buffer in item list. */} + {/* (item.length -1) to account for buffer in item list. */} Displaying {filteredItems.length - 1} of {items.length - 1} Options
From 19bddbbc6648eaf87b3d6b9f0b859333cddebe87 Mon Sep 17 00:00:00 2001 From: BrianWhitneyAI <94479316+BrianWhitneyAI@users.noreply.github.com> Date: Wed, 22 May 2024 10:00:47 -0700 Subject: [PATCH 4/8] Update packages/core/state/selection/reducer.ts Co-authored-by: Sean LeRoy <41307451+SeanLeRoy@users.noreply.github.com> --- packages/core/state/selection/reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index 30226bf86..5e4c3fc17 100644 --- a/packages/core/state/selection/reducer.ts +++ b/packages/core/state/selection/reducer.ts @@ -159,7 +159,7 @@ export default makeReducer( ...state, queries: action.payload, }), - [SET_SORT_COLUMN]: (state, action) => ({ + [SET_SORT_COLUMN]: (state, action: SetSortColumnAction) => ({ ...state, recentAnnotations: uniq([ ...castArray(action.payload?.annotationName ?? []), From 0faf718ed4318494cd1d819ddd75348c06910a2c Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Wed, 22 May 2024 10:46:16 -0700 Subject: [PATCH 5/8] comment resolution 2 --- .../components/AnnotationPicker/index.tsx | 16 +++++--------- .../FileDetails/FileAnnotationList.tsx | 4 ++-- .../core/components/QueryPart/QueryFilter.tsx | 4 ++-- .../core/components/QueryPart/QueryGroup.tsx | 4 ++-- .../core/components/QueryPart/QuerySort.tsx | 4 ++-- .../core/components/QuerySidebar/Query.tsx | 4 ++-- packages/core/state/metadata/selectors.ts | 22 +++++++++++++++++++ packages/core/state/selection/reducer.ts | 1 + packages/core/state/selection/selectors.ts | 20 +---------------- 9 files changed, 40 insertions(+), 39 deletions(-) diff --git a/packages/core/components/AnnotationPicker/index.tsx b/packages/core/components/AnnotationPicker/index.tsx index 2ea38ffce..98743c2c3 100644 --- a/packages/core/components/AnnotationPicker/index.tsx +++ b/packages/core/components/AnnotationPicker/index.tsx @@ -1,12 +1,13 @@ import * as React from "react"; +import { uniqBy } from "lodash"; import { useSelector } from "react-redux"; import ListPicker from "../ListPicker"; import { ListItem } from "../ListPicker/ListRow"; import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../constants"; -import Annotation, { AnnotationName } from "../../entity/Annotation"; -import { uniqBy } from "lodash"; -import { selection } from "../../state"; +import Annotation from "../../entity/Annotation"; + +import { selection, metadata } from "../../state"; interface Props { id?: string; @@ -28,7 +29,7 @@ interface Props { * downloading a manifest. */ export default function AnnotationPicker(props: Props) { - const annotations = useSelector(selection.selectors.getSortedAnnotations).filter( + const annotations = useSelector(metadata.selectors.getSortedAnnotations).filter( (annotation) => !props.disabledTopLevelAnnotations || !TOP_LEVEL_FILE_ANNOTATION_NAMES.includes(annotation.name) @@ -45,11 +46,6 @@ export default function AnnotationPicker(props: Props) { annotations.filter((annotation) => annotation.name === name) ); - const fileNameAnnotation = annotations.filter( - (annotation) => - annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" - ); - // Define buffer item const bufferBar = { selected: false, @@ -60,7 +56,7 @@ export default function AnnotationPicker(props: Props) { }; // combine all annotation lists and buffer item objects - const rawItems = [...recentAnnotations, bufferBar, ...fileNameAnnotation, ...annotations]; + const rawItems = [...recentAnnotations, bufferBar, ...annotations]; const items = uniqBy( rawItems.flatMap((annotation) => { diff --git a/packages/core/components/FileDetails/FileAnnotationList.tsx b/packages/core/components/FileDetails/FileAnnotationList.tsx index 9fe435640..c0aca5145 100644 --- a/packages/core/components/FileDetails/FileAnnotationList.tsx +++ b/packages/core/components/FileDetails/FileAnnotationList.tsx @@ -5,7 +5,7 @@ import { useSelector } from "react-redux"; import FileAnnotationRow from "./FileAnnotationRow"; import Annotation, { AnnotationName } from "../../entity/Annotation"; import FileDetail from "../../entity/FileDetail"; -import { interaction, selection } from "../../state"; +import { interaction, metadata } from "../../state"; import styles from "./FileAnnotationList.module.css"; @@ -21,7 +21,7 @@ interface FileAnnotationListProps { */ export default function FileAnnotationList(props: FileAnnotationListProps) { const { className, fileDetails, isLoading } = props; - const annotations = useSelector(selection.selectors.getSortedAnnotations); + const annotations = useSelector(metadata.selectors.getSortedAnnotations); const { executionEnvService } = useSelector(interaction.selectors.getPlatformDependentServices); // The path to this file on the host this application is running on diff --git a/packages/core/components/QueryPart/QueryFilter.tsx b/packages/core/components/QueryPart/QueryFilter.tsx index 6e0c06df6..8bee1ed6f 100644 --- a/packages/core/components/QueryPart/QueryFilter.tsx +++ b/packages/core/components/QueryPart/QueryFilter.tsx @@ -7,7 +7,7 @@ import AnnotationPicker from "../AnnotationPicker"; import AnnotationFilterForm from "../AnnotationFilterForm"; import Tutorial from "../../entity/Tutorial"; import FileFilter from "../../entity/FileFilter"; -import { selection } from "../../state"; +import { selection, metadata } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { @@ -20,7 +20,7 @@ interface Props { export default function QueryFilter(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(selection.selectors.getSortedAnnotations); + const annotations = useSelector(metadata.selectors.getSortedAnnotations); const filtersGroupedByName = useSelector(selection.selectors.getGroupedByFilterName); return ( diff --git a/packages/core/components/QueryPart/QueryGroup.tsx b/packages/core/components/QueryPart/QueryGroup.tsx index 6c6edbff3..74b78a8fa 100644 --- a/packages/core/components/QueryPart/QueryGroup.tsx +++ b/packages/core/components/QueryPart/QueryGroup.tsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; import Tutorial from "../../entity/Tutorial"; -import { selection } from "../../state"; +import { selection, metadata } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { @@ -17,7 +17,7 @@ interface Props { export default function QueryGroup(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(selection.selectors.getSortedAnnotations); + const annotations = useSelector(metadata.selectors.getSortedAnnotations); const selectedAnnotations = props.groups .map((annotationName) => diff --git a/packages/core/components/QueryPart/QuerySort.tsx b/packages/core/components/QueryPart/QuerySort.tsx index 7bfe1fe1a..3663b6992 100644 --- a/packages/core/components/QueryPart/QuerySort.tsx +++ b/packages/core/components/QueryPart/QuerySort.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; -import { selection } from "../../state"; +import { selection, metadata } from "../../state"; import FileSort, { SortOrder } from "../../entity/FileSort"; import Tutorial from "../../entity/Tutorial"; @@ -17,7 +17,7 @@ interface Props { export default function QuerySort(props: Props) { const dispatch = useDispatch(); - const annotations = useSelector(selection.selectors.getSortedAnnotations); + const annotations = useSelector(metadata.selectors.getSortedAnnotations); return ( state.metadata.annotations; export const getDataSources = (state: State) => state.metadata.dataSources; + +// COMPOSED SELECTORS +export const getSortedAnnotations = createSelector(getAnnotations, (annotations: Annotation[]) => { + // Sort annotations by file name first then everything else alphabetically + const fileNameAnnotationIndex = annotations.findIndex( + (annotation) => + annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" + ); + if (fileNameAnnotationIndex === -1) { + return Annotation.sort(annotations); + } + return [ + annotations[fileNameAnnotationIndex], + ...Annotation.sort([ + ...annotations.slice(0, fileNameAnnotationIndex), + ...annotations.slice(fileNameAnnotationIndex + 1), + ]), + ]; +}); diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index 5e4c3fc17..ecba81b5a 100644 --- a/packages/core/state/selection/reducer.ts +++ b/packages/core/state/selection/reducer.ts @@ -32,6 +32,7 @@ import { SET_FILE_GRID_COLUMN_COUNT, REMOVE_QUERY, RemoveQuery, + SetSortColumnAction, } from "./actions"; import FileSort, { SortOrder } from "../../entity/FileSort"; import Tutorial from "../../entity/Tutorial"; diff --git a/packages/core/state/selection/selectors.ts b/packages/core/state/selection/selectors.ts index fc51ccb38..e76ee70e2 100644 --- a/packages/core/state/selection/selectors.ts +++ b/packages/core/state/selection/selectors.ts @@ -2,7 +2,7 @@ import { groupBy, keyBy, map } from "lodash"; import { createSelector } from "reselect"; import { State } from "../"; -import Annotation, { AnnotationName } from "../../entity/Annotation"; +import Annotation from "../../entity/Annotation"; import FileExplorerURL, { FileExplorerURLComponents } from "../../entity/FileExplorerURL"; import FileFilter from "../../entity/FileFilter"; import FileFolder from "../../entity/FileFolder"; @@ -86,21 +86,3 @@ export const getUnavailableAnnotationsForHierarchy = createSelector( allAnnotations.filter((annotation) => !availableAnnotations.includes(annotation.name)) ) ); - -export const getSortedAnnotations = createSelector(getAnnotations, (annotations: Annotation[]) => { - // Sort annotations by file name first then everything else alphabetically - const fileNameAnnotationIndex = annotations.findIndex( - (annotation) => - annotation.name === AnnotationName.FILE_NAME || annotation.name === "File Name" - ); - if (fileNameAnnotationIndex === -1) { - return Annotation.sort(annotations); - } - return [ - annotations[fileNameAnnotationIndex], - ...Annotation.sort([ - ...annotations.slice(0, fileNameAnnotationIndex), - ...annotations.slice(fileNameAnnotationIndex + 1), - ]), - ]; -}); From abfca7899e1a478ef92b433a6df632d98bd40b8d Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Wed, 22 May 2024 10:50:30 -0700 Subject: [PATCH 6/8] alphabitazation --- packages/core/components/AnnotationPicker/index.tsx | 2 +- packages/core/components/QueryPart/QueryFilter.tsx | 2 +- packages/core/components/QueryPart/QueryGroup.tsx | 2 +- packages/core/components/QueryPart/QuerySort.tsx | 2 +- packages/core/components/QuerySidebar/Query.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/components/AnnotationPicker/index.tsx b/packages/core/components/AnnotationPicker/index.tsx index 98743c2c3..8fd5e1575 100644 --- a/packages/core/components/AnnotationPicker/index.tsx +++ b/packages/core/components/AnnotationPicker/index.tsx @@ -7,7 +7,7 @@ import { ListItem } from "../ListPicker/ListRow"; import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../constants"; import Annotation from "../../entity/Annotation"; -import { selection, metadata } from "../../state"; +import { metadata, selection } from "../../state"; interface Props { id?: string; diff --git a/packages/core/components/QueryPart/QueryFilter.tsx b/packages/core/components/QueryPart/QueryFilter.tsx index 8bee1ed6f..8f2497154 100644 --- a/packages/core/components/QueryPart/QueryFilter.tsx +++ b/packages/core/components/QueryPart/QueryFilter.tsx @@ -7,7 +7,7 @@ import AnnotationPicker from "../AnnotationPicker"; import AnnotationFilterForm from "../AnnotationFilterForm"; import Tutorial from "../../entity/Tutorial"; import FileFilter from "../../entity/FileFilter"; -import { selection, metadata } from "../../state"; +import { metadata, selection } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { diff --git a/packages/core/components/QueryPart/QueryGroup.tsx b/packages/core/components/QueryPart/QueryGroup.tsx index 74b78a8fa..ee393fdf1 100644 --- a/packages/core/components/QueryPart/QueryGroup.tsx +++ b/packages/core/components/QueryPart/QueryGroup.tsx @@ -4,7 +4,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; import Tutorial from "../../entity/Tutorial"; -import { selection, metadata } from "../../state"; +import { metadata, selection } from "../../state"; import Annotation from "../../entity/Annotation"; interface Props { diff --git a/packages/core/components/QueryPart/QuerySort.tsx b/packages/core/components/QueryPart/QuerySort.tsx index 3663b6992..0001c6ca0 100644 --- a/packages/core/components/QueryPart/QuerySort.tsx +++ b/packages/core/components/QueryPart/QuerySort.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import QueryPart from "."; import AnnotationPicker from "../AnnotationPicker"; -import { selection, metadata } from "../../state"; +import { metadata, selection } from "../../state"; import FileSort, { SortOrder } from "../../entity/FileSort"; import Tutorial from "../../entity/Tutorial"; diff --git a/packages/core/components/QuerySidebar/Query.tsx b/packages/core/components/QuerySidebar/Query.tsx index 967403086..f7ff4c04e 100644 --- a/packages/core/components/QuerySidebar/Query.tsx +++ b/packages/core/components/QuerySidebar/Query.tsx @@ -8,7 +8,7 @@ import QueryDataSource from "../QueryPart/QueryDataSource"; import QueryFilter from "../QueryPart/QueryFilter"; import QueryGroup from "../QueryPart/QueryGroup"; import QuerySort from "../QueryPart/QuerySort"; -import { selection, metadata } from "../../state"; +import { metadata, selection } from "../../state"; import { Query as QueryType } from "../../state/selection/actions"; import styles from "./Query.module.css"; From 32f367a17c7dc30fa0d3bec6a962e17330aa1941 Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Wed, 22 May 2024 12:14:10 -0700 Subject: [PATCH 7/8] comment resolution 3 --- packages/core/components/AnnotationPicker/index.tsx | 1 - packages/core/state/selection/reducer.ts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/components/AnnotationPicker/index.tsx b/packages/core/components/AnnotationPicker/index.tsx index 8fd5e1575..b1486d17e 100644 --- a/packages/core/components/AnnotationPicker/index.tsx +++ b/packages/core/components/AnnotationPicker/index.tsx @@ -6,7 +6,6 @@ import ListPicker from "../ListPicker"; import { ListItem } from "../ListPicker/ListRow"; import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../constants"; import Annotation from "../../entity/Annotation"; - import { metadata, selection } from "../../state"; interface Props { diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index ecba81b5a..55c3fdc65 100644 --- a/packages/core/state/selection/reducer.ts +++ b/packages/core/state/selection/reducer.ts @@ -33,6 +33,7 @@ import { REMOVE_QUERY, RemoveQuery, SetSortColumnAction, + SetFileFiltersAction, } from "./actions"; import FileSort, { SortOrder } from "../../entity/FileSort"; import Tutorial from "../../entity/Tutorial"; @@ -101,7 +102,7 @@ export default makeReducer( ...state, fileGridColumnCount: action.payload, }), - [SET_FILE_FILTERS]: (state, action) => ({ + [SET_FILE_FILTERS]: (state, action: SetFileFiltersAction) => ({ ...state, filters: action.payload, recentAnnotations: uniq([ From 5ee02dea00e14bf9a901995edc0e8428b95d7157 Mon Sep 17 00:00:00 2001 From: BrianW25 Date: Wed, 22 May 2024 12:26:22 -0700 Subject: [PATCH 8/8] comment resoluton 4 --- packages/core/state/selection/reducer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/state/selection/reducer.ts b/packages/core/state/selection/reducer.ts index 55c3fdc65..1693faa4a 100644 --- a/packages/core/state/selection/reducer.ts +++ b/packages/core/state/selection/reducer.ts @@ -106,7 +106,7 @@ export default makeReducer( ...state, filters: action.payload, recentAnnotations: uniq([ - ...action.payload.map((filter: any) => filter.annotationName), + ...action.payload.map((filter) => filter.name), ...state.recentAnnotations, ]).slice(0, 5),