chore: tweak responsible styles

pull/2650/head
Steven 1 year ago
parent fe4ec0b156
commit a3a1bbe8de

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

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

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

@ -1,5 +1,5 @@
body { 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 { #root {

@ -13,7 +13,7 @@ function Root() {
<Navigation /> <Navigation />
</div> </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 /> <Outlet />
</main> </main>
</div> </div>

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

@ -54,9 +54,9 @@ const Explore = () => {
}; };
return ( 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 /> <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 /> <MemoFilter />
{sortedMemos.map((memo) => ( {sortedMemos.map((memo) => (
<Memo key={memo.id} memo={memo} lazyRendering showCreator showParent /> <Memo key={memo.id} memo={memo} lazyRendering showCreator showParent />

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

@ -20,30 +20,32 @@ const Inboxes = () => {
}, []); }, []);
return ( 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 /> <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="w-full px-4">
<div className="relative w-full flex flex-row justify-between items-center"> <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">
<p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80"> <div className="relative w-full flex flex-row justify-between items-center">
<Icon.Bell className="w-5 h-auto mr-1" /> {t("common.inbox")} <p className="px-2 py-1 flex flex-row justify-start items-center select-none opacity-80">
</p> <Icon.Bell className="w-5 h-auto mr-1" /> {t("common.inbox")}
</div> </p>
<div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700"> </div>
{inboxes.length === 0 && ( <div className="w-full h-auto flex flex-col justify-start items-start px-2 pb-4 bg-white dark:bg-zinc-700">
<div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic"> {inboxes.length === 0 && (
<Empty /> <div className="w-full mt-4 mb-8 flex flex-col justify-center items-center italic">
<p className="mt-4 text-gray-600 dark:text-gray-400">{t("message.no-data")}</p> <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>
)}
<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> </div>
</div> </div>

@ -110,114 +110,116 @@ const MemoDetail = () => {
}; };
return ( 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 /> <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"> <div className="w-full px-4">
{memo.parent && ( <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">
<div className="w-auto mb-2"> {memo.parent && (
<Link <div className="w-auto mb-2">
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" <Link
to={`/m/${memo.parent.id}`} 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> <Icon.ArrowUpLeftFromCircle className="w-4 h-auto shrink-0 opacity-60" />
<span className="truncate">{memo.parent.content}</span> <span className="mx-1 opacity-60">#{memo.parent.id}</span>
</Link> <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>
)} <MemoContentV1 content={memo.content} />
<div className="w-full mb-2 flex flex-row justify-start items-center"> <MemoResourceListView resourceList={memo.resourceList} />
<span className="text-gray-400 select-none">{getDateTimeString(memo.displayTs)}</span> <MemoRelationListView memo={memo} relationList={referenceRelations} />
</div> <div className="w-full mt-4 flex flex-col sm:flex-row justify-start sm:justify-between sm:items-center gap-2">
<MemoContentV1 content={memo.content} /> <div className="flex flex-row justify-start items-center">
<MemoResourceListView resourceList={memo.resourceList} /> <Tooltip title={"Identifier"} placement="top">
<MemoRelationListView memo={memo} relationList={referenceRelations} /> <span className="text-sm text-gray-500 dark:text-gray-400">#{memo.id}</span>
<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> </Tooltip>
</Link> <Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
{allowEdit && ( <Link to={`/u/${encodeURIComponent(memo.creatorUsername)}`}>
<> <Tooltip title={"Creator"} placement="top">
<Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" /> <span className="flex flex-row justify-start items-center">
<Select <UserAvatar className="!w-5 !h-5 mr-1" avatarUrl={creator?.avatarUrl} />
className="w-auto text-sm" <span className="text-sm text-gray-600 max-w-[8em] truncate dark:text-gray-400">{creator?.nickname}</span>
variant="plain" </span>
value={memo.visibility} </Tooltip>
startDecorator={<VisibilityIcon visibility={memo.visibility} />} </Link>
onChange={(_, visibility) => { {allowEdit && (
if (visibility) { <>
handleMemoVisibilityOptionChanged(visibility); <Icon.Dot className="w-4 h-auto text-gray-400 dark:text-zinc-400" />
} <Select
}} className="w-auto text-sm"
> variant="plain"
{VISIBILITY_SELECTOR_ITEMS.map((item) => ( value={memo.visibility}
<Option key={item} value={item} className="whitespace-nowrap" disabled={disableOption(item)}> startDecorator={<VisibilityIcon visibility={memo.visibility} />}
{t(`memo.visibility.${item.toLowerCase() as Lowercase<typeof item>}`)} onChange={(_, visibility) => {
</Option> if (visibility) {
))} handleMemoVisibilityOptionChanged(visibility);
</Select> }
</> }}
)} >
</div> {VISIBILITY_SELECTOR_ITEMS.map((item) => (
<div className="flex flex-row sm:justify-end items-center"> <Option key={item} value={item} className="whitespace-nowrap" disabled={disableOption(item)}>
{allowEdit && ( {t(`memo.visibility.${item.toLowerCase() as Lowercase<typeof item>}`)}
<Tooltip title={"Edit"} placement="top"> </Option>
<IconButton size="sm" onClick={handleEditMemoClick}> ))}
<Icon.Edit3 className="w-4 h-auto text-gray-600 dark:text-gray-400" /> </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> </IconButton>
</Tooltip> </Tooltip>
)} <Tooltip title={"Share"} placement="top">
<Tooltip title={"Copy link"} placement="top"> <IconButton size="sm" onClick={() => showShareMemoDialog(memo)}>
<IconButton size="sm" onClick={handleCopyLinkBtnClick}> <Icon.Share className="w-4 h-auto text-gray-600 dark:text-gray-400" />
<Icon.Link className="w-4 h-auto text-gray-600 dark:text-gray-400" /> </IconButton>
</IconButton> </Tooltip>
</Tooltip> </div>
<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> <div className="pt-8 pb-16 w-full">
<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">
<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 ? (
{comments.length === 0 ? ( <div className="w-full flex flex-col justify-center items-center py-6 mb-2">
<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" />
<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>
<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> </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 */} {/* Only show comment editor when user login */}
{currentUser && ( {currentUser && (
<MemoEditor <MemoEditor
key={memo.id} key={memo.id}
cacheKey={`comment-editor-${memo.id}`} cacheKey={`comment-editor-${memo.id}`}
relationList={[{ memoId: UNKNOWN_ID, relatedMemoId: memo.id, type: "COMMENT" }]} relationList={[{ memoId: UNKNOWN_ID, relatedMemoId: memo.id, type: "COMMENT" }]}
onConfirm={handleCommentCreated} onConfirm={handleCommentCreated}
/> />
)} )}
</div>
</div> </div>
</div> </div>
</section> </section>

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

@ -42,82 +42,84 @@ const Setting = () => {
}; };
return ( 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 /> <MobileHeader />
<div className="setting-page-wrapper"> <div className="w-full px-4">
<div className="section-selector-container"> <div className="setting-page-wrapper">
<span className="section-title">{t("common.basic")}</span> <div className="section-selector-container">
<div className="section-items-container"> <span className="section-title">{t("common.basic")}</span>
<span <div className="section-items-container">
onClick={() => handleSectionSelectorItemClick("my-account")} <span
className={`section-item ${state.selectedSection === "my-account" ? "selected" : ""}`} 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> <Icon.User className="w-4 h-auto mr-2 opacity-80" /> {t("setting.my-account")}
<span </span>
onClick={() => handleSectionSelectorItemClick("preference")} <span
className={`section-item ${state.selectedSection === "preference" ? "selected" : ""}`} 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)}
> >
<Icon.Cog className="w-4 h-auto mr-2 opacity-80" /> {t("setting.preference")} {getSettingSectionList().map((settingSection) => (
</span> <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>
{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>
</div> </div>
</section> </section>

@ -104,7 +104,7 @@ const SignIn = () => {
}; };
return ( 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 py-4 grow flex flex-col justify-center items-center">
<div className="w-full flex flex-row justify-center items-center mb-6"> <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="" /> <img className="h-14 w-auto rounded-full shadow" src={systemStatus.customizedProfile.logoUrl} alt="" />

@ -70,7 +70,7 @@ const SignUp = () => {
}; };
return ( 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 py-4 grow flex flex-col justify-center items-center">
<div className="w-full flex flex-row justify-center items-center mb-6"> <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="" /> <img className="h-14 w-auto rounded-full shadow" src={systemStatus.customizedProfile.logoUrl} alt="" />

@ -64,73 +64,75 @@ const Timeline = () => {
}; };
return ( 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 /> <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="w-full px-4">
<div className="relative w-full flex flex-row justify-start items-center"> <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">
<p <div className="relative w-full flex flex-row justify-start items-center">
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" <p
onClick={() => toggleShowDatePicker()} 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)}
> >
{"Back to today"} <Icon.Calendar className="w-5 h-auto mr-2" />
</Button> <span className="font-mono mt-0.5">{new Date(selectedDateStamp).toLocaleDateString()}</span>
)} </p>
<DatePicker {selectedDateStamp !== currentDateStamp && (
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 ${ <Button
showDatePicker ? "" : "!hidden" variant="outlined"
}`} startDecorator={<Icon.Undo2 className="w-5 h-auto" />}
datestamp={selectedDateStamp} onClick={() => setSelectedDateStamp(currentDateStamp)}
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"> {"Back to today"}
<span className="opacity-80">{getTimeString(memo.displayTs)}</span> </Button>
<Icon.Dot className="w-5 h-auto opacity-60" /> )}
<span className="opacity-60">#{memo.id}</span> <DatePicker
</div> 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 ${
<MemoContentV1 content={memo.content} /> showDatePicker ? "" : "!hidden"
<MemoResourceListView resourceList={memo.resourceList} /> }`}
<MemoRelationListView memo={memo} relationList={memo.relationList.filter((relation) => relation.type === "REFERENCE")} /> datestamp={selectedDateStamp}
<div className="absolute left-1 sm:left-2 top-3 h-full"> isFutureDateDisabled
{index !== dailyMemos.length - 1 && ( handleDateStampChange={handleDataPickerChange}
<div className="absolute top-2 left-[7px] h-full w-0.5 bg-gray-400 dark:bg-gray-500 block"></div> handleClickAway={() => toggleShowDatePicker(false)}
)} />
<div className="border-4 rounded-full border-white relative dark:border-zinc-700"> </div>
<Icon.Circle className="w-2 h-auto bg-gray-400 text-gray-400 dark:bg-gray-500 dark:text-gray-500 rounded-full" /> <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>
</div> </div>
</div> ))}
))}
{selectedDateStamp === currentDateStamp && ( {selectedDateStamp === currentDateStamp && (
<div className="w-full pl-0 sm:pl-12 sm:mt-4"> <div className="w-full pl-0 sm:pl-12 sm:mt-4">
<MemoEditor cacheKey="timeline-editor" /> <MemoEditor cacheKey="timeline-editor" />
</div> </div>
)} )}
</div>
</div> </div>
</div> </div>
</div> </div>

@ -35,28 +35,20 @@ const UserProfile = () => {
}, [params.username]); }, [params.username]);
return ( 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 /> <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 && {!loadingState.isLoading &&
(user ? ( (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-col justify-start items-center py-8">
<div className="w-full flex flex-row justify-start items-start"> <UserAvatar className="!w-20 !h-20 mb-2 drop-shadow" avatarUrl={user?.avatarUrl} />
<div className="flex-grow shrink w-full"> <p className="text-3xl text-black opacity-80 dark:text-gray-200">{user?.nickname}</p>
<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> </div>
<MemoList />
</> </>
) : ( ) : (
<> <p>Not found</p>
<p>Not found</p>
</>
))} ))}
</div> </div>
</section> </section>

Loading…
Cancel
Save