Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/persistant-annotations #81

Closed

Conversation

BrianWhitneyAI
Copy link
Contributor

@BrianWhitneyAI BrianWhitneyAI commented May 2, 2024

Description of Changes

This PR proposes prioritizing recently used Groups, Filters, and Sorts in the ordering of their respective selection dropdowns ( resolves #57 ). This PR is a continuation of #76 feature/recent-hierarchy-annotations , but is based on the new generalize-ui #79 branch.

Testing

Monitored state through various selections and deselections of Groups, Filters, and Sorts including those with repeated no-file-found options.

I did experience some issues with reloading the application where it would disconnect on the first reload but succeed on the second reload. I believe this is an inherent issue to the base branch or my development environment as is still present without these changes.

This PR also passes tests locally with the exception of

  9) @renderer FileDownloadServiceElectron
       promptForDownloadDirectory
         complains about non-writeable directory when given:
     Error: Trying to stub property 'invoke' of undefined

I believe that this is unique to my local environment since I had to use WSL to run the full test suite. In reviewing this PR it would be super helpful to run tests locally with your dev env just to make sure everything is Ok.

Possible Changes/Adjustments

  1. Limiting the length of recent annotations to a smaller number, I foresee a situation where a user has a lot of interaction and the recent list becomes very long (and unalphabetized) and this feature becomes a hindrance
  2. A adjustment to ‘On Start” state to not include undefined elements.

@@ -102,6 +104,10 @@ store.subscribe(() => {
{}
);
if (JSON.stringify(appState) !== JSON.stringify(oldAppState)) {
if (appState.RECENT_ANNOTATIONS[0] == undefined) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the part I do not like. For some reason when a new user opens the app appstate.RECENT_ANNOTATIONS defaults to a list with one defined element rather than an empty list. This breaks when trying to persist the state because the typing does not match. While resetting on the first persist does work its hacky and would be better to fix at the source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should find a fix for this rather than special case it if at all possible. My immediate hunch is this is because of the way you are grabbing the values from the persisted storage on load based on your description.
I believe we discussed this, but did you check to see if this:

    const recentAnnotations =
        persistedConfig && persistedConfig[PersistedConfigKeys.RecentAnnotations];

Could be causing an array like so [undefined]? Probably worth trying to see if:

    const recentAnnotations = persistedConfig?.[PersistedConfigKeys.RecentAnnotations]?.length
           ? persistedConfig?.[PersistedConfigKeys.RecentAnnotations]
           : [];

fixes it?

);

// combine all annotation lists
const combinedAnnotations = fileNameAnnotation.concat(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making sure I understand -this results in: FileName, (recent annotations if any, in original order), (all other annotations, sorted)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep!

@BrianWhitneyAI BrianWhitneyAI marked this pull request as ready for review May 7, 2024 17:36
Copy link
Contributor

@SeanLeRoy SeanLeRoy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something this is missing is a visual indicator to the user that describes why these annotations are at the top of the list.
I think making it so the AnnotationSelector component does the special sort for recent annotations rather than the getSortedAnnotations would make it so that AnnotationSelector could then add some icon or something that would inform the user. Something I've seen before is an icon that looks like a redo icon on the list item, that could work well here for the recent annotations.
Screen Shot 2024-05-07 at 12 37 15 PM

...state.recentAnnotations,
])
);
//let recentAnnotations: string[] = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

...state,
filters: action.payload,
[SET_FILE_FILTERS]: (state, action) => {
const recentAnnotations = Array.from(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simpler, also removes unnecessary castArray:

        [SET_FILE_FILTERS]: (state, action) => ({
            ...state,
            filters: action.payload,
            recentAnnotations: uniq([
                    ...action.payload.map((filter: any) => filter.annotationName),
                    ...state.recentAnnotations,
             ]),
            // Reset file selections when file filters change
            fileSelection: new FileSelection(),
       }),

...state,
sortColumn: action.payload,
}),
[SET_SORT_COLUMN]: (state, action) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit simpler

        [SET_SORT_COLUMN]: (state, action) => ({
            ...state,
            recentAnnotations: uniq([...castArray(action.payload?.annotationName), ...state.recentAnnotations]),
            sortColumn: action.payload,
        }),

Also I think you'll need the optional chaining check that this adds because action.payload could be undefined

@@ -175,14 +194,22 @@ export default makeReducer<SelectionStateBranch>(
...state,
fileSelection: action.payload,
}),
[SET_ANNOTATION_HIERARCHY]: (state, action) => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit simpler (also removes unnecessary castArray)

        [SET_ANNOTATION_HIERARCHY]: (state, action) => ({
            ...state,
            annotationHierarchy: action.payload,
            availableAnnotationsForHierarchyLoading: true,
            recentAnnotations: uniq([...action.payload, ...state.recentAnnotations]),
            // Reset file selections when annotation hierarchy changes
            fileSelection: new FileSelection(),
        }),

// COMPOSED SELECTORS
export const getSortedAnnotations = createSelector(
[getAnnotations, getRecentAnnotations],
(annotations: Annotation[], recentAnnotationNames: string[]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW These typings are implicit and can be removed

[getAnnotations, getRecentAnnotations],
(annotations: Annotation[], recentAnnotationNames: string[]) => {
// Create Array of annotations from recentAnnotationNames
const recentAnnotations = recentAnnotationNames.flatMap((name) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simpler by having this inner callback return an individual element rather than an array like so:

        const recentAnnotations = recentAnnotationNames.flatMap((name) =>
                annotations.find(annotation => annotation.name === name)
        )
        .filter(annotation => !!annotation);

);

// get the File name annotaion in a list (if Present)
const fileNameAnnotation = annotations.filter(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are looking for find here which returns the first element that matches the condition found or undefined if none are found

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should make sure this isn't undefined

);

// create map for filtering duplicate annotations.
const combinedAnnotationsMap = new Map();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use reduce or similar function to build this map up FWIW

@@ -102,6 +104,10 @@ store.subscribe(() => {
{}
);
if (JSON.stringify(appState) !== JSON.stringify(oldAppState)) {
if (appState.RECENT_ANNOTATIONS[0] == undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think we should find a fix for this rather than special case it if at all possible. My immediate hunch is this is because of the way you are grabbing the values from the persisted storage on load based on your description.
I believe we discussed this, but did you check to see if this:

    const recentAnnotations =
        persistedConfig && persistedConfig[PersistedConfigKeys.RecentAnnotations];

Could be causing an array like so [undefined]? Probably worth trying to see if:

    const recentAnnotations = persistedConfig?.[PersistedConfigKeys.RecentAnnotations]?.length
           ? persistedConfig?.[PersistedConfigKeys.RecentAnnotations]
           : [];

fixes it?

@@ -76,3 +77,37 @@ export const getUnavailableAnnotationsForHierarchy = createSelector(
allAnnotations.filter((annotation) => !availableAnnotations.includes(annotation.name))
)
);

// COMPOSED SELECTORS
export const getSortedAnnotations = createSelector(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests around this selector would be good

@SeanLeRoy SeanLeRoy deleted the branch feature/generalize-ui May 7, 2024 19:40
@SeanLeRoy SeanLeRoy closed this May 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants