diff --git a/web/src/components/MemoTrashDialog.tsx b/web/src/components/MemoTrashDialog.tsx new file mode 100644 index 00000000..2e794db8 --- /dev/null +++ b/web/src/components/MemoTrashDialog.tsx @@ -0,0 +1,79 @@ +import { useCallback, useEffect, useState } from "react"; +import useLoading from "../hooks/useLoading"; +import { locationService, memoService } from "../services"; +import { showDialog } from "./Dialog"; +import toastHelper from "./Toast"; +import DeletedMemo from "./DeletedMemo"; +import "../less/memo-trash-dialog.less"; + +interface Props extends DialogProps {} + +const MemoTrashDialog: React.FC = (props: Props) => { + const { destroy } = props; + const loadingState = useLoading(); + const [deletedMemos, setDeletedMemos] = useState([]); + + useEffect(() => { + memoService.fetchAllMemos(); + memoService + .fetchDeletedMemos() + .then((result) => { + if (result !== false) { + setDeletedMemos(result); + } + }) + .catch((error) => { + toastHelper.error("Failed to fetch deleted memos: ", error); + }) + .finally(() => { + loadingState.setFinish(); + }); + locationService.clearQuery(); + }, []); + + const handleDeletedMemoAction = useCallback((memoId: string) => { + setDeletedMemos((deletedMemos) => deletedMemos.filter((memo) => memo.id !== memoId)); + }, []); + + return ( + <> +
+

+ 🗑️ + Trash Bin +

+ +
+
+ {loadingState.isLoading ? ( +
+

fetching data...

+
+ ) : deletedMemos.length === 0 ? ( +
+

Here is No Zettels.

+
+ ) : ( +
+ {deletedMemos.map((memo) => ( + + ))} +
+ )} +
+ + ); +}; + +export default function showMemoTrashDialog(): void { + showDialog( + { + className: "memo-trash-dialog", + useAppContext: true, + }, + MemoTrashDialog, + {} + ); +} diff --git a/web/src/components/MenuBtnsPopup.tsx b/web/src/components/MenuBtnsPopup.tsx index 3da1cbc5..34ab3be7 100644 --- a/web/src/components/MenuBtnsPopup.tsx +++ b/web/src/components/MenuBtnsPopup.tsx @@ -1,6 +1,8 @@ import { useEffect, useRef } from "react"; import { locationService, userService } from "../services"; import showAboutSiteDialog from "./AboutSiteDialog"; +import showSettingDialog from "./SettingDialog"; +import showMemoTrashDialog from "./MemoTrashDialog"; import "../less/menu-btns-popup.less"; interface Props { @@ -29,11 +31,11 @@ const MenuBtnsPopup: React.FC = (props: Props) => { }, [shownStatus]); const handleMyAccountBtnClick = () => { - locationService.pushHistory("/setting"); + showSettingDialog(); }; const handleMemosTrashBtnClick = () => { - locationService.pushHistory("/trash"); + showMemoTrashDialog(); }; const handleAboutBtnClick = () => { @@ -49,7 +51,7 @@ const MenuBtnsPopup: React.FC = (props: Props) => { return (
+
+
+ + +
+ + ); +}; + +export default function showSettingDialog(): void { + showDialog( + { + className: "setting-dialog", + useAppContext: true, + }, + SettingDialog, + {} + ); +} diff --git a/web/src/components/ShortcutList.tsx b/web/src/components/ShortcutList.tsx index b5382029..566b727a 100644 --- a/web/src/components/ShortcutList.tsx +++ b/web/src/components/ShortcutList.tsx @@ -67,14 +67,11 @@ const ShortcutContainer: React.FC = (props: ShortcutCont const { shortcut, isActive } = props; const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false); - console.log(props); - const handleShortcutClick = () => { - console.log("here"); if (isActive) { locationService.setMemoShortcut(""); } else { - if (!["/", "/trash"].includes(locationService.getState().pathname)) { + if (!["/"].includes(locationService.getState().pathname)) { locationService.setPathname("/"); } locationService.setMemoShortcut(shortcut.id); diff --git a/web/src/components/TagList.tsx b/web/src/components/TagList.tsx index 88213a39..d2501ae5 100644 --- a/web/src/components/TagList.tsx +++ b/web/src/components/TagList.tsx @@ -101,7 +101,7 @@ const TagItemContainer: React.FC = (props: TagItemContain locationService.setTagQuery(""); } else { utils.copyTextToClipboard(`#${tag.text} `); - if (!["/", "/trash"].includes(locationService.getState().pathname)) { + if (!["/"].includes(locationService.getState().pathname)) { locationService.setPathname("/"); } locationService.setTagQuery(tag.text); diff --git a/web/src/components/UsageHeatMap.tsx b/web/src/components/UsageHeatMap.tsx index 2d5a189d..d1687681 100644 --- a/web/src/components/UsageHeatMap.tsx +++ b/web/src/components/UsageHeatMap.tsx @@ -76,7 +76,7 @@ const UsageHeatMap: React.FC = () => { locationService.setFromAndToQuery(0, 0); setCurrentStat(null); } else if (item.count > 0) { - if (!["/", "/trash"].includes(locationService.getState().pathname)) { + if (!["/"].includes(locationService.getState().pathname)) { locationService.setPathname("/"); } locationService.setFromAndToQuery(item.timestamp, item.timestamp + DAILY_TIMESTAMP); diff --git a/web/src/less/memo-trash-dialog.less b/web/src/less/memo-trash-dialog.less new file mode 100644 index 00000000..10b084e1 --- /dev/null +++ b/web/src/less/memo-trash-dialog.less @@ -0,0 +1,23 @@ +@import "./mixin.less"; +@import "./memos-header.less"; + +.memo-trash-dialog { + > .dialog-container { + @apply w-128 max-w-full mb-8; + + > .dialog-content-container { + .flex(column, flex-start, flex-start); + @apply w-full overflow-y-scroll; + + > .tip-text-container { + @apply w-full h-32; + .flex(column, center, center); + } + + > .deleted-memos-container { + .flex(column, flex-start, flex-start); + @apply w-full; + } + } + } +} diff --git a/web/src/less/memo-trash.less b/web/src/less/memo-trash.less deleted file mode 100644 index fa5b20b4..00000000 --- a/web/src/less/memo-trash.less +++ /dev/null @@ -1,39 +0,0 @@ -@import "./mixin.less"; -@import "./memos-header.less"; - -.memo-trash-wrapper { - @apply px-8; - .flex(column, flex-start, flex-start); - width: 100%; - height: 100%; - flex-grow: 1; - overflow-y: scroll; - .hide-scroll-bar(); - - > .section-header-container { - width: 100%; - height: 40px; - margin-bottom: 0; - - > .title-text { - font-weight: bold; - font-size: 18px; - color: @text-black; - } - } - - > .tip-text-container { - width: 100%; - height: 128px; - .flex(column, center, center); - } - - > .deleted-memos-container { - .flex(column, flex-start, flex-start); - flex-grow: 1; - width: 100%; - overflow-y: scroll; - padding-bottom: 64px; - .hide-scroll-bar(); - } -} diff --git a/web/src/less/setting-dialog.less b/web/src/less/setting-dialog.less new file mode 100644 index 00000000..b9db59a1 --- /dev/null +++ b/web/src/less/setting-dialog.less @@ -0,0 +1,33 @@ +@import "./mixin.less"; +@import "./memos-header.less"; + +.setting-dialog { + > .dialog-container { + @apply w-3/5 max-w-full mb-8; + + > .dialog-content-container { + .flex(column, flex-start, flex-start); + @apply w-full overflow-y-scroll; + .hide-scroll-bar(); + + > .section-container { + .flex(column, flex-start, flex-start); + @apply w-full my-2; + + > .title-text { + @apply text-base font-bold mb-2; + color: @text-black; + } + + > .form-label { + .flex(row, flex-start, center); + @apply w-full text-sm mb-2; + + > .normal-text { + @apply shrink-0; + } + } + } + } + } +} diff --git a/web/src/less/setting.less b/web/src/less/setting.less deleted file mode 100644 index 60da39e1..00000000 --- a/web/src/less/setting.less +++ /dev/null @@ -1,47 +0,0 @@ -@import "./mixin.less"; -@import "./memos-header.less"; - -.preference-wrapper { - .flex(column, flex-start, flex-start); - @apply w-full h-full grow overflow-y-scroll px-8; - .hide-scroll-bar(); - - > .section-header-container { - @apply w-full h-10 mb-0; - - > .title-text { - @apply font-bold text-lg; - color: @text-black; - } - } - - > .tip-text-container { - .flex(column, center, center); - @apply w-full h-32; - } - - > .sections-wrapper { - .flex(column, flex-start, flex-start); - @apply grow w-full overflow-y-scroll pb-16; - .hide-scroll-bar(); - - > .section-container { - .flex(column, flex-start, flex-start); - @apply w-full bg-white my-2 mx-0 p-4 pb-2 rounded-lg; - - > .title-text { - @apply text-base font-bold mb-2; - color: @text-black; - } - - > .form-label { - .flex(row, flex-start, center); - @apply w-full text-sm mb-2; - - > .normal-text { - @apply shrink-0; - } - } - } - } -} diff --git a/web/src/pages/Setting.tsx b/web/src/pages/Setting.tsx deleted file mode 100644 index 9883b893..00000000 --- a/web/src/pages/Setting.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect } from "react"; -import { memoService } from "../services"; -import MyAccountSection from "../components/MyAccountSection"; -import PreferencesSection from "../components/PreferencesSection"; -import "../less/setting.less"; - -interface Props {} - -const Setting: React.FC = () => { - useEffect(() => { - memoService.fetchAllMemos(); - }, []); - - return ( -
-
-
- Settings -
-
- -
- - -
-
- ); -}; - -export default Setting; diff --git a/web/src/pages/Trash.tsx b/web/src/pages/Trash.tsx deleted file mode 100644 index 6737020f..00000000 --- a/web/src/pages/Trash.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { useCallback, useContext, useEffect, useState } from "react"; -import appContext from "../stores/appContext"; -import useLoading from "../hooks/useLoading"; -import { locationService, memoService, shortcutService } from "../services"; -import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG } from "../helpers/consts"; -import utils from "../helpers/utils"; -import { checkShouldShowMemoWithFilters } from "../helpers/filter"; -import toastHelper from "../components/Toast"; -import DeletedMemo from "../components/DeletedMemo"; -import MemoFilter from "../components/MemoFilter"; -import "../less/memo-trash.less"; - -interface Props {} - -const Trash: React.FC = () => { - const { - locationState: { query }, - } = useContext(appContext); - const loadingState = useLoading(); - const [deletedMemos, setDeletedMemos] = useState([]); - - const { tag: tagQuery, duration, type: memoType, text: textQuery, shortcutId } = query; - const queryFilter = shortcutService.getShortcutById(shortcutId); - const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || memoType || textQuery || queryFilter); - - const shownMemos = - showMemoFilter || queryFilter - ? deletedMemos.filter((memo) => { - let shouldShow = true; - - if (queryFilter) { - const filters = JSON.parse(queryFilter.payload) as Filter[]; - if (Array.isArray(filters)) { - shouldShow = checkShouldShowMemoWithFilters(memo, filters); - } - } - - if (tagQuery) { - const tagsSet = new Set(); - for (const t of Array.from(memo.content.match(TAG_REG) ?? [])) { - const tag = t.replace(TAG_REG, "$1").trim(); - const items = tag.split("/"); - let temp = ""; - for (const i of items) { - temp += i; - tagsSet.add(temp); - temp += "/"; - } - } - if (!tagsSet.has(tagQuery)) { - shouldShow = false; - } - } - if ( - duration && - duration.from < duration.to && - (utils.getTimeStampByDate(memo.createdAt) < duration.from || utils.getTimeStampByDate(memo.createdAt) > duration.to) - ) { - shouldShow = false; - } - if (memoType) { - if (memoType === "NOT_TAGGED" && memo.content.match(TAG_REG) !== null) { - shouldShow = false; - } else if (memoType === "LINKED" && memo.content.match(LINK_REG) === null) { - shouldShow = false; - } else if (memoType === "IMAGED" && memo.content.match(IMAGE_URL_REG) === null) { - shouldShow = false; - } else if (memoType === "CONNECTED" && memo.content.match(MEMO_LINK_REG) === null) { - shouldShow = false; - } - } - if (textQuery && !memo.content.includes(textQuery)) { - shouldShow = false; - } - - return shouldShow; - }) - : deletedMemos; - - useEffect(() => { - memoService.fetchAllMemos(); - memoService - .fetchDeletedMemos() - .then((result) => { - if (result !== false) { - setDeletedMemos(result); - } - }) - .catch((error) => { - toastHelper.error("Failed to fetch deleted memos: ", error); - }) - .finally(() => { - loadingState.setFinish(); - }); - locationService.clearQuery(); - }, []); - - const handleDeletedMemoAction = useCallback((memoId: string) => { - setDeletedMemos((deletedMemos) => deletedMemos.filter((memo) => memo.id !== memoId)); - }, []); - - return ( -
-
-
- Recycle Bin -
-
- - {loadingState.isLoading ? ( -
-

fetching data...

-
- ) : deletedMemos.length === 0 ? ( -
-

Here is No Zettels.

-
- ) : ( -
- {shownMemos.map((memo) => ( - - ))} -
- )} -
- ); -}; - -export default Trash; diff --git a/web/src/routers/homeRouter.tsx b/web/src/routers/homeRouter.tsx index 9e648ea4..b39661f0 100644 --- a/web/src/routers/homeRouter.tsx +++ b/web/src/routers/homeRouter.tsx @@ -1,10 +1,6 @@ import Memos from "../pages/Memos"; -import Trash from "../pages/Trash"; -import Setting from "../pages/Setting"; const homeRouter = { - "/trash": , - "/setting": , "*": , }; diff --git a/web/src/services/locationService.ts b/web/src/services/locationService.ts index cfb7319b..d8867e88 100644 --- a/web/src/services/locationService.ts +++ b/web/src/services/locationService.ts @@ -185,7 +185,7 @@ class LocationService { }; public getValidPathname = (pathname: string): AppRouter => { - if (["/", "/signin", "/trash", "/setting"].includes(pathname)) { + if (["/", "/signin"].includes(pathname)) { return pathname as AppRouter; } else { return "/"; diff --git a/web/src/types/location.d.ts b/web/src/types/location.d.ts index 3d6cf2f9..d7ffeea1 100644 --- a/web/src/types/location.d.ts +++ b/web/src/types/location.d.ts @@ -11,7 +11,7 @@ interface Query { shortcutId: string; } -type AppRouter = "/" | "/signin" | "/trash" | "/setting"; +type AppRouter = "/" | "/signin"; interface AppLocation { pathname: AppRouter;