Просмотр исходного кода

Merge pull request #9662 from overleaf/jel-checked-dash

[web] Refactor checked projects

GitOrigin-RevId: 5696cf8beb69d479d95c05273b11625c277b8761
Jessica Lawshe 3 лет назад
Родитель
Сommit
ab852d1955

+ 2 - 2
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(

+ 5 - 14
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<HTMLInputElement>) => {
-      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({
         <input
           type="checkbox"
           id={`select-project-${project.id}`}
-          checked={selectedProjects.includes(project)}
+          checked={project.selected === true}
           onChange={handleCheckboxChange}
           data-project-id={project.id}
         />

+ 8 - 8
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<HTMLInputElement>) => {
-      if (event.target.checked) {
-        setSelectedProjects(visibleProjects)
-      } else {
-        setSelectedProjects([])
-      }
+      selectOrUnselectAllProjects(event.target.checked)
     },
-    [setSelectedProjects, visibleProjects]
+    [selectOrUnselectAllProjects]
   )
 
   return (

+ 3 - 4
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 (
     <>

+ 5 - 3
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 (
     <Tooltip

+ 3 - 4
services/web/frontend/js/features/project-list/components/table/project-tools/buttons/trash-projects-button.tsx

@@ -8,8 +8,7 @@ import { useProjectListContext } from '../../../../context/project-list-context'
 import { trashProject } from '../../../../util/api'
 
 function TrashProjectsButton() {
-  const { selectedProjects, setSelectedProjects, updateProjectViewData } =
-    useProjectListContext()
+  const { selectedProjects, updateProjectViewData } = useProjectListContext()
   const { t } = useTranslation()
   const text = t('trash')
 
@@ -32,10 +31,10 @@ function TrashProjectsButton() {
       // update view
       project.trashed = true
       project.archived = false
+      project.selected = false
       updateProjectViewData(project)
     }
-    setSelectedProjects([])
-  }, [selectedProjects, setSelectedProjects, updateProjectViewData])
+  }, [selectedProjects, updateProjectViewData])
 
   return (
     <>

+ 22 - 7
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<React.SetStateAction<boolean>>
   visibleProjects: Project[]
   totalProjectsCount: number
   error: Error | null
@@ -86,7 +87,6 @@ type ProjectListContextValue = {
   searchText: string
   setSearchText: React.Dispatch<React.SetStateAction<string>>
   selectedProjects: Project[]
-  setSelectedProjects: React.Dispatch<React.SetStateAction<Project[]>>
   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<Tag[]>(getMeta('ol-tags', []) as Tag[])
   const [searchText, setSearchText] = useState('')
-  const [selectedProjects, setSelectedProjects] = useState<Project[]>([])
 
   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,

+ 7 - 2
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[]