diff --git a/web/src/components/ShortcutActionsDropdown.tsx b/web/src/components/ShortcutActionsDropdown.tsx new file mode 100644 index 0000000..a150742 --- /dev/null +++ b/web/src/components/ShortcutActionsDropdown.tsx @@ -0,0 +1,89 @@ +import { useState } from "react"; +import { shortcutService } from "../services"; +import useUserStore from "../stores/v1/user"; +import { showCommonDialog } from "./Alert"; +import AnalyticsDialog from "./AnalyticsDialog"; +import Dropdown from "./common/Dropdown"; +import CreateShortcutDialog from "./CreateShortcutDialog"; +import GenerateQRCodeDialog from "./GenerateQRCodeDialog"; +import Icon from "./Icon"; + +interface Props { + shortcut: Shortcut; +} + +const ShortcutActionsDropdown = (props: Props) => { + const { shortcut } = props; + const currentUser = useUserStore().getCurrentUser(); + const [showEditDialog, setShowEditDialog] = useState(false); + const [showQRCodeDialog, setShowQRCodeDialog] = useState(false); + const [showAnalyticsDialog, setShowAnalyticsDialog] = useState(false); + const havePermission = currentUser.role === "ADMIN" || shortcut.creatorId === currentUser.id; + + const handleDeleteShortcutButtonClick = (shortcut: Shortcut) => { + showCommonDialog({ + title: "Delete Shortcut", + content: `Are you sure to delete shortcut \`${shortcut.name}\`? You cannot undo this action.`, + style: "danger", + onConfirm: async () => { + await shortcutService.deleteShortcutById(shortcut.id); + }, + }); + }; + + return ( + <> + + {havePermission && ( + + )} + + + {havePermission && ( + + )} + + } + > + + {showEditDialog && ( + setShowEditDialog(false)} + onConfirm={() => setShowEditDialog(false)} + /> + )} + + {showQRCodeDialog && setShowQRCodeDialog(false)} />} + + {showAnalyticsDialog && setShowAnalyticsDialog(false)} />} + + ); +}; + +export default ShortcutActionsDropdown; diff --git a/web/src/components/ShortcutCard.tsx b/web/src/components/ShortcutCard.tsx new file mode 100644 index 0000000..ff20554 --- /dev/null +++ b/web/src/components/ShortcutCard.tsx @@ -0,0 +1,143 @@ +import { Tooltip } from "@mui/joy"; +import classNames from "classnames"; +import copy from "copy-to-clipboard"; +import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; +import { absolutifyLink } from "../helpers/utils"; +import useFaviconStore from "../stores/v1/favicon"; +import useViewStore from "../stores/v1/view"; +import AnalyticsDialog from "./AnalyticsDialog"; +import Icon from "./Icon"; +import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; +import VisibilityIcon from "./VisibilityIcon"; + +interface Props { + shortcut: Shortcut; +} + +const ShortcutView = (props: Props) => { + const { shortcut } = props; + const { t } = useTranslation(); + const viewStore = useViewStore(); + const faviconStore = useFaviconStore(); + const [favicon, setFavicon] = useState(undefined); + const [showAnalyticsDialog, setShowAnalyticsDialog] = useState(false); + const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); + + useEffect(() => { + faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => { + if (url) { + setFavicon(url); + } + }); + }, [shortcut.link]); + + const handleCopyButtonClick = () => { + copy(shortcutLink); + toast.success("Shortcut link copied to clipboard."); + }; + + return ( + <> +
+
+ +
+ +
+
+
+ {shortcut.tags.map((tag) => { + return ( + viewStore.setFilter({ tag: tag })} + > + #{tag} + + ); + })} + {shortcut.tags.length === 0 && No tags} +
+
+ +
+ + {shortcut.creator.nickname} +
+
+ +
viewStore.setFilter({ visibility: shortcut.visibility })} + > + + {t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)} +
+
+ +
setShowAnalyticsDialog(true)} + > + + {shortcut.view} visits +
+
+
+
+ + {showAnalyticsDialog && setShowAnalyticsDialog(false)} />} + + ); +}; + +export default ShortcutView; diff --git a/web/src/components/ShortcutView.tsx b/web/src/components/ShortcutView.tsx index 8a87fd0..5939824 100644 --- a/web/src/components/ShortcutView.tsx +++ b/web/src/components/ShortcutView.tsx @@ -1,39 +1,20 @@ -import { Tooltip } from "@mui/joy"; import classNames from "classnames"; -import copy from "copy-to-clipboard"; import { useEffect, useState } from "react"; -import toast from "react-hot-toast"; -import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { absolutifyLink } from "../helpers/utils"; -import { shortcutService } from "../services"; import useFaviconStore from "../stores/v1/favicon"; -import useUserStore from "../stores/v1/user"; -import useViewStore from "../stores/v1/view"; -import { showCommonDialog } from "./Alert"; -import AnalyticsDialog from "./AnalyticsDialog"; -import Dropdown from "./common/Dropdown"; -import GenerateQRCodeDialog from "./GenerateQRCodeDialog"; import Icon from "./Icon"; -import VisibilityIcon from "./VisibilityIcon"; +import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; interface Props { shortcut: Shortcut; - handleEdit: () => void; } const ShortcutView = (props: Props) => { - const { shortcut, handleEdit } = props; - const { t } = useTranslation(); - const currentUser = useUserStore().getCurrentUser(); - const viewStore = useViewStore(); + const { shortcut } = props; const faviconStore = useFaviconStore(); const [favicon, setFavicon] = useState(undefined); - const [showQRCodeDialog, setShowQRCodeDialog] = useState(false); - const [showAnalyticsDialog, setShowAnalyticsDialog] = useState(false); - const havePermission = currentUser.role === "ADMIN" || shortcut.creatorId === currentUser.id; const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); - const compactMode = viewStore.displayStyle === "compact"; useEffect(() => { faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => { @@ -43,48 +24,27 @@ const ShortcutView = (props: Props) => { }); }, [shortcut.link]); - const handleCopyButtonClick = () => { - copy(shortcutLink); - toast.success("Shortcut link copied to clipboard."); - }; - - const handleDeleteShortcutButtonClick = (shortcut: Shortcut) => { - showCommonDialog({ - title: "Delete Shortcut", - content: `Are you sure to delete shortcut \`${shortcut.name}\`? You cannot undo this action.`, - style: "danger", - onConfirm: async () => { - await shortcutService.deleteShortcutById(shortcut.id); - }, - }); - }; - return ( <>
- + {favicon ? ( ) : ( )} -
+
{ - {!compactMode && ( - - - - )}
- {!compactMode && ( - - {shortcut.link} - - )}
- {havePermission && ( - - - - - - - } - > - )} +
- - {!compactMode && ( - <> -
- {shortcut.tags.map((tag) => { - return ( - viewStore.setFilter({ tag: tag })} - > - #{tag} - - ); - })} - {shortcut.tags.length === 0 && No tags} -
-
- -
- - {shortcut.creator.nickname} -
-
- -
viewStore.setFilter({ visibility: shortcut.visibility })} - > - - {t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.self`)} -
-
- -
setShowAnalyticsDialog(true)} - > - - {shortcut.view} visits -
-
-
- - )}
- - {showQRCodeDialog && setShowQRCodeDialog(false)} />} - - {showAnalyticsDialog && setShowAnalyticsDialog(false)} />} ); }; diff --git a/web/src/components/ShortcutsContainer.tsx b/web/src/components/ShortcutsContainer.tsx index d39422f..e087333 100644 --- a/web/src/components/ShortcutsContainer.tsx +++ b/web/src/components/ShortcutsContainer.tsx @@ -1,7 +1,6 @@ import classNames from "classnames"; -import { useState } from "react"; import useViewStore from "../stores/v1/view"; -import CreateShortcutDialog from "./CreateShortcutDialog"; +import ShortcutCard from "./ShortcutCard"; import ShortcutView from "./ShortcutView"; interface Props { @@ -12,29 +11,19 @@ const ShortcutsContainer: React.FC = (props: Props) => { const { shortcutList } = props; const viewStore = useViewStore(); const displayStyle = viewStore.displayStyle || "full"; - const [editingShortcutId, setEditingShortcutId] = useState(); + const ShortcutItemView = viewStore.displayStyle === "compact" ? ShortcutView : ShortcutCard; return ( - <> -
- {shortcutList.map((shortcut) => { - return setEditingShortcutId(shortcut.id)} />; - })} -
- - {editingShortcutId && ( - setEditingShortcutId(undefined)} - onConfirm={() => setEditingShortcutId(undefined)} - /> +
+ > + {shortcutList.map((shortcut) => { + return ; + })} +
); }; diff --git a/web/src/components/common/Dropdown.tsx b/web/src/components/common/Dropdown.tsx index 11b5997..c3ea613 100644 --- a/web/src/components/common/Dropdown.tsx +++ b/web/src/components/common/Dropdown.tsx @@ -33,11 +33,16 @@ const Dropdown: React.FC = (props: Props) => { } }, [dropdownStatus]); + const handleToggleDropdownStatus = (e: React.MouseEvent) => { + e.stopPropagation(); + toggleDropdownStatus(); + }; + return (
toggleDropdownStatus()} + onClick={handleToggleDropdownStatus} > {trigger ? ( trigger