mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #9567 from overleaf/ab-react-dash-keep-selected-filter
[web] Remember selected dashboard filter on page reload GitOrigin-RevId: c5abbd4e74ed48c127ae5e60f69a0bd98fbfc876
This commit is contained in:
parent
0c5cdc2023
commit
3bacd89399
3 changed files with 66 additions and 31 deletions
|
@ -15,13 +15,13 @@ function SidebarFilter({ filter, text }: SidebarFilterProps) {
|
||||||
const {
|
const {
|
||||||
filter: activeFilter,
|
filter: activeFilter,
|
||||||
selectFilter,
|
selectFilter,
|
||||||
selectedTag,
|
selectedTagId,
|
||||||
} = useProjectListContext()
|
} = useProjectListContext()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={
|
className={
|
||||||
selectedTag === undefined && filter === activeFilter ? 'active' : ''
|
selectedTagId === undefined && filter === activeFilter ? 'active' : ''
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button onClick={() => selectFilter(filter)}>{text}</Button>
|
<Button onClick={() => selectFilter(filter)}>{text}</Button>
|
||||||
|
|
|
@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next'
|
||||||
import { Tag } from '../../../../../../app/src/Features/Tags/types'
|
import { Tag } from '../../../../../../app/src/Features/Tags/types'
|
||||||
import ColorManager from '../../../../ide/colors/ColorManager'
|
import ColorManager from '../../../../ide/colors/ColorManager'
|
||||||
import Icon from '../../../../shared/components/icon'
|
import Icon from '../../../../shared/components/icon'
|
||||||
import { useProjectListContext } from '../../context/project-list-context'
|
import {
|
||||||
|
UNCATEGORIZED_KEY,
|
||||||
|
useProjectListContext,
|
||||||
|
} from '../../context/project-list-context'
|
||||||
import CreateTagModal from './create-tag-modal'
|
import CreateTagModal from './create-tag-modal'
|
||||||
import DeleteTagModal from './delete-tag-modal'
|
import DeleteTagModal from './delete-tag-modal'
|
||||||
import RenameTagModal from './rename-tag-modal'
|
import RenameTagModal from './rename-tag-modal'
|
||||||
|
@ -15,7 +18,7 @@ export default function TagsList() {
|
||||||
const {
|
const {
|
||||||
tags,
|
tags,
|
||||||
untaggedProjectsCount,
|
untaggedProjectsCount,
|
||||||
selectedTag,
|
selectedTagId,
|
||||||
selectTag,
|
selectTag,
|
||||||
addTag,
|
addTag,
|
||||||
renameTag,
|
renameTag,
|
||||||
|
@ -98,7 +101,7 @@ export default function TagsList() {
|
||||||
{_.sortBy(tags, ['name']).map((tag, index) => {
|
{_.sortBy(tags, ['name']).map((tag, index) => {
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
className={`tag ${selectedTag === tag._id ? 'active' : ''}`}
|
className={`tag ${selectedTagId === tag._id ? 'active' : ''}`}
|
||||||
key={index}
|
key={index}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
|
@ -113,7 +116,7 @@ export default function TagsList() {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
type={selectedTag === tag._id ? 'folder-open' : 'folder'}
|
type={selectedTagId === tag._id ? 'folder-open' : 'folder'}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="name">
|
<span className="name">
|
||||||
|
@ -153,8 +156,15 @@ export default function TagsList() {
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<li className={`tag untagged ${selectedTag === null ? 'active' : ''}`}>
|
<li
|
||||||
<Button className="tag-name" onClick={() => selectTag(null)}>
|
className={`tag untagged ${
|
||||||
|
selectedTagId === UNCATEGORIZED_KEY ? 'active' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="tag-name"
|
||||||
|
onClick={() => selectTag(UNCATEGORIZED_KEY)}
|
||||||
|
>
|
||||||
<span className="name">{t('uncategorized')}</span>
|
<span className="name">{t('uncategorized')}</span>
|
||||||
<span className="subdued"> ({untaggedProjectsCount})</span>
|
<span className="subdued"> ({untaggedProjectsCount})</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
Project,
|
Project,
|
||||||
Sort,
|
Sort,
|
||||||
} from '../../../../../types/project/dashboard/api'
|
} from '../../../../../types/project/dashboard/api'
|
||||||
|
import usePersistedState from '../../../shared/hooks/use-persisted-state'
|
||||||
import getMeta from '../../../utils/meta'
|
import getMeta from '../../../utils/meta'
|
||||||
import useAsync from '../../../shared/hooks/use-async'
|
import useAsync from '../../../shared/hooks/use-async'
|
||||||
import { getProjects } from '../util/api'
|
import { getProjects } from '../util/api'
|
||||||
|
@ -46,6 +47,8 @@ const filters: FilterMap = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const UNCATEGORIZED_KEY = 'uncategorized'
|
||||||
|
|
||||||
type ProjectListContextValue = {
|
type ProjectListContextValue = {
|
||||||
visibleProjects: Project[]
|
visibleProjects: Project[]
|
||||||
setVisibleProjects: React.Dispatch<React.SetStateAction<Project[]>>
|
setVisibleProjects: React.Dispatch<React.SetStateAction<Project[]>>
|
||||||
|
@ -59,8 +62,8 @@ type ProjectListContextValue = {
|
||||||
untaggedProjectsCount: number
|
untaggedProjectsCount: number
|
||||||
filter: Filter
|
filter: Filter
|
||||||
selectFilter: (filter: Filter) => void
|
selectFilter: (filter: Filter) => void
|
||||||
selectedTag?: string | null
|
selectedTagId?: string | undefined
|
||||||
selectTag: (tagName: string | null) => void
|
selectTag: (tagId: string) => void
|
||||||
addTag: (tag: Tag) => void
|
addTag: (tag: Tag) => void
|
||||||
renameTag: (tagId: string, newTagName: string) => void
|
renameTag: (tagId: string, newTagName: string) => void
|
||||||
deleteTag: (tagId: string) => void
|
deleteTag: (tagId: string) => void
|
||||||
|
@ -86,9 +89,14 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
by: 'lastUpdated',
|
by: 'lastUpdated',
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
})
|
})
|
||||||
const [filter, setFilter] = useState<Filter>('all')
|
const [filter, setFilter] = usePersistedState<Filter>(
|
||||||
const [selectedTag, setSelectedTag] = useState<string | null>()
|
'project-list-filter',
|
||||||
const [tags, setTags] = useState<Tag[]>([])
|
'all'
|
||||||
|
)
|
||||||
|
const [selectedTagId, setSelectedTagId] = usePersistedState<
|
||||||
|
string | undefined
|
||||||
|
>('project-list-selected-tag-id', undefined)
|
||||||
|
const [tags, setTags] = useState<Tag[]>(getMeta('ol-tags', []) as Tag[])
|
||||||
const [searchText, setSearchText] = useState('')
|
const [searchText, setSearchText] = useState('')
|
||||||
const {
|
const {
|
||||||
isLoading: loading,
|
isLoading: loading,
|
||||||
|
@ -98,8 +106,6 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
} = useAsync<GetProjectsResponseBody>()
|
} = useAsync<GetProjectsResponseBody>()
|
||||||
const isLoading = isIdle ? true : loading
|
const isLoading = isIdle ? true : loading
|
||||||
|
|
||||||
useEffect(() => setTags(getMeta('ol-tags', []) as Tag[]), [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoadProgress(40)
|
setLoadProgress(40)
|
||||||
runAsync(getProjects({ by: 'lastUpdated', order: 'desc' }))
|
runAsync(getProjects({ by: 'lastUpdated', order: 'desc' }))
|
||||||
|
@ -122,8 +128,8 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedTag !== undefined) {
|
if (selectedTagId !== undefined) {
|
||||||
if (selectedTag === null) {
|
if (selectedTagId === UNCATEGORIZED_KEY) {
|
||||||
const taggedProjectIds = _.uniq(
|
const taggedProjectIds = _.uniq(
|
||||||
_.flatten(tags.map(tag => tag.project_ids))
|
_.flatten(tags.map(tag => tag.project_ids))
|
||||||
)
|
)
|
||||||
|
@ -134,17 +140,30 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
!taggedProjectIds.includes(project.id)
|
!taggedProjectIds.includes(project.id)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
const tag = _.find(tags, tag => tag._id === selectedTag)
|
const tag = _.find(tags, tag => tag._id === selectedTagId)
|
||||||
|
if (tag) {
|
||||||
filteredProjects = filteredProjects.filter(project =>
|
filteredProjects = filteredProjects.filter(project =>
|
||||||
tag?.project_ids?.includes(project.id)
|
tag?.project_ids?.includes(project.id)
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
setFilter('all')
|
||||||
|
setSelectedTagId(undefined)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filteredProjects = _.filter(filteredProjects, filters[filter])
|
filteredProjects = _.filter(filteredProjects, filters[filter])
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibleProjects(filteredProjects)
|
setVisibleProjects(filteredProjects)
|
||||||
}, [loadedProjects, tags, filter, selectedTag, searchText])
|
}, [
|
||||||
|
loadedProjects,
|
||||||
|
tags,
|
||||||
|
filter,
|
||||||
|
setFilter,
|
||||||
|
selectedTagId,
|
||||||
|
setSelectedTagId,
|
||||||
|
searchText,
|
||||||
|
])
|
||||||
|
|
||||||
const untaggedProjectsCount = useMemo(() => {
|
const untaggedProjectsCount = useMemo(() => {
|
||||||
const taggedProjectIds = _.uniq(_.flatten(tags.map(tag => tag.project_ids)))
|
const taggedProjectIds = _.uniq(_.flatten(tags.map(tag => tag.project_ids)))
|
||||||
|
@ -156,14 +175,20 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
).length
|
).length
|
||||||
}, [tags, loadedProjects])
|
}, [tags, loadedProjects])
|
||||||
|
|
||||||
const selectFilter = useCallback((filter: Filter) => {
|
const selectFilter = useCallback(
|
||||||
|
(filter: Filter) => {
|
||||||
setFilter(filter)
|
setFilter(filter)
|
||||||
setSelectedTag(undefined)
|
setSelectedTagId(undefined)
|
||||||
}, [])
|
},
|
||||||
|
[setFilter, setSelectedTagId]
|
||||||
|
)
|
||||||
|
|
||||||
const selectTag = useCallback((tagId: string | null) => {
|
const selectTag = useCallback(
|
||||||
setSelectedTag(tagId)
|
(tagId: string) => {
|
||||||
}, [])
|
setSelectedTagId(tagId)
|
||||||
|
},
|
||||||
|
[setSelectedTagId]
|
||||||
|
)
|
||||||
|
|
||||||
const addTag = useCallback((tag: Tag) => {
|
const addTag = useCallback((tag: Tag) => {
|
||||||
setTags(tags => _.uniqBy(_.concat(tags, [tag]), '_id'))
|
setTags(tags => _.uniqBy(_.concat(tags, [tag]), '_id'))
|
||||||
|
@ -219,7 +244,7 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
isLoading,
|
isLoading,
|
||||||
loadProgress,
|
loadProgress,
|
||||||
renameTag,
|
renameTag,
|
||||||
selectedTag,
|
selectedTagId,
|
||||||
selectFilter,
|
selectFilter,
|
||||||
selectTag,
|
selectTag,
|
||||||
setSearchText,
|
setSearchText,
|
||||||
|
@ -241,7 +266,7 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
|
||||||
isLoading,
|
isLoading,
|
||||||
loadProgress,
|
loadProgress,
|
||||||
renameTag,
|
renameTag,
|
||||||
selectedTag,
|
selectedTagId,
|
||||||
selectFilter,
|
selectFilter,
|
||||||
selectTag,
|
selectTag,
|
||||||
setSearchText,
|
setSearchText,
|
||||||
|
|
Loading…
Reference in a new issue