chore: rename delete to archive

This commit is contained in:
boojack 2022-07-02 14:14:18 +08:00
parent 536627007d
commit 06fc29aecd
11 changed files with 135 additions and 180 deletions

View File

@ -10,15 +10,14 @@ import "../less/memo.less";
interface Props { interface Props {
memo: Memo; memo: Memo;
handleDeletedMemoAction: (memoId: MemoId) => void;
} }
const DeletedMemo: React.FC<Props> = (props: Props) => { const ArchivedMemo: React.FC<Props> = (props: Props) => {
const { memo: propsMemo, handleDeletedMemoAction } = props; const { memo: propsMemo } = props;
const memo = { const memo = {
...propsMemo, ...propsMemo,
createdAtStr: utils.getDateTimeString(propsMemo.createdTs), createdAtStr: utils.getDateTimeString(propsMemo.createdTs),
deletedAtStr: utils.getDateTimeString(propsMemo.updatedTs ?? Date.now()), archivedAtStr: utils.getDateTimeString(propsMemo.updatedTs ?? Date.now()),
}; };
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false); const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []).map((s) => s.replace(IMAGE_URL_REG, "$1")); const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []).map((s) => s.replace(IMAGE_URL_REG, "$1"));
@ -27,7 +26,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
if (showConfirmDeleteBtn) { if (showConfirmDeleteBtn) {
try { try {
await memoService.deleteMemoById(memo.id); await memoService.deleteMemoById(memo.id);
handleDeletedMemoAction(memo.id); await memoService.fetchAllMemos();
} catch (error: any) { } catch (error: any) {
toastHelper.error(error.message); toastHelper.error(error.message);
} }
@ -42,7 +41,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
id: memo.id, id: memo.id,
rowStatus: "NORMAL", rowStatus: "NORMAL",
}); });
handleDeletedMemoAction(memo.id); await memoService.fetchAllMemos();
toastHelper.info("Restored successfully"); toastHelper.info("Restored successfully");
} catch (error: any) { } catch (error: any) {
toastHelper.error(error.message); toastHelper.error(error.message);
@ -56,9 +55,9 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
}; };
return ( return (
<div className={`memo-wrapper deleted-memo ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}> <div className={`memo-wrapper archived-memo ${"memos-" + memo.id}`} onMouseLeave={handleMouseLeaveMemoWrapper}>
<div className="memo-top-wrapper"> <div className="memo-top-wrapper">
<span className="time-text">Deleted at {memo.deletedAtStr}</span> <span className="time-text">Archived at {memo.archivedAtStr}</span>
<div className="btns-container"> <div className="btns-container">
<span className="btn restore-btn" onClick={handleRestoreMemoClick}> <span className="btn restore-btn" onClick={handleRestoreMemoClick}>
Restore Restore
@ -80,4 +79,4 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
); );
}; };
export default DeletedMemo; export default ArchivedMemo;

View File

@ -0,0 +1,73 @@
import { useEffect, useState } from "react";
import useLoading from "../hooks/useLoading";
import { memoService } from "../services";
import { useAppSelector } from "../store";
import { showDialog } from "./Dialog";
import toastHelper from "./Toast";
import ArchivedMemo from "./ArchivedMemo";
import "../less/archived-memo-dialog.less";
interface Props extends DialogProps {}
const ArchivedMemoDialog: React.FC<Props> = (props: Props) => {
const { destroy } = props;
const memos = useAppSelector((state) => state.memo.memos);
const loadingState = useLoading();
const [archivedMemos, setArchivedMemos] = useState<Memo[]>([]);
useEffect(() => {
memoService
.fetchArchivedMemos()
.then((result) => {
setArchivedMemos(result);
})
.catch((error) => {
toastHelper.error("Failed to fetch archived memos: ", error);
})
.finally(() => {
loadingState.setFinish();
});
}, [memos]);
return (
<>
<div className="dialog-header-container">
<p className="title-text">
<span className="icon-text">🗂</span>
Archived Memos
</p>
<button className="btn close-btn" onClick={destroy}>
<img className="icon-img" src="/icons/close.svg" />
</button>
</div>
<div className="dialog-content-container">
{loadingState.isLoading ? (
<div className="tip-text-container">
<p className="tip-text">fetching data...</p>
</div>
) : archivedMemos.length === 0 ? (
<div className="tip-text-container">
<p className="tip-text">Here is No Zettels.</p>
</div>
) : (
<div className="archived-memos-container">
{archivedMemos.map((memo) => (
<ArchivedMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} />
))}
</div>
)}
</div>
</>
);
};
export default function showArchivedMemo(): void {
showDialog(
{
className: "archived-memo-dialog",
useAppContext: true,
},
ArchivedMemoDialog,
{}
);
}

View File

@ -3,7 +3,6 @@ import { escape, indexOf } from "lodash-es";
import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts"; import { IMAGE_URL_REG, LINK_REG, MEMO_LINK_REG, TAG_REG, UNKNOWN_ID } from "../helpers/consts";
import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked"; import { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked";
import * as utils from "../helpers/utils"; import * as utils from "../helpers/utils";
import useToggle from "../hooks/useToggle";
import { editorStateService, locationService, memoService } from "../services"; import { editorStateService, locationService, memoService } from "../services";
import Only from "./common/OnlyWhen"; import Only from "./common/OnlyWhen";
import Image from "./Image"; import Image from "./Image";
@ -34,7 +33,6 @@ const Memo: React.FC<Props> = (props: Props) => {
expandButtonStatus: -1, expandButtonStatus: -1,
}); });
const memoContainerRef = useRef<HTMLDivElement>(null); const memoContainerRef = useRef<HTMLDivElement>(null);
const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false);
const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []).map((s) => s.replace(IMAGE_URL_REG, "$1")); const imageUrls = Array.from(memo.content.match(IMAGE_URL_REG) ?? []).map((s) => s.replace(IMAGE_URL_REG, "$1"));
useEffect(() => { useEffect(() => {
@ -74,28 +72,18 @@ const Memo: React.FC<Props> = (props: Props) => {
editorStateService.setEditMemoWithId(memo.id); editorStateService.setEditMemoWithId(memo.id);
}; };
const handleDeleteMemoClick = async () => { const handleArchiveMemoClick = async () => {
if (showConfirmDeleteBtn) { try {
try { await memoService.patchMemo({
await memoService.patchMemo({ id: memo.id,
id: memo.id, rowStatus: "ARCHIVED",
rowStatus: "ARCHIVED", });
}); } catch (error: any) {
} catch (error: any) { toastHelper.error(error.message);
toastHelper.error(error.message);
}
if (editorStateService.getState().editMemoId === memo.id) {
editorStateService.clearEditMemo();
}
} else {
toggleConfirmDeleteBtn();
} }
};
const handleMouseLeaveMemoWrapper = () => { if (editorStateService.getState().editMemoId === memo.id) {
if (showConfirmDeleteBtn) { editorStateService.clearEditMemo();
toggleConfirmDeleteBtn(false);
} }
}; };
@ -162,7 +150,7 @@ const Memo: React.FC<Props> = (props: Props) => {
}; };
return ( return (
<div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`} onMouseLeave={handleMouseLeaveMemoWrapper}> <div className={`memo-wrapper ${"memos-" + memo.id} ${memo.pinned ? "pinned" : ""}`}>
<div className="memo-top-wrapper"> <div className="memo-top-wrapper">
<span className="time-text" onClick={handleShowMemoStoryDialog}> <span className="time-text" onClick={handleShowMemoStoryDialog}>
{memo.createdAtStr} {memo.createdAtStr}
@ -196,8 +184,8 @@ const Memo: React.FC<Props> = (props: Props) => {
<span className="btn" onClick={handleShowMemoStoryDialog}> <span className="btn" onClick={handleShowMemoStoryDialog}>
View Story View Story
</span> </span>
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}> <span className="btn archive-btn" onClick={handleArchiveMemoClick}>
{showConfirmDeleteBtn ? "Delete!" : "Delete"} Archive
</span> </span>
</div> </div>
</div> </div>

View File

@ -1,77 +0,0 @@
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: Props) => {
const { destroy } = props;
const loadingState = useLoading();
const [deletedMemos, setDeletedMemos] = useState<Memo[]>([]);
useEffect(() => {
memoService
.fetchDeletedMemos()
.then((result) => {
setDeletedMemos(result);
})
.catch((error) => {
toastHelper.error("Failed to fetch deleted memos: ", error);
})
.finally(() => {
loadingState.setFinish();
});
locationService.clearQuery();
}, []);
const handleDeletedMemoAction = useCallback(async (memoId: MemoId) => {
setDeletedMemos((deletedMemos) => deletedMemos.filter((memo) => memo.id !== memoId));
await memoService.fetchAllMemos();
}, []);
return (
<>
<div className="dialog-header-container">
<p className="title-text">
<span className="icon-text">🗑</span>
Recycle Bin
</p>
<button className="btn close-btn" onClick={destroy}>
<img className="icon-img" src="/icons/close.svg" />
</button>
</div>
<div className="dialog-content-container">
{loadingState.isLoading ? (
<div className="tip-text-container">
<p className="tip-text">fetching data...</p>
</div>
) : deletedMemos.length === 0 ? (
<div className="tip-text-container">
<p className="tip-text">Here is No Zettels.</p>
</div>
) : (
<div className="deleted-memos-container">
{deletedMemos.map((memo) => (
<DeletedMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} handleDeletedMemoAction={handleDeletedMemoAction} />
))}
</div>
)}
</div>
</>
);
};
export default function showMemoTrashDialog(): void {
showDialog(
{
className: "memo-trash-dialog",
useAppContext: true,
},
MemoTrashDialog,
{}
);
}

View File

@ -2,7 +2,7 @@ import { useAppSelector } from "../store";
import * as utils from "../helpers/utils"; import * as utils from "../helpers/utils";
import showDailyReviewDialog from "./DailyReviewDialog"; import showDailyReviewDialog from "./DailyReviewDialog";
import showSettingDialog from "./SettingDialog"; import showSettingDialog from "./SettingDialog";
import showMemoTrashDialog from "./MemoTrashDialog"; import showArchivedMemoDialog from "./ArchivedMemoDialog";
import UserBanner from "./UserBanner"; import UserBanner from "./UserBanner";
import UsageHeatMap from "./UsageHeatMap"; import UsageHeatMap from "./UsageHeatMap";
import ShortcutList from "./ShortcutList"; import ShortcutList from "./ShortcutList";
@ -21,8 +21,8 @@ const Sidebar: React.FC<Props> = () => {
showSettingDialog(); showSettingDialog();
}; };
const handleMemosTrashBtnClick = () => { const handleArchivedBtnClick = () => {
showMemoTrashDialog(); showArchivedMemoDialog();
}; };
return ( return (
@ -55,8 +55,8 @@ const Sidebar: React.FC<Props> = () => {
<button className="btn action-btn" onClick={handleMyAccountBtnClick}> <button className="btn action-btn" onClick={handleMyAccountBtnClick}>
<span className="icon"></span> Setting <span className="icon"></span> Setting
</button> </button>
<button className="btn action-btn" onClick={handleMemosTrashBtnClick}> <button className="btn action-btn" onClick={handleArchivedBtnClick}>
<span className="icon">🗑</span> Recycle Bin <span className="icon">🗂</span> Archived
</button> </button>
</div> </div>
<ShortcutList /> <ShortcutList />

View File

@ -126,7 +126,7 @@ const TagItemContainer: React.FC<TagItemContainerProps> = (props: TagItemContain
</div> </div>
{hasSubTags ? ( {hasSubTags ? (
<div className={`subtags-container ${showSubTags ? "" : "hidden"}`}> <div className={`subtags-container ${showSubTags ? "" : "!hidden"}`}>
{tag.subTags.map((st, idx) => ( {tag.subTags.map((st, idx) => (
<TagItemContainer key={st.text + "-" + idx} tag={st} tagQuery={tagQuery} /> <TagItemContainer key={st.text + "-" + idx} tag={st} tagQuery={tagQuery} />
))} ))}

View File

@ -1,6 +1,6 @@
@import "./mixin.less"; @import "./mixin.less";
.memo-trash-dialog { .archived-memo-dialog {
@apply px-4; @apply px-4;
> .dialog-container { > .dialog-container {
@ -15,7 +15,7 @@
.flex(column, center, center); .flex(column, center, center);
} }
> .deleted-memos-container { > .archived-memos-container {
.flex(column, flex-start, flex-start); .flex(column, flex-start, flex-start);
@apply w-full; @apply w-full;
} }

View File

@ -4,7 +4,7 @@
.memo-wrapper { .memo-wrapper {
@apply flex flex-col justify-start items-start w-full max-w-full p-4 pt-3 mt-2 bg-white rounded-lg border border-white hover:border-gray-200; @apply flex flex-col justify-start items-start w-full max-w-full p-4 pt-3 mt-2 bg-white rounded-lg border border-white hover:border-gray-200;
&.deleted-memo { &.archived-memo {
@apply border-gray-200; @apply border-gray-200;
} }
@ -50,14 +50,10 @@
} }
> .btn { > .btn {
@apply w-full py-2 px-3 rounded justify-start; @apply w-full text-sm leading-6 py-1 px-3 rounded justify-start;
&.delete-btn { &.archive-btn {
@apply text-red-600; @apply text-orange-600;
&.final-confirm {
@apply font-bold;
}
} }
} }
} }

View File

@ -1,21 +1,18 @@
@import "./mixin.less"; @import "./mixin.less";
.shortcuts-wrapper { .shortcuts-wrapper {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start w-full py-0 px-2 mt-2 h-auto shrink-0 flex-nowrap;
@apply w-full py-0 px-2 mt-2 h-auto shrink-0 flex-nowrap;
.hide-scroll-bar(); .hide-scroll-bar();
> .title-text { > .title-text {
.flex(row, flex-start, center); @apply flex flex-row justify-start items-center w-full px-4;
@apply w-full px-4;
> .normal-text { > .normal-text {
@apply text-xs leading-6 font-mono text-gray-400; @apply text-sm leading-6 font-mono text-gray-400;
} }
> .btn { > .btn {
.flex(column, center, center); @apply flex flex-col justify-center items-center w-5 h-5 bg-gray-200 rounded ml-2 shadow hover:opacity-80;
@apply w-5 h-5 bg-gray-200 rounded ml-2 shadow hover:opacity-80;
> img { > img {
@apply w-4 h-4 opacity-80; @apply w-4 h-4 opacity-80;
@ -24,8 +21,7 @@
} }
> .create-shortcut-btn-container { > .create-shortcut-btn-container {
.flex(row, flex-start, center); @apply flex flex-row justify-start items-center w-full mt-4 mb-2 ml-4;
@apply w-full mt-4 mb-2 ml-4;
> .btn { > .btn {
@apply flex p-2 px-4 rounded-lg text-sm border border-dashed border-blue-600; @apply flex p-2 px-4 rounded-lg text-sm border border-dashed border-blue-600;
@ -37,23 +33,19 @@
} }
> .shortcuts-container { > .shortcuts-container {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mb-2;
@apply relative w-full h-auto flex-nowrap mb-2;
> .shortcut-container { > .shortcut-container {
.flex(row, space-between, center); @apply flex flex-row justify-between items-center w-full h-10 py-0 px-4 mt-px first:mt-2 rounded-lg text-base cursor-pointer select-none shrink-0 hover:bg-gray-200;
@apply w-full h-10 py-0 px-4 mt-px first:mt-2 rounded-lg text-base cursor-pointer select-none shrink-0;
&:hover { &:hover {
background-color: @bg-gray;
> .btns-container { > .btns-container {
@apply flex; @apply flex;
} }
} }
&.active { &.active {
background-color: @text-green !important; @apply bg-green-600;
> .shortcut-text-container { > .shortcut-text-container {
> * { > * {
@ -63,8 +55,7 @@
} }
> .shortcut-text-container { > .shortcut-text-container {
.flex(row, flex-start, center); @apply flex flex-row justify-start items-center truncate shrink leading-5 mr-1;
@apply truncate shrink leading-5 mr-1;
color: @text-black; color: @text-black;
> .icon-text { > .icon-text {
@ -77,8 +68,7 @@
} }
> .btns-container { > .btns-container {
.flex(row, flex-end, center); @apply flex-row justify-end items-center hidden shrink-0;
@apply hidden shrink-0;
> .action-btn { > .action-btn {
.flex(row, center, center); .flex(row, center, center);
@ -91,35 +81,33 @@
&.toggle-btn { &.toggle-btn {
&:hover { &:hover {
& + .action-btns-wrapper { & + .action-btns-wrapper {
display: flex; @apply flex;
} }
} }
} }
} }
> .action-btns-wrapper { > .action-btns-wrapper {
.flex(column, flex-start, flex-start); @apply flex-col justify-start items-start absolute top-6 right-0 w-auto h-auto px-4 pt-3 hidden z-10;
@apply absolute right-0 w-auto h-auto px-4 pt-3 translate-y-16 hidden z-10;
> .action-btns-container { > .action-btns-container {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start w-24 h-auto p-1 whitespace-nowrap rounded-md bg-white shadow;
@apply w-24 h-auto p-1 whitespace-nowrap rounded-md bg-white shadow;
> .btn { > .btn {
@apply w-full py-2 px-3 rounded text-sm text-left hover:bg-gray-100; @apply w-full text-sm leading-6 py-1 px-3 rounded text-left hover:bg-gray-100;
&.delete-btn { &.delete-btn {
color: @text-red; @apply text-orange-600;
&.final-confirm { &.final-confirm {
font-weight: bold; @apply font-black;
} }
} }
} }
} }
&:hover { &:hover {
display: flex; @apply flex;
} }
} }
} }

View File

@ -1,21 +1,18 @@
@import "./mixin.less"; @import "./mixin.less";
.tags-wrapper { .tags-wrapper {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start px-2 w-full h-auto flex-nowrap pb-4 mt-2 grow;
@apply px-2 w-full h-auto flex-nowrap pb-4 mt-2 grow;
.hide-scroll-bar(); .hide-scroll-bar();
> .title-text { > .title-text {
@apply w-full py-1 px-4 text-xs font-mono text-gray-400; @apply w-full px-4 text-sm leading-6 font-mono text-gray-400;
} }
> .tags-container { > .tags-container {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start relative w-full h-auto flex-nowrap mb-2 mt-1;
@apply relative w-full h-auto flex-nowrap mb-2 mt-1;
.subtags-container { .subtags-container {
.flex(column, flex-start, flex-start); @apply flex flex-col justify-start items-start h-auto mt-1 ml-4 pl-1;
@apply h-auto mt-1 ml-4 pl-1;
width: calc(100% - 18px); width: calc(100% - 18px);
min-width: 80px; min-width: 80px;
border-left: 2px solid @bg-gray; border-left: 2px solid @bg-gray;
@ -26,27 +23,19 @@
} }
.tag-item-container { .tag-item-container {
.flex(row, space-between, center); @apply flex flex-row justify-between items-center w-full h-10 py-0 px-4 rounded-lg text-base shrink-0 select-none cursor-pointer hover:bg-gray-200;
@apply w-full h-10 py-0 px-4 rounded-lg text-base shrink-0 select-none cursor-pointer;
&:hover {
background-color: @bg-gray;
}
&.active { &.active {
> .tag-text-container { > .tag-text-container {
> * { > * {
@apply font-bold; @apply text-green-600;
color: @text-green;
} }
} }
} }
> .tag-text-container { > .tag-text-container {
.flex(row, flex-start, center); @apply flex flex-row justify-start items-center overflow-hidden text-ellipsis shrink-0 leading-5 text-black;
@apply overflow-hidden text-ellipsis shrink-0 leading-5;
max-width: calc(100% - 24px); max-width: calc(100% - 24px);
color: @text-black;
> .icon-text { > .icon-text {
@apply block w-4 shrink-0; @apply block w-4 shrink-0;
@ -58,11 +47,10 @@
} }
> .btns-container { > .btns-container {
.flex(row, flex-end, center); @apply flex flex-row justify-end items-center;
> .action-btn { > .action-btn {
.flex(row, center, center); @apply flex flex-row justify-center items-center w-6 h-6 shrink-0 transition-all rotate-0;
@apply w-6 h-6 shrink-0 transition-all rotate-0;
> .icon-img { > .icon-img {
@apply w-5 h-5 opacity-80; @apply w-5 h-5 opacity-80;

View File

@ -23,12 +23,12 @@ const memoService = {
return memos; return memos;
}, },
fetchDeletedMemos: async () => { fetchArchivedMemos: async () => {
const { data } = (await api.getArchivedMemoList()).data; const { data } = (await api.getArchivedMemoList()).data;
const deletedMemos = data.map((m) => { const archivedMemos = data.map((m) => {
return convertResponseModelMemo(m); return convertResponseModelMemo(m);
}); });
return deletedMemos; return archivedMemos;
}, },
getMemoById: (memoId: MemoId) => { getMemoById: (memoId: MemoId) => {