diff --git a/web/src/components/HomeSidebar/TagsSection.tsx b/web/src/components/HomeSidebar/TagsSection.tsx index d8a11d68..ef2d5695 100644 --- a/web/src/components/HomeSidebar/TagsSection.tsx +++ b/web/src/components/HomeSidebar/TagsSection.tsx @@ -6,8 +6,7 @@ import useDebounce from "react-use/lib/useDebounce"; import useLocalStorage from "react-use/lib/useLocalStorage"; import { memoServiceClient } from "@/grpcweb"; import useCurrentUser from "@/hooks/useCurrentUser"; -import { useFilterStore } from "@/store/module"; -import { useMemoList, useTagStore } from "@/store/v1"; +import { useMemoFilterStore, useMemoList, useTagStore } from "@/store/v1"; import { useTranslate } from "@/utils/i18n"; import Icon from "../Icon"; import showRenameTagDialog from "../RenameTagDialog"; @@ -22,7 +21,7 @@ const TagsSection = (props: Props) => { const t = useTranslate(); const location = useLocation(); const user = useCurrentUser(); - const filterStore = useFilterStore(); + const memoFilterStore = useMemoFilterStore(); const tagStore = useTagStore(); const memoList = useMemoList(); const [treeMode, setTreeMode] = useLocalStorage("tag-view-as-tree", false); @@ -37,10 +36,14 @@ const TagsSection = (props: Props) => { }; const handleTagClick = (tag: string) => { - if (filterStore.getState().tag === tag) { - filterStore.setTagFilter(undefined); + const isActive = memoFilterStore.getFiltersByFactor("tag").some((filter) => filter.value === tag); + if (isActive) { + memoFilterStore.removeFilter((f) => f.factor === "tag" && f.value === tag); } else { - filterStore.setTagFilter(tag); + memoFilterStore.addFilter({ + factor: "tag", + value: tag, + }); } }; @@ -103,10 +106,7 @@ const TagsSection = (props: Props) => {
handleTagClick(tag)} > {tag} diff --git a/web/src/components/MemoFilters.tsx b/web/src/components/MemoFilters.tsx index 106e63f7..c6503480 100644 --- a/web/src/components/MemoFilters.tsx +++ b/web/src/components/MemoFilters.tsx @@ -1,34 +1,42 @@ import { isEqual } from "lodash-es"; -import { useMemoFilterStore } from "@/store/v1"; +import { FilterFactor, getMemoFilterKey, MemoFilter, useMemoFilterStore } from "@/store/v1"; import Icon from "./Icon"; const MemoFilters = () => { const memoFilterStore = useMemoFilterStore(); const filters = memoFilterStore.filters; + const getFilterDisplayText = (filter: MemoFilter) => { + if (filter.value) { + return filter.value; + } + if (filter.factor.startsWith("property.")) { + return filter.factor.replace("property.", ""); + } + return filter.factor; + }; + if (filters.length === 0) { return undefined; } return (
- + Filters -
+
{filters.map((filter) => (
memoFilterStore.removeFilter((f) => isEqual(f, filter))} > - {filter.factor} - {filter.value && {filter.value}} -
))} @@ -37,4 +45,17 @@ const MemoFilters = () => { ); }; +const FactorIcon = ({ factor, className }: { factor: FilterFactor; className?: string }) => { + const iconMap = { + tag: , + visibility: , + contentSearch: , + displayTime: , + "property.hasLink": , + "property.hasTaskList": , + "property.hasCode": , + }; + return iconMap[factor as keyof typeof iconMap] || <>; +}; + export default MemoFilters; diff --git a/web/src/components/SearchBar.tsx b/web/src/components/SearchBar.tsx index 19321580..392381b2 100644 --- a/web/src/components/SearchBar.tsx +++ b/web/src/components/SearchBar.tsx @@ -21,6 +21,7 @@ const SearchBar = () => { factor: "contentSearch", value: queryText, }); + setQueryText(""); } } }; diff --git a/web/src/components/UserStatisticsView.tsx b/web/src/components/UserStatisticsView.tsx index 55fa3076..0564282f 100644 --- a/web/src/components/UserStatisticsView.tsx +++ b/web/src/components/UserStatisticsView.tsx @@ -116,9 +116,7 @@ const UserStatisticsView = () => {
memoFilterStore.addFilter({ factor: "property.hasLink", value: "" })} >
@@ -128,9 +126,7 @@ const UserStatisticsView = () => { {memoStats.link}
memoFilterStore.addFilter({ factor: "property.hasTaskList", value: "" })} >
@@ -154,9 +150,7 @@ const UserStatisticsView = () => { )}
memoFilterStore.addFilter({ factor: "property.hasCode", value: "" })} >
diff --git a/web/src/store/index.ts b/web/src/store/index.ts index c2d62e2a..0f939145 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -1,13 +1,9 @@ import { configureStore } from "@reduxjs/toolkit"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import dialogReducer from "./reducer/dialog"; -import filterReducer from "./reducer/filter"; -import resourceReducer from "./reducer/resource"; const store = configureStore({ reducer: { - filter: filterReducer, - resource: resourceReducer, dialog: dialogReducer, }, }); diff --git a/web/src/store/module/filter.ts b/web/src/store/module/filter.ts deleted file mode 100644 index 913065d9..00000000 --- a/web/src/store/module/filter.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Visibility } from "@/types/proto/api/v1/memo_service"; -import store, { useAppSelector } from ".."; -import { MemoPropertyFilter, setFilter, setMemoPropertyFilter } from "../reducer/filter"; - -export const useFilterStore = () => { - const state = useAppSelector((state) => state.filter); - - return { - state, - getState: () => { - return store.getState().filter; - }, - clearFilter: () => { - store.dispatch( - setFilter({ - tag: undefined, - text: undefined, - visibility: undefined, - memoPropertyFilter: undefined, - }), - ); - }, - setTextFilter: (text?: string) => { - store.dispatch( - setFilter({ - text: text, - }), - ); - }, - setTagFilter: (tag?: string) => { - store.dispatch( - setFilter({ - tag: tag, - }), - ); - }, - setMemoVisibilityFilter: (visibility?: Visibility) => { - store.dispatch( - setFilter({ - visibility: visibility, - }), - ); - }, - setMemoPropertyFilter: (memoPropertyFilter: Partial) => { - store.dispatch( - setMemoPropertyFilter({ - ...memoPropertyFilter, - }), - ); - }, - }; -}; diff --git a/web/src/store/module/index.ts b/web/src/store/module/index.ts index ffa92e83..e5c28748 100644 --- a/web/src/store/module/index.ts +++ b/web/src/store/module/index.ts @@ -1,2 +1 @@ -export * from "./filter"; export * from "./dialog"; diff --git a/web/src/store/reducer/filter.ts b/web/src/store/reducer/filter.ts deleted file mode 100644 index b42feab1..00000000 --- a/web/src/store/reducer/filter.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { Visibility } from "@/types/proto/api/v1/memo_service"; - -interface State { - tag?: string; - text?: string; - visibility?: Visibility; - memoPropertyFilter?: MemoPropertyFilter; -} - -export interface MemoPropertyFilter { - hasLink?: boolean; - hasTaskList?: boolean; - hasCode?: boolean; -} - -export type Filter = State; - -const getInitialState = (): State => { - const state: State = {}; - const urlParams = new URLSearchParams(location.search); - const tag = urlParams.get("tag"); - const text = urlParams.get("text"); - if (tag) { - state.tag = tag; - } - if (text) { - state.text = text; - } - return state; -}; - -const filterSlice = createSlice({ - name: "filter", - initialState: getInitialState(), - reducers: { - setFilter: (state, action: PayloadAction>) => { - if (JSON.stringify(action.payload) === state) { - return state; - } - - return { - ...state, - ...action.payload, - }; - }, - setMemoPropertyFilter: (state, action: PayloadAction>) => { - if (JSON.stringify(action.payload) === state.memoPropertyFilter) { - return state; - } - - return { - ...state, - memoPropertyFilter: { - ...state.memoPropertyFilter, - ...action.payload, - }, - }; - }, - }, -}); - -export const { setFilter, setMemoPropertyFilter } = filterSlice.actions; - -export default filterSlice.reducer; diff --git a/web/src/store/reducer/resource.ts b/web/src/store/reducer/resource.ts deleted file mode 100644 index 1e379a4a..00000000 --- a/web/src/store/reducer/resource.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { uniqBy } from "lodash-es"; -import { Resource } from "@/types/proto/api/v1/resource_service"; - -interface State { - resources: Resource[]; -} - -const resourceSlice = createSlice({ - name: "resource", - initialState: { - resources: [], - } as State, - reducers: { - setResources: (state, action: PayloadAction) => { - return { - ...state, - resources: action.payload, - }; - }, - upsertResources: (state, action: PayloadAction) => { - return { - ...state, - resources: uniqBy([...action.payload, ...state.resources], "name"), - }; - }, - patchResource: (state, action: PayloadAction>) => { - return { - ...state, - resources: state.resources.map((resource) => { - if (resource.name === action.payload.name) { - return { - ...resource, - ...action.payload, - }; - } else { - return resource; - } - }), - }; - }, - }, -}); - -export const { setResources, upsertResources, patchResource } = resourceSlice.actions; - -export default resourceSlice.reducer; diff --git a/web/src/store/v1/memoFilter.ts b/web/src/store/v1/memoFilter.ts index 1d1d4f38..e8b2c019 100644 --- a/web/src/store/v1/memoFilter.ts +++ b/web/src/store/v1/memoFilter.ts @@ -1,8 +1,8 @@ -import { uniq } from "lodash-es"; +import { uniqBy } from "lodash-es"; import { create } from "zustand"; -import { combine, persist } from "zustand/middleware"; +import { combine } from "zustand/middleware"; -type FilterFactor = +export type FilterFactor = | "tag" | "visibility" | "contentSearch" @@ -16,25 +16,18 @@ export interface MemoFilter { value: string; } +export const getMemoFilterKey = (filter: MemoFilter) => `${filter.factor}:${filter.value}`; + interface State { filters: MemoFilter[]; } -const getDefaultState = (): State => ({ - filters: [], -}); - export const useMemoFilterStore = create( - persist( - combine(getDefaultState(), (set, get) => ({ - setState: (state: State) => set(state), - getState: () => get(), - getFiltersByFactor: (factor: FilterFactor) => get().filters.filter((f) => f.factor === factor), - addFilter: (filter: MemoFilter) => set((state) => ({ filters: uniq([...state.filters, filter]) })), - removeFilter: (filterFn: (f: MemoFilter) => boolean) => set((state) => ({ filters: state.filters.filter((f) => !filterFn(f)) })), - })), - { - name: "memo-filter", - }, - ), + combine(((): State => ({ filters: [] }))(), (set, get) => ({ + setState: (state: State) => set(state), + getState: () => get(), + getFiltersByFactor: (factor: FilterFactor) => get().filters.filter((f) => f.factor === factor), + addFilter: (filter: MemoFilter) => set((state) => ({ filters: uniqBy([...state.filters, filter], getMemoFilterKey) })), + removeFilter: (filterFn: (f: MemoFilter) => boolean) => set((state) => ({ filters: state.filters.filter((f) => !filterFn(f)) })), + })), );