feat: update memo editor

pull/3729/head
Steven 10 months ago
parent d2727e6825
commit c313596144

@ -18,11 +18,11 @@ const getCellAdditionalStyles = (count: number, maxCount: number) => {
const ratio = count / maxCount;
if (ratio > 0.7) {
return "bg-teal-600 text-gray-100 dark:opacity-80";
return "bg-teal-700 text-gray-100 dark:opacity-80";
} else if (ratio > 0.4) {
return "bg-teal-400 text-gray-100 dark:opacity-80";
return "bg-teal-600 text-gray-100 dark:opacity-80";
} else {
return "bg-teal-300 text-gray-100 dark:opacity-80";
return "bg-teal-500 text-gray-100 dark:opacity-70";
}
};
@ -58,29 +58,36 @@ const ActivityCalendar = (props: Props) => {
const tooltipText = count ? t("memo.count-memos-in-date", { count: count, date: date }) : date;
const isSelected = new Date(props.selectedDate).toDateString() === new Date(date).toDateString();
return day ? (
<Tooltip className="shrink-0" key={`${date}-${index}`} title={tooltipText} placement="top" arrow>
count > 0 ? (
<Tooltip className="shrink-0" key={`${date}-${index}`} title={tooltipText} placement="top" arrow>
<div
className={clsx(
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default",
getCellAdditionalStyles(count, maxCount),
isToday && "border-zinc-400 dark:border-zinc-300",
isSelected && "font-bold border-zinc-400 dark:border-zinc-300",
!isToday && !isSelected && "border-transparent",
)}
onClick={() => count && onClick && onClick(new Date(date).toDateString())}
>
{day}
</div>
</Tooltip>
) : (
<div
key={`${date}-${index}`}
className={clsx(
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border",
getCellAdditionalStyles(count, maxCount),
isToday && "border-gray-600 dark:border-zinc-300",
isSelected && "font-bold border-gray-600 dark:border-zinc-300",
"w-6 h-6 text-xs rounded-xl flex justify-center items-center border cursor-default",
"bg-gray-100 text-gray-400 dark:bg-zinc-800 dark:text-gray-500",
isToday && "border-zinc-400 dark:border-zinc-500",
!isToday && !isSelected && "border-transparent",
count > 0 ? "cursor-pointer" : "cursor-default",
)}
onClick={() => count && onClick && onClick(new Date(date).toDateString())}
>
{day}
</div>
</Tooltip>
)
) : (
<div
key={`${date}-${index}`}
className={clsx(
"shrink-0 opacity-30 w-6 h-6 rounded-xl flex justify-center items-center border border-transparent",
getCellAdditionalStyles(count, maxCount),
)}
></div>
<div key={`${date}-${index}`} className={clsx("shrink-0 w-6 h-6 opacity-0", getCellAdditionalStyles(count, maxCount))}></div>
);
})}
</div>

@ -9,7 +9,6 @@ import { useMemoStore } from "@/store/v1";
import { RowStatus } from "@/types/proto/api/v1/common";
import { Memo } from "@/types/proto/api/v1/memo_service";
import { useTranslate } from "@/utils/i18n";
import showMemoEditorDialog from "./MemoEditor/MemoEditorDialog";
interface Props {
memo: Memo;
@ -55,12 +54,6 @@ const MemoActionMenu = (props: Props) => {
props.onEdit();
return;
}
// TODO: remove me later.
showMemoEditorDialog({
memoName: memo.name,
cacheKey: `${memo.name}-${memo.updateTime}`,
});
};
const handleToggleMemoStatusClick = async () => {
@ -125,7 +118,7 @@ const MemoActionMenu = (props: Props) => {
{memo.pinned ? t("common.unpin") : t("common.pin")}
</MenuItem>
)}
{!hiddenActions?.includes("edit") && (
{!hiddenActions?.includes("edit") && props.onEdit && (
<MenuItem onClick={handleEditMemoClick}>
<Icon.Edit3 className="w-4 h-auto" />
{t("common.edit")}

@ -1,103 +0,0 @@
import { IconButton } from "@mui/joy";
import clsx from "clsx";
import { useEffect, useRef } from "react";
import useCurrentUser from "@/hooks/useCurrentUser";
import useDateTime from "@/hooks/useDateTime";
import { useMemoStore, useTagStore } from "@/store/v1";
import { Memo } from "@/types/proto/api/v1/memo_service";
import MemoEditor, { Props as MemoEditorProps } from ".";
import { generateDialog } from "../Dialog";
import Icon from "../Icon";
interface Props extends DialogProps, MemoEditorProps {}
const MemoEditorDialog: React.FC<Props> = ({
memoName,
parentMemoName,
placeholder,
cacheKey,
relationList,
onConfirm,
destroy,
}: Props) => {
const tagStore = useTagStore();
const memoStore = useMemoStore();
const { setDateTime, displayDateTime, datePickerDateTime } = useDateTime(memoStore.getMemoByName(memoName || "")?.displayTime);
const memoPatchRef = useRef<Partial<Memo>>({
displayTime: memoStore.getMemoByName(memoName || "")?.displayTime,
});
const user = useCurrentUser();
useEffect(() => {
tagStore.fetchTags({ user }, { skipCache: false });
}, []);
const updateDisplayTime = (displayTime: string) => {
setDateTime(displayTime);
memoPatchRef.current.displayTime = new Date(displayTime);
};
const handleCloseBtnClick = () => {
destroy();
};
const handleConfirm = (memoName: string) => {
handleCloseBtnClick();
if (onConfirm) {
onConfirm(memoName);
}
};
return (
<>
<div className="w-full flex flex-row justify-between items-center">
<div className={clsx("flex flex-row justify-start items-center", !displayDateTime && "mb-2")}>
{displayDateTime ? (
<div className="relative">
<span className="cursor-pointer text-gray-500 dark:text-gray-400">{displayDateTime}</span>
<input
className="inset-0 absolute z-1 opacity-0"
type="datetime-local"
value={datePickerDateTime}
onFocus={(e: any) => e.target.showPicker()}
onChange={(e) => updateDisplayTime(e.target.value)}
/>
</div>
) : (
<>
<img className="w-6 h-auto rounded-full shadow" src={"/full-logo.webp"} alt="" />
<p className="ml-1 text-lg opacity-80 dark:text-gray-300">Memos</p>
</>
)}
</div>
<IconButton size="sm" onClick={handleCloseBtnClick}>
<Icon.X className="w-5 h-auto" />
</IconButton>
</div>
<div className="flex flex-col justify-start items-start max-w-full w-[40rem]">
<MemoEditor
className="border-none !p-0 -mb-2"
cacheKey={`memo-editor-${cacheKey || memoName}`}
memoName={memoName}
parentMemoName={parentMemoName}
placeholder={placeholder}
relationList={relationList}
memoPatchRef={memoPatchRef}
onConfirm={handleConfirm}
autoFocus
/>
</div>
</>
);
};
export default function showMemoEditorDialog(props: Partial<Props> = {}): void {
generateDialog(
{
className: "memo-editor-dialog",
dialogName: "memo-editor-dialog",
},
MemoEditorDialog,
props,
);
}

@ -1,4 +1,5 @@
import { Select, Option, Button, Divider } from "@mui/joy";
import { isEqual } from "lodash-es";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next";
@ -6,6 +7,7 @@ import useLocalStorage from "react-use/lib/useLocalStorage";
import { memoServiceClient } from "@/grpcweb";
import { TAB_SPACE_WIDTH } from "@/helpers/consts";
import { isValidUrl } from "@/helpers/utils";
import useAsyncEffect from "@/hooks/useAsyncEffect";
import useCurrentUser from "@/hooks/useCurrentUser";
import { useMemoStore, useResourceStore, useUserStore, useWorkspaceSettingStore } from "@/store/v1";
import { MemoRelation, MemoRelation_Type } from "@/types/proto/api/v1/memo_relation_service";
@ -32,11 +34,11 @@ export interface Props {
className?: string;
cacheKey?: string;
placeholder?: string;
// The name of the memo to be edited.
memoName?: string;
// The name of the parent memo if the memo is a comment.
parentMemoName?: string;
relationList?: MemoRelation[];
autoFocus?: boolean;
memoPatchRef?: React.MutableRefObject<Partial<Memo>>;
onConfirm?: (memoName: string) => void;
onCancel?: () => void;
}
@ -62,11 +64,12 @@ const MemoEditor = (props: Props) => {
const [state, setState] = useState<State>({
memoVisibility: Visibility.PRIVATE,
resourceList: [],
relationList: props.relationList ?? [],
relationList: [],
isUploadingResource: false,
isRequesting: false,
isComposing: false,
});
const [displayTime, setDisplayTime] = useState<Date | undefined>();
const [hasContent, setHasContent] = useState<boolean>(false);
const editorRef = useRef<EditorRefActions>(null);
const userSetting = userStore.userSetting as UserSetting;
@ -102,22 +105,24 @@ const MemoEditor = (props: Props) => {
}));
}, [userSetting.memoVisibility, workspaceMemoRelatedSetting.disallowPublicVisibility]);
useEffect(() => {
if (memoName) {
memoStore.getOrFetchMemoByName(memoName).then((memo) => {
if (memo) {
handleEditorFocus();
setState((prevState) => ({
...prevState,
memoVisibility: memo.visibility,
resourceList: memo.resources,
relationList: memo.relations,
}));
if (!contentCache) {
editorRef.current?.setContent(memo.content ?? "");
}
}
});
useAsyncEffect(async () => {
if (!memoName) {
return;
}
const memo = await memoStore.getOrFetchMemoByName(memoName);
if (memo) {
handleEditorFocus();
setState((prevState) => ({
...prevState,
memoVisibility: memo.visibility,
resourceList: memo.resources,
relationList: memo.relations,
}));
setDisplayTime(memo.displayTime);
if (!contentCache) {
editorRef.current?.setContent(memo.content ?? "");
}
}
}, [memoName]);
@ -289,18 +294,16 @@ const MemoEditor = (props: Props) => {
const prevMemo = await memoStore.getOrFetchMemoByName(memoName);
if (prevMemo) {
const updateMask = ["content", "visibility"];
if (props.memoPatchRef?.current?.displayTime) {
const memoPatch: Partial<Memo> = {
name: prevMemo.name,
content,
visibility: state.memoVisibility,
};
if (!isEqual(displayTime, prevMemo.displayTime)) {
updateMask.push("display_time");
memoPatch.displayTime = displayTime;
}
const memo = await memoStore.updateMemo(
{
name: prevMemo.name,
content,
visibility: state.memoVisibility,
...props.memoPatchRef?.current,
},
updateMask,
);
const memo = await memoStore.updateMemo(memoPatch, updateMask);
await memoServiceClient.setMemoResources({
name: memo.name,
resources: state.resourceList,
@ -409,6 +412,18 @@ const MemoEditor = (props: Props) => {
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
>
{memoName && displayTime && (
<div className="relative text-sm">
<span className="cursor-pointer text-gray-400 dark:text-gray-500">{displayTime.toLocaleString()}</span>
<input
className="inset-0 absolute z-1 opacity-0"
type="datetime-local"
value={displayTime.toLocaleString()}
onFocus={(e: any) => e.target.showPicker()}
onChange={(e) => setDisplayTime(new Date(e.target.value))}
/>
</div>
)}
<Editor ref={editorRef} {...editorConfig} />
<ResourceListView resourceList={state.resourceList} setResourceList={handleSetResourceList} />
<RelationListView relationList={referenceRelations} setRelationList={handleSetRelationList} />

@ -44,7 +44,7 @@ const MemoRelationListView = (props: Props) => {
}
return (
<div className="relative flex flex-col justify-start items-start w-full px-2 pt-2 pb-1 bg-zinc-50 dark:bg-zinc-900 rounded-lg border border-gray-200 dark:border-zinc-700">
<div className="relative flex flex-col justify-start items-start w-full px-2 pt-2 pb-1.5 bg-zinc-50 dark:bg-zinc-900 rounded-lg border border-gray-200 dark:border-zinc-700">
<div className="w-full flex flex-row justify-start items-center mb-1 gap-3 opacity-60">
{referencingMemoList.length > 0 && (
<button
@ -56,6 +56,7 @@ const MemoRelationListView = (props: Props) => {
>
<Icon.Link className="w-3 h-auto shrink-0 opacity-70" />
<span>Referencing</span>
<span className="opacity-80">({referencingMemoList.length})</span>
</button>
)}
{referencedMemoList.length > 0 && (
@ -68,6 +69,7 @@ const MemoRelationListView = (props: Props) => {
>
<Icon.Milestone className="w-3 h-auto shrink-0 opacity-70" />
<span>Referenced by</span>
<span className="opacity-80">({referencedMemoList.length})</span>
</button>
)}
</div>
@ -81,7 +83,7 @@ const MemoRelationListView = (props: Props) => {
to={`/m/${memo.uid}`}
unstable_viewTransition
>
<Icon.Dot className="shrink-0 -ml-1 opacity-60" />
<Icon.Dot className="shrink-0 w-4 h-auto opacity-40" />
<span className="truncate">{memo.snippet}</span>
</Link>
);
@ -98,7 +100,7 @@ const MemoRelationListView = (props: Props) => {
to={`/m/${memo.uid}`}
unstable_viewTransition
>
<Icon.Dot className="shrink-0 -ml-1 opacity-60" />
<Icon.Dot className="shrink-0 w-4 h-auto opacity-40" />
<span className="truncate">{memo.snippet}</span>
</Link>
);

@ -106,77 +106,6 @@ const MemoView: React.FC<Props> = (props: Props) => {
)}
ref={memoContainerRef}
>
<div className="w-full flex flex-row justify-between items-center gap-2">
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
{props.showCreator && creator ? (
<div className="w-full flex flex-row justify-start items-center">
<Link className="w-auto hover:opacity-80" to={`/u/${encodeURIComponent(creator.username)}`} unstable_viewTransition>
<UserAvatar className="mr-2 shrink-0" avatarUrl={creator.avatarUrl} />
</Link>
<div className="w-full flex flex-col justify-center items-start">
<Link
className="w-full block leading-tight hover:opacity-80 truncate text-gray-600 dark:text-gray-400"
to={`/u/${encodeURIComponent(creator.username)}`}
unstable_viewTransition
>
{creator.nickname || creator.username}
</Link>
<div
className="w-auto -mt-0.5 text-xs leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick={handleGotoMemoDetailPage}
>
{displayTime}
</div>
</div>
</div>
) : (
<div className="w-full text-sm leading-tight text-gray-400 dark:text-gray-500 select-none" onClick={handleGotoMemoDetailPage}>
{displayTime}
</div>
)}
</div>
{!showEditor && (
<div className="flex flex-row justify-end items-center select-none shrink-0 gap-2">
<div className="w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2">
{props.showVisibility && memo.visibility !== Visibility.PRIVATE && (
<Tooltip title={t(`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}` as any)} placement="top">
<span className="flex justify-center items-center hover:opacity-70">
<VisibilityIcon visibility={memo.visibility} />
</span>
</Tooltip>
)}
{currentUser && <ReactionSelector className="border-none w-auto h-auto" memo={memo} />}
</div>
{!isInMemoDetailPage && (
<Link
className={clsx(
"flex flex-row justify-start items-center hover:opacity-70",
commentAmount === 0 && "invisible group-hover:visible",
)}
to={`/m/${memo.uid}#comments`}
unstable_viewTransition
>
<Icon.MessageCircleMore className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" />
{commentAmount > 0 && <span className="text-xs text-gray-500 dark:text-gray-400">{commentAmount}</span>}
</Link>
)}
{props.showPinned && memo.pinned && (
<Tooltip title={t("common.pinned")} placement="top">
<Icon.Bookmark className="w-4 h-auto text-amber-500" />
</Tooltip>
)}
{!readonly && (
<MemoActionMenu
className="-ml-1"
memo={memo}
hiddenActions={props.showPinned ? [] : ["pin"]}
onEdit={() => setShowEditor(true)}
/>
)}
</div>
)}
</div>
{showEditor ? (
<MemoEditor
autoFocus
@ -188,6 +117,77 @@ const MemoView: React.FC<Props> = (props: Props) => {
/>
) : (
<>
<div className="w-full flex flex-row justify-between items-center gap-2">
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
{props.showCreator && creator ? (
<div className="w-full flex flex-row justify-start items-center">
<Link className="w-auto hover:opacity-80" to={`/u/${encodeURIComponent(creator.username)}`} unstable_viewTransition>
<UserAvatar className="mr-2 shrink-0" avatarUrl={creator.avatarUrl} />
</Link>
<div className="w-full flex flex-col justify-center items-start">
<Link
className="w-full block leading-tight hover:opacity-80 truncate text-gray-600 dark:text-gray-400"
to={`/u/${encodeURIComponent(creator.username)}`}
unstable_viewTransition
>
{creator.nickname || creator.username}
</Link>
<div
className="w-auto -mt-0.5 text-xs leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick={handleGotoMemoDetailPage}
>
{displayTime}
</div>
</div>
</div>
) : (
<div
className="w-full text-sm leading-tight text-gray-400 dark:text-gray-500 select-none"
onClick={handleGotoMemoDetailPage}
>
{displayTime}
</div>
)}
</div>
<div className="flex flex-row justify-end items-center select-none shrink-0 gap-2">
<div className="w-auto invisible group-hover:visible flex flex-row justify-between items-center gap-2">
{props.showVisibility && memo.visibility !== Visibility.PRIVATE && (
<Tooltip title={t(`memo.visibility.${convertVisibilityToString(memo.visibility).toLowerCase()}` as any)} placement="top">
<span className="flex justify-center items-center hover:opacity-70">
<VisibilityIcon visibility={memo.visibility} />
</span>
</Tooltip>
)}
{currentUser && <ReactionSelector className="border-none w-auto h-auto" memo={memo} />}
</div>
{!isInMemoDetailPage && (
<Link
className={clsx(
"flex flex-row justify-start items-center hover:opacity-70",
commentAmount === 0 && "invisible group-hover:visible",
)}
to={`/m/${memo.uid}#comments`}
unstable_viewTransition
>
<Icon.MessageCircleMore className="w-4 h-4 mx-auto text-gray-500 dark:text-gray-400" />
{commentAmount > 0 && <span className="text-xs text-gray-500 dark:text-gray-400">{commentAmount}</span>}
</Link>
)}
{props.showPinned && memo.pinned && (
<Tooltip title={t("common.pinned")} placement="top">
<Icon.Bookmark className="w-4 h-auto text-amber-500" />
</Tooltip>
)}
{!readonly && (
<MemoActionMenu
className="-ml-1"
memo={memo}
hiddenActions={props.showPinned ? [] : ["pin"]}
onEdit={() => setShowEditor(true)}
/>
)}
</div>
</div>
<MemoContent
key={`${memo.name}-${memo.updateTime}`}
memoName={memo.name}

@ -12,6 +12,7 @@ import { useMemoStore } from "@/store/v1";
import { useTranslate } from "@/utils/i18n";
import ActivityCalendar from "./ActivityCalendar";
import Icon from "./Icon";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/Popover";
interface UserMemoStats {
link: number;
@ -26,15 +27,14 @@ const UserStatisticsView = () => {
const memoStore = useMemoStore();
const filterStore = useFilterStore();
const [memoAmount, setMemoAmount] = useState(0);
const [isRequesting, setIsRequesting] = useState(false);
const [memoStats, setMemoStats] = useState<UserMemoStats>({ link: 0, taskList: 0, code: 0, incompleteTasks: 0 });
const [activityStats, setActivityStats] = useState<Record<string, number>>({});
const monthString = dayjs(new Date().toDateString()).format("YYYY-MM");
const [selectedDate] = useState(new Date());
const [monthString, setMonthString] = useState(dayjs(selectedDate.toDateString()).format("YYYY-MM"));
const days = Math.ceil((Date.now() - currentUser.createTime!.getTime()) / 86400000);
const filter = filterStore.state;
useAsyncEffect(async () => {
setIsRequesting(true);
const { properties } = await memoServiceClient.listMemoProperties({
name: `memos/-`,
});
@ -62,7 +62,6 @@ const UserStatisticsView = () => {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
filter: filters.join(" && "),
});
setActivityStats(
Object.fromEntries(
Object.entries(stats).filter(([date]) => {
@ -70,7 +69,6 @@ const UserStatisticsView = () => {
}),
),
);
setIsRequesting(false);
}, [memoStore.stateId]);
const handleRebuildMemoTags = async () => {
@ -83,92 +81,94 @@ const UserStatisticsView = () => {
return (
<div className="group w-full border mt-2 py-2 px-3 rounded-lg space-y-0.5 text-gray-500 dark:text-gray-400 bg-zinc-50 dark:bg-zinc-900 dark:border-zinc-800">
<div className="w-full mb-1 flex flex-row justify-between items-center">
<p className="text-sm font-medium leading-6 dark:text-gray-400">
{new Date().toLocaleDateString(i18n.language, { month: "long", day: "numeric" })}
</p>
<div className="group-hover:block hidden">
<Tooltip title={"Refresh"} placement="top">
<Icon.RefreshCcw
className="text-gray-400 w-4 h-auto cursor-pointer opacity-60 hover:opacity-100"
onClick={handleRebuildMemoTags}
/>
</Tooltip>
<div className="w-full mb-2 flex flex-row justify-between items-center">
<div className="relative text-base font-medium leading-6 flex flex-row items-center dark:text-gray-400">
<Icon.CalendarDays className="w-5 h-auto mr-1 opacity-60" strokeWidth={1.5} />
<span>{new Date(monthString).toLocaleString(i18n.language, { year: "numeric", month: "long" })}</span>
<input
className="inset-0 absolute z-1 opacity-0"
type="month"
value={monthString}
onFocus={(e: any) => e.target.showPicker()}
onChange={(e) => setMonthString(e.target.value)}
/>
</div>
<div className="invisible group-hover:visible flex justify-end items-center">
<Popover>
<PopoverTrigger>
<Icon.MoreVertical className="w-4 h-auto shrink-0 opacity-60" />
</PopoverTrigger>
<PopoverContent>
<button className="w-auto flex flex-row justify-between items-center gap-2 hover:opacity-80" onClick={handleRebuildMemoTags}>
<Icon.RefreshCcw className="text-gray-400 w-4 h-auto cursor-pointer opacity-60" />
<span className="text-sm shrink-0 text-gray-500 dark:text-gray-400">Refresh</span>
</button>
</PopoverContent>
</Popover>
</div>
</div>
<div className="w-full pb-2">
<ActivityCalendar month={monthString} selectedDate={new Date().toDateString()} data={activityStats} />
<div className="w-full">
<ActivityCalendar month={monthString} selectedDate={selectedDate.toDateString()} data={activityStats} />
{memoAmount > 0 && (
<p className="mt-1 w-full text-xs italic opacity-80">
<span>{memoAmount}</span> memos in <span>{days}</span> days
</p>
)}
</div>
<div className="w-full grid grid-cols-1 gap-x-4">
<div className="w-full flex justify-between items-center">
<div className="w-auto flex justify-start items-center">
<Icon.CalendarDays className="w-4 h-auto mr-1" />
<span className="block text-base sm:text-sm">Days</span>
<Divider className="!my-2 opacity-50" />
<div className="w-full flex flex-row justify-start items-center gap-x-2 gap-y-1 flex-wrap">
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasLink ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasLink: !filter.memoPropertyFilter?.hasLink })}
>
<div className="w-auto flex justify-start items-center mr-1">
<Icon.Link className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.links")}</span>
</div>
<span>{days}</span>
<span className="text-sm truncate">{memoStats.link}</span>
</div>
<div className="w-full flex justify-between items-center">
<div className="w-auto flex justify-start items-center">
<Icon.Library className="w-4 h-auto mr-1" />
<span className="block text-base sm:text-sm">Memos</span>
</div>
{isRequesting ? <Icon.Loader className="animate-spin w-4 h-auto text-gray-400" /> : <span className="">{memoAmount}</span>}
</div>
<Divider className="!my-1 opacity-50" />
<div className="w-full mt-1 flex flex-row justify-start items-center gap-x-2 gap-y-1 flex-wrap">
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasLink ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasLink: !filter.memoPropertyFilter?.hasLink })}
>
<div className="w-auto flex justify-start items-center mr-1">
<Icon.Link className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.links")}</span>
</div>
<span className="text-sm truncate">{memoStats.link}</span>
</div>
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasTaskList ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasTaskList: !filter.memoPropertyFilter?.hasTaskList })}
>
<div className="w-auto flex justify-start items-center mr-1">
{memoStats.incompleteTasks > 0 ? (
<Icon.ListTodo className="w-4 h-auto mr-1" />
) : (
<Icon.CheckCircle className="w-4 h-auto mr-1" />
)}
<span className="block text-sm">{t("memo.to-do")}</span>
</div>
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasTaskList ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasTaskList: !filter.memoPropertyFilter?.hasTaskList })}
>
<div className="w-auto flex justify-start items-center mr-1">
{memoStats.incompleteTasks > 0 ? (
<Tooltip title={"Done / Total"} placement="top" arrow>
<div className="text-sm flex flex-row items-start justify-center">
<span className="truncate">{memoStats.taskList - memoStats.incompleteTasks}</span>
<span className="font-mono opacity-50">/</span>
<span className="truncate">{memoStats.taskList}</span>
</div>
</Tooltip>
<Icon.ListTodo className="w-4 h-auto mr-1" />
) : (
<span className="text-sm truncate">{memoStats.taskList}</span>
<Icon.CheckCircle className="w-4 h-auto mr-1" />
)}
<span className="block text-sm">{t("memo.to-do")}</span>
</div>
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasCode ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasCode: !filter.memoPropertyFilter?.hasCode })}
>
<div className="w-auto flex justify-start items-center mr-1">
<Icon.Code2 className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.code")}</span>
</div>
<span className="text-sm truncate">{memoStats.code}</span>
{memoStats.incompleteTasks > 0 ? (
<Tooltip title={"Done / Total"} placement="top" arrow>
<div className="text-sm flex flex-row items-start justify-center">
<span className="truncate">{memoStats.taskList - memoStats.incompleteTasks}</span>
<span className="font-mono opacity-50">/</span>
<span className="truncate">{memoStats.taskList}</span>
</div>
</Tooltip>
) : (
<span className="text-sm truncate">{memoStats.taskList}</span>
)}
</div>
<div
className={clsx(
"w-auto border dark:border-zinc-800 pl-1 pr-1.5 rounded-md flex justify-between items-center cursor-pointer hover:shadow",
filter.memoPropertyFilter?.hasCode ? "bg-blue-50 dark:bg-blue-900 shadow" : "",
)}
onClick={() => filterStore.setMemoPropertyFilter({ hasCode: !filter.memoPropertyFilter?.hasCode })}
>
<div className="w-auto flex justify-start items-center mr-1">
<Icon.Code2 className="w-4 h-auto mr-1" />
<span className="block text-sm">{t("memo.code")}</span>
</div>
<span className="text-sm truncate">{memoStats.code}</span>
</div>
</div>
</div>

@ -4,4 +4,3 @@ export * from "./useNavigateTo";
export * from "./useAsyncEffect";
export * from "./useFilterWithUrlParams";
export * from "./useResponsiveWidth";
export * from "./useDateTime";

@ -1,13 +0,0 @@
import { useState } from "react";
const useDateTime = (initalState?: Date) => {
const [dateTime, setDateTimeInternal] = useState<Date | undefined>(initalState && new Date(initalState));
return {
setDateTime: (dateTimeString: string) => setDateTimeInternal(new Date(dateTimeString)),
displayDateTime: dateTime && dateTime.toLocaleString(),
datePickerDateTime: dateTime && new Date(dateTime.getTime() - dateTime.getTimezoneOffset() * 60000).toISOString().split(".")[0],
};
};
export default useDateTime;
Loading…
Cancel
Save