diff --git a/frontend/web/src/components/LinkFavicon.tsx b/frontend/web/src/components/LinkFavicon.tsx new file mode 100644 index 0000000..1eeb650 --- /dev/null +++ b/frontend/web/src/components/LinkFavicon.tsx @@ -0,0 +1,36 @@ +import { useState } from "react"; +import { useWorkspaceStore } from "@/stores"; +import Icon from "./Icon"; + +interface Props { + url: string; +} + +const getFaviconUrlWithProvider = (url: string, provider: string) => { + try { + const searchParams = new URLSearchParams(); + searchParams.set("domain", new URL(url).hostname); + return new URL(`?${searchParams.toString()}`, provider).toString(); + } catch (error) { + return ""; + } +}; + +const LinkFavicon = (props: Props) => { + const { url } = props; + const workspaceStore = useWorkspaceStore(); + const faviconProvider = workspaceStore.profile.faviconProvider || "https://www.google.com/s2/favicons"; + const [faviconUrl, setFaviconUrl] = useState(getFaviconUrlWithProvider(url, faviconProvider)); + + const handleImgError = () => { + setFaviconUrl(""); + }; + + return faviconUrl ? ( + + ) : ( + + ); +}; + +export default LinkFavicon; diff --git a/frontend/web/src/components/ShortcutCard.tsx b/frontend/web/src/components/ShortcutCard.tsx index fe0cbdd..9f9889b 100644 --- a/frontend/web/src/components/ShortcutCard.tsx +++ b/frontend/web/src/components/ShortcutCard.tsx @@ -8,8 +8,9 @@ import { Link } from "react-router-dom"; import { useUserStore, useViewStore } from "@/stores"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import { convertVisibilityFromPb } from "@/utils/visibility"; -import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils"; +import { absolutifyLink } from "../helpers/utils"; import Icon from "./Icon"; +import LinkFavicon from "./LinkFavicon"; import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; import VisibilityIcon from "./VisibilityIcon"; @@ -24,7 +25,6 @@ const ShortcutCard = (props: Props) => { const viewStore = useViewStore(); const creator = userStore.getUserById(shortcut.creatorId); const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); - const favicon = getFaviconWithGoogleS2(shortcut.link); useEffect(() => { userStore.getOrFetchUserById(shortcut.creatorId); @@ -48,11 +48,7 @@ const ShortcutCard = (props: Props) => { to={`/shortcut/${shortcut.id}`} unstable_viewTransition > - {favicon ? ( - - ) : ( - - )} +
diff --git a/frontend/web/src/components/ShortcutFrame.tsx b/frontend/web/src/components/ShortcutFrame.tsx index 51f9c82..a9f8b14 100644 --- a/frontend/web/src/components/ShortcutFrame.tsx +++ b/frontend/web/src/components/ShortcutFrame.tsx @@ -1,17 +1,15 @@ import { Divider } from "@mui/joy"; import classNames from "classnames"; import { Link } from "react-router-dom"; -import { getFaviconWithGoogleS2 } from "@/helpers/utils"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import Icon from "./Icon"; +import LinkFavicon from "./LinkFavicon"; interface Props { shortcut: Shortcut; } const ShortcutFrame = ({ shortcut }: Props) => { - const favicon = getFaviconWithGoogleS2(shortcut.link); - return (
{ target="_blank" >
- {favicon ? ( - - ) : ( - - )} +

{shortcut.title || shortcut.name}

{shortcut.description}

diff --git a/frontend/web/src/components/ShortcutView.tsx b/frontend/web/src/components/ShortcutView.tsx index 2387c9a..6fa154f 100644 --- a/frontend/web/src/components/ShortcutView.tsx +++ b/frontend/web/src/components/ShortcutView.tsx @@ -1,8 +1,8 @@ import classNames from "classnames"; import { Link } from "react-router-dom"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; -import { getFaviconWithGoogleS2 } from "../helpers/utils"; import Icon from "./Icon"; +import LinkFavicon from "./LinkFavicon"; import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; interface Props { @@ -15,7 +15,6 @@ interface Props { const ShortcutView = (props: Props) => { const { shortcut, className, showActions, alwaysShowLink, onClick } = props; - const favicon = getFaviconWithGoogleS2(shortcut.link); return (
{ onClick={onClick} >
- {favicon ? ( - - ) : ( - - )} +
{shortcut.title ? ( diff --git a/frontend/web/src/helpers/utils.ts b/frontend/web/src/helpers/utils.ts index f019514..fa22b92 100644 --- a/frontend/web/src/helpers/utils.ts +++ b/frontend/web/src/helpers/utils.ts @@ -1,9 +1,3 @@ -import { isNull, isUndefined } from "lodash-es"; - -export const isNullorUndefined = (value: any) => { - return isNull(value) || isUndefined(value); -}; - export const absolutifyLink = (rel: string): string => { const anchor = document.createElement("a"); anchor.setAttribute("href", rel); @@ -15,19 +9,6 @@ export const isURL = (str: string): boolean => { return urlRegex.test(str); }; -export const releaseGuard = () => { - return import.meta.env.MODE === "development"; -}; - -export const getFaviconWithGoogleS2 = (url: string) => { - try { - const urlObject = new URL(url); - return `https://www.google.com/s2/favicons?sz=128&domain=${urlObject.hostname}`; - } catch (error) { - return undefined; - } -}; - export const generateRandomString = () => { const characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; let randomString = ""; diff --git a/frontend/web/src/pages/ShortcutDetail.tsx b/frontend/web/src/pages/ShortcutDetail.tsx index a54f583..8e5e13c 100644 --- a/frontend/web/src/pages/ShortcutDetail.tsx +++ b/frontend/web/src/pages/ShortcutDetail.tsx @@ -5,20 +5,21 @@ import { useEffect, useState } from "react"; import toast from "react-hot-toast"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; +import { showCommonDialog } from "@/components/Alert"; +import AnalyticsView from "@/components/AnalyticsView"; +import CreateShortcutDrawer from "@/components/CreateShortcutDrawer"; +import GenerateQRCodeDialog from "@/components/GenerateQRCodeDialog"; +import Icon from "@/components/Icon"; +import LinkFavicon from "@/components/LinkFavicon"; +import VisibilityIcon from "@/components/VisibilityIcon"; +import Dropdown from "@/components/common/Dropdown"; +import { absolutifyLink } from "@/helpers/utils"; import useLoading from "@/hooks/useLoading"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useUserStore, useShortcutStore } from "@/stores"; import { Shortcut } from "@/types/proto/api/v1/shortcut_service"; import { Role } from "@/types/proto/api/v1/user_service"; import { convertVisibilityFromPb } from "@/utils/visibility"; -import { showCommonDialog } from "../components/Alert"; -import AnalyticsView from "../components/AnalyticsView"; -import CreateShortcutDrawer from "../components/CreateShortcutDrawer"; -import GenerateQRCodeDialog from "../components/GenerateQRCodeDialog"; -import Icon from "../components/Icon"; -import VisibilityIcon from "../components/VisibilityIcon"; -import Dropdown from "../components/common/Dropdown"; -import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils"; interface State { showEditDrawer: boolean; @@ -41,7 +42,6 @@ const ShortcutDetail = () => { const creator = userStore.getUserById(shortcut.creatorId); const havePermission = currentUser.role === Role.ADMIN || shortcut.creatorId === currentUser.id; const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); - const favicon = getFaviconWithGoogleS2(shortcut.link); useEffect(() => { (async () => { @@ -78,11 +78,7 @@ const ShortcutDetail = () => { <>
- {favicon ? ( - - ) : ( - - )} +