From 6ab58f294ec289c480fba7d81062a5a966eab796 Mon Sep 17 00:00:00 2001 From: boojack Date: Sat, 4 Mar 2023 13:49:53 +0800 Subject: [PATCH] feat: update home layout (#1242) --- web/src/components/DailyReviewDialog.tsx | 2 +- web/src/components/Header.tsx | 92 +++ web/src/components/HomeSidebar.tsx | 48 ++ web/src/components/Memo.tsx | 28 +- web/src/components/MemosHeader.tsx | 62 -- web/src/components/MobileHeader.tsx | 62 ++ web/src/components/ResourcesDialog.tsx | 2 +- .../components/ResourcesSelectorDialog.tsx | 2 +- web/src/components/SearchBar.tsx | 2 +- web/src/components/Settings/SystemSection.tsx | 2 +- web/src/components/Sidebar.tsx | 93 --- web/src/components/TagList.tsx | 1 - web/src/components/UserBanner.tsx | 129 ++--- web/src/css/tailwind.css | 5 - web/src/less/auth.less | 2 +- web/src/less/explore.less | 53 -- web/src/less/{siderbar.less => header.less} | 4 +- web/src/less/home-sidebar.less | 15 + web/src/less/home.less | 20 +- web/src/less/memo-detail.less | 4 +- web/src/less/memos-header.less | 23 - web/src/less/search-bar.less | 2 +- web/src/locales/en.json | 16 +- web/src/locales/zh.json | 532 +++++++++--------- web/src/pages/Explore.tsx | 107 ++-- web/src/pages/Home.tsx | 23 +- 26 files changed, 610 insertions(+), 721 deletions(-) create mode 100644 web/src/components/Header.tsx create mode 100644 web/src/components/HomeSidebar.tsx delete mode 100644 web/src/components/MemosHeader.tsx create mode 100644 web/src/components/MobileHeader.tsx delete mode 100644 web/src/components/Sidebar.tsx delete mode 100644 web/src/less/explore.less rename web/src/less/{siderbar.less => header.less} (56%) create mode 100644 web/src/less/home-sidebar.less delete mode 100644 web/src/less/memos-header.less diff --git a/web/src/components/DailyReviewDialog.tsx b/web/src/components/DailyReviewDialog.tsx index b8acd07d..54b385fa 100644 --- a/web/src/components/DailyReviewDialog.tsx +++ b/web/src/components/DailyReviewDialog.tsx @@ -63,7 +63,7 @@ const DailyReviewDialog: React.FC = (props: Props) => { <>

toggleShowDatePicker()}> - 📅 {t("sidebar.daily-review")} + 📅 {t("common.daily-review")}

+ + {t("common.explore")} + + {!userStore.isVisitorMode() && ( + <> + + + + + )} +
+ + + ); +}; + +export const toggleHeader = (show?: boolean) => { + const headerEl = document.body.querySelector(".header-wrapper") as HTMLDivElement; + const maskEl = headerEl.previousSibling as HTMLDivElement; + + if (isUndefined(show)) { + show = !headerEl.classList.contains("show"); + } + + if (show) { + headerEl.classList.add("show"); + maskEl.classList.add("show"); + } else { + headerEl.classList.remove("show"); + maskEl.classList.remove("show"); + } +}; + +export default Header; diff --git a/web/src/components/HomeSidebar.tsx b/web/src/components/HomeSidebar.tsx new file mode 100644 index 00000000..f6be5fd2 --- /dev/null +++ b/web/src/components/HomeSidebar.tsx @@ -0,0 +1,48 @@ +import { isUndefined } from "lodash-es"; +import { useEffect } from "react"; +import { useLocationStore } from "../store/module"; +import ShortcutList from "./ShortcutList"; +import TagList from "./TagList"; +import SearchBar from "./SearchBar"; +import "../less/home-sidebar.less"; + +const HomeSidebar = () => { + const locationStore = useLocationStore(); + const query = locationStore.state.query; + + useEffect(() => { + toggleHomeSidebar(false); + }, [query]); + + return ( + <> +
toggleHomeSidebar(false)}>
+ + + ); +}; + +export const toggleHomeSidebar = (show?: boolean) => { + const sidebarEl = document.body.querySelector(".sidebar-wrapper") as HTMLDivElement; + const maskEl = sidebarEl.previousSibling as HTMLDivElement; + + if (isUndefined(show)) { + show = !sidebarEl.classList.contains("show"); + } + + if (show) { + sidebarEl.classList.add("show"); + maskEl.classList.add("show"); + } else { + sidebarEl.classList.remove("show"); + maskEl.classList.remove("show"); + } +}; + +export default HomeSidebar; diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index b75388dd..85a8a428 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -17,6 +17,7 @@ import "../less/memo.less"; interface Props { memo: Memo; + readonly?: boolean; } export const getFormatedMemoTimeStr = (time: number, locale = "en"): string => { @@ -28,7 +29,7 @@ export const getFormatedMemoTimeStr = (time: number, locale = "en"): string => { }; const Memo: React.FC = (props: Props) => { - const { memo } = props; + const { memo, readonly } = props; const { t, i18n } = useTranslation(); const navigate = useNavigate(); const editorStore = useEditorStore(); @@ -37,7 +38,7 @@ const Memo: React.FC = (props: Props) => { const memoStore = useMemoStore(); const [createdTimeStr, setCreatedTimeStr] = useState(getFormatedMemoTimeStr(memo.createdTs, i18n.language)); const memoContainerRef = useRef(null); - const isVisitorMode = userStore.isVisitorMode(); + const isVisitorMode = userStore.isVisitorMode() || readonly; const updatedTimeStr = getFormatedMemoTimeStr(memo.updatedTs, i18n.language); useEffect(() => { @@ -67,6 +68,10 @@ const Memo: React.FC = (props: Props) => { }; const handleTogglePinMemoBtnClick = async () => { + if (isVisitorMode) { + return; + } + try { if (memo.pinned) { await memoStore.unpinMemo(memo.id); @@ -79,10 +84,18 @@ const Memo: React.FC = (props: Props) => { }; const handleEditMemoClick = () => { + if (isVisitorMode) { + return; + } + editorStore.setEditMemoWithId(memo.id); }; const handleArchiveMemoClick = async () => { + if (isVisitorMode) { + return; + } + try { await memoStore.patchMemo({ id: memo.id, @@ -114,7 +127,7 @@ const Memo: React.FC = (props: Props) => { locationStore.setTagQuery(tagName); } } else if (targetEl.classList.contains("todo-block")) { - if (userStore.isVisitorMode()) { + if (isVisitorMode) { return; } @@ -153,6 +166,10 @@ const Memo: React.FC = (props: Props) => { }; const handleMemoContentDoubleClick = (e: React.MouseEvent) => { + if (isVisitorMode) { + return; + } + const loginUser = userStore.state.user; if (loginUser && !loginUser.localSetting.enableDoubleClickEditing) { return; @@ -191,6 +208,11 @@ const Memo: React.FC = (props: Props) => { {createdTimeStr} + {isVisitorMode && ( + + @{memo.creatorName} + + )} {memo.visibility !== "PRIVATE" && !isVisitorMode && ( { - const locationStore = useLocationStore(); - const memoStore = useMemoStore(); - const shortcutStore = useShortcutStore(); - const userStore = useUserStore(); - const user = userStore.state.user; - const query = locationStore.state.query; - const shortcuts = shortcutStore.state.shortcuts; - const [titleText, setTitleText] = useState("MEMOS"); - - useEffect(() => { - if (!query?.shortcutId) { - setTitleText("MEMOS"); - return; - } - - const shortcut = shortcutStore.getShortcutById(query?.shortcutId); - if (shortcut) { - setTitleText(shortcut.title); - } - }, [query, shortcuts]); - - const handleTitleTextClick = useCallback(() => { - const now = Date.now(); - if (now - prevRequestTimestamp > 1 * 1000) { - prevRequestTimestamp = now; - memoStore.fetchMemos().catch(() => { - // do nth - }); - } - }, []); - - return ( -
-
-
toggleSidebar(true)}> - -
- - {titleText} - - {user && ( - - - - )} -
- -
- ); -}; - -export default MemosHeader; diff --git a/web/src/components/MobileHeader.tsx b/web/src/components/MobileHeader.tsx new file mode 100644 index 00000000..d1936ebb --- /dev/null +++ b/web/src/components/MobileHeader.tsx @@ -0,0 +1,62 @@ +import { useCallback, useEffect, useState } from "react"; +import { useLocationStore, useMemoStore, useShortcutStore } from "../store/module"; +import Icon from "./Icon"; +import { toggleHeader } from "./Header"; +import { toggleHomeSidebar } from "./HomeSidebar"; + +let prevRequestTimestamp = Date.now(); + +const MobileHeader = () => { + const locationStore = useLocationStore(); + const memoStore = useMemoStore(); + const shortcutStore = useShortcutStore(); + const query = locationStore.state.query; + const shortcuts = shortcutStore.state.shortcuts; + const [titleText, setTitleText] = useState("MEMOS"); + + useEffect(() => { + if (!query?.shortcutId) { + setTitleText("MEMOS"); + return; + } + + const shortcut = shortcutStore.getShortcutById(query?.shortcutId); + if (shortcut) { + setTitleText(shortcut.title); + } + }, [query, shortcuts]); + + const handleTitleTextClick = useCallback(() => { + const now = Date.now(); + if (now - prevRequestTimestamp > 1 * 1000) { + prevRequestTimestamp = now; + memoStore.fetchMemos().catch(() => { + // do nth + }); + } + }, []); + + return ( +
+
+
toggleHeader(true)} + > + +
+ + {titleText} + +
+
+ toggleHomeSidebar(true)} /> +
+
+ ); +}; + +export default MobileHeader; diff --git a/web/src/components/ResourcesDialog.tsx b/web/src/components/ResourcesDialog.tsx index 919b060c..db608a85 100644 --- a/web/src/components/ResourcesDialog.tsx +++ b/web/src/components/ResourcesDialog.tsx @@ -104,7 +104,7 @@ const ResourcesDialog: React.FC = (props: Props) => { return ( <>
-

{t("sidebar.resources")}

+

{t("common.resources")}

diff --git a/web/src/components/ResourcesSelectorDialog.tsx b/web/src/components/ResourcesSelectorDialog.tsx index f9c6f377..460c8df6 100644 --- a/web/src/components/ResourcesSelectorDialog.tsx +++ b/web/src/components/ResourcesSelectorDialog.tsx @@ -79,7 +79,7 @@ const ResourcesSelectorDialog: React.FC = (props: Props) => { return ( <>
-

{t("sidebar.resources")}

+

{t("common.resources")}

diff --git a/web/src/components/SearchBar.tsx b/web/src/components/SearchBar.tsx index 2c99600b..a78c7b94 100644 --- a/web/src/components/SearchBar.tsx +++ b/web/src/components/SearchBar.tsx @@ -86,7 +86,7 @@ const SearchBar = () => { onBlur={handleBlur} />
-
+

{t("search.quickly-filter").toUpperCase()}

diff --git a/web/src/components/Settings/SystemSection.tsx b/web/src/components/Settings/SystemSection.tsx index 51561597..81d3bb25 100644 --- a/web/src/components/Settings/SystemSection.tsx +++ b/web/src/components/Settings/SystemSection.tsx @@ -148,7 +148,7 @@ const SystemSection = () => {
-

{t("sidebar.setting")}

+

{t("common.settings")}

{t("setting.system-section.allow-user-signup")} handleAllowSignUpChanged(event.target.checked)} /> diff --git a/web/src/components/Sidebar.tsx b/web/src/components/Sidebar.tsx deleted file mode 100644 index 000503ab..00000000 --- a/web/src/components/Sidebar.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { isUndefined } from "lodash-es"; -import { useEffect } from "react"; -import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import { useLocationStore, useUserStore } from "../store/module"; -import showDailyReviewDialog from "./DailyReviewDialog"; -import showResourcesDialog from "./ResourcesDialog"; -import showSettingDialog from "./SettingDialog"; -import UserBanner from "./UserBanner"; -import UsageHeatMap from "./UsageHeatMap"; -import ShortcutList from "./ShortcutList"; -import TagList from "./TagList"; -import "../less/siderbar.less"; - -const Sidebar = () => { - const { t } = useTranslation(); - const userStore = useUserStore(); - const locationStore = useLocationStore(); - const query = locationStore.state.query; - - useEffect(() => { - toggleSidebar(false); - }, [query]); - - const handleSettingBtnClick = () => { - showSettingDialog(); - }; - - return ( - <> -
toggleSidebar(false)}>
- - - ); -}; - -export const toggleSidebar = (show?: boolean) => { - const sidebarEl = document.body.querySelector(".sidebar-wrapper") as HTMLDivElement; - const maskEl = document.body.querySelector(".mask") as HTMLDivElement; - - if (isUndefined(show)) { - show = !sidebarEl.classList.contains("show"); - } - - if (show) { - sidebarEl.classList.add("show"); - maskEl.classList.add("show"); - } else { - sidebarEl.classList.remove("show"); - maskEl.classList.remove("show"); - } -}; - -export default Sidebar; diff --git a/web/src/components/TagList.tsx b/web/src/components/TagList.tsx index 6c6373dc..c1ef38fb 100644 --- a/web/src/components/TagList.tsx +++ b/web/src/components/TagList.tsx @@ -83,7 +83,6 @@ const TagList = () => { {tags.map((t, idx) => ( ))} - {tags.length <= 3 &&

{t("tag-list.tip-text")}

}
); diff --git a/web/src/components/UserBanner.tsx b/web/src/components/UserBanner.tsx index b9b4d212..a4653782 100644 --- a/web/src/components/UserBanner.tsx +++ b/web/src/components/UserBanner.tsx @@ -1,10 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useMemoStore, useTagStore, useUserStore } from "../store/module"; -import { getMemoStats } from "../helpers/api"; -import * as utils from "../helpers/utils"; +import { useUserStore } from "../store/module"; import Dropdown from "./common/Dropdown"; -import showArchivedMemoDialog from "./ArchivedMemoDialog"; import showAboutSiteDialog from "./AboutSiteDialog"; import UserAvatar from "./UserAvatar"; import showSettingDialog from "./SettingDialog"; @@ -12,14 +9,8 @@ import showSettingDialog from "./SettingDialog"; const UserBanner = () => { const { t } = useTranslation(); const userStore = useUserStore(); - const memoStore = useMemoStore(); - const tagStore = useTagStore(); const { user, owner } = userStore.state; - const { memos } = memoStore.state; - const tags = tagStore.state.tags; const [username, setUsername] = useState("Memos"); - const [memoAmount, setMemoAmount] = useState(0); - const [createdDays, setCreatedDays] = useState(0); const isVisitorMode = userStore.isVisitorMode(); useEffect(() => { @@ -28,31 +19,15 @@ const UserBanner = () => { return; } setUsername(owner.nickname || owner.username); - setCreatedDays(Math.ceil((Date.now() - utils.getTimeStampByDate(owner.createdTs)) / 1000 / 3600 / 24)); } else if (user) { setUsername(user.nickname || user.username); - setCreatedDays(Math.ceil((Date.now() - utils.getTimeStampByDate(user.createdTs)) / 1000 / 3600 / 24)); } }, [isVisitorMode, user, owner]); - useEffect(() => { - getMemoStats(userStore.getCurrentUserId()) - .then(({ data: { data } }) => { - setMemoAmount(data.length); - }) - .catch((error) => { - console.error(error); - }); - }, [memos]); - const handleMyAccountClick = () => { showSettingDialog("my-account"); }; - const handleArchivedBtnClick = () => { - showArchivedMemoDialog(); - }; - const handleAboutBtnClick = () => { showAboutSiteDialog(); }; @@ -63,72 +38,50 @@ const UserBanner = () => { }; return ( - <> -
- - - {username} - {!isVisitorMode && user?.role === "HOST" ? ( - MOD - ) : null} -
- } - actionsClassName="min-w-[128px] max-w-full" - positionClassName="top-full mt-2" - actions={ - <> - {!userStore.isVisitorMode() && ( - <> - - - - )} - - {!userStore.isVisitorMode() && ( +
+ + + {username} + {!isVisitorMode && user?.role === "HOST" ? ( + MOD + ) : null} +
+ } + actionsClassName="min-w-[128px] max-w-full" + positionClassName="top-full mt-2" + actions={ + <> + {!userStore.isVisitorMode() && ( + <> - )} - - } - /> -
-
-
- {memoAmount} - {t("amount-text.memo", { count: memoAmount })} -
-
- {tags.length} - {t("amount-text.tag", { count: tags.length })} -
-
- {createdDays} - {t("amount-text.day", { count: createdDays })} -
-
- + + )} + + {!userStore.isVisitorMode() && ( + + )} + + } + /> +
); }; diff --git a/web/src/css/tailwind.css b/web/src/css/tailwind.css index 9f63d6e3..c2b773f2 100644 --- a/web/src/css/tailwind.css +++ b/web/src/css/tailwind.css @@ -17,11 +17,6 @@ overflow-wrap: anywhere; word-break: normal; } - @media screen and (min-width: 1024px) { - .ml-calc { - margin-left: calc(100vw - 100%); - } - } } @layer components { diff --git a/web/src/less/auth.less b/web/src/less/auth.less index ed1bb0e4..92856be0 100644 --- a/web/src/less/auth.less +++ b/web/src/less/auth.less @@ -2,7 +2,7 @@ @apply flex flex-row justify-center items-center w-full h-full dark:bg-zinc-800; > .page-container { - @apply w-80 max-w-full h-full py-4 flex flex-col justify-start items-center ml-calc; + @apply w-80 max-w-full h-full py-4 flex flex-col justify-start items-center; > .auth-form-wrapper { @apply w-full py-4 grow flex flex-col justify-center items-center; diff --git a/web/src/less/explore.less b/web/src/less/explore.less deleted file mode 100644 index 9e174199..00000000 --- a/web/src/less/explore.less +++ /dev/null @@ -1,53 +0,0 @@ -.page-wrapper.explore { - @apply w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800; - - > .page-container { - @apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8; - - > .page-header { - @apply sticky top-0 z-10 max-w-2xl w-full h-auto flex flex-row justify-between backdrop-blur-sm items-center px-4 sm:pr-6 pt-6 mb-2 ml-calc; - - > .title-container { - @apply flex flex-row justify-start items-center; - - > .logo-img { - @apply h-12 w-auto rounded-md mr-2; - } - - > .title-text { - @apply text-xl sm:text-4xl text-gray-700 dark:text-gray-200; - } - } - } - - > .memos-wrapper { - @apply relative flex-grow max-w-2xl w-full h-auto flex flex-col justify-start items-start px-4 sm:pr-6 ml-calc; - - > .memo-container { - @apply relative flex flex-col justify-start items-start w-full p-4 mt-2 bg-white dark:bg-zinc-700 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-600; - - &.pinned { - @apply border-gray-200 border-2 dark:border-zinc-600; - } - - > .corner-container { - @apply absolute top-0 right-0 z-1; - - &::after { - @apply rounded-tr-md absolute top-0 right-0 border-transparent border-t-green-600 border-r-green-600; - content: ""; - border-width: 6px; - } - } - - > .memo-header { - @apply mb-2 w-full flex flex-row justify-start items-center text-sm text-gray-400; - - > .name-text { - @apply ml-2 hover:text-green-600 hover:underline; - } - } - } - } - } -} diff --git a/web/src/less/siderbar.less b/web/src/less/header.less similarity index 56% rename from web/src/less/siderbar.less rename to web/src/less/header.less index 08efe92d..2aed9164 100644 --- a/web/src/less/siderbar.less +++ b/web/src/less/header.less @@ -1,5 +1,5 @@ -.sidebar-wrapper { - @apply fixed sm:sticky top-0 z-30 sm:z-0 -translate-x-64 sm:translate-x-0 sm:flex flex-col justify-start items-start w-64 h-auto max-h-screen py-4 pl-2 bg-white dark:bg-zinc-800 sm:bg-transparent overflow-x-hidden overflow-y-auto transition-transform duration-300 overscroll-contain hide-scrollbar; +.header-wrapper { + @apply fixed sm:sticky top-0 z-30 sm:z-0 -translate-x-64 sm:translate-x-0 sm:flex flex-col justify-start items-start w-56 h-full py-4 pl-2 bg-white dark:bg-zinc-800 sm:bg-transparent overflow-x-hidden overflow-y-auto transition-transform duration-300 overscroll-contain hide-scrollbar; &.show { @apply translate-x-0 shadow-2xl sm:shadow-none; diff --git a/web/src/less/home-sidebar.less b/web/src/less/home-sidebar.less new file mode 100644 index 00000000..86412472 --- /dev/null +++ b/web/src/less/home-sidebar.less @@ -0,0 +1,15 @@ +.sidebar-wrapper { + @apply flex-shrink-0 fixed sm:sticky top-0 z-30 sm:z-0 translate-x-56 sm:translate-x-0 hidden md:flex flex-col justify-start items-start w-56 h-full py-4 bg-white dark:bg-zinc-800 sm:bg-transparent overflow-x-hidden overflow-y-auto transition-transform duration-300 overscroll-contain hide-scrollbar; + + &.show { + @apply flex translate-x-0 right-0 shadow-2xl sm:shadow-none; + } +} + +.mask { + @apply fixed top-0 left-0 w-full h-full bg-black opacity-0 transition-opacity duration-300 pointer-events-none z-20 sm:hidden; + + &.show { + @apply opacity-60 pointer-events-auto; + } +} diff --git a/web/src/less/home.less b/web/src/less/home.less index 42371f14..8a5eb3a4 100644 --- a/web/src/less/home.less +++ b/web/src/less/home.less @@ -1,5 +1,5 @@ .page-wrapper.home { - @apply w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800; + @apply w-full h-full overflow-y-auto bg-zinc-100 dark:bg-zinc-800; > .banner-wrapper { @apply w-full flex flex-col justify-start items-center; @@ -8,28 +8,16 @@ > .page-container { @apply relative w-full h-auto mx-auto flex flex-row justify-start sm:justify-center items-start; - > .sidebar-wrapper { - @apply flex-shrink-0 h-full ml-calc; + > .header-wrapper { + @apply flex-shrink-0 h-full; } > .memos-wrapper { - @apply relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4 sm:pr-6; + @apply relative flex-grow max-w-2xl w-full h-auto flex flex-col justify-start items-start px-2 sm:pt-4; > .memos-editor-wrapper { @apply w-full h-auto flex flex-col justify-start items-start bg-zinc-100 dark:bg-zinc-800 rounded-lg; } - - > .addition-btn-container { - @apply fixed bottom-12 left-1/2 -translate-x-1/2; - - > .btn { - @apply bg-blue-600 dark:bg-blue-800 text-white dark:text-gray-200 px-4 py-2 rounded-3xl shadow-2xl hover:opacity-80; - - > .icon { - @apply text-lg mr-1; - } - } - } } } } diff --git a/web/src/less/memo-detail.less b/web/src/less/memo-detail.less index c6038ec8..05ea3f8e 100644 --- a/web/src/less/memo-detail.less +++ b/web/src/less/memo-detail.less @@ -5,7 +5,7 @@ @apply relative w-full min-h-full mx-auto flex flex-col justify-start items-center pb-8; > .page-header { - @apply sticky top-0 z-10 max-w-2xl w-full min-h-full flex flex-row justify-between items-center px-4 pt-6 mb-2 bg-zinc-100 dark:bg-zinc-800 ml-calc; + @apply sticky top-0 z-10 max-w-2xl w-full min-h-full flex flex-row justify-between items-center px-4 pt-6 mb-2 bg-zinc-100 dark:bg-zinc-800; > .title-container { @apply flex flex-row justify-start items-center; @@ -35,7 +35,7 @@ } > .memos-wrapper { - @apply relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4 ml-calc; + @apply relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4; > .memo-container { @apply flex flex-col justify-start items-start w-full p-4 mt-2 bg-white dark:bg-zinc-700 rounded-lg border border-white dark:border-zinc-800 hover:border-gray-200 dark:hover:border-zinc-700; diff --git a/web/src/less/memos-header.less b/web/src/less/memos-header.less deleted file mode 100644 index 66b61844..00000000 --- a/web/src/less/memos-header.less +++ /dev/null @@ -1,23 +0,0 @@ -.memos-header-container { - @apply sticky top-0 pt-4 pb-1 mb-1 backdrop-blur-sm flex flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-10; - - > .title-container { - @apply flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden; - - > .action-btn { - @apply flex sm:hidden flex-row justify-center items-center w-6 h-6 mr-1 shrink-0 bg-transparent; - - > .icon-img { - @apply w-5 h-auto dark:text-gray-200; - } - } - - > .title-text { - @apply font-bold text-lg leading-10 mr-1 text-ellipsis shrink-0 cursor-pointer overflow-hidden text-gray-700 dark:text-gray-200; - } - } - - > .btns-container { - @apply flex flex-row justify-end items-center; - } -} diff --git a/web/src/less/search-bar.less b/web/src/less/search-bar.less index 0c350951..ad613b68 100644 --- a/web/src/less/search-bar.less +++ b/web/src/less/search-bar.less @@ -1,5 +1,5 @@ .search-bar-container { - @apply relative w-auto; + @apply relative w-full; &:hover, &:active { diff --git a/web/src/locales/en.json b/web/src/locales/en.json index c245ae74..92753489 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -1,15 +1,17 @@ { "common": { "about": "About", + "home": "Home", + "resources": "Resources", + "settings": "Settings", + "daily-review": "Daily Review", "email": "Email", "password": "Password", - "repeat-password-short": "Repeat", - "repeat-password": "Repeat the password", - "new-password": "New password", - "repeat-new-password": "Repeat the new password", "avatar": "Avatar", "username": "Username", "nickname": "Nickname", + "new-password": "New password", + "repeat-new-password": "Repeat the new password", "save": "Save", "close": "Close", "cancel": "Cancel", @@ -57,12 +59,6 @@ "host-tip": "You are registering as the Site Host.", "not-host-tip": "If you don't have an account, please contact the site host." }, - "sidebar": { - "daily-review": "Daily Review", - "resources": "Resources", - "setting": "Settings", - "archived": "Archived" - }, "daily-review": { "oops-nothing": "Oops, there is nothing." }, diff --git a/web/src/locales/zh.json b/web/src/locales/zh.json index 0cefa9ab..5aa94230 100644 --- a/web/src/locales/zh.json +++ b/web/src/locales/zh.json @@ -1,271 +1,267 @@ { - "common": { - "about": "关于", - "email": "邮箱", - "password": "密码", - "repeat-password-short": "重复密码", - "repeat-password": "重复密码", - "new-password": "新密码", - "repeat-new-password": "重复新密码", - "username": "用户名", - "nickname": "昵称", - "save": "保存", - "close": "关闭", - "cancel": "退出", - "create": "创建", - "change": "修改", - "confirm": "确定", - "reset": "重置", - "language": "语言", - "version": "版本", - "pin": "置顶", - "unpin": "取消置顶", - "edit": "编辑", - "restore": "恢复", - "delete": "删除", - "null": "空", - "share": "分享", - "archive": "归档", - "basic": "基础", - "admin": "管理员", - "explore": "探索", - "sign-in": "登录", - "sign-up": "注册", - "sign-out": "退出登录", - "back-to-home": "回到主页", - "type": "类型", - "shortcuts": "捷径", - "title": "标题", - "filter": "过滤器", - "tags": "全部标签", - "yourself": "你自己", - "archived-at": "归档于", - "changed": "已更改", - "update-on": "更新于", - "fold": "折叠", - "expand": "展开", - "image": "图片", - "link": "链接", - "vacuum": "清理", - "select": "选择", - "database": "数据库", - "avatar": "头像" - }, - "slogan": "一个可用于知识管理和社交网络的开源、自托管的备忘录中心。", - "auth": { - "signup-as-host": "注册为 Host", - "host-tip": "你正在注册为 Host 用户账号。", - "not-host-tip": "如果你没有账号,请联系站点 Host" - }, - "sidebar": { - "daily-review": "每日回顾", - "resources": "资源库", - "setting": "设置", - "archived": "已归档" - }, - "daily-review": { - "oops-nothing": "啊哦,空空荡荡。" - }, - "resources": { - "description": "查看在 Memo 中的静态资源。例如:图片", - "no-resources": "没有资源", - "fetching-data": "请求数据中...", - "upload": "上传", - "preview": "预览", - "copy-link": "拷贝链接", - "delete-resource": "删除资源", - "warning-text": "确定删除这个资源么?此操作不可逆❗", - "linked-amount": "链接的 Memo 数量", - "rename": "重命名", - "clear": "清除", - "warning-text-unused": "确定删除这些无用资源么?此操作不可逆❗", - "no-unused-resources": "无可删除的资源", - "name": "资源名称" - }, - "archived": { - "archived-memos": "已归档的 Memo", - "no-archived-memos": "没有归档的 Memo", - "fetching-data": "请求数据中..." - }, - "editor": { - "editing": "编辑中...", - "cancel-edit": "退出编辑", - "save": "记下", - "placeholder": "现在的想法是...", - "only-image-supported": "仅支持图片文件。", - "cant-empty": "内容不能为空", - "local": "本地上传", - "resources": "资源库" - }, - "memo": { - "view-detail": "查看详情", - "copy": "复制", - "visibility": { - "private": "仅自己可见", - "protected": "登录用户可见", - "public": "所有人可见", - "disabled": "公共memos已禁用" - } - }, - "memo-list": { - "fetching-data": "请求数据中...", - "fetch-more": "点击加载更多" - }, - "shortcut-list": { - "shortcut-title": "捷径标题", - "create-shortcut": "创建捷径", - "edit-shortcut": "编辑捷径", - "eligible-memo": "符合条件的 Memo", - "fill-previous": "请填写之前的过滤值", - "title-required": "标题是必填项", - "value-required": "过滤值是必填项" - }, - "filter": { - "new-filter": "新建过滤器", - "type": { - "tag": "标签", - "type": "类型", - "text": "文本", - "display-time": "显示时间", - "visibility": "可见性" - }, - "operator": { - "contains": "包含", - "not-contains": "不包含", - "is": "是", - "is-not": "不是", - "before": "早于", - "after": "晚于" - }, - "value": { - "not-tagged": "无标签", - "linked": "包含链接" - }, - "text-placeholder": "以 ^ 开头使用正则表达式" - }, - "tag-list": { - "tip-text": "输入`#tag `来创建标签" - }, - "search": { - "quickly-filter": "快速过滤" - }, - "setting": { - "my-account": "我的账号", - "preference": "偏好设置", - "storage": "存储设置", - "member": "成员", - "member-list": "成员列表", - "system": "系统", - "account-section": { - "title": "账号信息", - "update-information": "更新个人信息", - "change-password": "修改密码" - }, - "preference-section": { - "theme": "主题", - "default-memo-visibility": "默认 Memo 可见性", - "enable-folding-memo": "开启折叠 Memo", - "enable-double-click": "开启双击编辑", - "editor-font-style": "编辑器字体样式", - "mobile-editor-style": "移动端编辑器样式", - "default-memo-sort-option": "Memo 显示时间", - "created_ts": "创建时间", - "updated_ts": "更新时间" - }, - "storage-section": { - "storage-services-list": "存储服务列表", - "create-a-service": "新建服务", - "update-a-service": "更新服务", - "delete-storage": "删除存储服务", - "warning-text": "确定删除这个存储服务么?此操作不可逆❗" - }, - "member-section": { - "create-a-member": "创建成员" - }, - "system-section": { - "server-name": "服务名称", - "customize-server": { - "title": "自定义服务", - "default": "默认为 memos", - "icon-url": "图标 URL" - }, - "database-file-size": "数据库文件大小", - "allow-user-signup": "允许用户注册", - "additional-style": "自定义样式", - "additional-script": "自定义脚本", - "additional-style-placeholder": "自定义 CSS 代码", - "additional-script-placeholder": "自定义 JavaScript 代码", - "disable-public-memos": "禁用公共memos" - }, - "apperance-option": { - "system": "跟随系统", - "light": "总是浅色", - "dark": "总是深色" - }, - "sso": "SSO" - }, - "amount-text": { - "memo_other": "MEMOS", - "tag_other": "TAGS", - "day_other": "DAYS", - "memo_one": "MEMO", - "tag_one": "TAG", - "day_one": "DAY" - }, - "message": { - "no-memos": "没有 Memo 了 🌃", - "memos-ready": "所有 Memo 已就绪 🎉", - "restored-successfully": "恢复成功", - "memo-updated-datetime": "Memo 创建日期时间已更改。", - "invalid-created-datetime": "创建的日期时间无效。", - "change-memo-created-time": "更改 Memo 创建时间", - "memo-not-found": "找不到 Memo。", - "fill-all": "请填写所有字段。", - "password-not-match": "密码不一致。", - "new-password-not-match": "新密码不匹配。", - "image-load-failed": "图片加载失败", - "fill-form": "请填写此表单", - "login-failed": "登录失败", - "signup-failed": "注册失败", - "user-not-found": "未找到用户", - "password-changed": "密码已修改", - "private-only": "此 Memo 仅自己可见", - "copied": "已复制", - "succeed-copy-content": "复制内容到剪贴板成功。", - "succeed-copy-code": "复制代码到剪贴板成功。", - "succeed-copy-link": "复制链接到剪贴板成功。", - "change-resource-filename": "更改资源文件名", - "resource-filename-updated": "资源文件名更改成功。", - "invalid-resource-filename": "无效的资源文件名", - "click-to-save-the-image": "点击保存图片", - "generating-the-screenshot": "正在生成图片...", - "count-selected-resources": "所选资源总数", - "too-short": "过短", - "too-long": "过长", - "not-allow-space": "不允许包含空格", - "not-allow-chinese": "不允许包含中文", - "succeed-update-additional-style": "更新附加样式成功", - "succeed-copy-resource-link": "复制资源链接到剪贴板成功", - "succeed-update-customized-profile": "更新自定义配置文件成功", - "succeed-update-additional-script": "更新附加脚本成功", - "update-succeed": "更新成功", - "page-not-found": "404 - 未找到网页 😥", - "succeed-vacuum-database": "清理数据库成功" - }, - "days": { - "monday": "星期一", - "mon": "一", - "tuesday": "星期二", - "tue": "二", - "wednesday": "星期三", - "wed": "三", - "thursday": "星期四", - "thu": "四", - "friday": "星期五", - "fri": "五", - "saturday": "星期六", - "sat": "六", - "sunday": "星期天", - "sun": "日" + "common": { + "about": "关于", + "home": "主页", + "resources": "资源库", + "settings": "设置", + "daily-review": "每日回顾", + "email": "邮箱", + "password": "密码", + "username": "用户名", + "nickname": "昵称", + "new-password": "新密码", + "repeat-new-password": "重复新密码", + "save": "保存", + "close": "关闭", + "cancel": "退出", + "create": "创建", + "change": "修改", + "confirm": "确定", + "reset": "重置", + "language": "语言", + "version": "版本", + "pin": "置顶", + "unpin": "取消置顶", + "edit": "编辑", + "restore": "恢复", + "delete": "删除", + "null": "空", + "share": "分享", + "archive": "归档", + "basic": "基础", + "admin": "管理员", + "explore": "探索", + "sign-in": "登录", + "sign-up": "注册", + "sign-out": "退出登录", + "back-to-home": "回到主页", + "type": "类型", + "shortcuts": "捷径", + "title": "标题", + "filter": "过滤器", + "tags": "全部标签", + "yourself": "你自己", + "archived-at": "归档于", + "changed": "已更改", + "update-on": "更新于", + "fold": "折叠", + "expand": "展开", + "image": "图片", + "link": "链接", + "vacuum": "清理", + "select": "选择", + "database": "数据库", + "avatar": "头像" + }, + "slogan": "一个可用于知识管理和社交网络的开源、自托管的备忘录中心。", + "auth": { + "signup-as-host": "注册为 Host", + "host-tip": "你正在注册为 Host 用户账号。", + "not-host-tip": "如果你没有账号,请联系站点 Host" + }, + "daily-review": { + "oops-nothing": "啊哦,空空荡荡。" + }, + "resources": { + "description": "查看在 Memo 中的静态资源。例如:图片", + "no-resources": "没有资源", + "fetching-data": "请求数据中...", + "upload": "上传", + "preview": "预览", + "copy-link": "拷贝链接", + "delete-resource": "删除资源", + "warning-text": "确定删除这个资源么?此操作不可逆❗", + "linked-amount": "链接的 Memo 数量", + "rename": "重命名", + "clear": "清除", + "warning-text-unused": "确定删除这些无用资源么?此操作不可逆❗", + "no-unused-resources": "无可删除的资源", + "name": "资源名称" + }, + "archived": { + "archived-memos": "已归档的 Memo", + "no-archived-memos": "没有归档的 Memo", + "fetching-data": "请求数据中..." + }, + "editor": { + "editing": "编辑中...", + "cancel-edit": "退出编辑", + "save": "记下", + "placeholder": "现在的想法是...", + "only-image-supported": "仅支持图片文件。", + "cant-empty": "内容不能为空", + "local": "本地上传", + "resources": "资源库" + }, + "memo": { + "view-detail": "查看详情", + "copy": "复制", + "visibility": { + "private": "仅自己可见", + "protected": "登录用户可见", + "public": "所有人可见", + "disabled": "公共memos已禁用" } + }, + "memo-list": { + "fetching-data": "请求数据中...", + "fetch-more": "点击加载更多" + }, + "shortcut-list": { + "shortcut-title": "捷径标题", + "create-shortcut": "创建捷径", + "edit-shortcut": "编辑捷径", + "eligible-memo": "符合条件的 Memo", + "fill-previous": "请填写之前的过滤值", + "title-required": "标题是必填项", + "value-required": "过滤值是必填项" + }, + "filter": { + "new-filter": "新建过滤器", + "type": { + "tag": "标签", + "type": "类型", + "text": "文本", + "display-time": "显示时间", + "visibility": "可见性" + }, + "operator": { + "contains": "包含", + "not-contains": "不包含", + "is": "是", + "is-not": "不是", + "before": "早于", + "after": "晚于" + }, + "value": { + "not-tagged": "无标签", + "linked": "包含链接" + }, + "text-placeholder": "以 ^ 开头使用正则表达式" + }, + "tag-list": { + "tip-text": "输入`#tag `来创建标签" + }, + "search": { + "quickly-filter": "快速过滤" + }, + "setting": { + "my-account": "我的账号", + "preference": "偏好设置", + "storage": "存储设置", + "member": "成员", + "member-list": "成员列表", + "system": "系统", + "account-section": { + "title": "账号信息", + "update-information": "更新个人信息", + "change-password": "修改密码" + }, + "preference-section": { + "theme": "主题", + "default-memo-visibility": "默认 Memo 可见性", + "enable-folding-memo": "开启折叠 Memo", + "enable-double-click": "开启双击编辑", + "editor-font-style": "编辑器字体样式", + "mobile-editor-style": "移动端编辑器样式", + "default-memo-sort-option": "Memo 显示时间", + "created_ts": "创建时间", + "updated_ts": "更新时间" + }, + "storage-section": { + "storage-services-list": "存储服务列表", + "create-a-service": "新建服务", + "update-a-service": "更新服务", + "delete-storage": "删除存储服务", + "warning-text": "确定删除这个存储服务么?此操作不可逆❗" + }, + "member-section": { + "create-a-member": "创建成员" + }, + "system-section": { + "server-name": "服务名称", + "customize-server": { + "title": "自定义服务", + "default": "默认为 memos", + "icon-url": "图标 URL" + }, + "database-file-size": "数据库文件大小", + "allow-user-signup": "允许用户注册", + "additional-style": "自定义样式", + "additional-script": "自定义脚本", + "additional-style-placeholder": "自定义 CSS 代码", + "additional-script-placeholder": "自定义 JavaScript 代码", + "disable-public-memos": "禁用公共memos" + }, + "apperance-option": { + "system": "跟随系统", + "light": "总是浅色", + "dark": "总是深色" + }, + "sso": "SSO" + }, + "amount-text": { + "memo_other": "MEMOS", + "tag_other": "TAGS", + "day_other": "DAYS", + "memo_one": "MEMO", + "tag_one": "TAG", + "day_one": "DAY" + }, + "message": { + "no-memos": "没有 Memo 了 🌃", + "memos-ready": "所有 Memo 已就绪 🎉", + "restored-successfully": "恢复成功", + "memo-updated-datetime": "Memo 创建日期时间已更改。", + "invalid-created-datetime": "创建的日期时间无效。", + "change-memo-created-time": "更改 Memo 创建时间", + "memo-not-found": "找不到 Memo。", + "fill-all": "请填写所有字段。", + "password-not-match": "密码不一致。", + "new-password-not-match": "新密码不匹配。", + "image-load-failed": "图片加载失败", + "fill-form": "请填写此表单", + "login-failed": "登录失败", + "signup-failed": "注册失败", + "user-not-found": "未找到用户", + "password-changed": "密码已修改", + "private-only": "此 Memo 仅自己可见", + "copied": "已复制", + "succeed-copy-content": "复制内容到剪贴板成功。", + "succeed-copy-code": "复制代码到剪贴板成功。", + "succeed-copy-link": "复制链接到剪贴板成功。", + "change-resource-filename": "更改资源文件名", + "resource-filename-updated": "资源文件名更改成功。", + "invalid-resource-filename": "无效的资源文件名", + "click-to-save-the-image": "点击保存图片", + "generating-the-screenshot": "正在生成图片...", + "count-selected-resources": "所选资源总数", + "too-short": "过短", + "too-long": "过长", + "not-allow-space": "不允许包含空格", + "not-allow-chinese": "不允许包含中文", + "succeed-update-additional-style": "更新附加样式成功", + "succeed-copy-resource-link": "复制资源链接到剪贴板成功", + "succeed-update-customized-profile": "更新自定义配置文件成功", + "succeed-update-additional-script": "更新附加脚本成功", + "update-succeed": "更新成功", + "page-not-found": "404 - 未找到网页 😥", + "succeed-vacuum-database": "清理数据库成功" + }, + "days": { + "monday": "星期一", + "mon": "一", + "tuesday": "星期二", + "tue": "二", + "wednesday": "星期三", + "wed": "三", + "thursday": "星期四", + "thu": "四", + "friday": "星期五", + "fri": "五", + "saturday": "星期六", + "sat": "六", + "sunday": "星期天", + "sun": "日" + } } diff --git a/web/src/pages/Explore.tsx b/web/src/pages/Explore.tsx index bdb19097..f23a7a67 100644 --- a/web/src/pages/Explore.tsx +++ b/web/src/pages/Explore.tsx @@ -1,24 +1,21 @@ -import dayjs from "dayjs"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useGlobalStore, useLocationStore, useMemoStore, useUserStore } from "../store/module"; +import { TAG_REG } from "../labs/marked/parser"; import { DEFAULT_MEMO_LIMIT } from "../helpers/consts"; import useLoading from "../hooks/useLoading"; import toastHelper from "../components/Toast"; -import MemoContent from "../components/MemoContent"; -import MemoResources from "../components/MemoResources"; -import MemoFilter from "../components/MemoFilter"; import Icon from "../components/Icon"; -import { TAG_REG } from "../labs/marked/parser"; -import "../less/explore.less"; +import MemoFilter from "../components/MemoFilter"; +import Memo from "../components/Memo"; interface State { memos: Memo[]; } const Explore = () => { - const { t, i18n } = useTranslation(); + const { t } = useTranslation(); const navigate = useNavigate(); const globalStore = useGlobalStore(); const locationStore = useLocationStore(); @@ -91,20 +88,6 @@ const Explore = () => { } }; - const handleMemoContentClick = async (e: React.MouseEvent) => { - const targetEl = e.target as HTMLElement; - - if (targetEl.className === "tag-span") { - const tagName = targetEl.innerText.slice(1); - const currTagQuery = locationStore.getState().query?.tag; - if (currTagQuery === tagName) { - locationStore.setTagQuery(undefined); - } else { - locationStore.setTagQuery(tagName); - } - } - }; - const handleTitleClick = () => { if (user) { navigate("/"); @@ -114,58 +97,40 @@ const Explore = () => { }; return ( -
-
-
-
- - {customizedProfile.name} -
-
- - - -
+
+
+
+ + {customizedProfile.name} +
+ - {!loadingState.isLoading && ( -
- - {sortedMemos.map((memo) => { - const createdAtStr = dayjs(memo.createdTs).locale(i18n.language).format("YYYY/MM/DD HH:mm:ss"); - return ( -
- {memo.pinned &&
} -
- {createdAtStr} - - @{memo.creatorName} - -
- - -
- ); - })} - {isComplete ? ( - state.memos.length === 0 ? ( -

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

- ) : null - ) : ( -

- {t("memo-list.fetch-more")} -

- )} -
- )}
+ {!loadingState.isLoading && ( +
+ + {sortedMemos.map((memo) => { + return ; + })} + {isComplete ? ( + state.memos.length === 0 ? ( +

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

+ ) : null + ) : ( +

+ {t("memo-list.fetch-more")} +

+ )} +
+ )}
); }; diff --git a/web/src/pages/Home.tsx b/web/src/pages/Home.tsx index 567feece..1be40dc1 100644 --- a/web/src/pages/Home.tsx +++ b/web/src/pages/Home.tsx @@ -3,12 +3,13 @@ import { useTranslation } from "react-i18next"; import { useLocation } from "react-router-dom"; import { useGlobalStore, useUserStore } from "../store/module"; import toastHelper from "../components/Toast"; -import Sidebar from "../components/Sidebar"; -import MemosHeader from "../components/MemosHeader"; +import Header from "../components/Header"; import MemoEditor from "../components/MemoEditor"; import MemoFilter from "../components/MemoFilter"; import MemoList from "../components/MemoList"; import UpdateVersionBanner from "../components/UpdateVersionBanner"; +import MobileHeader from "../components/MobileHeader"; +import HomeSidebar from "../components/HomeSidebar"; import "../less/home.less"; function Home() { @@ -40,28 +41,16 @@ function Home() {
- +
- +
{!userStore.isVisitorMode() && }
- {userStore.isVisitorMode() && ( -
- {user ? ( - - ) : ( - - )} -
- )}
+ {!userStore.isVisitorMode() && }
);