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 {
memo: Memo;
handleDeletedMemoAction: (memoId: MemoId) => void;
}
const DeletedMemo: React.FC<Props> = (props: Props) => {
const { memo: propsMemo, handleDeletedMemoAction } = props;
const ArchivedMemo: React.FC<Props> = (props: Props) => {
const { memo: propsMemo } = props;
const memo = {
...propsMemo,
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 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) {
try {
await memoService.deleteMemoById(memo.id);
handleDeletedMemoAction(memo.id);
await memoService.fetchAllMemos();
} catch (error: any) {
toastHelper.error(error.message);
}
@ -42,7 +41,7 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
id: memo.id,
rowStatus: "NORMAL",
});
handleDeletedMemoAction(memo.id);
await memoService.fetchAllMemos();
toastHelper.info("Restored successfully");
} catch (error: any) {
toastHelper.error(error.message);
@ -56,9 +55,9 @@ const DeletedMemo: React.FC<Props> = (props: Props) => {
};
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">
<span className="time-text">Deleted at {memo.deletedAtStr}</span>
<span className="time-text">Archived at {memo.archivedAtStr}</span>
<div className="btns-container">
<span className="btn restore-btn" onClick={handleRestoreMemoClick}>
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 { DONE_BLOCK_REG, parseMarkedToHtml, TODO_BLOCK_REG } from "../helpers/marked";
import * as utils from "../helpers/utils";
import useToggle from "../hooks/useToggle";
import { editorStateService, locationService, memoService } from "../services";
import Only from "./common/OnlyWhen";
import Image from "./Image";
@ -34,7 +33,6 @@ const Memo: React.FC<Props> = (props: Props) => {
expandButtonStatus: -1,
});
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"));
useEffect(() => {
@ -74,28 +72,18 @@ const Memo: React.FC<Props> = (props: Props) => {
editorStateService.setEditMemoWithId(memo.id);
};
const handleDeleteMemoClick = async () => {
if (showConfirmDeleteBtn) {
try {
await memoService.patchMemo({
id: memo.id,
rowStatus: "ARCHIVED",
});
} catch (error: any) {
toastHelper.error(error.message);
}
if (editorStateService.getState().editMemoId === memo.id) {
editorStateService.clearEditMemo();
}
} else {
toggleConfirmDeleteBtn();
const handleArchiveMemoClick = async () => {
try {
await memoService.patchMemo({
id: memo.id,
rowStatus: "ARCHIVED",
});
} catch (error: any) {
toastHelper.error(error.message);
}
};
const handleMouseLeaveMemoWrapper = () => {
if (showConfirmDeleteBtn) {
toggleConfirmDeleteBtn(false);
if (editorStateService.getState().editMemoId === memo.id) {
editorStateService.clearEditMemo();
}
};
@ -162,7 +150,7 @@ const Memo: React.FC<Props> = (props: Props) => {
};
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">
<span className="time-text" onClick={handleShowMemoStoryDialog}>
{memo.createdAtStr}
@ -196,8 +184,8 @@ const Memo: React.FC<Props> = (props: Props) => {
<span className="btn" onClick={handleShowMemoStoryDialog}>
View Story
</span>
<span className={`btn delete-btn ${showConfirmDeleteBtn ? "final-confirm" : ""}`} onClick={handleDeleteMemoClick}>
{showConfirmDeleteBtn ? "Delete!" : "Delete"}
<span className="btn archive-btn" onClick={handleArchiveMemoClick}>
Archive
</span>
</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 showDailyReviewDialog from "./DailyReviewDialog";
import showSettingDialog from "./SettingDialog";
import showMemoTrashDialog from "./MemoTrashDialog";
import showArchivedMemoDialog from "./ArchivedMemoDialog";
import UserBanner from "./UserBanner";
import UsageHeatMap from "./UsageHeatMap";
import ShortcutList from "./ShortcutList";
@ -21,8 +21,8 @@ const Sidebar: React.FC<Props> = () => {
showSettingDialog();
};
const handleMemosTrashBtnClick = () => {
showMemoTrashDialog();
const handleArchivedBtnClick = () => {
showArchivedMemoDialog();
};
return (
@ -55,8 +55,8 @@ const Sidebar: React.FC<Props> = () => {
<button className="btn action-btn" onClick={handleMyAccountBtnClick}>
<span className="icon"></span> Setting
</button>
<button className="btn action-btn" onClick={handleMemosTrashBtnClick}>
<span className="icon">🗑</span> Recycle Bin
<button className="btn action-btn" onClick={handleArchivedBtnClick}>
<span className="icon">🗂</span> Archived
</button>
</div>
<ShortcutList />

View File

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

View File

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

View File

@ -4,7 +4,7 @@
.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;
&.deleted-memo {
&.archived-memo {
@apply border-gray-200;
}
@ -50,14 +50,10 @@
}
> .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 {
@apply text-red-600;
&.final-confirm {
@apply font-bold;
}
&.archive-btn {
@apply text-orange-600;
}
}
}

View File

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

View File

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

View File

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