From ab852d195577cc5c8ca1f1d727b92c67326adee9 Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Mon, 26 Sep 2022 12:39:31 -0500 Subject: [PATCH] Merge pull request #9662 from overleaf/jel-checked-dash [web] Refactor checked projects GitOrigin-RevId: 5696cf8beb69d479d95c05273b11625c277b8761 --- .../Features/Project/ProjectListController.js | 4 +-- .../table/project-list-table-row.tsx | 19 ++++-------- .../components/table/project-list-table.tsx | 16 +++++----- .../buttons/archive-projects-button.tsx | 7 ++--- .../buttons/download-projects-button.tsx | 8 +++-- .../buttons/trash-projects-button.tsx | 7 ++--- .../context/project-list-context.tsx | 29 ++++++++++++++----- services/web/types/project/dashboard/api.d.ts | 9 ++++-- 8 files changed, 55 insertions(+), 44 deletions(-) diff --git a/services/web/app/src/Features/Project/ProjectListController.js b/services/web/app/src/Features/Project/ProjectListController.js index 9c35778280..bfa7ea7c08 100644 --- a/services/web/app/src/Features/Project/ProjectListController.js +++ b/services/web/app/src/Features/Project/ProjectListController.js @@ -22,7 +22,7 @@ const UserController = require('../User/UserController') /** @typedef {import("./types").GetProjectsRequest} GetProjectsRequest */ /** @typedef {import("./types").GetProjectsResponse} GetProjectsResponse */ -/** @typedef {import("../../../../types/project/dashboard/api").Project} Project */ +/** @typedef {import("../../../../types/project/dashboard/api").ProjectApi} ProjectApi */ /** @typedef {import("../../../../types/project/dashboard/api").Filters} Filters */ /** @typedef {import("../../../../types/project/dashboard/api").Page} Page */ /** @typedef {import("../../../../types/project/dashboard/api").Sort} Sort */ @@ -308,7 +308,7 @@ async function getProjectsJson(req, res) { * @param {Filters} filters * @param {Sort} sort * @param {Page} page - * @returns {Promise<{totalSize: number, projects: Project[]}>} + * @returns {Promise<{totalSize: number, projects: ProjectApi[]}>} * @private */ async function _getProjects( diff --git a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx index 1b6a7b5f94..74e5e1ecc4 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-list-table-row.tsx @@ -17,23 +17,14 @@ export default function ProjectListTableRow({ }: ProjectListTableRowProps) { const { t } = useTranslation() const ownerName = getOwnerName(project) - const { selectedProjects, setSelectedProjects } = useProjectListContext() + const { updateProjectViewData } = useProjectListContext() const handleCheckboxChange = useCallback( (event: React.ChangeEvent) => { - const checked = event.target.checked - setSelectedProjects(selectedProjects => { - let projects = [...selectedProjects] - if (checked) { - projects.push(project) - } else { - const projectId = event.target.getAttribute('data-project-id') - projects = projects.filter(p => p.id !== projectId) - } - return projects - }) + project.selected = event.target.checked + updateProjectViewData(project) }, - [project, setSelectedProjects] + [project, updateProjectViewData] ) return ( @@ -42,7 +33,7 @@ export default function ProjectListTableRow({ diff --git a/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx b/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx index 9b30ef3a2c..ed50435180 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-list-table.tsx @@ -21,19 +21,19 @@ const SortByButton = withContent(SortBtn) function ProjectListTable() { const { t } = useTranslation() - const { visibleProjects, sort, selectedProjects, setSelectedProjects } = - useProjectListContext() + const { + visibleProjects, + sort, + selectedProjects, + selectOrUnselectAllProjects, + } = useProjectListContext() const { handleSort } = useSort() const handleAllProjectsCheckboxChange = useCallback( (event: React.ChangeEvent) => { - if (event.target.checked) { - setSelectedProjects(visibleProjects) - } else { - setSelectedProjects([]) - } + selectOrUnselectAllProjects(event.target.checked) }, - [setSelectedProjects, visibleProjects] + [selectOrUnselectAllProjects] ) return ( diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx index 39b79c7dd1..12add72f6a 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/archive-projects-button.tsx @@ -8,8 +8,7 @@ import { useProjectListContext } from '../../../../context/project-list-context' import { archiveProject } from '../../../../util/api' function ArchiveProjectsButton() { - const { selectedProjects, updateProjectViewData, setSelectedProjects } = - useProjectListContext() + const { selectedProjects, updateProjectViewData } = useProjectListContext() const { t } = useTranslation() const text = t('archive') @@ -31,10 +30,10 @@ function ArchiveProjectsButton() { await archiveProject(project.id) // update view project.archived = true + project.selected = false updateProjectViewData(project) } - setSelectedProjects([]) - }, [selectedProjects, setSelectedProjects, updateProjectViewData]) + }, [selectedProjects, updateProjectViewData]) return ( <> diff --git a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/download-projects-button.tsx b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/download-projects-button.tsx index cc8396c590..462e76d0ac 100644 --- a/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/download-projects-button.tsx +++ b/services/web/frontend/js/features/project-list/components/table/project-tools/buttons/download-projects-button.tsx @@ -6,7 +6,8 @@ import * as eventTracking from '../../../../../../infrastructure/event-tracking' import { useProjectListContext } from '../../../../context/project-list-context' function DownloadProjectsButton() { - const { selectedProjects, setSelectedProjects } = useProjectListContext() + const { selectedProjects, selectOrUnselectAllProjects } = + useProjectListContext() const { t } = useTranslation() const text = t('download') @@ -23,8 +24,9 @@ function DownloadProjectsButton() { `/project/download/zip?project_ids=${projectIds.join(',')}` ) - setSelectedProjects([]) - }, [projectIds, setSelectedProjects]) + const selected = false + selectOrUnselectAllProjects(selected) + }, [projectIds, selectOrUnselectAllProjects]) return ( diff --git a/services/web/frontend/js/features/project-list/context/project-list-context.tsx b/services/web/frontend/js/features/project-list/context/project-list-context.tsx index 2b0c242fe7..96dbd6e186 100644 --- a/services/web/frontend/js/features/project-list/context/project-list-context.tsx +++ b/services/web/frontend/js/features/project-list/context/project-list-context.tsx @@ -8,7 +8,7 @@ import { uniqBy, without, } from 'lodash' -import { +import React, { createContext, ReactNode, useCallback, @@ -64,6 +64,7 @@ export const UNCATEGORIZED_KEY = 'uncategorized' type ProjectListContextValue = { addClonedProjectToViewData: (project: Project) => void + selectOrUnselectAllProjects: React.Dispatch> visibleProjects: Project[] totalProjectsCount: number error: Error | null @@ -86,7 +87,6 @@ type ProjectListContextValue = { searchText: string setSearchText: React.Dispatch> selectedProjects: Project[] - setSelectedProjects: React.Dispatch> hiddenProjects: Project[] loadMoreCount: number showAllProjects: () => void @@ -122,7 +122,6 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { >('project-list-selected-tag-id', undefined) const [tags, setTags] = useState(getMeta('ol-tags', []) as Tag[]) const [searchText, setSearchText] = useState('') - const [selectedProjects, setSelectedProjects] = useState([]) const { isLoading: loading, @@ -240,6 +239,21 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { } }, [visibleProjects, hiddenProjects, loadMoreCount]) + const selectedProjects = useMemo(() => { + return visibleProjects.filter(project => project.selected) + }, [visibleProjects]) + + const selectOrUnselectAllProjects = useCallback( + checked => { + const projects = visibleProjects.map(project => { + project.selected = checked + return project + }) + setVisibleProjects(projects) + }, + [visibleProjects] + ) + const untaggedProjectsCount = useMemo(() => { const taggedProjectIds = uniq(flatten(tags.map(tag => tag.project_ids))) return loadedProjects.filter( @@ -254,9 +268,10 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { (filter: Filter) => { setFilter(filter) setSelectedTagId(undefined) - setSelectedProjects([]) + const selected = false + selectOrUnselectAllProjects(selected) }, - [setFilter, setSelectedTagId] + [selectOrUnselectAllProjects, setFilter, setSelectedTagId] ) const selectTag = useCallback( @@ -354,6 +369,7 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { () => ({ addTag, addClonedProjectToViewData, + selectOrUnselectAllProjects, deleteTag, error, filter, @@ -371,7 +387,6 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { selectTag, searchText, setSearchText, - setSelectedProjects, setSort, showAllProjects, sort, @@ -384,6 +399,7 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { [ addTag, addClonedProjectToViewData, + selectOrUnselectAllProjects, deleteTag, error, filter, @@ -401,7 +417,6 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) { selectTag, searchText, setSearchText, - setSelectedProjects, setSort, showAllProjects, sort, diff --git a/services/web/types/project/dashboard/api.d.ts b/services/web/types/project/dashboard/api.d.ts index 114962a700..03d9339373 100644 --- a/services/web/types/project/dashboard/api.d.ts +++ b/services/web/types/project/dashboard/api.d.ts @@ -32,11 +32,11 @@ export type UserRef = { lastName: string } -export type Project = { +export type ProjectApi = { id: string name: string owner?: UserRef - lastUpdated: string + lastUpdated: Date lastUpdatedBy: UserRef | null archived: boolean trashed: boolean @@ -44,6 +44,11 @@ export type Project = { source: 'owner' | 'invite' | 'token' } +export type Project = ProjectApi & { + lastUpdated: string + selected?: boolean +} + export type GetProjectsResponseBody = { totalSize: number projects: Project[]