mirror of
https://github.com/usememos/memos.git
synced 2024-12-26 23:22:47 +08:00
feat: implement part of full-screen layout
This commit is contained in:
parent
15a091fe4c
commit
d6656db20d
21 changed files with 116 additions and 255 deletions
|
@ -12,8 +12,8 @@ import { useUserV1Store } from "./store/v1";
|
|||
const App = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const navigateTo = useNavigateTo();
|
||||
const globalStore = useGlobalStore();
|
||||
const { mode, setMode } = useColorScheme();
|
||||
const globalStore = useGlobalStore();
|
||||
const userV1Store = useUserV1Store();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { appearance, locale, systemStatus } = globalStore.state;
|
||||
|
@ -30,7 +30,7 @@ const App = () => {
|
|||
try {
|
||||
await userV1Store.fetchCurrentUser();
|
||||
} catch (error) {
|
||||
// Skip.
|
||||
// Do nothing.
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
@ -75,8 +75,8 @@ const App = () => {
|
|||
}
|
||||
}, [systemStatus.additionalScript]);
|
||||
|
||||
// Dynamic update metadata with customized profile.
|
||||
useEffect(() => {
|
||||
// dynamic update metadata with customized profile.
|
||||
document.title = systemStatus.customizedProfile.name;
|
||||
const link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
|
||||
link.href = systemStatus.customizedProfile.logoUrl || "/logo.png";
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import copy from "copy-to-clipboard";
|
||||
import React from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import Icon from "./Icon";
|
||||
|
||||
interface Props extends DialogProps {
|
||||
memoId: MemoId;
|
||||
}
|
||||
|
||||
const EmbedMemoDialog: React.FC<Props> = (props: Props) => {
|
||||
const t = useTranslate();
|
||||
const { memoId, destroy } = props;
|
||||
|
||||
const memoEmbeddedCode = () => {
|
||||
return `<iframe style="width:100%;height:auto;min-width:256px;" src="${window.location.origin}/m/${memoId}/embed" frameBorder="0"></iframe>`;
|
||||
};
|
||||
|
||||
const handleCopyCode = () => {
|
||||
copy(memoEmbeddedCode());
|
||||
toast.success("Succeed to copy code to clipboard.");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="dialog-header-container">
|
||||
<p className="title-text">{t("embed-memo.title")}</p>
|
||||
<button className="btn close-btn" onClick={() => destroy()}>
|
||||
<Icon.X />
|
||||
</button>
|
||||
</div>
|
||||
<div className="dialog-content-container !w-80">
|
||||
<p className="text-base leading-6 mb-2">{t("embed-memo.text")}</p>
|
||||
<pre className="w-full font-mono text-sm p-3 border rounded-lg">
|
||||
<code className="w-full break-all whitespace-pre-wrap">{memoEmbeddedCode()}</code>
|
||||
</pre>
|
||||
<p className="w-full text-sm leading-6 flex flex-row justify-between items-center mt-2">
|
||||
<span className="italic opacity-80">{t("embed-memo.only-public-supported")}</span>
|
||||
<span className="btn-primary" onClick={handleCopyCode}>
|
||||
{t("embed-memo.copy")}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function showEmbedMemoDialog(memoId: MemoId) {
|
||||
generateDialog(
|
||||
{
|
||||
className: "embed-memo-dialog",
|
||||
dialogName: "embed-memo-dialog",
|
||||
},
|
||||
EmbedMemoDialog,
|
||||
{
|
||||
memoId,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default showEmbedMemoDialog;
|
|
@ -12,7 +12,7 @@ const MobileHeader = (props: Props) => {
|
|||
const [titleText] = useState("MEMOS");
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 pt-4 sm:pt-1 pb-1 mb-1 backdrop-blur bg-zinc-100 dark:bg-zinc-800 bg-opacity-70 flex md:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-2">
|
||||
<div className="sticky top-0 pt-4 sm:pt-1 pb-1 mb-1 backdrop-blur flex md:hidden flex-row justify-between items-center w-full h-auto flex-nowrap shrink-0 z-2">
|
||||
<div className="flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden">
|
||||
{!sm && <NavigationDrawer />}
|
||||
<span
|
||||
|
|
|
@ -72,7 +72,7 @@ const Navigation = () => {
|
|||
id: "header-explore",
|
||||
path: "/explore",
|
||||
title: t("common.explore"),
|
||||
icon: <Icon.Hash className="mr-3 w-6 h-auto opacity-70" />,
|
||||
icon: <Icon.Globe2 className="mr-3 w-6 h-auto opacity-70" />,
|
||||
};
|
||||
const archivedNavLink: NavLinkItem = {
|
||||
id: "header-archived",
|
||||
|
@ -108,7 +108,7 @@ const Navigation = () => {
|
|||
id={navLink.id}
|
||||
className={({ isActive }) =>
|
||||
classNames(
|
||||
"px-4 pr-5 py-2 rounded-2xl border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||
"w-full px-4 pr-5 py-2 rounded-2xl border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700",
|
||||
isActive ? "bg-white drop-shadow-sm dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import toImage from "@/labs/html2image";
|
|||
import { useUserV1Store, extractUsernameFromName } from "@/store/v1";
|
||||
import { useTranslate } from "@/utils/i18n";
|
||||
import { generateDialog } from "./Dialog";
|
||||
import showEmbedMemoDialog from "./EmbedMemoDialog";
|
||||
import Icon from "./Icon";
|
||||
import MemoContentV1 from "./MemoContentV1";
|
||||
import MemoResourceListView from "./MemoResourceListView";
|
||||
|
@ -65,10 +64,6 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleShowEmbedMemoDialog = () => {
|
||||
showEmbedMemoDialog(memo.id);
|
||||
};
|
||||
|
||||
const handleCopyLinkBtnClick = () => {
|
||||
copy(`${window.location.origin}/m/${memo.id}`);
|
||||
toast.success(t("message.succeed-copy-link"));
|
||||
|
@ -96,10 +91,6 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
|
|||
)}
|
||||
{t("common.image")}
|
||||
</Button>
|
||||
<Button color="neutral" variant="outlined" onClick={handleShowEmbedMemoDialog}>
|
||||
<Icon.Code className="w-4 h-auto mr-1" />
|
||||
{t("memo.embed")}
|
||||
</Button>
|
||||
<Button color="neutral" variant="outlined" onClick={handleCopyLinkBtnClick}>
|
||||
<Icon.Link className="w-4 h-auto mr-1" />
|
||||
{t("common.link")}
|
||||
|
|
|
@ -16,7 +16,7 @@ const UserBanner = () => {
|
|||
const globalStore = useGlobalStore();
|
||||
const { systemStatus } = globalStore.state;
|
||||
const user = useCurrentUser();
|
||||
const title = user ? user.nickname : systemStatus.customizedProfile.name || "memos";
|
||||
const title = user ? user.nickname || extractUsernameFromName(user.name) : systemStatus.customizedProfile.name || "memos";
|
||||
|
||||
const handleMyAccountClick = () => {
|
||||
navigateTo(`/u/${encodeURIComponent(extractUsernameFromName(user.name))}`);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
html,
|
||||
body {
|
||||
@apply text-base w-full h-full overflow-hidden dark:bg-zinc-800;
|
||||
@apply text-base w-full min-h-full bg-zinc-100 dark:bg-zinc-800;
|
||||
}
|
||||
|
||||
#root {
|
||||
@apply w-full h-full overflow-auto;
|
||||
@apply w-full h-auto overflow-auto;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
import { Outlet } from "react-router-dom";
|
||||
import DemoBanner from "@/components/DemoBanner";
|
||||
import Navigation from "@/components/Navigation";
|
||||
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
||||
|
||||
function Root() {
|
||||
const { sm } = useResponsiveWidth();
|
||||
|
||||
return (
|
||||
<div className="w-full min-h-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div className="w-full min-h-full">
|
||||
<div className="w-full h-auto flex flex-col justify-start items-center">
|
||||
<DemoBanner />
|
||||
</div>
|
||||
<div className="w-full max-w-6xl mx-auto flex flex-row justify-center items-start sm:px-4">
|
||||
<div className="hidden sm:block sticky top-0 left-0 w-56">
|
||||
<Navigation />
|
||||
</div>
|
||||
<main className="w-full min-h-screen sm:max-w-[calc(100%-14rem)] flex-grow shrink flex flex-col justify-start items-start">
|
||||
<div className="w-full sm:pl-56 mx-auto flex flex-row justify-center items-start">
|
||||
{sm && (
|
||||
<div className="hidden sm:block fixed top-0 left-0 w-56 border-r dark:border-zinc-800 h-full bg-zinc-50 dark:bg-zinc-700 dark:bg-opacity-40 transition-all hover:shadow-xl">
|
||||
<Navigation />
|
||||
</div>
|
||||
)}
|
||||
<main className="w-full sm:px-4 min-h-screen flex-grow shrink flex flex-col justify-start items-center">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
@ -35,7 +35,7 @@ const Archived = () => {
|
|||
}, [memos, textQuery]);
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-start px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-start px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<MemoFilter />
|
||||
{loadingState.isLoading ? (
|
||||
|
|
|
@ -64,7 +64,7 @@ const DailyReview = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
||||
<div className="relative w-full flex flex-row justify-start items-center">
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useParams } from "react-router-dom";
|
||||
import MemoContentV1 from "@/components/MemoContentV1";
|
||||
import MemoResourceListView from "@/components/MemoResourceListView";
|
||||
import { UNKNOWN_ID } from "@/helpers/consts";
|
||||
import { getDateTimeString } from "@/helpers/datetime";
|
||||
import useLoading from "@/hooks/useLoading";
|
||||
import { useMemoStore } from "@/store/module";
|
||||
|
||||
interface State {
|
||||
memo: Memo;
|
||||
}
|
||||
|
||||
const EmbedMemo = () => {
|
||||
const params = useParams();
|
||||
const memoStore = useMemoStore();
|
||||
const [state, setState] = useState<State>({
|
||||
memo: {
|
||||
id: UNKNOWN_ID,
|
||||
} as Memo,
|
||||
});
|
||||
const loadingState = useLoading();
|
||||
|
||||
useEffect(() => {
|
||||
const memoId = Number(params.memoId);
|
||||
if (memoId && !isNaN(memoId)) {
|
||||
memoStore
|
||||
.fetchMemoById(memoId)
|
||||
.then((memo) => {
|
||||
setState({
|
||||
memo,
|
||||
});
|
||||
loadingState.setFinish();
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error(error.response.data.message);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="w-full h-full flex flex-row justify-start items-start p-2">
|
||||
{!loadingState.isLoading && (
|
||||
<div className="w-full max-w-lg mx-auto my-auto shadow px-4 py-4 rounded-lg">
|
||||
<div className="w-full flex flex-col justify-start items-start">
|
||||
<div className="w-full mb-2 flex flex-row justify-start items-center text-sm text-gray-400 dark:text-gray-300">
|
||||
<span>{getDateTimeString(state.memo.displayTs)}</span>
|
||||
<a className="ml-2 hover:underline hover:text-green-600" href={`/u/${state.memo.creatorUsername}`}>
|
||||
@{state.memo.creatorName}
|
||||
</a>
|
||||
</div>
|
||||
<MemoContentV1 className="memo-content" content={state.memo.content} onMemoContentClick={() => undefined} />
|
||||
<MemoResourceListView resourceList={state.memo.resourceList} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmbedMemo;
|
|
@ -54,7 +54,7 @@ const Explore = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<div className="relative w-full h-auto flex flex-col justify-start items-start">
|
||||
<MemoFilter />
|
||||
|
|
|
@ -8,8 +8,8 @@ import useResponsiveWidth from "@/hooks/useResponsiveWidth";
|
|||
const Home = () => {
|
||||
const { md } = useResponsiveWidth();
|
||||
return (
|
||||
<div className="w-full flex flex-row justify-start items-start">
|
||||
<div className="w-full px-4 md:max-w-[calc(100%-14rem)] sm:px-2 sm:pt-4">
|
||||
<div className="w-full flex flex-row justify-center items-start">
|
||||
<div className="w-full px-4 max-w-3xl sm:px-2 sm:pt-4">
|
||||
<MobileHeader>{!md && <HomeSidebarDrawer />}</MobileHeader>
|
||||
<MemoEditor className="mb-2" cacheKey="home-memo-editor" />
|
||||
<MemoList />
|
||||
|
|
|
@ -20,7 +20,7 @@ const Inboxes = () => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
||||
<div className="relative w-full flex flex-row justify-between items-center">
|
||||
|
|
|
@ -2,7 +2,7 @@ import Icon from "@/components/Icon";
|
|||
|
||||
function Loading() {
|
||||
return (
|
||||
<div className="flex flex-row justify-center items-center w-full h-full bg-zinc-100 dark:bg-zinc-800">
|
||||
<div className="flex flex-row justify-center items-center w-full h-full">
|
||||
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-center items-center">
|
||||
<Icon.Loader className="animate-spin dark:text-gray-200" />
|
||||
</div>
|
||||
|
|
|
@ -111,92 +111,86 @@ const MemoDetail = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<section className="relative top-0 w-full min-h-full overflow-x-hidden bg-zinc-100 dark:bg-zinc-900">
|
||||
<div className="relative w-full h-auto mx-auto flex flex-col justify-start items-center bg-white dark:bg-zinc-700">
|
||||
<div className="w-full flex flex-col justify-start items-center pt-16 pb-8">
|
||||
<UserAvatar className="!w-20 !h-20 mb-2 drop-shadow" avatarUrl={systemStatus.customizedProfile.logoUrl} />
|
||||
<p className="text-3xl text-black opacity-80 dark:text-gray-200">{systemStatus.customizedProfile.name}</p>
|
||||
</div>
|
||||
<div className="relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4 pb-6">
|
||||
{memo.parent && (
|
||||
<div className="w-auto mb-4">
|
||||
<Link
|
||||
className="px-3 py-1 border rounded-full max-w-xs w-auto text-sm flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-gray-500 hover:shadow hover:opacity-80"
|
||||
to={`/m/${memo.parent.id}`}
|
||||
>
|
||||
<Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60" />
|
||||
<span className="mx-1 opacity-60">#{memo.parent.id}</span>
|
||||
<span className="truncate">{memo.parent.content}</span>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mb-4 flex flex-row justify-start items-center mr-1">
|
||||
<span className="text-gray-400 select-none">{getDateTimeString(memo.displayTs)}</span>
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<div className="relative flex-grow w-full min-h-full flex flex-col justify-start items-start border dark:border-zinc-700 bg-white dark:bg-zinc-700 shadow hover:shadow-xl transition-all p-4 pb-3 rounded-lg">
|
||||
{memo.parent && (
|
||||
<div className="w-auto mb-2">
|
||||
<Link
|
||||
className="px-3 py-1 border rounded-full max-w-xs w-auto text-sm flex flex-row justify-start items-center flex-nowrap text-gray-600 dark:text-gray-400 dark:border-gray-500 hover:shadow hover:opacity-80"
|
||||
to={`/m/${memo.parent.id}`}
|
||||
>
|
||||
<Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60" />
|
||||
<span className="mx-1 opacity-60">#{memo.parent.id}</span>
|
||||
<span className="truncate">{memo.parent.content}</span>
|
||||
</Link>
|
||||
</div>
|
||||
<MemoContentV1 content={memo.content} />
|
||||
<MemoResourceListView resourceList={memo.resourceList} />
|
||||
<MemoRelationListView memo={memo} relationList={referenceRelations} />
|
||||
<div className="w-full mt-4 flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2">
|
||||
<div className="flex flex-row justify-start items-center">
|
||||
<Tooltip title={"Identifier"} placement="top">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">#{memo.id}</span>
|
||||
)}
|
||||
<div className="w-full mb-2 flex flex-row justify-start items-center">
|
||||
<span className="text-gray-400 select-none">{getDateTimeString(memo.displayTs)}</span>
|
||||
</div>
|
||||
<MemoContentV1 content={memo.content} />
|
||||
<MemoResourceListView resourceList={memo.resourceList} />
|
||||
<MemoRelationListView memo={memo} relationList={referenceRelations} />
|
||||
<div className="w-full mt-4 flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2">
|
||||
<div className="flex flex-row justify-start items-center">
|
||||
<Tooltip title={"Identifier"} placement="top">
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">#{memo.id}</span>
|
||||
</Tooltip>
|
||||
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||
<Link to={`/u/${encodeURIComponent(memo.creatorUsername)}`}>
|
||||
<Tooltip title={"Creator"} placement="top">
|
||||
<span className="flex flex-row justify-start items-center">
|
||||
<UserAvatar className="!w-5 !h-5 mr-1" avatarUrl={creator?.avatarUrl} />
|
||||
<span className="text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400">{creator?.nickname}</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||
<Link to={`/u/${encodeURIComponent(memo.creatorUsername)}`}>
|
||||
<Tooltip title={"Creator"} placement="top">
|
||||
<span className="flex flex-row justify-start items-center">
|
||||
<UserAvatar className="!w-5 !h-5 mr-1" avatarUrl={creator?.avatarUrl} />
|
||||
<span className="text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400">{creator?.nickname}</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
{allowEdit && (
|
||||
<>
|
||||
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||
<Select
|
||||
className="w-auto text-sm"
|
||||
variant="plain"
|
||||
value={memo.visibility}
|
||||
startDecorator={<VisibilityIcon visibility={memo.visibility} />}
|
||||
onChange={(_, visibility) => {
|
||||
if (visibility) {
|
||||
handleMemoVisibilityOptionChanged(visibility);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{VISIBILITY_SELECTOR_ITEMS.map((item) => (
|
||||
<Option key={item} value={item} className="whitespace-nowrap" disabled={disableOption(item)}>
|
||||
{t(`memo.visibility.${item.toLowerCase() as Lowercase<typeof item>}`)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row sm:justify-end items-center">
|
||||
{allowEdit && (
|
||||
<Tooltip title={"Edit"} placement="top">
|
||||
<IconButton size="sm" onClick={handleEditMemoClick}>
|
||||
<Icon.Edit3 className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title={"Copy link"} placement="top">
|
||||
<IconButton size="sm" onClick={handleCopyLinkBtnClick}>
|
||||
<Icon.Link className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</Link>
|
||||
{allowEdit && (
|
||||
<>
|
||||
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
|
||||
<Select
|
||||
className="w-auto text-sm"
|
||||
variant="plain"
|
||||
value={memo.visibility}
|
||||
startDecorator={<VisibilityIcon visibility={memo.visibility} />}
|
||||
onChange={(_, visibility) => {
|
||||
if (visibility) {
|
||||
handleMemoVisibilityOptionChanged(visibility);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{VISIBILITY_SELECTOR_ITEMS.map((item) => (
|
||||
<Option key={item} value={item} className="whitespace-nowrap" disabled={disableOption(item)}>
|
||||
{t(`memo.visibility.${item.toLowerCase() as Lowercase<typeof item>}`)}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row sm:justify-end items-center">
|
||||
{allowEdit && (
|
||||
<Tooltip title={"Edit"} placement="top">
|
||||
<IconButton size="sm" onClick={handleEditMemoClick}>
|
||||
<Icon.Edit3 className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={"Share"} placement="top">
|
||||
<IconButton size="sm" onClick={() => showShareMemoDialog(memo)}>
|
||||
<Icon.Share className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
<Tooltip title={"Copy link"} placement="top">
|
||||
<IconButton size="sm" onClick={handleCopyLinkBtnClick}>
|
||||
<Icon.Link className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={"Share"} placement="top">
|
||||
<IconButton size="sm" onClick={() => showShareMemoDialog(memo)}>
|
||||
<Icon.Share className="w-4 h-auto text-gray-600 dark:text-gray-400" />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-8 pb-16 w-full border-t dark:border-t-zinc-700">
|
||||
<div className="relative mx-auto flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4 gap-y-1">
|
||||
<div className="pt-8 pb-16 w-full">
|
||||
<div className="relative mx-auto flex-grow w-full min-h-full flex flex-col justify-start items-start gap-y-1">
|
||||
{comments.length === 0 ? (
|
||||
<div className="w-full flex flex-col justify-center items-center py-6 mb-2">
|
||||
<Icon.MessageCircle strokeWidth={1} className="w-8 h-auto text-gray-400" />
|
||||
|
|
|
@ -7,7 +7,7 @@ const NotFound = () => {
|
|||
const t = useTranslate();
|
||||
|
||||
return (
|
||||
<div className="w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800">
|
||||
<div className="w-full h-full overflow-y-auto overflow-x-hidden">
|
||||
<div className="w-full h-full flex flex-col justify-center items-center">
|
||||
<Icon.Meh strokeWidth={1} className="w-20 h-auto opacity-80 dark:text-gray-300" />
|
||||
<p className="mt-4 text-5xl font-mono dark:text-gray-300">404</p>
|
||||
|
|
|
@ -66,7 +66,7 @@ const Resources = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<div className="w-full shadow flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300">
|
||||
<div className="relative w-full flex flex-row justify-between items-center">
|
||||
|
|
|
@ -42,7 +42,7 @@ const Setting = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-start px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="@container w-full max-w-3xl min-h-full flex flex-col justify-start items-start px-4 sm:px-2 sm:pt-4 pb-8">
|
||||
<MobileHeader />
|
||||
<div className="setting-page-wrapper">
|
||||
<div className="section-selector-container">
|
||||
|
|
|
@ -36,7 +36,7 @@ const UserProfile = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<section className="relative top-0 w-full min-h-full overflow-x-hidden bg-zinc-100 dark:bg-zinc-800">
|
||||
<section className="relative top-0 w-full min-h-full overflow-x-hidden">
|
||||
<div className="relative w-full min-h-full mx-auto flex flex-col justify-start items-center">
|
||||
{!loadingState.isLoading &&
|
||||
(user ? (
|
||||
|
|
|
@ -12,7 +12,6 @@ const Explore = lazy(() => import("@/pages/Explore"));
|
|||
const Home = lazy(() => import("@/pages/Home"));
|
||||
const UserProfile = lazy(() => import("@/pages/UserProfile"));
|
||||
const MemoDetail = lazy(() => import("@/pages/MemoDetail"));
|
||||
const EmbedMemo = lazy(() => import("@/pages/EmbedMemo"));
|
||||
const Archived = lazy(() => import("@/pages/Archived"));
|
||||
const DailyReview = lazy(() => import("@/pages/DailyReview"));
|
||||
const Resources = lazy(() => import("@/pages/Resources"));
|
||||
|
@ -103,24 +102,20 @@ const router = createBrowserRouter([
|
|||
path: "explore",
|
||||
element: <Explore />,
|
||||
},
|
||||
{
|
||||
path: "m/:memoId",
|
||||
element: <MemoDetail />,
|
||||
},
|
||||
{
|
||||
path: "u/:username",
|
||||
element: <UserProfile />,
|
||||
},
|
||||
{
|
||||
path: "*",
|
||||
element: <NotFound />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/m/:memoId",
|
||||
element: <MemoDetail />,
|
||||
},
|
||||
{
|
||||
path: "/m/:memoId/embed",
|
||||
element: <EmbedMemo />,
|
||||
},
|
||||
{
|
||||
path: "/u/:username",
|
||||
element: <UserProfile />,
|
||||
},
|
||||
{
|
||||
path: "*",
|
||||
element: <NotFound />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
|
Loading…
Reference in a new issue