diff --git a/web/public/assets/empty.png b/web/public/assets/empty.png new file mode 100644 index 00000000..79b82596 Binary files /dev/null and b/web/public/assets/empty.png differ diff --git a/web/src/components/ArchivedMemo.tsx b/web/src/components/ArchivedMemo.tsx index 7ca82492..a9e9418c 100644 --- a/web/src/components/ArchivedMemo.tsx +++ b/web/src/components/ArchivedMemo.tsx @@ -3,10 +3,10 @@ import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { useMemoStore } from "@/store/module"; import { getDateTimeString } from "@/helpers/datetime"; -import useToggle from "@/hooks/useToggle"; import Icon from "./Icon"; import MemoContent from "./MemoContent"; import MemoResourceListView from "./MemoResourceListView"; +import { showCommonDialog } from "./Dialog/CommonDialog"; import "@/less/memo.less"; interface Props { @@ -17,19 +17,17 @@ const ArchivedMemo: React.FC = (props: Props) => { const { memo } = props; const { t } = useTranslation(); const memoStore = useMemoStore(); - const [showConfirmDeleteBtn, toggleConfirmDeleteBtn] = useToggle(false); const handleDeleteMemoClick = async () => { - if (showConfirmDeleteBtn) { - try { + showCommonDialog({ + title: t("memo.delete-memo"), + content: t("memo.delete-confirm"), + style: "warning", + dialogName: "delete-memo-dialog", + onConfirm: async () => { await memoStore.deleteMemoById(memo.id); - } catch (error: any) { - console.error(error); - toast.error(error.response.data.message); - } - } else { - toggleConfirmDeleteBtn(); - } + }, + }); }; const handleRestoreMemoClick = async () => { @@ -46,14 +44,8 @@ const ArchivedMemo: React.FC = (props: Props) => { } }; - const handleMouseLeaveMemoWrapper = () => { - if (showConfirmDeleteBtn) { - toggleConfirmDeleteBtn(false); - } - }; - return ( -
+
{getDateTimeString(memo.updatedTs)} @@ -65,10 +57,7 @@ const ArchivedMemo: React.FC = (props: Props) => { - diff --git a/web/src/components/CreateIdentityProviderDialog.tsx b/web/src/components/CreateIdentityProviderDialog.tsx index 3c58783c..4e7392f4 100644 --- a/web/src/components/CreateIdentityProviderDialog.tsx +++ b/web/src/components/CreateIdentityProviderDialog.tsx @@ -1,12 +1,12 @@ +import { Button, Divider, Input, Option, Select, Typography } from "@mui/joy"; import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { toast } from "react-hot-toast"; -import { Button, Divider, Input, Option, Select, Typography } from "@mui/joy"; import * as api from "@/helpers/api"; import { UNKNOWN_ID } from "@/helpers/consts"; import { absolutifyLink } from "@/helpers/utils"; import { generateDialog } from "./Dialog"; import Icon from "./Icon"; -import { useTranslation } from "react-i18next"; const templateList: IdentityProvider[] = [ { @@ -170,7 +170,6 @@ const CreateIdentityProviderDialog: React.FC = (props: Props) => { if (type === "OAUTH2") { if ( oauth2Config.clientId === "" || - oauth2Config.clientSecret === "" || oauth2Config.authUrl === "" || oauth2Config.tokenUrl === "" || oauth2Config.userInfoUrl === "" || @@ -179,7 +178,13 @@ const CreateIdentityProviderDialog: React.FC = (props: Props) => { ) { return false; } + if (isCreating) { + if (oauth2Config.clientSecret === "") { + return false; + } + } } + return true; }; diff --git a/web/src/components/CreateStorageServiceDialog.tsx b/web/src/components/CreateStorageServiceDialog.tsx index 108f4712..4bb072b7 100644 --- a/web/src/components/CreateStorageServiceDialog.tsx +++ b/web/src/components/CreateStorageServiceDialog.tsx @@ -6,7 +6,7 @@ import * as api from "@/helpers/api"; import { generateDialog } from "./Dialog"; import Icon from "./Icon"; import RequiredBadge from "./RequiredBadge"; -import HelpButton from "./kit/HelpButton"; +import LearnMore from "./LearnMore"; interface Props extends DialogProps { storage?: ObjectStorage; @@ -183,11 +183,9 @@ const CreateStorageServiceDialog: React.FC = (props: Props) => { onChange={(e) => setPartialS3Config({ bucket: e.target.value })} fullWidth /> -
- - {t("setting.storage-section.path")} - - +
+ {t("setting.storage-section.path")} +
{ + return ( +
+ +
+ ); +}; + +export default Empty; diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index 52f7c4df..bb5c0b7f 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -132,12 +132,12 @@ const Header = () => { {t("common.settings")} -
+
diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index 2e770ed4..24d30d09 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -21,12 +21,13 @@ import "@/less/memo.less"; interface Props { memo: Memo; - readonly?: boolean; + showCreator?: boolean; + showVisibility?: boolean; showRelatedMemos?: boolean; } const Memo: React.FC = (props: Props) => { - const { memo, readonly, showRelatedMemos } = props; + const { memo, showCreator, showVisibility, showRelatedMemos } = props; const { t, i18n } = useTranslation(); const filterStore = useFilterStore(); const userStore = useUserStore(); @@ -35,7 +36,7 @@ const Memo: React.FC = (props: Props) => { const [createdTimeStr, setCreatedTimeStr] = useState(getRelativeTimeString(memo.displayTs)); const [relatedMemoList, setRelatedMemoList] = useState([]); const memoContainerRef = useRef(null); - const isVisitorMode = userStore.isVisitorMode() || readonly; + const readonly = userStore.isVisitorMode() || userStore.getCurrentUserId() !== memo.creatorId; useEffect(() => { Promise.allSettled(memo.relationList.map((memoRelation) => memoCacheStore.getOrFetchMemoById(memoRelation.relatedMemoId))).then( @@ -134,7 +135,7 @@ const Memo: React.FC = (props: Props) => { filterStore.setTagFilter(tagName); } } else if (targetEl.classList.contains("todo-block")) { - if (isVisitorMode) { + if (readonly) { return; } @@ -173,7 +174,7 @@ const Memo: React.FC = (props: Props) => { }; const handleMemoContentDoubleClick = (e: React.MouseEvent) => { - if (isVisitorMode) { + if (readonly) { return; } @@ -216,60 +217,62 @@ const Memo: React.FC = (props: Props) => { {createdTimeStr} - {isVisitorMode && ( + {showCreator && ( @{memo.creatorName} )}
- {!isVisitorMode && ( -
- {memo.visibility !== "PRIVATE" && ( - -
handleMemoVisibilityClick(memo.visibility)}> - {memo.visibility === "PUBLIC" ? ( - - ) : ( - - )} +
+ {showVisibility && memo.visibility !== "PRIVATE" && ( + +
handleMemoVisibilityClick(memo.visibility)}> + {memo.visibility === "PUBLIC" ? ( + + ) : ( + + )} +
+
+ )} + {memo.pinned && } + {!readonly && ( + <> + + + +
+
+ + {memo.pinned ? : } + {memo.pinned ? t("common.unpin") : t("common.pin")} + + + + {t("common.edit")} + + + + {t("common.share")} + + + + Mark + + + + + {t("common.archive")} + + + + {t("common.delete")} +
- - )} - {memo.pinned && } - - - -
-
- - {memo.pinned ? : } - {memo.pinned ? t("common.unpin") : t("common.pin")} - - - - {t("common.edit")} - - - - {t("common.share")} - - - - Mark - - - - - {t("common.archive")} - - - - {t("common.delete")} -
-
-
- )} + + )} +
= (props: Props) => { {showRelatedMemos && relatedMemoList.length > 0 && ( <> -

+

Related memos

{relatedMemoList.map((relatedMemo) => { return (
- +
); })} diff --git a/web/src/components/MemoContent.tsx b/web/src/components/MemoContent.tsx index 5f650ec4..ba5c4a81 100644 --- a/web/src/components/MemoContent.tsx +++ b/web/src/components/MemoContent.tsx @@ -24,14 +24,11 @@ interface State { const MemoContent: React.FC = (props: Props) => { const { className, content, showFull, onMemoContentClick, onMemoContentDoubleClick } = props; const { t } = useTranslation(); - + const userStore = useUserStore(); const [state, setState] = useState({ expandButtonStatus: -1, }); const memoContentContainerRef = useRef(null); - - //variable for auto-collapse - const userStore = useUserStore(); const isVisitorMode = userStore.isVisitorMode(); const autoCollapse: boolean = isVisitorMode ? true : (userStore.state.user as User).localSetting.enableAutoCollapse; diff --git a/web/src/components/MemoList.tsx b/web/src/components/MemoList.tsx index 4e5799f1..f30ba483 100644 --- a/web/src/components/MemoList.tsx +++ b/web/src/components/MemoList.tsx @@ -2,13 +2,13 @@ import { useEffect, useRef, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { useFilterStore, useMemoStore, useShortcutStore, useUserStore } from "@/store/module"; -import { TAG_REG, LINK_REG } from "@/labs/marked/parser"; +import { TAG_REG, LINK_REG, PLAIN_LINK_REG } from "@/labs/marked/parser"; import { getTimeStampByDate } from "@/helpers/datetime"; import { DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; import { checkShouldShowMemoWithFilters } from "@/helpers/filter"; +import Empty from "./Empty"; import Memo from "./Memo"; import "@/less/memo-list.less"; -import { PLAIN_LINK_REG } from "@/labs/marked/parser"; const MemoList = () => { const { t } = useTranslation(); @@ -153,7 +153,7 @@ const MemoList = () => { return (
{sortedMemos.map((memo) => ( - + ))} {isFetching ? (
@@ -163,10 +163,11 @@ const MemoList = () => {

{isComplete ? ( - sortedMemos.length === 0 ? ( - t("message.no-memos") - ) : ( - t("message.memos-ready") + sortedMemos.length === 0 && ( +

+ +

{t("message.no-data")}

+
) ) : ( <> diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index d9115440..839beb7d 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -28,6 +28,8 @@ const MyAccountSection = () => { }); }; + const exampleWithCurl = `curl '${openAPIRoute}' -H 'Content-Type: application/json' --data-raw '{"content":"Hello world!"}'`; + return ( <>
@@ -48,22 +50,15 @@ const MyAccountSection = () => {
-

{t("setting.account-section.openapi-title")}

- - -