mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
refactor: view store
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import { Option, Select, Switch } from "@mui/joy";
|
import { Option, Select } from "@mui/joy";
|
||||||
import { Settings2Icon } from "lucide-react";
|
import { Settings2Icon } from "lucide-react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { viewStore } from "@/store/v2";
|
||||||
import { cn } from "@/utils";
|
import { cn } from "@/utils";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
|
||||||
@@ -12,34 +12,44 @@ interface Props {
|
|||||||
|
|
||||||
const MemoDisplaySettingMenu = observer(({ className }: Props) => {
|
const MemoDisplaySettingMenu = observer(({ className }: Props) => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const memoFilterStore = useMemoFilterStore();
|
const isApplying = viewStore.state.orderByTimeAsc !== false || viewStore.state.layout !== "LIST";
|
||||||
const isApplying = Boolean(memoFilterStore.orderByTimeAsc) !== false || memoFilterStore.masonry;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger
|
<PopoverTrigger
|
||||||
className={cn(className, isApplying ? "text-teal-600 bg-teal-50 dark:text-teal-500 dark:bg-teal-900 rounded-sm" : "opacity-40")}
|
className={cn(className, isApplying ? "text-teal-600 bg-teal-100 dark:text-teal-500 dark:bg-teal-900 rounded" : "opacity-40")}
|
||||||
>
|
>
|
||||||
<Settings2Icon className="w-4 h-auto shrink-0" />
|
<Settings2Icon className="w-4 h-auto shrink-0" />
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent align="end" alignOffset={-12} sideOffset={14}>
|
<PopoverContent align="end" alignOffset={-12} sideOffset={14}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
|
||||||
<span className="text-sm shrink-0 mr-3">{t("memo.order-by")}</span>
|
|
||||||
<Select value="displayTime">
|
|
||||||
<Option value={"displayTime"}>{t("memo.display-time")}</Option>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
<span className="text-sm shrink-0 mr-3">{t("memo.direction")}</span>
|
<span className="text-sm shrink-0 mr-3">{t("memo.direction")}</span>
|
||||||
<Select value={memoFilterStore.orderByTimeAsc} onChange={(_, value) => memoFilterStore.setOrderByTimeAsc(Boolean(value))}>
|
<Select
|
||||||
|
value={viewStore.state.orderByTimeAsc}
|
||||||
|
onChange={(_, value) =>
|
||||||
|
viewStore.state.setPartial({
|
||||||
|
orderByTimeAsc: Boolean(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
<Option value={false}>{t("memo.direction-desc")}</Option>
|
<Option value={false}>{t("memo.direction-desc")}</Option>
|
||||||
<Option value={true}>{t("memo.direction-asc")}</Option>
|
<Option value={true}>{t("memo.direction-asc")}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex flex-row justify-between items-center">
|
<div className="w-full flex flex-row justify-between items-center">
|
||||||
<span className="text-sm shrink-0 mr-3">{t("memo.masonry-view")}</span>
|
<span className="text-sm shrink-0 mr-3">{t("memo.masonry-view")}</span>
|
||||||
<Switch checked={memoFilterStore.masonry} onChange={(event) => memoFilterStore.setMasonry(event.target.checked)} />
|
<Select
|
||||||
|
value={viewStore.state.layout}
|
||||||
|
onChange={(_, value) =>
|
||||||
|
viewStore.state.setPartial({
|
||||||
|
layout: value as "LIST" | "MASONRY",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Option value={"LIST"}>{"List"}</Option>
|
||||||
|
<Option value={"MASONRY"}>{"Masonry"}</Option>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
|
@@ -1,49 +1,22 @@
|
|||||||
import { isEqual } from "lodash-es";
|
import { isEqual } from "lodash-es";
|
||||||
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, HashIcon, LinkIcon, SearchIcon, XIcon } from "lucide-react";
|
import { CalendarIcon, CheckCircleIcon, CodeIcon, EyeIcon, HashIcon, LinkIcon, SearchIcon, XIcon } from "lucide-react";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { FilterFactor, getMemoFilterKey, MemoFilter, parseFilterQuery, stringifyFilters, useMemoFilterStore } from "@/store/v1";
|
import { FilterFactor, getMemoFilterKey, MemoFilter, stringifyFilters, useMemoFilterStore } from "@/store/v1";
|
||||||
|
|
||||||
const MemoFilters = () => {
|
const MemoFilters = () => {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const memoFilterStore = useMemoFilterStore();
|
const memoFilterStore = useMemoFilterStore();
|
||||||
const filters = memoFilterStore.filters;
|
const filters = memoFilterStore.filters;
|
||||||
const orderByTimeAsc = memoFilterStore.orderByTimeAsc;
|
|
||||||
const lastUpdateRef = useRef<"url" | "store">("url");
|
|
||||||
|
|
||||||
// set lastUpdateRef to store when filters or orderByTimeAsc changes
|
|
||||||
useEffect(() => {
|
|
||||||
lastUpdateRef.current = "store";
|
|
||||||
}, [filters, orderByTimeAsc]);
|
|
||||||
|
|
||||||
// set lastUpdateRef to url when searchParams changes
|
|
||||||
useEffect(() => {
|
|
||||||
lastUpdateRef.current = "url";
|
|
||||||
}, [searchParams]);
|
|
||||||
|
|
||||||
const checkAndSync = () => {
|
const checkAndSync = () => {
|
||||||
const filtersInURL = searchParams.get("filter") || "";
|
const filtersInURL = searchParams.get("filter") || "";
|
||||||
const orderByTimeAscInURL = searchParams.get("orderBy") === "asc";
|
const storeMatchesURL = filtersInURL === stringifyFilters(filters);
|
||||||
const storeMatchesURL = filtersInURL === stringifyFilters(filters) && orderByTimeAscInURL === orderByTimeAsc;
|
|
||||||
|
|
||||||
if (!storeMatchesURL) {
|
if (!storeMatchesURL) {
|
||||||
if (lastUpdateRef.current === "url") {
|
|
||||||
// Sync URL -> Store
|
|
||||||
memoFilterStore.setState({
|
|
||||||
filters: parseFilterQuery(filtersInURL),
|
|
||||||
orderByTimeAsc: orderByTimeAscInURL,
|
|
||||||
masonry: memoFilterStore.masonry,
|
|
||||||
});
|
|
||||||
} else if (lastUpdateRef.current === "store") {
|
|
||||||
// Sync Store -> URL
|
// Sync Store -> URL
|
||||||
const newSearchParams = new URLSearchParams(searchParams);
|
const newSearchParams = new URLSearchParams(searchParams);
|
||||||
|
|
||||||
if (orderByTimeAsc) {
|
|
||||||
newSearchParams.set("orderBy", "asc");
|
|
||||||
} else {
|
|
||||||
newSearchParams.delete("orderBy");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.length > 0) {
|
if (filters.length > 0) {
|
||||||
newSearchParams.set("filter", stringifyFilters(filters));
|
newSearchParams.set("filter", stringifyFilters(filters));
|
||||||
} else {
|
} else {
|
||||||
@@ -52,11 +25,9 @@ const MemoFilters = () => {
|
|||||||
|
|
||||||
setSearchParams(newSearchParams);
|
setSearchParams(newSearchParams);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Watch both URL and store changes
|
useEffect(checkAndSync, [searchParams, filters]);
|
||||||
useEffect(checkAndSync, [searchParams, filters, orderByTimeAsc]);
|
|
||||||
|
|
||||||
const getFilterDisplayText = (filter: MemoFilter) => {
|
const getFilterDisplayText = (filter: MemoFilter) => {
|
||||||
if (filter.value) {
|
if (filter.value) {
|
||||||
|
@@ -7,7 +7,8 @@ import PullToRefresh from "react-simple-pull-to-refresh";
|
|||||||
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
|
import { DEFAULT_LIST_MEMOS_PAGE_SIZE } from "@/helpers/consts";
|
||||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||||
import { Routes } from "@/router";
|
import { Routes } from "@/router";
|
||||||
import { useMemoFilterStore, useMemoList, useMemoStore } from "@/store/v1";
|
import { useMemoList, useMemoStore } from "@/store/v1";
|
||||||
|
import { viewStore } from "@/store/v2";
|
||||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
@@ -36,7 +37,6 @@ const PagedMemoList = observer((props: Props) => {
|
|||||||
const { md } = useResponsiveWidth();
|
const { md } = useResponsiveWidth();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const memoList = useMemoList();
|
const memoList = useMemoList();
|
||||||
const memoFilterStore = useMemoFilterStore();
|
|
||||||
const [state, setState] = useState<LocalState>({
|
const [state, setState] = useState<LocalState>({
|
||||||
isRequesting: true, // Initial request
|
isRequesting: true, // Initial request
|
||||||
nextPageToken: "",
|
nextPageToken: "",
|
||||||
@@ -77,7 +77,7 @@ const PagedMemoList = observer((props: Props) => {
|
|||||||
memoList={sortedMemoList}
|
memoList={sortedMemoList}
|
||||||
renderer={props.renderer}
|
renderer={props.renderer}
|
||||||
prefixElement={showMemoEditor ? <MemoEditor className="mb-2" cacheKey="home-memo-editor" /> : undefined}
|
prefixElement={showMemoEditor ? <MemoEditor className="mb-2" cacheKey="home-memo-editor" /> : undefined}
|
||||||
listMode={!memoFilterStore.masonry}
|
listMode={viewStore.state.layout === "LIST"}
|
||||||
/>
|
/>
|
||||||
{state.isRequesting && (
|
{state.isRequesting && (
|
||||||
<div className="w-full flex flex-row justify-center items-center my-4">
|
<div className="w-full flex flex-row justify-center items-center my-4">
|
||||||
@@ -92,7 +92,7 @@ const PagedMemoList = observer((props: Props) => {
|
|||||||
<p className="mt-2 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
<p className="mt-2 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full flex flex-row justify-center items-center my-4">
|
<div className="w-full opacity-70 flex flex-row justify-center items-center my-4">
|
||||||
{state.nextPageToken && (
|
{state.nextPageToken && (
|
||||||
<Button variant="plain" onClick={() => fetchMoreMemos(state.nextPageToken)}>
|
<Button variant="plain" onClick={() => fetchMoreMemos(state.nextPageToken)}>
|
||||||
{t("memo.load-more")}
|
{t("memo.load-more")}
|
||||||
|
@@ -4,6 +4,7 @@ import MemoView from "@/components/MemoView";
|
|||||||
import PagedMemoList from "@/components/PagedMemoList";
|
import PagedMemoList from "@/components/PagedMemoList";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { useMemoFilterStore } from "@/store/v1";
|
||||||
|
import { viewStore } from "@/store/v2";
|
||||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||||
|
|
||||||
@@ -38,14 +39,14 @@ const Archived = () => {
|
|||||||
memos
|
memos
|
||||||
.filter((memo) => memo.state === State.ARCHIVED)
|
.filter((memo) => memo.state === State.ARCHIVED)
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
memoFilterStore.orderByTimeAsc
|
viewStore.state.orderByTimeAsc
|
||||||
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
||||||
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
owner={user.name}
|
owner={user.name}
|
||||||
state={State.ARCHIVED}
|
state={State.ARCHIVED}
|
||||||
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
direction={viewStore.state.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
||||||
oldFilter={memoListFilter}
|
oldFilter={memoListFilter}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -4,6 +4,7 @@ import MemoView from "@/components/MemoView";
|
|||||||
import PagedMemoList from "@/components/PagedMemoList";
|
import PagedMemoList from "@/components/PagedMemoList";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { useMemoFilterStore } from "@/store/v1";
|
||||||
|
import { viewStore } from "@/store/v2";
|
||||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ const Explore = () => {
|
|||||||
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
|
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
|
||||||
}
|
}
|
||||||
return conditions.join(" && ");
|
return conditions.join(" && ");
|
||||||
}, [user, memoFilterStore.filters, memoFilterStore.orderByTimeAsc]);
|
}, [user, memoFilterStore.filters, viewStore.state.orderByTimeAsc]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PagedMemoList
|
<PagedMemoList
|
||||||
@@ -50,12 +51,12 @@ const Explore = () => {
|
|||||||
memos
|
memos
|
||||||
.filter((memo) => memo.state === State.NORMAL)
|
.filter((memo) => memo.state === State.NORMAL)
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
memoFilterStore.orderByTimeAsc
|
viewStore.state.orderByTimeAsc
|
||||||
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
||||||
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
direction={viewStore.state.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
||||||
oldFilter={memoListFilter}
|
oldFilter={memoListFilter}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -5,7 +5,7 @@ import MemoView from "@/components/MemoView";
|
|||||||
import PagedMemoList from "@/components/PagedMemoList";
|
import PagedMemoList from "@/components/PagedMemoList";
|
||||||
import useCurrentUser from "@/hooks/useCurrentUser";
|
import useCurrentUser from "@/hooks/useCurrentUser";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { useMemoFilterStore } from "@/store/v1";
|
||||||
import { userStore } from "@/store/v2";
|
import { viewStore, userStore } from "@/store/v2";
|
||||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ const Home = observer(() => {
|
|||||||
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
|
conditions.push(`tag_search == [${tagSearch.join(", ")}]`);
|
||||||
}
|
}
|
||||||
return conditions.join(" && ");
|
return conditions.join(" && ");
|
||||||
}, [user, memoFilterStore.filters, memoFilterStore.orderByTimeAsc]);
|
}, [user, memoFilterStore.filters, viewStore.state.orderByTimeAsc]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PagedMemoList
|
<PagedMemoList
|
||||||
@@ -53,14 +53,14 @@ const Home = observer(() => {
|
|||||||
memos
|
memos
|
||||||
.filter((memo) => memo.state === State.NORMAL)
|
.filter((memo) => memo.state === State.NORMAL)
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
memoFilterStore.orderByTimeAsc
|
viewStore.state.orderByTimeAsc
|
||||||
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
||||||
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
||||||
)
|
)
|
||||||
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
|
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
|
||||||
}
|
}
|
||||||
owner={user.name}
|
owner={user.name}
|
||||||
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
direction={viewStore.state.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
||||||
filter={selectedShortcut?.filter || ""}
|
filter={selectedShortcut?.filter || ""}
|
||||||
oldFilter={memoListFilter}
|
oldFilter={memoListFilter}
|
||||||
/>
|
/>
|
||||||
|
@@ -2,6 +2,7 @@ import { Button } from "@usememos/mui";
|
|||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { ExternalLinkIcon } from "lucide-react";
|
import { ExternalLinkIcon } from "lucide-react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
@@ -11,13 +12,13 @@ import PagedMemoList from "@/components/PagedMemoList";
|
|||||||
import UserAvatar from "@/components/UserAvatar";
|
import UserAvatar from "@/components/UserAvatar";
|
||||||
import useLoading from "@/hooks/useLoading";
|
import useLoading from "@/hooks/useLoading";
|
||||||
import { useMemoFilterStore } from "@/store/v1";
|
import { useMemoFilterStore } from "@/store/v1";
|
||||||
import { userStore } from "@/store/v2";
|
import { viewStore, userStore } from "@/store/v2";
|
||||||
import { Direction, State } from "@/types/proto/api/v1/common";
|
import { Direction, State } from "@/types/proto/api/v1/common";
|
||||||
import { Memo } from "@/types/proto/api/v1/memo_service";
|
import { Memo } from "@/types/proto/api/v1/memo_service";
|
||||||
import { User } from "@/types/proto/api/v1/user_service";
|
import { User } from "@/types/proto/api/v1/user_service";
|
||||||
import { useTranslate } from "@/utils/i18n";
|
import { useTranslate } from "@/utils/i18n";
|
||||||
|
|
||||||
const UserProfile = () => {
|
const UserProfile = observer(() => {
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
@@ -107,14 +108,14 @@ const UserProfile = () => {
|
|||||||
memos
|
memos
|
||||||
.filter((memo) => memo.state === State.NORMAL)
|
.filter((memo) => memo.state === State.NORMAL)
|
||||||
.sort((a, b) =>
|
.sort((a, b) =>
|
||||||
memoFilterStore.orderByTimeAsc
|
viewStore.state.orderByTimeAsc
|
||||||
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
? dayjs(a.displayTime).unix() - dayjs(b.displayTime).unix()
|
||||||
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
: dayjs(b.displayTime).unix() - dayjs(a.displayTime).unix(),
|
||||||
)
|
)
|
||||||
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
|
.sort((a, b) => Number(b.pinned) - Number(a.pinned))
|
||||||
}
|
}
|
||||||
owner={user.name}
|
owner={user.name}
|
||||||
direction={memoFilterStore.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
direction={viewStore.state.orderByTimeAsc ? Direction.ASC : Direction.DESC}
|
||||||
oldFilter={memoListFilter}
|
oldFilter={memoListFilter}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
@@ -124,6 +125,6 @@ const UserProfile = () => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default UserProfile;
|
export default UserProfile;
|
||||||
|
@@ -40,19 +40,14 @@ export const stringifyFilters = (filters: MemoFilter[]): string => {
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
filters: MemoFilter[];
|
filters: MemoFilter[];
|
||||||
orderByTimeAsc: boolean;
|
|
||||||
// The id of selected shortcut.
|
// The id of selected shortcut.
|
||||||
shortcut?: string;
|
shortcut?: string;
|
||||||
// TODO: Remove this when the masonry view is implemented.
|
|
||||||
masonry: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getInitialState = (): State => {
|
const getInitialState = (): State => {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
return {
|
return {
|
||||||
filters: parseFilterQuery(searchParams.get("filter")),
|
filters: parseFilterQuery(searchParams.get("filter")),
|
||||||
orderByTimeAsc: searchParams.get("orderBy") === "asc",
|
|
||||||
masonry: false,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,8 +58,6 @@ export const useMemoFilterStore = create(
|
|||||||
getFiltersByFactor: (factor: FilterFactor) => get().filters.filter((f) => f.factor === factor),
|
getFiltersByFactor: (factor: FilterFactor) => get().filters.filter((f) => f.factor === factor),
|
||||||
addFilter: (filter: MemoFilter) => set((state) => ({ filters: uniqBy([...state.filters, filter], getMemoFilterKey) })),
|
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)) })),
|
removeFilter: (filterFn: (f: MemoFilter) => boolean) => set((state) => ({ filters: state.filters.filter((f) => !filterFn(f)) })),
|
||||||
setOrderByTimeAsc: (orderByTimeAsc: boolean) => set({ orderByTimeAsc }),
|
|
||||||
setShortcut: (shortcut?: string) => set({ shortcut }),
|
setShortcut: (shortcut?: string) => set({ shortcut }),
|
||||||
setMasonry: (masonry: boolean) => set({ masonry }),
|
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import userStore from "./user";
|
import userStore from "./user";
|
||||||
|
import viewStore from "./view";
|
||||||
import workspaceStore from "./workspace";
|
import workspaceStore from "./workspace";
|
||||||
|
|
||||||
export { workspaceStore, userStore };
|
export { workspaceStore, userStore, viewStore };
|
||||||
|
49
web/src/store/v2/view.ts
Normal file
49
web/src/store/v2/view.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { makeAutoObservable } from "mobx";
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_KEY = "memos-view-setting";
|
||||||
|
|
||||||
|
class LocalState {
|
||||||
|
orderByTimeAsc: boolean = false;
|
||||||
|
layout: "LIST" | "MASONRY" = "LIST";
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPartial(partial: Partial<LocalState>) {
|
||||||
|
Object.assign(this, partial);
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewStore = (() => {
|
||||||
|
const state = new LocalState();
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Initial state from localStorage.
|
||||||
|
(async () => {
|
||||||
|
const localCache = localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
|
if (!localCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cache = JSON.parse(localCache);
|
||||||
|
if (Object.hasOwn(cache, "orderByTimeAsc")) {
|
||||||
|
viewStore.state.setPartial({ orderByTimeAsc: Boolean(cache.orderByTimeAsc) });
|
||||||
|
}
|
||||||
|
if (Object.hasOwn(cache, "layout")) {
|
||||||
|
if (["LIST", "MASONRY"].includes(cache.layout)) {
|
||||||
|
viewStore.state.setPartial({ layout: cache.layout });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default viewStore;
|
Reference in New Issue
Block a user