mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
refactor: filter store (#1331)
This commit is contained in:
@ -4,7 +4,7 @@ import { Toaster } from "react-hot-toast";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { RouterProvider } from "react-router-dom";
|
import { RouterProvider } from "react-router-dom";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import { useLocationStore, useGlobalStore } from "./store/module";
|
import { useGlobalStore } from "./store/module";
|
||||||
import * as storage from "./helpers/storage";
|
import * as storage from "./helpers/storage";
|
||||||
import { getSystemColorScheme } from "./helpers/utils";
|
import { getSystemColorScheme } from "./helpers/utils";
|
||||||
import Loading from "./pages/Loading";
|
import Loading from "./pages/Loading";
|
||||||
@ -12,17 +12,9 @@ import Loading from "./pages/Loading";
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const locationStore = useLocationStore();
|
|
||||||
const { mode, setMode } = useColorScheme();
|
const { mode, setMode } = useColorScheme();
|
||||||
const { appearance, locale, systemStatus } = globalStore.state;
|
const { appearance, locale, systemStatus } = globalStore.state;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
locationStore.updateStateWithLocation();
|
|
||||||
window.onpopstate = () => {
|
|
||||||
locationStore.updateStateWithLocation();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
const handleColorSchemeChange = (e: MediaQueryListEvent) => {
|
const handleColorSchemeChange = (e: MediaQueryListEvent) => {
|
||||||
|
@ -26,9 +26,9 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
<Icon.X />
|
<Icon.X />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-start items-start max-w-full">
|
<div className="flex flex-col justify-start items-start max-w-full w-96">
|
||||||
<p className="text-sm">{customizedProfile.description || "No description"}</p>
|
<p className="text-sm">{customizedProfile.description || "No description"}</p>
|
||||||
<div className="mt-4 flex flex-row text-sm justify-start items-center">
|
<div className="mt-4 w-full flex flex-row text-sm justify-start items-center">
|
||||||
<div className="flex flex-row justify-start items-center mr-2">
|
<div className="flex flex-row justify-start items-center mr-2">
|
||||||
Powered by
|
Powered by
|
||||||
<a href="https://usememos.com" target="_blank" className="flex flex-row justify-start items-center mr-1 hover:underline">
|
<a href="https://usememos.com" target="_blank" className="flex flex-row justify-start items-center mr-1 hover:underline">
|
||||||
@ -39,8 +39,8 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
<GitHubBadge />
|
<GitHubBadge />
|
||||||
</div>
|
</div>
|
||||||
<div className="border-t mt-3 pt-2 text-sm flex flex-row justify-start items-center">
|
<div className="border-t w-full mt-3 pt-2 text-sm flex flex-row justify-start items-center space-x-2">
|
||||||
<span className="text-gray-500 mr-2">Other projects:</span>
|
<span className="text-gray-500">Other projects:</span>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/boojack/sticky-notes"
|
href="https://github.com/boojack/sticky-notes"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -53,6 +53,14 @@ const AboutSiteDialog: React.FC<Props> = ({ destroy }: Props) => {
|
|||||||
/>
|
/>
|
||||||
<span>Sticky notes</span>
|
<span>Sticky notes</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="https://github.com/boojack/sticky-notes"
|
||||||
|
target="_blank"
|
||||||
|
className="flex items-center underline text-blue-600 hover:opacity-80"
|
||||||
|
>
|
||||||
|
<img className="w-4 h-auto mr-1" src="https://star-history.com/icon.png" alt="" />
|
||||||
|
<span>Star history</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink, useLocation } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLayoutStore, useUserStore } from "../store/module";
|
import { useLayoutStore, useUserStore } from "../store/module";
|
||||||
import { resolution } from "../utils/layout";
|
import { resolution } from "../utils/layout";
|
||||||
@ -12,6 +12,7 @@ import UserBanner from "./UserBanner";
|
|||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const location = useLocation();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const showHeader = layoutStore.state.showHeader;
|
const showHeader = layoutStore.state.showHeader;
|
||||||
@ -27,7 +28,7 @@ const Header = () => {
|
|||||||
};
|
};
|
||||||
window.addEventListener("resize", handleWindowResize);
|
window.addEventListener("resize", handleWindowResize);
|
||||||
handleWindowResize();
|
handleWindowResize();
|
||||||
}, []);
|
}, [location]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -42,7 +43,7 @@ const Header = () => {
|
|||||||
onClick={() => layoutStore.setHeaderStatus(false)}
|
onClick={() => layoutStore.setHeaderStatus(false)}
|
||||||
></div>
|
></div>
|
||||||
<header
|
<header
|
||||||
className={`relative w-56 sm:w-full h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 z-30 bg-white dark:bg-zinc-800 sm:bg-transparent sm:shadow-none transition-all duration-300 -translate-x-full sm:translate-x-0 ${
|
className={`relative w-56 sm:w-full h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 z-30 bg-zinc-100 dark:bg-zinc-800 sm:bg-transparent sm:shadow-none transition-all duration-300 -translate-x-full sm:translate-x-0 ${
|
||||||
showHeader && "translate-x-0 shadow-2xl"
|
showHeader && "translate-x-0 shadow-2xl"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
@ -5,8 +5,10 @@ import ShortcutList from "./ShortcutList";
|
|||||||
import TagList from "./TagList";
|
import TagList from "./TagList";
|
||||||
import SearchBar from "./SearchBar";
|
import SearchBar from "./SearchBar";
|
||||||
import UsageHeatMap from "./UsageHeatMap";
|
import UsageHeatMap from "./UsageHeatMap";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const HomeSidebar = () => {
|
const HomeSidebar = () => {
|
||||||
|
const location = useLocation();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const showHomeSidebar = layoutStore.state.showHomeSidebar;
|
const showHomeSidebar = layoutStore.state.showHomeSidebar;
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ const HomeSidebar = () => {
|
|||||||
};
|
};
|
||||||
window.addEventListener("resize", handleWindowResize);
|
window.addEventListener("resize", handleWindowResize);
|
||||||
handleWindowResize();
|
handleWindowResize();
|
||||||
}, []);
|
}, [location]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -35,7 +37,7 @@ const HomeSidebar = () => {
|
|||||||
onClick={() => layoutStore.setHomeSidebarStatus(false)}
|
onClick={() => layoutStore.setHomeSidebarStatus(false)}
|
||||||
></div>
|
></div>
|
||||||
<aside
|
<aside
|
||||||
className={`absolute md:relative top-0 right-0 w-56 pr-2 md:w-full h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 z-30 bg-white dark:bg-zinc-800 md:bg-transparent md:shadow-none transition-all duration-300 translate-x-full md:translate-x-0 ${
|
className={`absolute md:relative top-0 right-0 w-56 pr-2 md:w-full h-full max-h-screen overflow-auto hide-scrollbar flex flex-col justify-start items-start py-4 z-30 bg-zinc-100 dark:bg-zinc-800 md:bg-transparent md:shadow-none transition-all duration-300 translate-x-full md:translate-x-0 ${
|
||||||
showHomeSidebar && "!translate-x-0 shadow-2xl"
|
showHomeSidebar && "!translate-x-0 shadow-2xl"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
@ -4,7 +4,7 @@ import { memo, useEffect, useRef, useState } from "react";
|
|||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { useEditorStore, useLocationStore, useMemoStore, useUserStore } from "../store/module";
|
import { useEditorStore, useFilterStore, useMemoStore, useUserStore } from "../store/module";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import MemoContent from "./MemoContent";
|
import MemoContent from "./MemoContent";
|
||||||
import MemoResources from "./MemoResources";
|
import MemoResources from "./MemoResources";
|
||||||
@ -32,7 +32,7 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const editorStore = useEditorStore();
|
const editorStore = useEditorStore();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getFormatedMemoTimeStr(memo.createdTs, i18n.language));
|
const [createdTimeStr, setCreatedTimeStr] = useState<string>(getFormatedMemoTimeStr(memo.createdTs, i18n.language));
|
||||||
@ -106,11 +106,11 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
if (targetEl.className === "tag-span") {
|
if (targetEl.className === "tag-span") {
|
||||||
const tagName = targetEl.innerText.slice(1);
|
const tagName = targetEl.innerText.slice(1);
|
||||||
const currTagQuery = locationStore.getState().query?.tag;
|
const currTagQuery = filterStore.getState().tag;
|
||||||
if (currTagQuery === tagName) {
|
if (currTagQuery === tagName) {
|
||||||
locationStore.setTagQuery(undefined);
|
filterStore.setTagFilter(undefined);
|
||||||
} else {
|
} else {
|
||||||
locationStore.setTagQuery(tagName);
|
filterStore.setTagFilter(tagName);
|
||||||
}
|
}
|
||||||
} else if (targetEl.classList.contains("todo-block")) {
|
} else if (targetEl.classList.contains("todo-block")) {
|
||||||
if (isVisitorMode) {
|
if (isVisitorMode) {
|
||||||
@ -176,11 +176,11 @@ const Memo: React.FC<Props> = (props: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMemoVisibilityClick = (visibility: Visibility) => {
|
const handleMemoVisibilityClick = (visibility: Visibility) => {
|
||||||
const currVisibilityQuery = locationStore.getState().query?.visibility;
|
const currVisibilityQuery = filterStore.getState().visibility;
|
||||||
if (currVisibilityQuery === visibility) {
|
if (currVisibilityQuery === visibility) {
|
||||||
locationStore.setMemoVisibilityQuery(undefined);
|
filterStore.setMemoVisibilityFilter(undefined);
|
||||||
} else {
|
} else {
|
||||||
locationStore.setMemoVisibilityQuery(visibility);
|
filterStore.setMemoVisibilityFilter(visibility);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,15 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { getMatchedNodes } from "../labs/marked";
|
import { getMatchedNodes } from "../labs/marked";
|
||||||
import { deleteMemoResource, upsertMemoResource } from "../helpers/api";
|
import { deleteMemoResource, upsertMemoResource } from "../helpers/api";
|
||||||
import { TAB_SPACE_WIDTH, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts";
|
import { TAB_SPACE_WIDTH, UNKNOWN_ID, VISIBILITY_SELECTOR_ITEMS } from "../helpers/consts";
|
||||||
import {
|
import { useEditorStore, useGlobalStore, useFilterStore, useMemoStore, useResourceStore, useTagStore, useUserStore } from "../store/module";
|
||||||
useEditorStore,
|
|
||||||
useGlobalStore,
|
|
||||||
useLocationStore,
|
|
||||||
useMemoStore,
|
|
||||||
useResourceStore,
|
|
||||||
useTagStore,
|
|
||||||
useUserStore,
|
|
||||||
} from "../store/module";
|
|
||||||
import * as storage from "../helpers/storage";
|
import * as storage from "../helpers/storage";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import Selector from "./base/Selector";
|
import Selector from "./base/Selector";
|
||||||
@ -46,7 +38,7 @@ const MemoEditor = () => {
|
|||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const editorStore = useEditorStore();
|
const editorStore = useEditorStore();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const tagStore = useTagStore();
|
const tagStore = useTagStore();
|
||||||
const resourceStore = useResourceStore();
|
const resourceStore = useResourceStore();
|
||||||
@ -289,7 +281,7 @@ const MemoEditor = () => {
|
|||||||
visibility: editorState.memoVisibility,
|
visibility: editorState.memoVisibility,
|
||||||
resourceIdList: editorState.resourceList.map((resource) => resource.id),
|
resourceIdList: editorState.resourceList.map((resource) => resource.id),
|
||||||
});
|
});
|
||||||
locationStore.clearQuery();
|
filterStore.clearFilter();
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocationStore, useShortcutStore } from "../store/module";
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { useFilterStore, useShortcutStore } from "../store/module";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { getTextWithMemoType } from "../helpers/filter";
|
import { getTextWithMemoType } from "../helpers/filter";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
@ -7,20 +9,25 @@ import "../less/memo-filter.less";
|
|||||||
|
|
||||||
const MemoFilter = () => {
|
const MemoFilter = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const location = useLocation();
|
||||||
|
const filterStore = useFilterStore();
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = query;
|
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = filter;
|
||||||
const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null;
|
const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null;
|
||||||
const showFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility);
|
const showFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
filterStore.clearFilter();
|
||||||
|
}, [location]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`filter-query-container ${showFilter ? "" : "!hidden"}`}>
|
<div className={`filter-query-container ${showFilter ? "" : "!hidden"}`}>
|
||||||
<span className="mx-2 text-gray-400">{t("common.filter")}:</span>
|
<span className="mx-2 text-gray-400">{t("common.filter")}:</span>
|
||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (shortcut ? "" : "!hidden")}
|
className={"filter-item-container " + (shortcut ? "" : "!hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setMemoShortcut(undefined);
|
filterStore.setMemoShortcut(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Target className="icon-text" /> {shortcut?.title}
|
<Icon.Target className="icon-text" /> {shortcut?.title}
|
||||||
@ -28,7 +35,7 @@ const MemoFilter = () => {
|
|||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (tagQuery ? "" : "!hidden")}
|
className={"filter-item-container " + (tagQuery ? "" : "!hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setTagQuery(undefined);
|
filterStore.setTagFilter(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Tag className="icon-text" /> {tagQuery}
|
<Icon.Tag className="icon-text" /> {tagQuery}
|
||||||
@ -36,7 +43,7 @@ const MemoFilter = () => {
|
|||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (memoType ? "" : "!hidden")}
|
className={"filter-item-container " + (memoType ? "" : "!hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setMemoTypeQuery(undefined);
|
filterStore.setMemoTypeFilter(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Box className="icon-text" /> {t(getTextWithMemoType(memoType as MemoSpecType))}
|
<Icon.Box className="icon-text" /> {t(getTextWithMemoType(memoType as MemoSpecType))}
|
||||||
@ -44,7 +51,7 @@ const MemoFilter = () => {
|
|||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (visibility ? "" : "!hidden")}
|
className={"filter-item-container " + (visibility ? "" : "!hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setMemoVisibilityQuery(undefined);
|
filterStore.setMemoVisibilityFilter(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Eye className="icon-text" /> {visibility}
|
<Icon.Eye className="icon-text" /> {visibility}
|
||||||
@ -53,7 +60,7 @@ const MemoFilter = () => {
|
|||||||
<div
|
<div
|
||||||
className="filter-item-container"
|
className="filter-item-container"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setFromAndToQuery();
|
filterStore.setFromAndToFilter();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Calendar className="icon-text" /> {utils.getDateString(duration.from)} to {utils.getDateString(duration.to)}
|
<Icon.Calendar className="icon-text" /> {utils.getDateString(duration.from)} to {utils.getDateString(duration.to)}
|
||||||
@ -62,7 +69,7 @@ const MemoFilter = () => {
|
|||||||
<div
|
<div
|
||||||
className={"filter-item-container " + (textQuery ? "" : "!hidden")}
|
className={"filter-item-container " + (textQuery ? "" : "!hidden")}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
locationStore.setTextQuery(undefined);
|
filterStore.setTextFilter(undefined);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon.Search className="icon-text" /> {textQuery}
|
<Icon.Search className="icon-text" /> {textQuery}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocationStore, useMemoStore, useShortcutStore, useUserStore } from "../store/module";
|
import { useFilterStore, useMemoStore, useShortcutStore, useUserStore } from "../store/module";
|
||||||
import { TAG_REG, LINK_REG } from "../labs/marked/parser";
|
import { TAG_REG, LINK_REG } from "../labs/marked/parser";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import { DEFAULT_MEMO_LIMIT } from "../helpers/consts";
|
import { DEFAULT_MEMO_LIMIT } from "../helpers/consts";
|
||||||
@ -14,13 +14,13 @@ const MemoList = () => {
|
|||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const { memos, isFetching } = memoStore.state;
|
const { memos, isFetching } = memoStore.state;
|
||||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
const [isComplete, setIsComplete] = useState<boolean>(false);
|
||||||
|
|
||||||
const currentUserId = userStore.getCurrentUserId();
|
const currentUserId = userStore.getCurrentUserId();
|
||||||
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = query ?? {};
|
const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId, visibility } = filter;
|
||||||
const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null;
|
const shortcut = shortcutId ? shortcutStore.getShortcutById(shortcutId) : null;
|
||||||
const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility);
|
const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || shortcut || visibility);
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ const MemoList = () => {
|
|||||||
if (pageWrapper) {
|
if (pageWrapper) {
|
||||||
pageWrapper.scrollTo(0, 0);
|
pageWrapper.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
}, [query]);
|
}, [filter]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isFetching || isComplete) {
|
if (isFetching || isComplete) {
|
||||||
@ -116,7 +116,7 @@ const MemoList = () => {
|
|||||||
if (sortedMemos.length < DEFAULT_MEMO_LIMIT) {
|
if (sortedMemos.length < DEFAULT_MEMO_LIMIT) {
|
||||||
handleFetchMoreClick();
|
handleFetchMoreClick();
|
||||||
}
|
}
|
||||||
}, [isFetching, isComplete, query, sortedMemos.length]);
|
}, [isFetching, isComplete, filter, sortedMemos.length]);
|
||||||
|
|
||||||
const handleFetchMoreClick = async () => {
|
const handleFetchMoreClick = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useLayoutStore, useLocationStore, useShortcutStore } from "../store/module";
|
import { useLayoutStore, useFilterStore, useShortcutStore } from "../store/module";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -8,24 +8,24 @@ interface Props {
|
|||||||
|
|
||||||
const MobileHeader = (props: Props) => {
|
const MobileHeader = (props: Props) => {
|
||||||
const { showSearch = true } = props;
|
const { showSearch = true } = props;
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const shortcuts = shortcutStore.state.shortcuts;
|
const shortcuts = shortcutStore.state.shortcuts;
|
||||||
const [titleText, setTitleText] = useState("MEMOS");
|
const [titleText, setTitleText] = useState("MEMOS");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!query?.shortcutId) {
|
if (!filter.shortcutId) {
|
||||||
setTitleText("MEMOS");
|
setTitleText("MEMOS");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const shortcut = shortcutStore.getShortcutById(query?.shortcutId);
|
const shortcut = shortcutStore.getShortcutById(filter.shortcutId);
|
||||||
if (shortcut) {
|
if (shortcut) {
|
||||||
setTitleText(shortcut.title);
|
setTitleText(shortcut.title);
|
||||||
}
|
}
|
||||||
}, [query, shortcuts]);
|
}, [filter, shortcuts]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-0 pt-4 pb-1 mb-1 backdrop-blur-sm flex sm:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-1">
|
<div className="sticky top-0 pt-4 pb-1 mb-1 backdrop-blur-sm flex sm:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-1">
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
import useDebounce from "../hooks/useDebounce";
|
import useDebounce from "../hooks/useDebounce";
|
||||||
import { useLocationStore, useDialogStore, useLayoutStore } from "../store/module";
|
import { useFilterStore, useDialogStore, useLayoutStore } from "../store/module";
|
||||||
import { resolution } from "../utils/layout";
|
import { resolution } from "../utils/layout";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
const SearchBar = () => {
|
const SearchBar = () => {
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const dialogStore = useDialogStore();
|
const dialogStore = useDialogStore();
|
||||||
const layoutStore = useLayoutStore();
|
const layoutStore = useLayoutStore();
|
||||||
const [queryText, setQueryText] = useState("");
|
const [queryText, setQueryText] = useState("");
|
||||||
@ -33,9 +33,9 @@ const SearchBar = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const text = locationStore.getState().query.text;
|
const text = filterStore.getState().text;
|
||||||
setQueryText(text === undefined ? "" : text);
|
setQueryText(text === undefined ? "" : text);
|
||||||
}, [locationStore.state.query.text]);
|
}, [filterStore.state.text]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (layoutStore.state.showHomeSidebar) {
|
if (layoutStore.state.showHomeSidebar) {
|
||||||
@ -47,7 +47,7 @@ const SearchBar = () => {
|
|||||||
|
|
||||||
useDebounce(
|
useDebounce(
|
||||||
() => {
|
() => {
|
||||||
locationStore.setTextQuery(queryText.length === 0 ? undefined : queryText);
|
filterStore.setTextFilter(queryText.length === 0 ? undefined : queryText);
|
||||||
},
|
},
|
||||||
200,
|
200,
|
||||||
[queryText]
|
[queryText]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocationStore, useShortcutStore } from "../store/module";
|
import { useFilterStore, useShortcutStore } from "../store/module";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
import useLoading from "../hooks/useLoading";
|
import useLoading from "../hooks/useLoading";
|
||||||
@ -10,9 +10,9 @@ import showCreateShortcutDialog from "./CreateShortcutDialog";
|
|||||||
|
|
||||||
const ShortcutList = () => {
|
const ShortcutList = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const shortcuts = shortcutStore.state.shortcuts;
|
const shortcuts = shortcutStore.state.shortcuts;
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ const ShortcutList = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mb-2">
|
<div className="flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mb-2">
|
||||||
{sortedShortcuts.map((s) => {
|
{sortedShortcuts.map((s) => {
|
||||||
return <ShortcutContainer key={s.id} shortcut={s} isActive={s.id === Number(query?.shortcutId)} />;
|
return <ShortcutContainer key={s.id} shortcut={s} isActive={s.id === Number(filter?.shortcutId)} />;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,15 +63,15 @@ interface ShortcutContainerProps {
|
|||||||
const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutContainerProps) => {
|
const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutContainerProps) => {
|
||||||
const { shortcut, isActive } = props;
|
const { shortcut, isActive } = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const shortcutStore = useShortcutStore();
|
const shortcutStore = useShortcutStore();
|
||||||
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
|
||||||
|
|
||||||
const handleShortcutClick = () => {
|
const handleShortcutClick = () => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
locationStore.setMemoShortcut(undefined);
|
filterStore.setMemoShortcut(undefined);
|
||||||
} else {
|
} else {
|
||||||
locationStore.setMemoShortcut(shortcut.id);
|
filterStore.setMemoShortcut(shortcut.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,9 +81,9 @@ const ShortcutContainer: React.FC<ShortcutContainerProps> = (props: ShortcutCont
|
|||||||
if (showConfirmDeleteBtn) {
|
if (showConfirmDeleteBtn) {
|
||||||
try {
|
try {
|
||||||
await shortcutStore.deleteShortcutById(shortcut.id);
|
await shortcutStore.deleteShortcutById(shortcut.id);
|
||||||
if (locationStore.getState().query?.shortcutId === shortcut.id) {
|
if (filterStore.getState().shortcutId === shortcut.id) {
|
||||||
// need clear shortcut filter
|
// need clear shortcut filter
|
||||||
locationStore.setMemoShortcut(undefined);
|
filterStore.setMemoShortcut(undefined);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocationStore, useTagStore } from "../store/module";
|
import { useFilterStore, useTagStore } from "../store/module";
|
||||||
import useToggle from "../hooks/useToggle";
|
import useToggle from "../hooks/useToggle";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
import showCreateTagDialog from "./CreateTagDialog";
|
import showCreateTagDialog from "./CreateTagDialog";
|
||||||
@ -13,10 +13,10 @@ interface Tag {
|
|||||||
|
|
||||||
const TagList = () => {
|
const TagList = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const tagStore = useTagStore();
|
const tagStore = useTagStore();
|
||||||
const tagsText = tagStore.state.tags;
|
const tagsText = tagStore.state.tags;
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -80,7 +80,7 @@ const TagList = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mt-2 mb-2">
|
<div className="flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mt-2 mb-2">
|
||||||
{tags.map((t, idx) => (
|
{tags.map((t, idx) => (
|
||||||
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={query?.tag} />
|
<TagItemContainer key={t.text + "-" + idx} tag={t} tagQuery={filter.tag} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -93,7 +93,7 @@ interface TagItemContainerProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContainerProps) => {
|
const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContainerProps) => {
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const { tag, tagQuery } = props;
|
const { tag, tagQuery } = props;
|
||||||
const isActive = tagQuery === tag.text;
|
const isActive = tagQuery === tag.text;
|
||||||
const hasSubTags = tag.subTags.length > 0;
|
const hasSubTags = tag.subTags.length > 0;
|
||||||
@ -101,9 +101,9 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
|
|||||||
|
|
||||||
const handleTagClick = () => {
|
const handleTagClick = () => {
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
locationStore.setTagQuery(undefined);
|
filterStore.setTagFilter(undefined);
|
||||||
} else {
|
} else {
|
||||||
locationStore.setTagQuery(tag.text);
|
filterStore.setTagFilter(tag.text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { useLocationStore, useMemoStore, useUserStore } from "../store/module";
|
import { useFilterStore, useMemoStore, useUserStore } from "../store/module";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { getMemoStats } from "../helpers/api";
|
import { getMemoStats } from "../helpers/api";
|
||||||
import { DAILY_TIMESTAMP } from "../helpers/consts";
|
import { DAILY_TIMESTAMP } from "../helpers/consts";
|
||||||
@ -29,7 +29,7 @@ interface DailyUsageStat {
|
|||||||
|
|
||||||
const UsageHeatMap = () => {
|
const UsageHeatMap = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const filterStore = useFilterStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const todayTimeStamp = utils.getDateStampByDate(Date.now());
|
const todayTimeStamp = utils.getDateStampByDate(Date.now());
|
||||||
@ -87,11 +87,11 @@ const UsageHeatMap = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleUsageStatItemClick = useCallback((item: DailyUsageStat) => {
|
const handleUsageStatItemClick = useCallback((item: DailyUsageStat) => {
|
||||||
if (locationStore.getState().query?.duration?.from === item.timestamp) {
|
if (filterStore.getState().duration?.from === item.timestamp) {
|
||||||
locationStore.setFromAndToQuery();
|
filterStore.setFromAndToFilter();
|
||||||
setCurrentStat(null);
|
setCurrentStat(null);
|
||||||
} else if (item.count > 0) {
|
} else if (item.count > 0) {
|
||||||
locationStore.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP);
|
filterStore.setFromAndToFilter(item.timestamp, item.timestamp + DAILY_TIMESTAMP);
|
||||||
setCurrentStat(item);
|
setCurrentStat(item);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLocationStore, useMemoStore } from "../store/module";
|
import { useFilterStore, useMemoStore } from "../store/module";
|
||||||
import { TAG_REG } from "../labs/marked/parser";
|
import { TAG_REG } from "../labs/marked/parser";
|
||||||
import { DEFAULT_MEMO_LIMIT } from "../helpers/consts";
|
import { DEFAULT_MEMO_LIMIT } from "../helpers/consts";
|
||||||
import useLoading from "../hooks/useLoading";
|
import useLoading from "../hooks/useLoading";
|
||||||
import MemoFilter from "../components/MemoFilter";
|
import MemoFilter from "../components/MemoFilter";
|
||||||
import Memo from "../components/Memo";
|
import Memo from "../components/Memo";
|
||||||
import MobileHeader from "../components/MobileHeader";
|
import MobileHeader from "../components/MobileHeader";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
memos: Memo[];
|
memos: Memo[];
|
||||||
@ -15,15 +16,15 @@ interface State {
|
|||||||
|
|
||||||
const Explore = () => {
|
const Explore = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const locationStore = useLocationStore();
|
const location = useLocation();
|
||||||
|
const filterStore = useFilterStore();
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const query = locationStore.state.query;
|
const filter = filterStore.state;
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
memos: [],
|
memos: [],
|
||||||
});
|
});
|
||||||
const [isComplete, setIsComplete] = useState<boolean>(false);
|
const [isComplete, setIsComplete] = useState<boolean>(false);
|
||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const location = locationStore.state;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, 0).then((memos) => {
|
memoStore.fetchAllMemos(DEFAULT_MEMO_LIMIT, 0).then((memos) => {
|
||||||
@ -37,7 +38,7 @@ const Explore = () => {
|
|||||||
});
|
});
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
const { tag: tagQuery, text: textQuery } = query ?? {};
|
const { tag: tagQuery, text: textQuery } = filter;
|
||||||
const showMemoFilter = Boolean(tagQuery || textQuery);
|
const showMemoFilter = Boolean(tagQuery || textQuery);
|
||||||
|
|
||||||
const shownMemos = showMemoFilter
|
const shownMemos = showMemoFilter
|
||||||
|
@ -2,9 +2,9 @@ import dayjs from "dayjs";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useLocation, useParams } from "react-router-dom";
|
||||||
import { UNKNOWN_ID } from "../helpers/consts";
|
import { UNKNOWN_ID } from "../helpers/consts";
|
||||||
import { useGlobalStore, useLocationStore, useMemoStore, useUserStore } from "../store/module";
|
import { useGlobalStore, useMemoStore, useUserStore } from "../store/module";
|
||||||
import useLoading from "../hooks/useLoading";
|
import useLoading from "../hooks/useLoading";
|
||||||
import MemoContent from "../components/MemoContent";
|
import MemoContent from "../components/MemoContent";
|
||||||
import MemoResources from "../components/MemoResources";
|
import MemoResources from "../components/MemoResources";
|
||||||
@ -17,8 +17,8 @@ interface State {
|
|||||||
const MemoDetail = () => {
|
const MemoDetail = () => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
const location = useLocation();
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore();
|
||||||
const locationStore = useLocationStore();
|
|
||||||
const memoStore = useMemoStore();
|
const memoStore = useMemoStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const [state, setState] = useState<State>({
|
const [state, setState] = useState<State>({
|
||||||
@ -29,7 +29,6 @@ const MemoDetail = () => {
|
|||||||
const loadingState = useLoading();
|
const loadingState = useLoading();
|
||||||
const customizedProfile = globalStore.state.systemStatus.customizedProfile;
|
const customizedProfile = globalStore.state.systemStatus.customizedProfile;
|
||||||
const user = userStore.state.user;
|
const user = userStore.state.user;
|
||||||
const location = locationStore.state;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const memoId = Number(params.memoId);
|
const memoId = Number(params.memoId);
|
||||||
|
@ -5,7 +5,7 @@ import userReducer from "./reducer/user";
|
|||||||
import memoReducer from "./reducer/memo";
|
import memoReducer from "./reducer/memo";
|
||||||
import editorReducer from "./reducer/editor";
|
import editorReducer from "./reducer/editor";
|
||||||
import shortcutReducer from "./reducer/shortcut";
|
import shortcutReducer from "./reducer/shortcut";
|
||||||
import locationReducer from "./reducer/location";
|
import filterReducer from "./reducer/filter";
|
||||||
import resourceReducer from "./reducer/resource";
|
import resourceReducer from "./reducer/resource";
|
||||||
import dialogReducer from "./reducer/dialog";
|
import dialogReducer from "./reducer/dialog";
|
||||||
import tagReducer from "./reducer/tag";
|
import tagReducer from "./reducer/tag";
|
||||||
@ -19,7 +19,7 @@ const store = configureStore({
|
|||||||
tag: tagReducer,
|
tag: tagReducer,
|
||||||
editor: editorReducer,
|
editor: editorReducer,
|
||||||
shortcut: shortcutReducer,
|
shortcut: shortcutReducer,
|
||||||
location: locationReducer,
|
filter: filterReducer,
|
||||||
resource: resourceReducer,
|
resource: resourceReducer,
|
||||||
dialog: dialogReducer,
|
dialog: dialogReducer,
|
||||||
layout: layoutReducer,
|
layout: layoutReducer,
|
||||||
|
77
web/src/store/module/filter.ts
Normal file
77
web/src/store/module/filter.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import store, { useAppSelector } from "..";
|
||||||
|
import { setFilter, Filter } from "../reducer/filter";
|
||||||
|
|
||||||
|
export const useFilterStore = () => {
|
||||||
|
const state = useAppSelector((state) => state.filter);
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
getState: () => {
|
||||||
|
return store.getState().filter;
|
||||||
|
},
|
||||||
|
setFilter: (filter: Filter) => {
|
||||||
|
store.dispatch(setFilter(filter));
|
||||||
|
},
|
||||||
|
clearFilter: () => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
tag: undefined,
|
||||||
|
type: undefined,
|
||||||
|
duration: undefined,
|
||||||
|
text: undefined,
|
||||||
|
shortcutId: undefined,
|
||||||
|
visibility: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setMemoTypeFilter: (type?: MemoSpecType) => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
type: type,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setMemoShortcut: (shortcutId?: ShortcutId) => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
shortcutId: shortcutId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setTextFilter: (text?: string) => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
text: text,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setTagFilter: (tag?: string) => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
tag: tag,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setFromAndToFilter: (from?: number, to?: number) => {
|
||||||
|
let duration = undefined;
|
||||||
|
if (from && to && from < to) {
|
||||||
|
duration = {
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
duration,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
setMemoVisibilityFilter: (visibility?: Visibility) => {
|
||||||
|
store.dispatch(
|
||||||
|
setFilter({
|
||||||
|
visibility: visibility,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
export * from "./editor";
|
export * from "./editor";
|
||||||
export * from "./global";
|
export * from "./global";
|
||||||
export * from "./location";
|
export * from "./filter";
|
||||||
export * from "./memo";
|
export * from "./memo";
|
||||||
export * from "./tag";
|
export * from "./tag";
|
||||||
export * from "./resource";
|
export * from "./resource";
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
import { stringify } from "qs";
|
|
||||||
import store, { useAppSelector } from "../";
|
|
||||||
import { setQuery, setPathname, Query, updateStateWithLocation, updatePathnameStateWithLocation } from "../reducer/location";
|
|
||||||
|
|
||||||
const updateLocationUrl = (method: "replace" | "push" = "replace") => {
|
|
||||||
// avoid pathname confusion when entering from non-home page
|
|
||||||
store.dispatch(updatePathnameStateWithLocation());
|
|
||||||
|
|
||||||
const { query, pathname, hash } = store.getState().location;
|
|
||||||
let queryString = stringify(query);
|
|
||||||
if (queryString) {
|
|
||||||
queryString = "?" + queryString;
|
|
||||||
} else {
|
|
||||||
queryString = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method === "replace") {
|
|
||||||
window.history.replaceState(null, "", pathname + hash + queryString);
|
|
||||||
} else {
|
|
||||||
window.history.pushState(null, "", pathname + hash + queryString);
|
|
||||||
}
|
|
||||||
store.dispatch(updateStateWithLocation());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useLocationStore = () => {
|
|
||||||
const state = useAppSelector((state) => state.location);
|
|
||||||
|
|
||||||
return {
|
|
||||||
state,
|
|
||||||
getState: () => {
|
|
||||||
return store.getState().location;
|
|
||||||
},
|
|
||||||
updateStateWithLocation: () => {
|
|
||||||
store.dispatch(updateStateWithLocation());
|
|
||||||
},
|
|
||||||
setPathname: (pathname: string) => {
|
|
||||||
store.dispatch(setPathname(pathname));
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
pushHistory: (pathname: string) => {
|
|
||||||
store.dispatch(setPathname(pathname));
|
|
||||||
updateLocationUrl("push");
|
|
||||||
},
|
|
||||||
replaceHistory: (pathname: string) => {
|
|
||||||
store.dispatch(setPathname(pathname));
|
|
||||||
updateLocationUrl("replace");
|
|
||||||
},
|
|
||||||
setQuery: (query: Query) => {
|
|
||||||
store.dispatch(setQuery(query));
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
clearQuery: () => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
tag: undefined,
|
|
||||||
type: undefined,
|
|
||||||
duration: undefined,
|
|
||||||
text: undefined,
|
|
||||||
shortcutId: undefined,
|
|
||||||
visibility: undefined,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setMemoTypeQuery: (type?: MemoSpecType) => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
type: type,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setMemoShortcut: (shortcutId?: ShortcutId) => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
shortcutId: shortcutId,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setTextQuery: (text?: string) => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
text: text,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setTagQuery: (tag?: string) => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
tag: tag,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setFromAndToQuery: (from?: number, to?: number) => {
|
|
||||||
let duration = undefined;
|
|
||||||
if (from && to && from < to) {
|
|
||||||
duration = {
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
duration,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
setMemoVisibilityQuery: (visibility?: Visibility) => {
|
|
||||||
store.dispatch(
|
|
||||||
setQuery({
|
|
||||||
visibility: visibility,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updateLocationUrl();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
@ -64,7 +64,7 @@ export const initialUserState = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getUserIdFromPath = () => {
|
const getUserIdFromPath = () => {
|
||||||
const pathname = location.pathname;
|
const pathname = window.location.pathname;
|
||||||
const userIdRegex = /^\/u\/(\d+).*/;
|
const userIdRegex = /^\/u\/(\d+).*/;
|
||||||
const result = pathname.match(userIdRegex);
|
const result = pathname.match(userIdRegex);
|
||||||
if (result && result.length === 2) {
|
if (result && result.length === 2) {
|
||||||
|
38
web/src/store/reducer/filter.ts
Normal file
38
web/src/store/reducer/filter.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
interface Duration {
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
tag?: string;
|
||||||
|
duration?: Duration;
|
||||||
|
type?: MemoSpecType;
|
||||||
|
text?: string;
|
||||||
|
shortcutId?: ShortcutId;
|
||||||
|
visibility?: Visibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Filter = State;
|
||||||
|
|
||||||
|
const filterSlice = createSlice({
|
||||||
|
name: "filter",
|
||||||
|
initialState: {} as State,
|
||||||
|
reducers: {
|
||||||
|
setFilter: (state, action: PayloadAction<Partial<State>>) => {
|
||||||
|
if (JSON.stringify(action.payload) === state) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...action.payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setFilter } = filterSlice.actions;
|
||||||
|
|
||||||
|
export default filterSlice.reducer;
|
@ -1,107 +0,0 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
||||||
import { parse, ParsedQs } from "qs";
|
|
||||||
|
|
||||||
interface Duration {
|
|
||||||
from: number;
|
|
||||||
to: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Query {
|
|
||||||
tag?: string;
|
|
||||||
duration?: Duration;
|
|
||||||
type?: MemoSpecType;
|
|
||||||
text?: string;
|
|
||||||
shortcutId?: ShortcutId;
|
|
||||||
visibility?: Visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
pathname: string;
|
|
||||||
hash: string;
|
|
||||||
query: Query;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getValidPathname = (pathname: string): string => {
|
|
||||||
const userPageUrlRegex = /^\/u\/\d+.*/;
|
|
||||||
if (["/", "/auth", "/explore"].includes(pathname) || userPageUrlRegex.test(pathname)) {
|
|
||||||
return pathname;
|
|
||||||
} else {
|
|
||||||
return "/";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getStateFromLocation = () => {
|
|
||||||
const { pathname, search, hash } = window.location;
|
|
||||||
const urlParams = parse(search.slice(1));
|
|
||||||
const state: State = {
|
|
||||||
pathname: getValidPathname(pathname),
|
|
||||||
hash: hash,
|
|
||||||
query: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (search !== "") {
|
|
||||||
state.query = {};
|
|
||||||
state.query.tag = urlParams["tag"] as string;
|
|
||||||
state.query.type = urlParams["type"] as MemoSpecType;
|
|
||||||
state.query.text = urlParams["text"] as string;
|
|
||||||
const shortcutIdStr = urlParams["shortcutId"] as string;
|
|
||||||
state.query.shortcutId = shortcutIdStr ? Number(shortcutIdStr) : undefined;
|
|
||||||
const durationObj = urlParams["duration"] as ParsedQs;
|
|
||||||
if (durationObj) {
|
|
||||||
const duration: Duration = {
|
|
||||||
from: Number(durationObj["from"]),
|
|
||||||
to: Number(durationObj["to"]),
|
|
||||||
};
|
|
||||||
if (duration.to > duration.from && duration.to !== 0) {
|
|
||||||
state.query.duration = duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.query.visibility = urlParams["visibility"] as Visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
};
|
|
||||||
|
|
||||||
const locationSlice = createSlice({
|
|
||||||
name: "location",
|
|
||||||
initialState: getStateFromLocation(),
|
|
||||||
reducers: {
|
|
||||||
updateStateWithLocation: () => {
|
|
||||||
return getStateFromLocation();
|
|
||||||
},
|
|
||||||
updatePathnameStateWithLocation: (state) => {
|
|
||||||
const { pathname } = window.location;
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
pathname: getValidPathname(pathname),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setPathname: (state, action: PayloadAction<string>) => {
|
|
||||||
if (state.pathname === action.payload) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
pathname: action.payload,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
setQuery: (state, action: PayloadAction<Partial<Query>>) => {
|
|
||||||
if (JSON.stringify(action.payload) === state.query) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
query: {
|
|
||||||
...state.query,
|
|
||||||
...action.payload,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { setPathname, setQuery, updateStateWithLocation, updatePathnameStateWithLocation } = locationSlice.actions;
|
|
||||||
|
|
||||||
export default locationSlice.reducer;
|
|
0
web/src/types/location.d.ts
vendored
0
web/src/types/location.d.ts
vendored
Reference in New Issue
Block a user