chore: update shortcut view

This commit is contained in:
Steven 2023-11-12 11:34:09 +08:00
parent 4a25fbb2f6
commit cb3e3bfaef
8 changed files with 68 additions and 121 deletions

View File

@ -28,7 +28,7 @@ const ShortcutView = (props: Props) => {
<div className="w-full flex flex-row justify-start items-center"> <div className="w-full flex flex-row justify-start items-center">
<span className={classNames("w-5 h-5 flex justify-center items-center overflow-clip shrink-0")}> <span className={classNames("w-5 h-5 flex justify-center items-center overflow-clip shrink-0")}>
{favicon ? ( {favicon ? (
<img className="w-full h-auto rounded-full" src={favicon} decoding="async" loading="lazy" /> <img className="w-full h-auto rounded-lg" src={favicon} decoding="async" loading="lazy" />
) : ( ) : (
<Icon.CircleSlash className="w-full h-auto text-gray-400" /> <Icon.CircleSlash className="w-full h-auto text-gray-400" />
)} )}

View File

@ -3,13 +3,15 @@ import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo"; import useNavigateTo from "@/hooks/useNavigateTo";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useAppSelector } from "@/stores"; import { useAppSelector } from "@/stores";
import useCollectionStore from "@/stores/v1/collection"; import useCollectionStore from "@/stores/v1/collection";
import { Collection } from "@/types/proto/api/v2/collection_service"; import { Collection } from "@/types/proto/api/v2/collection_service";
import { Visibility } from "@/types/proto/api/v2/common"; import { Visibility } from "@/types/proto/api/v2/common";
import { showCommonDialog } from "./Alert"; import { showCommonDialog } from "./Alert";
import CreateCollectionDialog, { ShortcutItem } from "./CreateCollectionDialog"; import CreateCollectionDialog from "./CreateCollectionDialog";
import Icon from "./Icon"; import Icon from "./Icon";
import ShortcutView from "./ShortcutView";
import Dropdown from "./common/Dropdown"; import Dropdown from "./common/Dropdown";
interface Props { interface Props {
@ -19,6 +21,7 @@ interface Props {
const CollectionView = (props: Props) => { const CollectionView = (props: Props) => {
const { collection } = props; const { collection } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const { sm } = useResponsiveWidth();
const navigateTo = useNavigateTo(); const navigateTo = useNavigateTo();
const collectionStore = useCollectionStore(); const collectionStore = useCollectionStore();
const { shortcutList } = useAppSelector((state) => state.shortcut); const { shortcutList } = useAppSelector((state) => state.shortcut);
@ -82,7 +85,9 @@ const CollectionView = (props: Props) => {
</div> </div>
<div className="w-full p-3 flex flex-row justify-start items-start flex-wrap gap-3"> <div className="w-full p-3 flex flex-row justify-start items-start flex-wrap gap-3">
{shortcuts.map((shortcut) => { {shortcuts.map((shortcut) => {
return <ShortcutItem key={shortcut.id} shortcut={shortcut} onClick={() => handleShortcutClick(shortcut)} />; return (
<ShortcutView key={shortcut.id} shortcut={shortcut} alwaysShowLink={!sm} onClick={() => handleShortcutClick(shortcut)} />
);
})} })}
</div> </div>
</div> </div>

View File

@ -1,12 +1,8 @@
import { Button, Input, Modal, ModalDialog, Radio, RadioGroup } from "@mui/joy"; import { Button, Input, Modal, ModalDialog, Radio, RadioGroup } from "@mui/joy";
import classNames from "classnames";
import { isUndefined } from "lodash-es"; import { isUndefined } from "lodash-es";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { getFaviconWithGoogleS2 } from "@/helpers/utils";
import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import { useAppSelector } from "@/stores"; import { useAppSelector } from "@/stores";
import useCollectionStore from "@/stores/v1/collection"; import useCollectionStore from "@/stores/v1/collection";
import { Collection } from "@/types/proto/api/v2/collection_service"; import { Collection } from "@/types/proto/api/v2/collection_service";
@ -14,6 +10,7 @@ import { Visibility } from "@/types/proto/api/v2/common";
import { convertVisibilityFromPb } from "@/utils/visibility"; import { convertVisibilityFromPb } from "@/utils/visibility";
import useLoading from "../hooks/useLoading"; import useLoading from "../hooks/useLoading";
import Icon from "./Icon"; import Icon from "./Icon";
import ShortcutView from "./ShortcutView";
interface Props { interface Props {
collectionId?: number; collectionId?: number;
@ -206,7 +203,7 @@ const CreateCollectionDialog: React.FC<Props> = (props: Props) => {
<div className="w-full py-1 flex flex-row justify-start items-start flex-wrap overflow-hidden gap-2"> <div className="w-full py-1 flex flex-row justify-start items-start flex-wrap overflow-hidden gap-2">
{selectedShortcuts.map((shortcut) => { {selectedShortcuts.map((shortcut) => {
return ( return (
<ShortcutItem <ShortcutView
key={shortcut.id} key={shortcut.id}
className="bg-gray-100 shadow dark:bg-zinc-800 dark:border-zinc-700 dark:text-gray-400" className="bg-gray-100 shadow dark:bg-zinc-800 dark:border-zinc-700 dark:text-gray-400"
shortcut={shortcut} shortcut={shortcut}
@ -218,7 +215,7 @@ const CreateCollectionDialog: React.FC<Props> = (props: Props) => {
})} })}
{unselectedShortcuts.map((shortcut) => { {unselectedShortcuts.map((shortcut) => {
return ( return (
<ShortcutItem <ShortcutView
key={shortcut.id} key={shortcut.id}
className="border-dashed" className="border-dashed"
shortcut={shortcut} shortcut={shortcut}
@ -245,58 +242,4 @@ const CreateCollectionDialog: React.FC<Props> = (props: Props) => {
); );
}; };
interface ShortcutItemProps {
shortcut: Shortcut;
className?: string;
onClick?: () => void;
}
export const ShortcutItem = (props: ShortcutItemProps) => {
const { shortcut, className, onClick } = props;
const { sm } = useResponsiveWidth();
const favicon = getFaviconWithGoogleS2(shortcut.link);
return (
<div
className={classNames(
"group w-auto select-none px-2 py-1 flex flex-row justify-start items-center border rounded-lg hover:bg-gray-100 dark:border-zinc-800 dark:hover:bg-zinc-800 cursor-pointer",
className
)}
onClick={onClick}
>
<span className={classNames("w-5 h-5 flex justify-center items-center overflow-clip shrink-0")}>
{favicon ? (
<img className="w-full h-auto rounded-full" src={favicon} decoding="async" loading="lazy" />
) : (
<Icon.CircleSlash className="w-full h-auto text-gray-400" />
)}
</span>
<div className="ml-1 w-full flex flex-col justify-start items-start truncate">
<div className="w-full flex flex-row justify-start items-center">
<span className={classNames("max-w-full flex flex-row px-1 justify-start items-center rounded-md")}>
<div className="truncate">
<span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? (
<span className="text-gray-500">(s/{shortcut.name})</span>
) : (
<>
<span className="text-gray-400 dark:text-gray-500">s/</span>
<span className="truncate dark:text-gray-400">{shortcut.name}</span>
</>
)}
</div>
</span>
</div>
</div>
<Link
className={classNames("w-6 h-6 p-1 rounded-lg bg-gray-200 dark:bg-zinc-900 hover:opacity-80", sm && "hidden group-hover:block")}
to={`/s/${shortcut.name}`}
target="_blank"
>
<Icon.ArrowUpRight className="w-4 h-auto text-gray-400 shrink-0" />
</Link>
</div>
);
};
export default CreateCollectionDialog; export default CreateCollectionDialog;

View File

@ -37,7 +37,7 @@ const ShortcutCard = (props: Props) => {
<div className="w-[calc(100%-16px)] flex flex-row justify-start items-center mr-1 shrink-0"> <div className="w-[calc(100%-16px)] flex flex-row justify-start items-center mr-1 shrink-0">
<Link to={`/shortcut/${shortcut.id}`} className={classNames("w-8 h-8 flex justify-center items-center overflow-clip shrink-0")}> <Link to={`/shortcut/${shortcut.id}`} className={classNames("w-8 h-8 flex justify-center items-center overflow-clip shrink-0")}>
{favicon ? ( {favicon ? (
<img className="w-full h-auto rounded-full" src={favicon} decoding="async" loading="lazy" /> <img className="w-full h-auto rounded-lg" src={favicon} decoding="async" loading="lazy" />
) : ( ) : (
<Icon.CircleSlash className="w-full h-auto text-gray-400" /> <Icon.CircleSlash className="w-full h-auto text-gray-400" />
)} )}

View File

@ -1,69 +1,65 @@
import classNames from "classnames"; import classNames from "classnames";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils"; import { getFaviconWithGoogleS2 } from "../helpers/utils";
import Icon from "./Icon"; import Icon from "./Icon";
import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; import ShortcutActionsDropdown from "./ShortcutActionsDropdown";
interface Props { interface Props {
shortcut: Shortcut; shortcut: Shortcut;
className?: string; className?: string;
showActions?: boolean;
alwaysShowLink?: boolean;
onClick?: () => void;
} }
const ShortcutView = (props: Props) => { const ShortcutView = (props: Props) => {
const { shortcut, className } = props; const { shortcut, className, showActions, alwaysShowLink, onClick } = props;
const shortcutLink = absolutifyLink(`/s/${shortcut.name}`);
const favicon = getFaviconWithGoogleS2(shortcut.link); const favicon = getFaviconWithGoogleS2(shortcut.link);
return ( return (
<>
<div <div
className={classNames( className={classNames(
"group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow dark:border-zinc-800 dark:hover:bg-zinc-800", "group w-full px-3 py-2 flex flex-row justify-start items-center border rounded-lg hover:bg-gray-100 dark:border-zinc-800 dark:hover:bg-zinc-800",
className className
)} )}
onClick={onClick}
> >
<div className="w-full flex flex-row justify-between items-center">
<div className="w-[calc(100%-16px)] flex flex-row justify-start items-center mr-1 shrink-0">
<Link to={`/shortcut/${shortcut.id}`} className={classNames("w-5 h-5 flex justify-center items-center overflow-clip shrink-0")}> <Link to={`/shortcut/${shortcut.id}`} className={classNames("w-5 h-5 flex justify-center items-center overflow-clip shrink-0")}>
{favicon ? ( {favicon ? (
<img className="w-full h-auto rounded-full" src={favicon} decoding="async" loading="lazy" /> <img className="w-full h-auto rounded-lg" src={favicon} decoding="async" loading="lazy" />
) : ( ) : (
<Icon.CircleSlash className="w-full h-auto text-gray-400" /> <Icon.CircleSlash className="w-full h-auto text-gray-400" />
)} )}
</Link> </Link>
<div className="ml-1 w-[calc(100%-20px)] flex flex-col justify-start items-start"> <div className="ml-1 w-full truncate">
<div className="w-full flex flex-row justify-start items-center">
<a
className={classNames(
"max-w-full flex flex-row px-1 mr-1 justify-start items-center cursor-pointer rounded-md hover:underline"
)}
href={shortcutLink}
target="_blank"
>
<div className="truncate">
<span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? ( {shortcut.title ? (
<>
<span className="dark:text-gray-400">{shortcut.title}</span>
<span className="text-gray-500">(s/{shortcut.name})</span> <span className="text-gray-500">(s/{shortcut.name})</span>
</>
) : ( ) : (
<> <>
<span className="text-gray-400 dark:text-gray-500">s/</span> <span className="text-gray-400 dark:text-gray-500">s/</span>
<span className="truncate dark:text-gray-400">{shortcut.name}</span> <span className="dark:text-gray-400">{shortcut.name}</span>
</> </>
)} )}
</div> </div>
<span className="hidden group-hover:block ml-1 cursor-pointer shrink-0"> <Link
<Icon.ExternalLink className="w-4 h-auto text-gray-600" /> className={classNames(
</span> "hidden group-hover:block ml-1 w-6 h-6 p-1 shrink-0 rounded-lg bg-gray-200 dark:bg-zinc-900 hover:opacity-80",
</a> alwaysShowLink && "!block"
</div> )}
</div> to={`/s/${shortcut.name}`}
</div> target="_blank"
<div className="flex flex-row justify-end items-center"> >
<Icon.ArrowUpRight className="w-4 h-auto text-gray-400 shrink-0" />
</Link>
{showActions && (
<div className="ml-1 flex flex-row justify-end items-center shrink-0">
<ShortcutActionsDropdown shortcut={shortcut} /> <ShortcutActionsDropdown shortcut={shortcut} />
</div> </div>
)}
</div> </div>
</div>
</>
); );
}; };

View File

@ -21,7 +21,7 @@ const ShortcutsContainer: React.FC<Props> = (props: Props) => {
)} )}
> >
{shortcutList.map((shortcut) => { {shortcutList.map((shortcut) => {
return <ShortcutItemView key={shortcut.id} shortcut={shortcut} />; return <ShortcutItemView key={shortcut.id} shortcut={shortcut} showActions={true} />;
})} })}
</div> </div>
); );

View File

@ -2,8 +2,8 @@ import { Divider } from "@mui/joy";
import classNames from "classnames"; import classNames from "classnames";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { ShortcutItem } from "@/components/CreateCollectionDialog";
import Icon from "@/components/Icon"; import Icon from "@/components/Icon";
import ShortcutView from "@/components/ShortcutView";
import useResponsiveWidth from "@/hooks/useResponsiveWidth"; import useResponsiveWidth from "@/hooks/useResponsiveWidth";
import useCollectionStore from "@/stores/v1/collection"; import useCollectionStore from "@/stores/v1/collection";
import useShortcutStore from "@/stores/v1/shortcut"; import useShortcutStore from "@/stores/v1/shortcut";
@ -66,16 +66,19 @@ const CollectionSpace = () => {
<p className="text-gray-500 text-sm">{collection.description}</p> <p className="text-gray-500 text-sm">{collection.description}</p>
<Divider className="!my-2" /> <Divider className="!my-2" />
</div> </div>
<div className="w-full flex flex-col justify-start items-start gap-1"> <div className="w-full flex flex-col justify-start items-start gap-2 sm:gap-1 px-px">
{shortcuts.map((shortcut) => { {shortcuts.map((shortcut) => {
return ( return (
<ShortcutItem <ShortcutView
className={classNames( className={classNames(
"w-full py-2", "w-full py-2 cursor-pointer",
selectedShortcut?.id === shortcut.id ? "bg-gray-100 dark:bg-zinc-800" : "border-transparent dark:border-transparent" selectedShortcut?.id === shortcut.id
? "bg-gray-100 dark:bg-zinc-800"
: "sm:border-transparent dark:sm:border-transparent"
)} )}
key={shortcut.name} key={shortcut.name}
shortcut={convertShortcutFromPb(shortcut)} shortcut={convertShortcutFromPb(shortcut)}
alwaysShowLink={!sm}
onClick={() => handleShortcutClick(shortcut)} onClick={() => handleShortcutClick(shortcut)}
/> />
); );

View File

@ -59,7 +59,7 @@ const ShortcutDetail = () => {
<div className="mx-auto max-w-8xl w-full px-3 md:px-12 pt-4 pb-6 flex flex-col justify-start items-start"> <div className="mx-auto max-w-8xl w-full px-3 md:px-12 pt-4 pb-6 flex flex-col justify-start items-start">
<div className="mt-8 w-12 h-12 flex justify-center items-center overflow-clip"> <div className="mt-8 w-12 h-12 flex justify-center items-center overflow-clip">
{favicon ? ( {favicon ? (
<img className="w-full h-auto rounded-full" src={favicon} decoding="async" loading="lazy" /> <img className="w-full h-auto rounded-lg" src={favicon} decoding="async" loading="lazy" />
) : ( ) : (
<Icon.CircleSlash className="w-full h-auto text-gray-400" /> <Icon.CircleSlash className="w-full h-auto text-gray-400" />
)} )}