From 97c891e6a0d227a2792a90fdc39c6ce0b9b356d3 Mon Sep 17 00:00:00 2001 From: NFish Date: Thu, 8 May 2025 14:36:59 +0800 Subject: [PATCH] fix: show configured brand info in browser tabs --- .../(appDetailLayout)/[appId]/layout-main.tsx | 4 +++- web/app/(commonLayout)/apps/Apps.tsx | 4 +++- web/app/(commonLayout)/apps/page.tsx | 2 -- .../[datasetId]/layout-main.tsx | 6 ++---- web/app/(commonLayout)/datasets/Container.tsx | 4 ++-- web/app/(commonLayout)/datasets/Datasets.tsx | 1 - .../base/chat/chat-with-history/index.tsx | 9 +++------ .../base/chat/embedded-chatbot/index.tsx | 9 +++------ web/app/components/explore/index.tsx | 4 +++- .../components/plugins/plugin-page/index.tsx | 4 ++-- .../share/text-generation/index.tsx | 16 ++++------------ web/hooks/use-document-title.ts | 19 +++++-------------- 12 files changed, 30 insertions(+), 52 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx index 0a2c764d89..7d5d4cb52d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx @@ -25,6 +25,7 @@ import { useAppContext } from '@/context/app-context' import Loading from '@/app/components/base/loading' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import type { App } from '@/types/app' +import useDocumentTitle from '@/hooks/use-document-title' export type IAppDetailLayoutProps = { children: React.ReactNode @@ -94,9 +95,10 @@ const AppDetailLayout: FC = (props) => { return navs }, []) + useDocumentTitle(appDetail?.name || t('common.menus.appDetail')) + useEffect(() => { if (appDetail) { - document.title = `${(appDetail.name || 'App')} - Dify` const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand' const mode = isMobile ? 'collapse' : 'expand' setAppSiderbarExpand(isMobile ? mode : localeMode) diff --git a/web/app/(commonLayout)/apps/Apps.tsx b/web/app/(commonLayout)/apps/Apps.tsx index 1375f4dfd6..8560787e95 100644 --- a/web/app/(commonLayout)/apps/Apps.tsx +++ b/web/app/(commonLayout)/apps/Apps.tsx @@ -29,6 +29,7 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st import TagManagementModal from '@/app/components/base/tag-management' import TagFilter from '@/app/components/base/tag-management/filter' import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' +import useDocumentTitle from '@/hooks/use-document-title' const getKey = ( pageIndex: number, @@ -96,7 +97,6 @@ const Apps = () => { ] useEffect(() => { - document.title = `${t('common.menus.apps')} - Dify` if (localStorage.getItem(NEED_REFRESH_APP_LIST_KEY) === '1') { localStorage.removeItem(NEED_REFRESH_APP_LIST_KEY) mutate() @@ -128,6 +128,8 @@ const Apps = () => { return () => observer?.disconnect() }, [isLoading, setSize, anchorRef, mutate, data, error]) + useDocumentTitle(isLoading ? '' : t('common.menus.apps')) + const { run: handleSearch } = useDebounceFn(() => { setSearchKeywords(keywords) }, { wait: 500 }) diff --git a/web/app/(commonLayout)/apps/page.tsx b/web/app/(commonLayout)/apps/page.tsx index 1a78a8fcd0..3f617d41c9 100644 --- a/web/app/(commonLayout)/apps/page.tsx +++ b/web/app/(commonLayout)/apps/page.tsx @@ -6,13 +6,11 @@ import style from '../list.module.css' import Apps from './Apps' import { useEducationInit } from '@/app/education-apply/hooks' import { useGlobalPublicStore } from '@/context/global-public-context' -import useDocumentTitle from '@/hooks/use-document-title' const AppList = () => { const { t } = useTranslation() useEducationInit() const { systemFeatures } = useGlobalPublicStore() - useDocumentTitle(t('common.menus.apps')) return (
diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx index 078c7ebd8c..c15b1cbabd 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx @@ -31,6 +31,7 @@ import { getLocaleOnClient } from '@/i18n' import { useAppContext } from '@/context/app-context' import Tooltip from '@/app/components/base/tooltip' import LinkedAppsPanel from '@/app/components/base/linked-apps-panel' +import useDocumentTitle from '@/hooks/use-document-title' export type IAppDetailLayoutProps = { children: React.ReactNode @@ -158,10 +159,7 @@ const DatasetDetailLayout: FC = (props) => { return baseNavigation }, [datasetRes?.provider, datasetId, t]) - useEffect(() => { - if (datasetRes) - document.title = `${datasetRes.name || 'Dataset'} - Dify` - }, [datasetRes]) + useDocumentTitle(datasetRes?.name || t('common.menus.datasets')) const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand) diff --git a/web/app/(commonLayout)/datasets/Container.tsx b/web/app/(commonLayout)/datasets/Container.tsx index a836cd644a..ff355e864f 100644 --- a/web/app/(commonLayout)/datasets/Container.tsx +++ b/web/app/(commonLayout)/datasets/Container.tsx @@ -30,6 +30,7 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st import { useAppContext } from '@/context/app-context' import { useExternalApiPanel } from '@/context/external-api-panel-context' import { useGlobalPublicStore } from '@/context/global-public-context' +import useDocumentTitle from '@/hooks/use-document-title' const Container = () => { const { t } = useTranslation() @@ -39,8 +40,7 @@ const Container = () => { const showTagManagementModal = useTagStore(s => s.showTagManagementModal) const { showExternalApiPanel, setShowExternalApiPanel } = useExternalApiPanel() const [includeAll, { toggle: toggleIncludeAll }] = useBoolean(false) - - document.title = `${t('dataset.knowledge')} - Dify` + useDocumentTitle(t('dataset.knowledge')) const options = useMemo(() => { return [ diff --git a/web/app/(commonLayout)/datasets/Datasets.tsx b/web/app/(commonLayout)/datasets/Datasets.tsx index fa7ac447e8..28461e8617 100644 --- a/web/app/(commonLayout)/datasets/Datasets.tsx +++ b/web/app/(commonLayout)/datasets/Datasets.tsx @@ -60,7 +60,6 @@ const Datasets = ({ useEffect(() => { loadingStateRef.current = isLoading - document.title = `${t('dataset.knowledge')} - Dify` }, [isLoading, t]) const onScroll = useCallback( diff --git a/web/app/components/base/chat/chat-with-history/index.tsx b/web/app/components/base/chat/chat-with-history/index.tsx index bc9bdd1dbc..2ba50ea49b 100644 --- a/web/app/components/base/chat/chat-with-history/index.tsx +++ b/web/app/components/base/chat/chat-with-history/index.tsx @@ -20,6 +20,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { checkOrSetAccessToken } from '@/app/components/share/utils' import AppUnavailable from '@/app/components/base/app-unavailable' import cn from '@/utils/classnames' +import useDocumentTitle from '@/hooks/use-document-title' type ChatWithHistoryProps = { className?: string @@ -46,14 +47,10 @@ const ChatWithHistory: FC = ({ useEffect(() => { themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted) - if (site) { - if (customConfig) - document.title = `${site.title}` - else - document.title = `${site.title} - Powered by Dify` - } }, [site, customConfig, themeBuilder]) + useDocumentTitle(site?.title || 'Chat') + if (appInfoLoading) { return ( diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index 3021d64989..44a8edbc0f 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -21,6 +21,7 @@ import Header from '@/app/components/base/chat/embedded-chatbot/header' import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper' import LogoSite from '@/app/components/base/logo/logo-site' import cn from '@/utils/classnames' +import useDocumentTitle from '@/hooks/use-document-title' const Chatbot = () => { const { @@ -44,14 +45,10 @@ const Chatbot = () => { useEffect(() => { themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted) - if (site) { - if (customConfig) - document.title = `${site.title}` - else - document.title = `${site.title} - Powered by Dify` - } }, [site, customConfig, themeBuilder]) + useDocumentTitle(site?.title || 'Chat') + if (appInfoLoading) { return ( <> diff --git a/web/app/components/explore/index.tsx b/web/app/components/explore/index.tsx index a06b533048..bae2610cba 100644 --- a/web/app/components/explore/index.tsx +++ b/web/app/components/explore/index.tsx @@ -8,6 +8,7 @@ import { useAppContext } from '@/context/app-context' import { fetchMembers } from '@/service/common' import type { InstalledApp } from '@/models/explore' import { useTranslation } from 'react-i18next' +import useDocumentTitle from '@/hooks/use-document-title' export type IExploreProps = { children: React.ReactNode @@ -23,8 +24,9 @@ const Explore: FC = ({ const [installedApps, setInstalledApps] = useState([]) const { t } = useTranslation() + useDocumentTitle(t('common.menus.explore')) + useEffect(() => { - document.title = `${t('explore.title')} - Dify`; (async () => { const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} }) if (!accounts) diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index 25080da7aa..7cba865c66 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -42,6 +42,7 @@ import { noop } from 'lodash-es' import { PLUGIN_TYPE_SEARCH_MAP } from '../marketplace/plugin-type-switch' import { PLUGIN_PAGE_TABS_MAP } from '../hooks' import { useGlobalPublicStore } from '@/context/global-public-context' +import useDocumentTitle from '@/hooks/use-document-title' const PACKAGE_IDS_KEY = 'package-ids' const BUNDLE_INFO_KEY = 'bundle-info' @@ -58,8 +59,7 @@ const PluginPage = ({ const { locale } = useContext(I18n) const searchParams = useSearchParams() const { replace } = useRouter() - - document.title = `${t('plugin.metadata.title')} - Dify` + useDocumentTitle(t('plugin.metadata.title')) // just support install one package now const packageId = useMemo(() => { diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index a6bc801f6a..2de5863859 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -42,6 +42,7 @@ import cn from '@/utils/classnames' import { useGetAppAccessMode, useGetUserCanAccessApp } from '@/service/access-control' import { AccessMode } from '@/models/access-control' import { useGlobalPublicStore } from '@/context/global-public-context' +import useDocumentTitle from '@/hooks/use-document-title' const GROUP_SIZE = 5 // to avoid RPM(Request per minute) limit. The group task finished then the next group. enum TaskStatus { @@ -105,7 +106,6 @@ const TextGeneration: FC = ({ const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) const [appId, setAppId] = useState('') const [siteInfo, setSiteInfo] = useState(null) - const [canReplaceLogo, setCanReplaceLogo] = useState(false) const [customConfig, setCustomConfig] = useState | null>(null) const [promptConfig, setPromptConfig] = useState(null) const [moreLikeThisConfig, setMoreLikeThisConfig] = useState(null) @@ -411,10 +411,9 @@ const TextGeneration: FC = ({ useEffect(() => { (async () => { const [appData, appParams]: any = await fetchInitData() - const { app_id: appId, site: siteInfo, can_replace_logo, custom_config } = appData + const { app_id: appId, site: siteInfo, custom_config } = appData setAppId(appId) setSiteInfo(siteInfo as SiteInfo) - setCanReplaceLogo(can_replace_logo) setCustomConfig(custom_config) changeLanguage(siteInfo.default_language) @@ -435,17 +434,10 @@ const TextGeneration: FC = ({ setMoreLikeThisConfig(more_like_this) setTextToSpeechConfig(text_to_speech) })() - }, []) + }, [fetchInitData]) // Can Use metadata(https://beta.nextjs.org/docs/api-reference/metadata) to set title. But it only works in server side client. - useEffect(() => { - if (siteInfo?.title) { - if (canReplaceLogo) - document.title = `${siteInfo.title}` - else - document.title = `${siteInfo.title} - Powered by Dify` - } - }, [siteInfo?.title, canReplaceLogo]) + useDocumentTitle(siteInfo?.title || t('share.generation.title')) useAppFavicon({ enable: !isInstalledApp, diff --git a/web/hooks/use-document-title.ts b/web/hooks/use-document-title.ts index cb8740d144..9ad71e77c6 100644 --- a/web/hooks/use-document-title.ts +++ b/web/hooks/use-document-title.ts @@ -1,19 +1,10 @@ 'use client' -import { useLayoutEffect } from 'react' import { useGlobalPublicStore } from '@/context/global-public-context' +import { useFavicon, useTitle } from 'ahooks' export default function useDocumentTitle(title: string) { - const { systemFeatures } = useGlobalPublicStore() - useLayoutEffect(() => { - const prefix = title ? `${title} - ` : '' - if (systemFeatures.branding.enabled) { - document.title = `${prefix}${systemFeatures.branding.application_title}` - const faviconEle = document.querySelector('link[rel*=\'icon\']') as HTMLLinkElement - if (faviconEle) - faviconEle.href = systemFeatures.branding.favicon - } - else { - document.title = `${prefix}Dify` - } - }, [systemFeatures, title]) + const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) + const prefix = title ? `${title} - ` : '' + useTitle(systemFeatures.branding.enabled ? `${prefix}${systemFeatures.branding.application_title}` : `${prefix}Dify`) + useFavicon(systemFeatures.branding.enabled ? systemFeatures.branding.favicon : '/favicon.ico') }