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

Austenem/CAT-754 Refactor "My Lists" #3671

Open
wants to merge 57 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a3d8db0
add ukv endpoint
austenem Jan 6, 2025
c175b6c
pull authenticated lists into my lists page
austenem Jan 9, 2025
f053c9e
implement add functionality
austenem Jan 9, 2025
79e358b
refactor hooks
austenem Jan 9, 2025
474e6ff
update deletion functionality
austenem Jan 10, 2025
535979c
update lists functionality
austenem Jan 13, 2025
72d9e54
fix typing
austenem Jan 13, 2025
b2721d8
add entity to list functionality
austenem Jan 13, 2025
f9a2919
edit and deletion functionality
austenem Jan 13, 2025
8a4b318
liststobedeleted functionality
austenem Jan 14, 2025
5ac2d16
remove savedlistscontent
austenem Jan 14, 2025
e0a60e1
update stores
austenem Jan 14, 2025
b0c654f
move logic to hooks
austenem Jan 16, 2025
2ad59ef
typescript conversions and list page update
austenem Jan 16, 2025
56476b0
adjust useeffect and language
austenem Jan 16, 2025
5ab11ea
add documentation, minor updates
austenem Jan 16, 2025
c3a2e27
fix re-rendering issue
austenem Jan 16, 2025
7a1b127
copy over local lists and entities
austenem Jan 16, 2025
62be1cd
update local to remote copy logic
austenem Jan 17, 2025
d45d82d
update save entity logic
austenem Jan 17, 2025
4f8b5e2
update alerts
austenem Jan 17, 2025
5a86baa
continue adding alerts
austenem Jan 17, 2025
2714c12
add save entities button to search page
austenem Jan 21, 2025
65dd5ab
update saved items table, add button to table
austenem Jan 21, 2025
6738147
adjust button tooltips
austenem Jan 21, 2025
19f6c9b
add barrel files
austenem Jan 21, 2025
8b6ca64
convert to ts
austenem Jan 21, 2025
b6f1209
continue conversions
austenem Jan 21, 2025
972cba2
separate out api and store
austenem Jan 21, 2025
c994ddc
update tests
austenem Jan 21, 2025
c4126bc
fix minor uuid bug, adjust menu button
austenem Jan 21, 2025
da6a178
sort items
austenem Jan 22, 2025
6d017c6
continue to add sorting
austenem Jan 22, 2025
76511e4
clean up and add changelog
austenem Jan 22, 2025
135fbf3
update list page
austenem Jan 22, 2025
7be52bb
update isLoading bool to pass test
austenem Jan 22, 2025
dc73e1f
remove lists page for unauthenticated users
austenem Jan 24, 2025
264190b
refactor alerts
austenem Jan 24, 2025
bbbc912
hide list buttons from unauthenticated users
austenem Jan 24, 2025
b5d114e
finish refactoring hooks
austenem Jan 27, 2025
6f60d73
fix new list dialog
austenem Jan 27, 2025
b4091cb
minor updates and extend alerts
austenem Jan 27, 2025
255339d
update jest test
austenem Jan 27, 2025
cdc7d18
update test and implement copy logic
austenem Jan 28, 2025
5706f9b
update hook to use default export
austenem Jan 28, 2025
7b60842
update storage description
austenem Jan 28, 2025
aa17827
minor cleanup
austenem Jan 28, 2025
e665d2f
finish deleting stores
austenem Jan 28, 2025
71d431e
update logged out description
austenem Jan 28, 2025
d22059f
add save entity button to processed datasets
austenem Jan 28, 2025
3acb253
review cleanup 1
austenem Jan 31, 2025
bc39893
update import
austenem Jan 31, 2025
73bf0ef
Merge branch 'main' into austenem/cat-754-refactor-my-lists
austenem Feb 4, 2025
471b152
update hooks
austenem Feb 4, 2025
add1bf4
Austenem/CAT-1135 Update /services (#3687)
austenem Feb 11, 2025
6ee9dc2
memoize entity counts
austenem Feb 12, 2025
8de28a8
Austenem/CAT-1113 Add list metrics (#3689)
austenem Feb 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG-add-my-lists-metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add metrics for the "My Lists" feature.
2 changes: 2 additions & 0 deletions CHANGELOG-refactor-my-lists.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Update "My Lists" feature to persist across devices for logged-in users.
- Update "My Lists" UI and messaging.
1 change: 1 addition & 0 deletions CHANGELOG-update-services.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Update services page to include new UKV endpoint.
1 change: 1 addition & 0 deletions context/app/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class DefaultConfig(object):
USER_TEMPLATES_ENDPOINT = 'should-be-overriden'
UBKG_ENDPOINT = 'should-be-overridden'
SOFT_ASSAY_ENDPOINT = 'should-be-overridden'
UKV_ENDPOINT = 'should-be-overridden'

SECRET_KEY = 'should-be-overridden'
APP_CLIENT_ID = 'should-be-overridden'
Expand Down
1 change: 1 addition & 0 deletions context/app/static/js/components/Contexts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface AppContextType {
workspacesEndpoint: string;
userTemplatesEndpoint: string;
ubkgEndpoint: string;
ukvEndpoint: string;
protocolsClientToken: string;
isAuthenticated: boolean;
isWorkspacesUser: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function ServiceStatusTable({
workspacesEndpoint,
ubkgEndpoint,
softAssayEndpoint,
ukvEndpoint,
}) {
const gatewayStatus = useGatewayStatus(`${gatewayEndpoint}/status.json`);

Expand Down Expand Up @@ -105,6 +106,12 @@ function ServiceStatusTable({
endpointUrl: ubkgEndpoint,
githubUrl: 'https://github.com/x-atlas-consortia/hs-ontology-api',
}),
buildServiceStatus({
apiName: 'ukv-api',
response: gatewayStatus.ukv_api,
endpointUrl: ukvEndpoint,
githubUrl: 'https://github.com/x-atlas-consortia/user-key-value-api',
}),
]
: [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import Stack from '@mui/material/Stack';

import type { Entity } from 'js/components/types';
import { CollapsibleDetailPageSection } from 'js/components/detailPage/DetailPageSection';
import RelatedEntitiesTable from 'js/components/detailPage/related-entities/RelatedEntitiesTable';
import BulkDownloadButton from 'js/components/bulkDownload/buttons/BulkDownloadButton';
import SaveEntitiesButton from 'js/components/savedLists/SaveEntitiesButton';
import { SpacedSectionButtonRow } from 'js/shared-styles/sections/SectionButtonRow';
import { sectionIconMap } from 'js/shared-styles/icons/sectionIconMap';

Expand All @@ -33,7 +35,12 @@ function CollectionDatasetsTable({ datasets }: CollectionDatasetsTableProps) {
{datasets.length} Datasets
</Typography>
}
buttons={<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />}
buttons={
<Stack direction="row" spacing={1}>
<SaveEntitiesButton uuids={uuids} entity_type="Dataset" />
<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />
</Stack>
}
/>
<Paper>
<RelatedEntitiesTable columns={columns} entities={data} entityType="dataset" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,11 @@ import React, { PropsWithChildren } from 'react';
import { createPortal } from 'react-dom';
import Stack from '@mui/material/Stack';

import useEntityStore, { savedAlertStatus, editedAlertStatus, EntityStore } from 'js/stores/useEntityStore';
import { leftRouteBoundaryID, rightRouteBoundaryID } from 'js/components/Routes/Route/Route';
import { SavedListsSuccessAlert } from 'js/components/savedLists/SavedListsAlerts';
import TableOfContents from 'js/shared-styles/sections/TableOfContents';
import { TableOfContentsItems } from 'js/shared-styles/sections/TableOfContents/types';
import { leftRouteBoundaryID, rightRouteBoundaryID } from 'js/components/Routes/Route/Route';
import { SectionOrder, getSections } from 'js/shared-styles/sections/TableOfContents/utils';
import { StyledAlert } from './style';

const entityStoreSelector = (state: EntityStore) => ({
shouldDisplaySavedOrEditedAlert: state.shouldDisplaySavedOrEditedAlert,
setShouldDisplaySavedOrEditedAlert: state.setShouldDisplaySavedOrEditedAlert,
});

interface DetailLayoutProps extends PropsWithChildren {
sections: SectionOrder;
Expand Down Expand Up @@ -46,34 +40,12 @@ export function HelperPanelPortal({ children }: PropsWithChildren) {
);
}

function DetailAlert() {
const { shouldDisplaySavedOrEditedAlert, setShouldDisplaySavedOrEditedAlert } = useEntityStore(entityStoreSelector);

if (shouldDisplaySavedOrEditedAlert === savedAlertStatus) {
return (
<StyledAlert severity="success" onClose={() => setShouldDisplaySavedOrEditedAlert(false)}>
Successfully added to My Saves List. All lists are currently stored on local storage and are not transferable
between devices.
</StyledAlert>
);
}

if (shouldDisplaySavedOrEditedAlert === editedAlertStatus) {
return (
<StyledAlert severity="success" onClose={() => setShouldDisplaySavedOrEditedAlert(false)}>
Successfully updated save status. All lists are currently stored on local storage and are not transferable
between devices.
</StyledAlert>
);
}
}

function DetailLayout({ sections, children, isLoading = false }: DetailLayoutProps) {
const items = getSections(sections);

return (
<>
<DetailAlert />
<SavedListsSuccessAlert />
<TableOfContentsPortal items={items} isLoading={isLoading} />
{children}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SecondaryBackgroundTooltip } from 'js/shared-styles/tooltips';
import IconButton from '@mui/material/IconButton';
import ContentCopyIcon from '@mui/icons-material/ContentCopyRounded';
import Stack from '@mui/material/Stack';
import SummarySaveEntityButton from 'js/components/detailPage/summary/SummarySaveEntityButton/SummarySaveEntityButton';
import { useVitessceConfLink } from 'js/pages/Dataset/hooks';
import StatusIcon from '../../StatusIcon';
import { useProcessedDatasetContext } from './ProcessedDatasetContext';
Expand Down Expand Up @@ -41,6 +42,7 @@ export function DatasetTitle() {
</SecondaryBackgroundTooltip>
<Stack ml="auto" direction="row" gap={1} alignItems="center">
{conf && <VisualizationIconButton href={vitessceConfUrl} />}
<SummarySaveEntityButton uuid={uuid} />
<SummaryJSONButton entity_type={entity_type} uuid={uuid} />
<VersionSelect />
</Stack>
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import UnfoldLessRoundedIcon from '@mui/icons-material/UnfoldLessRounded';

import { TooltipButtonProps, TooltipIconButton } from 'js/shared-styles/buttons/TooltipButton';
import { CheckIcon, EditSavedEntityIcon, FileIcon, SaveEntityIcon } from 'js/shared-styles/icons';
import useEntityStore, { savedAlertStatus, SummaryViewsType } from 'js/stores/useEntityStore';
import { SummaryViewsType } from 'js/stores/useEntityStore';
import { useTrackEntityPageEvent } from 'js/components/detailPage/useTrackEntityPageEvent';
import EditSavedStatusDialog from 'js/components/savedLists/EditSavedStatusDialog';
import useSavedEntitiesStore, { SavedEntitiesStore } from 'js/stores/useSavedEntitiesStore';
import { Entity } from 'js/components/types';
import { AllEntityTypes } from 'js/shared-styles/icons/entityIconMap';
import { useFlaskDataContext } from 'js/components/Contexts';
import { useAppContext, useFlaskDataContext } from 'js/components/Contexts';
import { sectionIconMap } from 'js/shared-styles/icons/sectionIconMap';
import { useIsLargeDesktop } from 'js/hooks/media-queries';
import ProcessedDataWorkspaceMenu from 'js/components/detailPage/ProcessedData/ProcessedDataWorkspaceMenu';
import useSavedLists from 'js/components/savedLists/hooks';
import WorkspacesIcon from 'assets/svg/workspaces.svg';

function ActionButton<E extends ElementType = IconButtonTypeMap['defaultComponent']>({
Expand Down Expand Up @@ -44,25 +44,13 @@ function JSONButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type:
}

function SaveEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const saveEntity = useSavedEntitiesStore((state) => state.saveEntity);
const setShouldDisplaySavedOrEditedAlert = useEntityStore((state) => state.setShouldDisplaySavedOrEditedAlert);
const { useHandleSaveEntity } = useSavedLists();
const handleSaveEntity = useHandleSaveEntity({ entityUUID: uuid });

const trackSave = useTrackEntityPageEvent();

return (
<ActionButton
onClick={() => {
saveEntity(uuid);
trackSave({ action: 'Save To List', label: uuid });
setShouldDisplaySavedOrEditedAlert(savedAlertStatus);
}}
icon={SaveEntityIcon}
tooltip="Save to list"
/>
);
return <ActionButton onClick={handleSaveEntity} icon={SaveEntityIcon} tooltip="Save to list" />;
}

function EditSavedEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type: AllEntityTypes }) {
function EditSavedEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const [dialogIsOpen, setDialogIsOpen] = useState(false);

return (
Expand All @@ -74,12 +62,7 @@ function EditSavedEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { e
icon={EditSavedEntityIcon}
tooltip="Edit saved status"
/>
<EditSavedStatusDialog
dialogIsOpen={dialogIsOpen}
setDialogIsOpen={setDialogIsOpen}
uuid={uuid}
entity_type={entity_type}
/>
<EditSavedStatusDialog dialogIsOpen={dialogIsOpen} setDialogIsOpen={setDialogIsOpen} uuid={uuid} />
</>
);
}
Expand All @@ -88,16 +71,15 @@ function WorkspaceSVGIcon({ color = 'primary', ...props }: SvgIconProps) {
return <SvgIcon component={WorkspacesIcon} color={color} {...props} />;
}

const useSavedEntitiesSelector = (state: SavedEntitiesStore) => state.savedEntities;
function SaveEditEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const { savedEntities } = useSavedLists();
const { isAuthenticated } = useAppContext();

function SaveEditEntityButton({ entity_type, uuid }: Pick<Entity, 'uuid'> & { entity_type: AllEntityTypes }) {
const savedEntities = useSavedEntitiesStore(useSavedEntitiesSelector);
if (!isAuthenticated) {
return null;
}

return uuid in savedEntities ? (
<EditSavedEntityButton uuid={uuid} entity_type={entity_type} />
) : (
<SaveEntityButton uuid={uuid} />
);
return uuid in savedEntities.savedEntities ? <EditSavedEntityButton uuid={uuid} /> : <SaveEntityButton uuid={uuid} />;
}

function ViewSelectChip({
Expand Down Expand Up @@ -197,7 +179,7 @@ function EntityHeaderActionButtons({
return (
<Stack direction="row" spacing={1} alignItems="center">
{isLargeDesktop && <ViewSelectChips selectedView={view} setView={setView} entity_type={entity_type} />}
<SaveEditEntityButton uuid={uuid} entity_type={entity_type} />
<SaveEditEntityButton uuid={uuid} />
<JSONButton entity_type={entity_type} uuid={uuid} />
{isDataset && (
<ProcessedDataWorkspaceMenu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Stack from '@mui/material/Stack';
import { useFlaskDataContext } from 'js/components/Contexts';
import { useTrackEntityPageEvent } from 'js/components/detailPage/useTrackEntityPageEvent';
import BulkDownloadButton from 'js/components/bulkDownload/buttons/BulkDownloadButton';
import SaveEntitiesButton from 'js/components/savedLists/SaveEntitiesButton';

interface RelatedEntitiesSectionHeaderProps {
searchPageHref: string;
Expand All @@ -19,6 +20,7 @@ export function RelatedEntitiesSectionActions({ searchPageHref, uuids }: Related

return (
<Stack direction="row" spacing={1}>
<SaveEntitiesButton uuids={uuids} entity_type="Dataset" />
<BulkDownloadButton uuids={uuids} tooltip="Bulk download files for datasets in this table." />
<Button
variant="contained"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import LabelledSectionText from 'js/shared-styles/sections/LabelledSectionText';
import OutboundIconLink from 'js/shared-styles/Links/iconLinks/OutboundIconLink';
import Citation from 'js/components/detailPage/Citation';
import { isCollection, isDataset, isPublication } from 'js/components/types';
import { SavedEntitiesList } from 'js/components/savedLists/types';
import { useFlaskDataContext } from 'js/components/Contexts';
import { getCollectionDOI } from 'js/pages/Collection/utils';
import { getEntityCreationInfo } from 'js/helpers/functions';
Expand Down Expand Up @@ -117,11 +118,25 @@ function CollectionCitation() {
function SummaryBodyContent({
isEntityHeader = false,
direction = 'column',
description: propDescription,
creationLabel: propCreationLabel,
creationDate: propCreationDate,
Comment on lines +121 to +123
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why rename the variables with prop*?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was to avoid naming the final description/creation info variables in the function body like checkedDescription - this way they get to keep their intended names (description, etc) and there's a clear distinction between these and the optional prop variables.

...stackProps
}: { isEntityHeader?: boolean } & Partial<StackProps>) {
}: {
isEntityHeader?: boolean;
description?: string;
creationLabel?: string;
creationDate?: string;
} & Partial<StackProps> &
Partial<SavedEntitiesList>) {
const { entity } = useFlaskDataContext();
const { description } = entity;
const { creationLabel, creationDate } = getEntityCreationInfo(entity);

// Use description and creation info from props if provided, otherwise use entity data
const description = propDescription ?? entity.description;
const { creationLabel, creationDate } =
propCreationLabel && propCreationDate
? { creationLabel: propCreationLabel, creationDate: propCreationDate }
: getEntityCreationInfo(entity);

if (isPublication(entity)) {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { useState } from 'react';
import { useAppContext } from 'js/components/Contexts';
import useSavedLists from 'js/components/savedLists/hooks';
import { Entity } from 'js/components/types';
import EditSavedStatusDialog from 'js/components/savedLists/EditSavedStatusDialog';
import { WhiteRectangularTooltipIconButton } from 'js/shared-styles/buttons/TooltipButton';
import { EditSavedEntityIcon, SaveEntityIcon } from 'js/shared-styles/icons';

function SaveEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const { useHandleSaveEntity } = useSavedLists();
const handleSaveEntity = useHandleSaveEntity({ entityUUID: uuid });

return (
<WhiteRectangularTooltipIconButton onClick={handleSaveEntity} tooltip="Save to list">
<SaveEntityIcon color="primary" />
</WhiteRectangularTooltipIconButton>
);
}

function EditSavedEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const [dialogIsOpen, setDialogIsOpen] = useState(false);

return (
<>
<WhiteRectangularTooltipIconButton
onClick={() => {
setDialogIsOpen(true);
}}
tooltip="Edit saved status"
>
<EditSavedEntityIcon color="primary" />
</WhiteRectangularTooltipIconButton>
<EditSavedStatusDialog dialogIsOpen={dialogIsOpen} setDialogIsOpen={setDialogIsOpen} uuid={uuid} />
</>
);
}

export default function SummarySaveEntityButton({ uuid }: Pick<Entity, 'uuid'>) {
const { savedEntities } = useSavedLists();
const { isAuthenticated } = useAppContext();

if (!isAuthenticated) {
return null;
}

return uuid in savedEntities.savedEntities ? <EditSavedEntityButton uuid={uuid} /> : <SaveEntityButton uuid={uuid} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SummarySaveEntityButton from './SummarySaveEntityButton';

export default SummarySaveEntityButton;
Loading
Loading