From ec1895b5d14e886ce8f2af853a3e63193d027078 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Mon, 13 Feb 2023 03:32:35 +0100 Subject: [PATCH] Title: Display current chat in page title (#2564) --- .eslintignore | 1 - src/App.tsx | 14 ++++---- src/components/main/Main.tsx | 8 ++--- src/config.ts | 4 +-- src/global/actions/ui/chats.ts | 2 ++ src/global/actions/ui/misc.ts | 60 ++++++++++++++++++++++++++-------- src/global/types.ts | 5 +++ src/index.html | 14 ++++---- tsconfig.json | 1 + webpack.config.js | 30 ++++++++++------- 10 files changed, 95 insertions(+), 44 deletions(-) diff --git a/.eslintignore b/.eslintignore index 7ff00dfdb..ea337da98 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,7 +11,6 @@ src/lib/lovely-chart src/lib/music-metadata-browser -webpack.config.js jest.config.js src/lib/secret-sauce/ playwright.config.ts diff --git a/src/App.tsx b/src/App.tsx index ba663773c..46eb1b741 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,8 +5,8 @@ import { getActions, withGlobal } from './global'; import type { GlobalState } from './global/types'; import type { UiLoaderPage } from './components/common/UiLoader'; -import { INACTIVE_MARKER, PAGE_TITLE } from './config'; import { IS_MULTITAB_SUPPORTED, PLATFORM_ENV } from './util/environment'; +import { INACTIVE_MARKER, PAGE_TITLE } from './config'; import { selectTabState } from './global/selectors'; import { updateSizes } from './util/windowSize'; import { addActiveTabChangeListener } from './util/activeTabMonitor'; @@ -40,6 +40,8 @@ enum AppScreens { inactive, } +const INACTIVE_PAGE_TITLE = `${PAGE_TITLE} ${INACTIVE_MARKER}`; + const App: FC = ({ authState, isScreenLocked, @@ -47,7 +49,7 @@ const App: FC = ({ hasWebAuthTokenFailed, isInactiveAuth, }) => { - const { disconnect } = getActions(); + const { disconnect, updatePageTitle } = getActions(); const [isInactive, markInactive, unmarkInactive] = useFlag(false); const { isMobile } = useAppLayout(); @@ -144,20 +146,20 @@ const App: FC = ({ addActiveTabChangeListener(() => { disconnect(); - document.title = `${PAGE_TITLE}${INACTIVE_MARKER}`; + document.title = INACTIVE_PAGE_TITLE; markInactive(); }); - }, [activeKey, disconnect, markInactive]); + }, [activeKey, disconnect, markInactive, updatePageTitle]); useEffect(() => { if (isInactiveAuth) { - document.title = `${PAGE_TITLE}${INACTIVE_MARKER}`; + document.title = INACTIVE_PAGE_TITLE; markInactive(); } else { unmarkInactive(); } - }, [isInactiveAuth, markInactive, unmarkInactive]); + }, [isInactiveAuth, markInactive, unmarkInactive, updatePageTitle]); const prevActiveKey = usePrevious(activeKey); diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index cb32684ff..dd42d4bb9 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -13,7 +13,7 @@ import type { ApiLimitTypeWithModal, TabState } from '../../global/types'; import '../../global/actions/all'; import { - BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, PAGE_TITLE, + BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, } from '../../config'; import { IS_ANDROID } from '../../util/environment'; import { @@ -43,7 +43,6 @@ import useShowTransition from '../../hooks/useShowTransition'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useInterval from '../../hooks/useInterval'; import useAppLayout from '../../hooks/useAppLayout'; -import updatePageTitle from '../../util/updatePageTitle'; import updateIcon from '../../util/updateIcon'; import StickerSetModal from '../common/StickerSetModal.async'; @@ -214,6 +213,7 @@ const Main: FC = ({ openChat, toggleLeftColumn, loadRecentEmojiStatuses, + updatePageTitle, } = getActions(); if (DEBUG && !DEBUG_isLogged) { @@ -415,11 +415,11 @@ const Main: FC = ({ onTabFocusChange({ isBlurred: false }); if (!document.title.includes(INACTIVE_MARKER)) { - updatePageTitle(PAGE_TITLE); + updatePageTitle(); } updateIcon(false); - }, [onTabFocusChange]); + }, [onTabFocusChange, updatePageTitle]); const handleStickerSetModalClose = useCallback(() => { closeStickerSetModal(); diff --git a/src/config.ts b/src/config.ts index cd21a9db6..4adaec4b2 100644 --- a/src/config.ts +++ b/src/config.ts @@ -19,8 +19,8 @@ export const BETA_CHANGELOG_URL = 'https://telegra.ph/WebZ-Beta-04-01'; export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.'; export const DEBUG_GRAMJS = false; -export const PAGE_TITLE = 'Telegram'; -export const INACTIVE_MARKER = ' [Inactive]'; +export const PAGE_TITLE = process.env.APP_TITLE!; +export const INACTIVE_MARKER = '[Inactive]'; export const DEBUG_PAYMENT_SMART_GLOCAL = false; diff --git a/src/global/actions/ui/chats.ts b/src/global/actions/ui/chats.ts index ab3def4d3..9adcca111 100644 --- a/src/global/actions/ui/chats.ts +++ b/src/global/actions/ui/chats.ts @@ -66,6 +66,8 @@ addActionHandler('openChat', (global, actions, payload): ActionReturnType => { } } + actions.updatePageTitle({ tabId }); + return updateCurrentMessageList(global, id, threadId, type, shouldReplaceHistory, tabId); }); diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index 7099f3638..1efedb585 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -1,24 +1,26 @@ -import { addActionHandler, getGlobal, setGlobal } from '../../index'; +import { addCallback } from '../../../lib/teact/teactn'; +import { + addActionHandler, getActions, getGlobal, setGlobal, +} from '../../index'; import type { ApiError, ApiNotification } from '../../../api/types'; import { MAIN_THREAD_ID } from '../../../api/types'; +import type { ActionReturnType, GlobalState } from '../../types'; import { APP_VERSION, DEBUG, GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT, INACTIVE_MARKER, PAGE_TITLE, } from '../../../config'; import getReadableErrorText from '../../../util/getReadableErrorText'; import { - selectChatMessage, selectCurrentChat, selectCurrentMessageList, selectTabState, selectIsTrustedBot, + selectChatMessage, selectCurrentChat, selectCurrentMessageList, selectTabState, selectIsTrustedBot, selectChat, } from '../../selectors'; import generateIdFor from '../../../util/generateIdFor'; import { unique } from '../../../util/iteratees'; import { getAllMultitabTokens, getCurrentTabId, reestablishMasterToSelf } from '../../../util/establishMultitabRole'; import { getAllNotificationsCount } from '../../../util/folderManager'; import updateIcon from '../../../util/updateIcon'; -import updatePageTitle from '../../../util/updatePageTitle'; -import type { ActionReturnType, GlobalState } from '../../types'; +import setPageTitle from '../../../util/updatePageTitle'; import { updateTabState } from '../../reducers/tabs'; -import { addCallback } from '../../../lib/teact/teactn'; import { getIsMobile, getIsTablet } from '../../../hooks/useAppLayout'; export const APP_VERSION_URL = 'version.txt'; @@ -601,29 +603,61 @@ addActionHandler('onTabFocusChange', (global, actions, payload): ActionReturnTyp }; }); +addActionHandler('updatePageTitle', (global, actions, payload): ActionReturnType => { + const { isInactive, notificationCount, tabId = getCurrentTabId() } = payload || {}; + + if (isInactive) { + setPageTitle(`${PAGE_TITLE} ${INACTIVE_MARKER}`); + return; + } + + if (notificationCount) { + setPageTitle(`${notificationCount} notification${notificationCount > 1 ? 's' : ''}`); + return; + } + + const messageList = selectCurrentMessageList(global, tabId); + if (messageList) { + const { chatId, threadId } = messageList; + const currentChat = selectChat(global, chatId); + if (currentChat) { + if (currentChat.isForum && currentChat.topics?.[threadId]) { + setPageTitle(`${currentChat.title} › ${currentChat.topics[threadId].title}`); + return; + } + + setPageTitle(currentChat.title); + return; + } + } + + setPageTitle(PAGE_TITLE); +}); + addCallback((global: GlobalState) => { if (global.notificationIndex === undefined || global.allNotificationsCount === undefined) return; + const { updatePageTitle } = getActions(); const index = global.notificationIndex; const allNotificationsCount = global.allNotificationsCount; if (document.title.includes(INACTIVE_MARKER) || !global.initialUnreadNotifications) { updateIcon(false); - updatePageTitle(PAGE_TITLE); + updatePageTitle(); return; } if (index % 2 === 0) { const newUnread = allNotificationsCount - global.initialUnreadNotifications; if (newUnread > 0) { - updatePageTitle(`${newUnread} notification${newUnread > 1 ? 's' : ''}`); + updatePageTitle({ + notificationCount: newUnread, + }); updateIcon(true); - } else { - updatePageTitle(PAGE_TITLE); - updateIcon(false); + return; } - } else { - updatePageTitle(PAGE_TITLE); - updateIcon(false); } + + updatePageTitle(); + updateIcon(false); }); diff --git a/src/global/types.ts b/src/global/types.ts index 2e78cb8bf..68d24f21c 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -2098,6 +2098,11 @@ export interface ActionPayloads { } & WithTabId; dismissNotification: { localId: string } & WithTabId; + updatePageTitle: { + isInactive?: boolean; + notificationCount?: number; + } & WithTabId | undefined; + // Calls joinGroupCall: { chatId?: string; diff --git a/src/index.html b/src/index.html index f1dd5c31e..cb8280635 100644 --- a/src/index.html +++ b/src/index.html @@ -4,16 +4,16 @@ - <%= htmlWebpackPlugin.options.appName %> + <%= htmlWebpackPlugin.options.appTitle %> - + - + - - + + @@ -22,14 +22,14 @@ - + - + diff --git a/tsconfig.json b/tsconfig.json index a8ae4c047..2c5078a3e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,5 +26,6 @@ "tests", "plugins", "dev", + "webpack.config.js", ] } diff --git a/webpack.config.js b/webpack.config.js index e6a2c01ad..d6dbbfc66 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,7 +23,12 @@ const { dotenv.config(); -const { BASE_URL = 'https://web.telegram.org/z/' } = process.env; +const DEFAULT_APP_TITLE = `Telegram${APP_ENV !== 'production' ? ' Beta' : ''}`; + +const { + BASE_URL = 'https://web.telegram.org/z/', + APP_TITLE = DEFAULT_APP_TITLE, +} = process.env; module.exports = (_env, { mode = 'production' }) => { return { @@ -34,7 +39,7 @@ module.exports = (_env, { mode = 'production' }) => { devServer: { port: 1234, host: '0.0.0.0', - allowedHosts: "all", + allowedHosts: 'all', hot: false, static: [ { @@ -99,9 +104,9 @@ module.exports = (_env, { mode = 'production' }) => { modules: { exportLocalsConvention: 'camelCase', auto: true, - localIdentName: mode === 'production' ? '[hash:base64]' : '[name]__[local]' - } - } + localIdentName: mode === 'production' ? '[hash:base64]' : '[name]__[local]', + }, + }, }, 'postcss-loader', 'sass-loader', @@ -136,15 +141,15 @@ module.exports = (_env, { mode = 'production' }) => { plugins: [ // Clearing of the unused files for code highlight for smaller chunk count new ContextReplacementPlugin( - /highlight\.js[\\\/]lib[\\\/]languages/, - /^((?!\.js\.js).)*$/ + /highlight\.js[\\/]lib[\\/]languages/, + /^((?!\.js\.js).)*$/, ), ...(APP_MOCKED_CLIENT === '1' ? [new NormalModuleReplacementPlugin( - /src[\\\/]lib[\\\/]gramjs[\\\/]client[\\\/]TelegramClient\.js/, - './MockClient.ts' + /src[\\/]lib[\\/]gramjs[\\/]client[\\/]TelegramClient\.js/, + './MockClient.ts', )] : []), new HtmlWebpackPlugin({ - appName: APP_ENV === 'production' ? 'Telegram Web' : 'Telegram Web Beta', + appTitle: APP_TITLE, appleIcon: APP_ENV === 'production' ? 'apple-touch-icon' : 'apple-touch-icon-dev', mainIcon: APP_ENV === 'production' ? 'icon-192x192' : 'icon-dev-192x192', manifest: APP_ENV === 'production' ? 'site.webmanifest' : 'site_dev.webmanifest', @@ -159,11 +164,14 @@ module.exports = (_env, { mode = 'production' }) => { new EnvironmentPlugin({ APP_ENV, APP_MOCKED_CLIENT, + // eslint-disable-next-line no-null/no-null APP_NAME: null, APP_VERSION: appVersion, + APP_TITLE, RELEASE_DATETIME: Date.now(), TELEGRAM_T_API_ID: undefined, TELEGRAM_T_API_HASH: undefined, + // eslint-disable-next-line no-null/no-null TEST_SESSION: null, }), new DefinePlugin({ @@ -193,7 +201,7 @@ module.exports = (_env, { mode = 'production' }) => { ...(APP_ENV !== 'production' && { optimization: { chunkIds: 'named', - } + }, }), }; };