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

[web] Refactor checked projects

GitOrigin-RevId: 5696cf8beb69d479d95c05273b11625c277b8761
This commit is contained in:
Jessica Lawshe 2022-09-26 12:39:31 -05:00 committed by Copybot
parent 171cd4f21c
commit ab852d1955
8 changed files with 55 additions and 44 deletions

View file

@ -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(

View file

@ -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}
/>

View file

@ -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 (

View file

@ -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 (
<>

View file

@ -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

View file

@ -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 (
<>

View file

@ -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,

View file

@ -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[]