feat: add visibility select in ShareMemoDialog (#2941)

In ShareMemoDialog, user can change the visibility
of the memo, so that the memo can be set to public
to be viewed by anyone with the link.
This commit is contained in:
Brilliant Hanabi
2024-02-13 09:28:16 +08:00
committed by GitHub
parent b3ca9969c4
commit ebe3678288
3 changed files with 68 additions and 26 deletions

View File

@ -101,7 +101,7 @@ const MemoActionMenu = (props: Props) => {
<Icon.Edit3 className="w-4 h-auto" /> <Icon.Edit3 className="w-4 h-auto" />
{t("common.edit")} {t("common.edit")}
</MenuItem> </MenuItem>
<MenuItem onClick={() => showShareMemoDialog(memo)}> <MenuItem onClick={() => showShareMemoDialog(memo.id)}>
<Icon.Share className="w-4 h-auto" /> <Icon.Share className="w-4 h-auto" />
{t("common.share")} {t("common.share")}
</MenuItem> </MenuItem>

View File

@ -1,33 +1,40 @@
import { Button, IconButton } from "@mui/joy"; import { Button, IconButton, Select, Option } from "@mui/joy";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { getDateTimeString } from "@/helpers/datetime"; import { getDateTimeString } from "@/helpers/datetime";
import { downloadFileFromUrl } from "@/helpers/utils"; import { downloadFileFromUrl } from "@/helpers/utils";
import useCurrentUser from "@/hooks/useCurrentUser";
import useLoading from "@/hooks/useLoading"; import useLoading from "@/hooks/useLoading";
import toImage from "@/labs/html2image"; import toImage from "@/labs/html2image";
import { useUserStore, extractUsernameFromName } from "@/store/v1"; import { useUserStore, extractUsernameFromName, useMemoStore } from "@/store/v1";
import { Memo, Visibility } from "@/types/proto/api/v2/memo_service"; import { Visibility } from "@/types/proto/api/v2/memo_service";
import { useTranslate } from "@/utils/i18n"; import { useTranslate } from "@/utils/i18n";
import { convertVisibilityToString } from "@/utils/memo";
import { generateDialog } from "./Dialog"; import { generateDialog } from "./Dialog";
import Icon from "./Icon"; import Icon from "./Icon";
import MemoContent from "./MemoContent"; import MemoContent from "./MemoContent";
import MemoResourceListView from "./MemoResourceListView"; import MemoResourceListView from "./MemoResourceListView";
import UserAvatar from "./UserAvatar"; import UserAvatar from "./UserAvatar";
import VisibilityIcon from "./VisibilityIcon";
import "@/less/share-memo-dialog.less"; import "@/less/share-memo-dialog.less";
interface Props extends DialogProps { interface Props extends DialogProps {
memo: Memo; memoId: number;
} }
const ShareMemoDialog: React.FC<Props> = (props: Props) => { const ShareMemoDialog: React.FC<Props> = (props: Props) => {
const { memo, destroy } = props; const { memoId, destroy } = props;
const t = useTranslate(); const t = useTranslate();
const userStore = useUserStore(); const userStore = useUserStore();
const downloadingImageState = useLoading(false); const downloadingImageState = useLoading(false);
const loadingState = useLoading(); const loadingState = useLoading();
const memoElRef = useRef<HTMLDivElement>(null); const memoElRef = useRef<HTMLDivElement>(null);
const memoStore = useMemoStore();
const memo = memoStore.getMemoById(memoId);
const user = userStore.getUserByUsername(extractUsernameFromName(memo.creator)); const user = userStore.getUserByUsername(extractUsernameFromName(memo.creator));
const currentUser = useCurrentUser();
const readonly = memo?.creatorId !== currentUser?.id;
useEffect(() => { useEffect(() => {
(async () => { (async () => {
@ -75,6 +82,20 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
} }
}; };
const handleMemoVisibilityOptionChanged = async (visibility: Visibility) => {
const updatedMemo = await memoStore.updateMemo(
{
id: memo.id,
visibility: visibility,
},
["visibility"],
);
if (updatedMemo.visibility == visibility) {
toast.success(t("common.changed"));
}
};
if (loadingState.isLoading) { if (loadingState.isLoading) {
return null; return null;
} }
@ -88,7 +109,8 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
</IconButton> </IconButton>
</div> </div>
<div className="dialog-content-container w-full flex flex-col justify-start items-start relative"> <div className="dialog-content-container w-full flex flex-col justify-start items-start relative">
<div className="px-4 pb-3 w-full flex flex-row justify-start items-center space-x-2"> <div className="px-4 pb-3 w-full flex flex-row justify-between items-center space-x-2">
<div className="flex flex-row justify-start items-center space-x-2">
<Button color="neutral" variant="outlined" disabled={downloadingImageState.isLoading} onClick={handleDownloadImageBtnClick}> <Button color="neutral" variant="outlined" disabled={downloadingImageState.isLoading} onClick={handleDownloadImageBtnClick}>
{downloadingImageState.isLoading ? ( {downloadingImageState.isLoading ? (
<Icon.Loader className="w-4 h-auto mr-1 animate-spin" /> <Icon.Loader className="w-4 h-auto mr-1 animate-spin" />
@ -106,6 +128,26 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
{t("common.link")} {t("common.link")}
</Button> </Button>
</div> </div>
{!readonly && (
<Select
className="w-auto text-sm"
variant="plain"
value={memo.visibility}
startDecorator={<VisibilityIcon visibility={memo.visibility} />}
onChange={(_, visibility) => {
if (visibility) {
handleMemoVisibilityOptionChanged(visibility);
}
}}
>
{[Visibility.PRIVATE, Visibility.PROTECTED, Visibility.PUBLIC].map((item) => (
<Option key={item} value={item} className="whitespace-nowrap">
{t(`memo.visibility.${convertVisibilityToString(item).toLowerCase()}` as any)}
</Option>
))}
</Select>
)}
</div>
<div className="w-full border-t dark:border-zinc-700 overflow-clip"> <div className="w-full border-t dark:border-zinc-700 overflow-clip">
<div <div
className="w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800" className="w-full h-auto select-none relative flex flex-col justify-start items-start bg-white dark:bg-zinc-800"
@ -134,13 +176,13 @@ const ShareMemoDialog: React.FC<Props> = (props: Props) => {
); );
}; };
export default function showShareMemoDialog(memo: Memo): void { export default function showShareMemoDialog(memoId: number): void {
generateDialog( generateDialog(
{ {
className: "share-memo-dialog", className: "share-memo-dialog",
dialogName: "share-memo-dialog", dialogName: "share-memo-dialog",
}, },
ShareMemoDialog, ShareMemoDialog,
{ memo }, { memoId },
); );
} }

View File

@ -180,7 +180,7 @@ const MemoDetail = () => {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<Tooltip title={"Share"} placement="top"> <Tooltip title={"Share"} placement="top">
<IconButton size="sm" onClick={() => showShareMemoDialog(memo)}> <IconButton size="sm" onClick={() => showShareMemoDialog(memo.id)}>
<Icon.Share className="w-4 h-auto text-gray-600 dark:text-gray-400" /> <Icon.Share className="w-4 h-auto text-gray-600 dark:text-gray-400" />
</IconButton> </IconButton>
</Tooltip> </Tooltip>