chore: tweak responsible styles

This commit is contained in:
Steven 2023-12-19 08:41:41 +08:00
parent fe4ec0b156
commit a3a1bbe8de
16 changed files with 411 additions and 399 deletions

View File

@ -20,8 +20,7 @@ VALUES
'#TODO
- [x] Take more photos about **🌄 sunset**;
- [x] Clean the room;
- [ ] Read *📖 The Little Prince*;
(👆 click to toggle status)',
- [ ] Read *📖 The Little Prince*;',
101,
'PROTECTED'
);

View File

@ -72,7 +72,6 @@ const MemoEditor = (props: Props) => {
useEffect(() => {
editorRef.current?.setContent(contentCache || "");
handleEditorFocus();
}, []);
useEffect(() => {

View File

@ -1,4 +1,6 @@
import classNames from "classnames";
import { useState } from "react";
import { useWindowScroll } from "react-use";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import NavigationDrawer from "./NavigationDrawer";
@ -10,9 +12,15 @@ const MobileHeader = (props: Props) => {
const { children } = props;
const { sm } = useResponsiveWidth();
const [titleText] = useState("MEMOS");
const { y: offsetTop } = useWindowScroll();
return (
<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={classNames(
"sticky top-0 pt-4 sm:pt-1 px-4 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",
offsetTop > 0 && "shadow-md"
)}
>
<div className="flex flex-row justify-start items-center mr-2 shrink-0 overflow-hidden">
{!sm && <NavigationDrawer />}
<span

View File

@ -1,5 +1,5 @@
body {
@apply text-base w-full min-h-full p-0 m-0 bg-zinc-100 dark:bg-zinc-800;
@apply text-base w-full min-h-[100svh] p-0 m-0 bg-zinc-100 dark:bg-zinc-800;
}
#root {

View File

@ -13,7 +13,7 @@ function Root() {
<Navigation />
</div>
)}
<main className="w-full sm:px-4 h-auto flex-grow shrink flex flex-col justify-start items-center">
<main className="w-full h-auto flex-grow shrink flex flex-col justify-start items-center">
<Outlet />
</main>
</div>

View File

@ -30,24 +30,26 @@ const Archived = () => {
}, [memos]);
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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-start sm:pt-4 pb-8">
<MobileHeader />
{loadingState.isLoading ? (
<div className="w-full h-32 flex flex-col justify-center items-center">
<p className="opacity-70">{t("memo.fetching-data")}</p>
</div>
) : archivedMemos.length === 0 ? (
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
) : (
<div className="w-full flex flex-col justify-start items-start">
{archivedMemos.map((memo) => (
<ArchivedMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} />
))}
</div>
)}
<div className="w-full px-4">
{loadingState.isLoading ? (
<div className="w-full h-32 flex flex-col justify-center items-center">
<p className="opacity-70">{t("memo.fetching-data")}</p>
</div>
) : archivedMemos.length === 0 ? (
<div className="w-full mt-16 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
) : (
<div className="w-full flex flex-col justify-start items-start">
{archivedMemos.map((memo) => (
<ArchivedMemo key={`${memo.id}-${memo.updatedTs}`} memo={memo} />
))}
</div>
)}
</div>
</section>
);
};

View File

@ -54,9 +54,9 @@ 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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-center sm:pt-4 pb-8">
<MobileHeader />
<div className="relative w-full h-auto flex flex-col justify-start items-start">
<div className="relative w-full h-auto flex flex-col justify-start items-start px-4">
<MemoFilter />
{sortedMemos.map((memo) => (
<Memo key={memo.id} memo={memo} lazyRendering showCreator showParent />

View File

@ -9,14 +9,16 @@ const Home = () => {
const { md } = useResponsiveWidth();
return (
<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">
<div className="w-full max-w-4xl flex flex-row justify-center items-start">
<div className="w-full sm:pt-4">
<MobileHeader>{!md && <HomeSidebarDrawer />}</MobileHeader>
<MemoEditor className="mb-2" cacheKey="home-memo-editor" />
<MemoList />
<div className="w-full px-4 md:pr-2">
<MemoEditor className="mb-2" cacheKey="home-memo-editor" />
<MemoList />
</div>
</div>
{md && (
<div className="hidden md:block sticky top-0 left-0 w-56">
<div className="hidden md:block sticky top-0 left-0 shrink-0 w-56">
<HomeSidebar />
</div>
)}

View File

@ -20,30 +20,32 @@ 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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-center 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">
<p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Bell className="w-5 h-auto mr-1" /> {t("common.inbox")}
</p>
</div>
<div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700">
{inboxes.length === 0 && (
<div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
<div className="w-full px-4">
<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">
<p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Bell className="w-5 h-auto mr-1" /> {t("common.inbox")}
</p>
</div>
<div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700">
{inboxes.length === 0 && (
<div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
)}
<div className="flex flex-col justify-start items-start w-full mt-4 gap-4">
{inboxes.map((inbox) => {
if (inbox.type === Inbox_Type.TYPE_MEMO_COMMENT) {
return <MemoCommentMessage key={`${inbox.name}-${inbox.status}`} inbox={inbox} />;
} else if (inbox.type === Inbox_Type.TYPE_VERSION_UPDATE) {
return <VersionUpdateMessage key={`${inbox.name}-${inbox.status}`} inbox={inbox} />;
}
return undefined;
})}
</div>
)}
<div className="flex flex-col justify-start items-start w-full mt-4 gap-4">
{inboxes.map((inbox) => {
if (inbox.type === Inbox_Type.TYPE_MEMO_COMMENT) {
return <MemoCommentMessage key={`${inbox.name}-${inbox.status}`} inbox={inbox} />;
} else if (inbox.type === Inbox_Type.TYPE_VERSION_UPDATE) {
return <VersionUpdateMessage key={`${inbox.name}-${inbox.status}`} inbox={inbox} />;
}
return undefined;
})}
</div>
</div>
</div>

View File

@ -110,114 +110,116 @@ const MemoDetail = () => {
};
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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-center sm:pt-4 pb-8">
<MobileHeader />
<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 className="w-full px-4">
<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>
)}
<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>
)}
<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>
<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>
</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" />
<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" />
</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" />
</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>
<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>
<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" />
<p className="text-gray-400 italic text-sm">{t("memo.comment.no-comment")}</p>
</div>
) : (
<>
<div className="w-full flex flex-row justify-start items-center pl-3 mb-3">
<Icon.MessageCircle className="w-5 h-auto text-gray-400 mr-1" />
<span className="text-gray-400 text-sm">{t("memo.comment.self")}</span>
<span className="text-gray-400 text-sm ml-0.5">({comments.length})</span>
<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" />
<p className="text-gray-400 italic text-sm">{t("memo.comment.no-comment")}</p>
</div>
{comments.map((comment) => (
<Memo key={comment.id} memo={comment} showCreator />
))}
</>
)}
) : (
<>
<div className="w-full flex flex-row justify-start items-center pl-3 mb-3">
<Icon.MessageCircle className="w-5 h-auto text-gray-400 mr-1" />
<span className="text-gray-400 text-sm">{t("memo.comment.self")}</span>
<span className="text-gray-400 text-sm ml-0.5">({comments.length})</span>
</div>
{comments.map((comment) => (
<Memo key={comment.id} memo={comment} showCreator />
))}
</>
)}
{/* Only show comment editor when user login */}
{currentUser && (
<MemoEditor
key={memo.id}
cacheKey={`comment-editor-${memo.id}`}
relationList={[{ memoId: UNKNOWN_ID, relatedMemoId: memo.id, type: "COMMENT" }]}
onConfirm={handleCommentCreated}
/>
)}
{/* Only show comment editor when user login */}
{currentUser && (
<MemoEditor
key={memo.id}
cacheKey={`comment-editor-${memo.id}`}
relationList={[{ memoId: UNKNOWN_ID, relatedMemoId: memo.id, type: "COMMENT" }]}
onConfirm={handleCommentCreated}
/>
)}
</div>
</div>
</div>
</section>

View File

@ -66,106 +66,108 @@ 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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-center 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">
<p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Paperclip className="w-5 h-auto mr-1" /> {t("common.resources")}
</p>
<div>
<Input
className="max-w-[8rem]"
variant="plain"
placeholder={t("common.search")}
startDecorator={<Icon.Search className="w-4 h-auto" />}
value={state.searchQuery}
onChange={(e) => setState({ ...state, searchQuery: e.target.value })}
/>
</div>
</div>
<div className="w-full flex flex-col justify-start items-start mt-4 mb-6">
{loadingState.isLoading ? (
<div className="w-full h-32 flex flex-col justify-center items-center">
<p className="w-full text-center text-base my-6 mt-8">{t("resource.fetching-data")}</p>
<div className="w-full px-4">
<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">
<p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80">
<Icon.Paperclip className="w-5 h-auto mr-1" /> {t("common.resources")}
</p>
<div>
<Input
className="max-w-[8rem]"
variant="plain"
placeholder={t("common.search")}
startDecorator={<Icon.Search className="w-4 h-auto" />}
value={state.searchQuery}
onChange={(e) => setState({ ...state, searchQuery: e.target.value })}
/>
</div>
) : (
<>
{filteredResources.length === 0 ? (
<div className="w-full mt-8 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
) : (
<div className={"w-full h-auto px-2 flex flex-col justify-start items-start gap-y-8"}>
{Array.from(groupedResources.entries()).map(([timestamp, resources]) => {
const date = new Date(timestamp);
return (
<div key={timestamp} className="w-full flex flex-row justify-start items-start">
<div className="w-16 sm:w-24 pt-4 sm:pl-4 flex flex-col justify-start items-start">
<span className="text-sm opacity-60">{date.getFullYear()}</span>
<span className="font-medium text-xl">{date.toLocaleString(i18n.language, { month: "short" })}</span>
</div>
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
{resources.map((resource) => {
return (
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
<ResourceIcon resource={resource} strokeWidth={0.5} />
</div>
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
<Link
className="shrink-0 text-xs ml-1 text-gray-400 hover:underline hover:text-blue-600"
to={`/m/${resource.memoId}`}
target="_blank"
>
#{resource.memoId}
</Link>
</div>
</div>
);
})}
</div>
</div>
);
})}
{unusedResources.length > 0 && (
<>
<Divider />
<div className="w-full flex flex-row justify-start items-start">
<div className="w-16 sm:w-24 sm:pl-4 flex flex-col justify-start items-start"></div>
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
<div className="w-full flex flex-row justify-start items-center gap-2">
<span className="text-gray-600 dark:text-gray-400">Unused resources</span>
<span className="text-gray-500 dark:text-gray-500 opacity-80">({unusedResources.length})</span>
<Tooltip title="Delete all" placement="top">
<IconButton size="sm" onClick={handleDeleteUnusedResources}>
<Icon.Trash className="w-4 h-auto opacity-60" />
</IconButton>
</Tooltip>
</div>
<div className="w-full flex flex-col justify-start items-start mt-4 mb-6">
{loadingState.isLoading ? (
<div className="w-full h-32 flex flex-col justify-center items-center">
<p className="w-full text-center text-base my-6 mt-8">{t("resource.fetching-data")}</p>
</div>
) : (
<>
{filteredResources.length === 0 ? (
<div className="w-full mt-8 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
) : (
<div className={"w-full h-auto px-2 flex flex-col justify-start items-start gap-y-8"}>
{Array.from(groupedResources.entries()).map(([timestamp, resources]) => {
const date = new Date(timestamp);
return (
<div key={timestamp} className="w-full flex flex-row justify-start items-start">
<div className="w-16 sm:w-24 pt-4 sm:pl-4 flex flex-col justify-start items-start">
<span className="text-sm opacity-60">{date.getFullYear()}</span>
<span className="font-medium text-xl">{date.toLocaleString(i18n.language, { month: "short" })}</span>
</div>
{unusedResources.map((resource) => {
return (
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
<ResourceIcon resource={resource} strokeWidth={0.5} />
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
{resources.map((resource) => {
return (
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
<ResourceIcon resource={resource} strokeWidth={0.5} />
</div>
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
<Link
className="shrink-0 text-xs ml-1 text-gray-400 hover:underline hover:text-blue-600"
to={`/m/${resource.memoId}`}
target="_blank"
>
#{resource.memoId}
</Link>
</div>
</div>
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
</div>
</div>
);
})}
);
})}
</div>
</div>
</div>
</>
)}
</div>
)}
</>
)}
);
})}
{unusedResources.length > 0 && (
<>
<Divider />
<div className="w-full flex flex-row justify-start items-start">
<div className="w-16 sm:w-24 sm:pl-4 flex flex-col justify-start items-start"></div>
<div className="w-full max-w-[calc(100%-4rem)] sm:max-w-[calc(100%-6rem)] flex flex-row justify-start items-start gap-4 flex-wrap">
<div className="w-full flex flex-row justify-start items-center gap-2">
<span className="text-gray-600 dark:text-gray-400">Unused resources</span>
<span className="text-gray-500 dark:text-gray-500 opacity-80">({unusedResources.length})</span>
<Tooltip title="Delete all" placement="top">
<IconButton size="sm" onClick={handleDeleteUnusedResources}>
<Icon.Trash className="w-4 h-auto opacity-60" />
</IconButton>
</Tooltip>
</div>
{unusedResources.map((resource) => {
return (
<div key={resource.id} className="w-24 sm:w-32 h-auto flex flex-col justify-start items-start">
<div className="w-24 h-24 flex justify-center items-center sm:w-32 sm:h-32 border dark:border-zinc-900 overflow-clip rounded-xl cursor-pointer hover:shadow hover:opacity-80">
<ResourceIcon resource={resource} strokeWidth={0.5} />
</div>
<div className="w-full max-w-full flex flex-row justify-between items-center mt-1 px-1">
<p className="text-xs shrink text-gray-400 truncate">{resource.filename}</p>
</div>
</div>
);
})}
</div>
</div>
</>
)}
</div>
)}
</>
)}
</div>
</div>
</div>
</section>

View File

@ -42,82 +42,84 @@ 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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-start sm:pt-4 pb-8">
<MobileHeader />
<div className="setting-page-wrapper">
<div className="section-selector-container">
<span className="section-title">{t("common.basic")}</span>
<div className="section-items-container">
<span
onClick={() => handleSectionSelectorItemClick("my-account")}
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`}
>
<Icon.User className="w-4 h-auto mr-2 opacity-80" /> {t("setting.my-account")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("preference")}
className={`section-item ${state.selectedSection === "preference" ? "selected" : ""}`}
>
<Icon.Cog className="w-4 h-auto mr-2 opacity-80" /> {t("setting.preference")}
</span>
<div className="w-full px-4">
<div className="setting-page-wrapper">
<div className="section-selector-container">
<span className="section-title">{t("common.basic")}</span>
<div className="section-items-container">
<span
onClick={() => handleSectionSelectorItemClick("my-account")}
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`}
>
<Icon.User className="w-4 h-auto mr-2 opacity-80" /> {t("setting.my-account")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("preference")}
className={`section-item ${state.selectedSection === "preference" ? "selected" : ""}`}
>
<Icon.Cog className="w-4 h-auto mr-2 opacity-80" /> {t("setting.preference")}
</span>
</div>
{isHost ? (
<>
<span className="section-title">{t("common.admin")}</span>
<div className="section-items-container">
<span
onClick={() => handleSectionSelectorItemClick("member")}
className={`section-item ${state.selectedSection === "member" ? "selected" : ""}`}
>
<Icon.Users className="w-4 h-auto mr-2 opacity-80" /> {t("setting.member")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("system")}
className={`section-item ${state.selectedSection === "system" ? "selected" : ""}`}
>
<Icon.Settings2 className="w-4 h-auto mr-2 opacity-80" /> {t("setting.system")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("storage")}
className={`section-item ${state.selectedSection === "storage" ? "selected" : ""}`}
>
<Icon.Database className="w-4 h-auto mr-2 opacity-80" /> {t("setting.storage")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("sso")}
className={`section-item ${state.selectedSection === "sso" ? "selected" : ""}`}
>
<Icon.Key className="w-4 h-auto mr-2 opacity-80" /> {t("setting.sso")}
</span>
</div>
</>
) : null}
</div>
<div className="section-content-container sm:max-w-[calc(100%-12rem)]">
<Select
className="block mb-2 sm:!hidden"
value={state.selectedSection}
onChange={(_, value) => handleSectionSelectorItemClick(value as SettingSection)}
>
{getSettingSectionList().map((settingSection) => (
<Option key={settingSection} value={settingSection}>
{t(`setting.${settingSection}`)}
</Option>
))}
</Select>
{state.selectedSection === "my-account" ? (
<MyAccountSection />
) : state.selectedSection === "preference" ? (
<PreferencesSection />
) : state.selectedSection === "member" ? (
<MemberSection />
) : state.selectedSection === "system" ? (
<SystemSection />
) : state.selectedSection === "storage" ? (
<StorageSection />
) : state.selectedSection === "sso" ? (
<SSOSection />
) : null}
</div>
{isHost ? (
<>
<span className="section-title">{t("common.admin")}</span>
<div className="section-items-container">
<span
onClick={() => handleSectionSelectorItemClick("member")}
className={`section-item ${state.selectedSection === "member" ? "selected" : ""}`}
>
<Icon.Users className="w-4 h-auto mr-2 opacity-80" /> {t("setting.member")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("system")}
className={`section-item ${state.selectedSection === "system" ? "selected" : ""}`}
>
<Icon.Settings2 className="w-4 h-auto mr-2 opacity-80" /> {t("setting.system")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("storage")}
className={`section-item ${state.selectedSection === "storage" ? "selected" : ""}`}
>
<Icon.Database className="w-4 h-auto mr-2 opacity-80" /> {t("setting.storage")}
</span>
<span
onClick={() => handleSectionSelectorItemClick("sso")}
className={`section-item ${state.selectedSection === "sso" ? "selected" : ""}`}
>
<Icon.Key className="w-4 h-auto mr-2 opacity-80" /> {t("setting.sso")}
</span>
</div>
</>
) : null}
</div>
<div className="section-content-container sm:max-w-[calc(100%-12rem)]">
<Select
className="block mb-2 sm:!hidden"
value={state.selectedSection}
onChange={(_, value) => handleSectionSelectorItemClick(value as SettingSection)}
>
{getSettingSectionList().map((settingSection) => (
<Option key={settingSection} value={settingSection}>
{t(`setting.${settingSection}`)}
</Option>
))}
</Select>
{state.selectedSection === "my-account" ? (
<MyAccountSection />
) : state.selectedSection === "preference" ? (
<PreferencesSection />
) : state.selectedSection === "member" ? (
<MemberSection />
) : state.selectedSection === "system" ? (
<SystemSection />
) : state.selectedSection === "storage" ? (
<StorageSection />
) : state.selectedSection === "sso" ? (
<SSOSection />
) : null}
</div>
</div>
</section>

View File

@ -104,7 +104,7 @@ const SignIn = () => {
};
return (
<div className="pt-12 pb-8 w-80 max-w-full h-auto mx-auto flex flex-col justify-start items-center">
<div className="py-8 w-80 max-w-full min-h-[100svh] mx-auto flex flex-col justify-start items-center">
<div className="w-full py-4 grow flex flex-col justify-center items-center">
<div className="w-full flex flex-row justify-center items-center mb-6">
<img className="h-14 w-auto rounded-full shadow" src={systemStatus.customizedProfile.logoUrl} alt="" />

View File

@ -70,7 +70,7 @@ const SignUp = () => {
};
return (
<div className="pt-12 pb-8 w-80 max-w-full h-auto mx-auto flex flex-col justify-start items-center">
<div className="py-8 w-80 max-w-full min-h-[100svh] mx-auto flex flex-col justify-start items-center">
<div className="w-full py-4 grow flex flex-col justify-center items-center">
<div className="w-full flex flex-row justify-center items-center mb-6">
<img className="h-14 w-auto rounded-full shadow" src={systemStatus.customizedProfile.logoUrl} alt="" />

View File

@ -64,73 +64,75 @@ const Timeline = () => {
};
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">
<section className="@container w-full max-w-4xl min-h-full flex flex-col justify-start items-center 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">
<p
className="px-2 py-1 mr-2 flex flex-row justify-start items-center cursor-pointer select-none rounded opacity-80 hover:bg-gray-100 dark:hover:bg-zinc-700"
onClick={() => toggleShowDatePicker()}
>
<Icon.Calendar className="w-5 h-auto mr-2" />
<span className="font-mono mt-0.5">{new Date(selectedDateStamp).toLocaleDateString()}</span>
</p>
{selectedDateStamp !== currentDateStamp && (
<Button
variant="outlined"
startDecorator={<Icon.Undo2 className="w-5 h-auto" />}
onClick={() => setSelectedDateStamp(currentDateStamp)}
<div className="w-full px-4">
<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">
<p
className="px-2 py-1 mr-2 flex flex-row justify-start items-center cursor-pointer select-none rounded opacity-80 hover:bg-gray-100 dark:hover:bg-zinc-700"
onClick={() => toggleShowDatePicker()}
>
{"Back to today"}
</Button>
)}
<DatePicker
className={`absolute top-8 mt-2 z-20 mx-auto border bg-white shadow dark:bg-zinc-800 dark:border-zinc-800 rounded-lg mb-6 ${
showDatePicker ? "" : "!hidden"
}`}
datestamp={selectedDateStamp}
isFutureDateDisabled
handleDateStampChange={handleDataPickerChange}
handleClickAway={() => toggleShowDatePicker(false)}
/>
</div>
<div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700">
{dailyMemos.length === 0 && (
<div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
)}
<div className="flex flex-col justify-start items-start w-full mt-2">
{dailyMemos.map((memo, index) => (
<div
key={`${memo.id}-${memo.displayTs}`}
className="relative w-full flex flex-col justify-start items-start pl-8 sm:pl-12 pt-2 pb-4"
<Icon.Calendar className="w-5 h-auto mr-2" />
<span className="font-mono mt-0.5">{new Date(selectedDateStamp).toLocaleDateString()}</span>
</p>
{selectedDateStamp !== currentDateStamp && (
<Button
variant="outlined"
startDecorator={<Icon.Undo2 className="w-5 h-auto" />}
onClick={() => setSelectedDateStamp(currentDateStamp)}
>
<div className="w-full flex flex-row justify-start items-center mt-0.5 mb-1 text-sm font-mono text-gray-500 dark:text-gray-400">
<span className="opacity-80">{getTimeString(memo.displayTs)}</span>
<Icon.Dot className="w-5 h-auto opacity-60" />
<span className="opacity-60">#{memo.id}</span>
</div>
<MemoContentV1 content={memo.content} />
<MemoResourceListView resourceList={memo.resourceList} />
<MemoRelationListView memo={memo} relationList={memo.relationList.filter((relation) => relation.type === "REFERENCE")} />
<div className="absolute left-1 sm:left-2 top-3 h-full">
{index !== dailyMemos.length - 1 && (
<div className="absolute top-2 left-[7px] h-full w-0.5 bg-gray-400 dark:bg-gray-500 block"></div>
)}
<div className="border-4 rounded-full border-white relative dark:border-zinc-700">
<Icon.Circle className="w-2 h-auto bg-gray-400 text-gray-400 dark:bg-gray-500 dark:text-gray-500 rounded-full" />
</div>
</div>
</div>
))}
{selectedDateStamp === currentDateStamp && (
<div className="w-full pl-0 sm:pl-12 sm:mt-4">
<MemoEditor cacheKey="timeline-editor" />
{"Back to today"}
</Button>
)}
<DatePicker
className={`absolute top-8 mt-2 z-20 mx-auto border bg-white shadow dark:bg-zinc-800 dark:border-zinc-800 rounded-lg mb-6 ${
showDatePicker ? "" : "!hidden"
}`}
datestamp={selectedDateStamp}
isFutureDateDisabled
handleDateStampChange={handleDataPickerChange}
handleClickAway={() => toggleShowDatePicker(false)}
/>
</div>
<div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700">
{dailyMemos.length === 0 && (
<div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic">
<Empty />
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p>
</div>
)}
<div className="flex flex-col justify-start items-start w-full mt-2">
{dailyMemos.map((memo, index) => (
<div
key={`${memo.id}-${memo.displayTs}`}
className="relative w-full flex flex-col justify-start items-start pl-8 sm:pl-12 pt-2 pb-4"
>
<div className="w-full flex flex-row justify-start items-center mt-0.5 mb-1 text-sm font-mono text-gray-500 dark:text-gray-400">
<span className="opacity-80">{getTimeString(memo.displayTs)}</span>
<Icon.Dot className="w-5 h-auto opacity-60" />
<span className="opacity-60">#{memo.id}</span>
</div>
<MemoContentV1 content={memo.content} />
<MemoResourceListView resourceList={memo.resourceList} />
<MemoRelationListView memo={memo} relationList={memo.relationList.filter((relation) => relation.type === "REFERENCE")} />
<div className="absolute left-1 sm:left-2 top-3 h-full">
{index !== dailyMemos.length - 1 && (
<div className="absolute top-2 left-[7px] h-full w-0.5 bg-gray-400 dark:bg-gray-500 block"></div>
)}
<div className="border-4 rounded-full border-white relative dark:border-zinc-700">
<Icon.Circle className="w-2 h-auto bg-gray-400 text-gray-400 dark:bg-gray-500 dark:text-gray-500 rounded-full" />
</div>
</div>
</div>
))}
{selectedDateStamp === currentDateStamp && (
<div className="w-full pl-0 sm:pl-12 sm:mt-4">
<MemoEditor cacheKey="timeline-editor" />
</div>
)}
</div>
</div>
</div>
</div>

View File

@ -35,28 +35,20 @@ const UserProfile = () => {
}, [params.username]);
return (
<section className="relative top-0 w-full min-h-full overflow-x-hidden">
<section className="w-full max-w-4xl min-h-full flex flex-col justify-start items-center sm:pt-4 pb-8">
<MobileHeader />
<div className="relative w-full min-h-full mx-auto flex flex-col justify-start items-center">
<div className="w-full px-4 flex flex-col justify-start items-center">
{!loadingState.isLoading &&
(user ? (
<>
<div className="relative flex-grow max-w-2xl w-full min-h-full flex flex-col justify-start items-start px-4">
<div className="w-full flex flex-row justify-start items-start">
<div className="flex-grow shrink w-full">
<div className="w-full flex flex-col justify-start items-center py-8">
<UserAvatar className="!w-20 !h-20 mb-2 drop-shadow" avatarUrl={user?.avatarUrl} />
<p className="text-3xl text-black opacity-80 dark:text-gray-200">{user?.nickname}</p>
</div>
<MemoList />
</div>
</div>
<div className="w-full flex flex-col justify-start items-center py-8">
<UserAvatar className="!w-20 !h-20 mb-2 drop-shadow" avatarUrl={user?.avatarUrl} />
<p className="text-3xl text-black opacity-80 dark:text-gray-200">{user?.nickname}</p>
</div>
<MemoList />
</>
) : (
<>
<p>Not found</p>
</>
<p>Not found</p>
))}
</div>
</section>