Check active Web K tabs (#6319)
This commit is contained in:
parent
c011a436f9
commit
750b45ed1c
@ -1296,9 +1296,11 @@
|
|||||||
"SettingsPasscodeStart2" = "Note: if you forget your local passcode, you'll need to log out of Telegram Web A and log in again.";
|
"SettingsPasscodeStart2" = "Note: if you forget your local passcode, you'll need to log out of Telegram Web A and log in again.";
|
||||||
"CurrentPasswordPlaceholder" = "Current password";
|
"CurrentPasswordPlaceholder" = "Current password";
|
||||||
"ChangeYourProfilePicture" = "Change your profile picture";
|
"ChangeYourProfilePicture" = "Change your profile picture";
|
||||||
"TooManyTabsTitle" = "Such error, many tabs";
|
"AppInactiveOtherClientTitle" = "Many sessions. So conflict.";
|
||||||
"TooManyTabsDescription" = "Telegram supports only one active tab with the app.\nPlease reload this page to continue using this tab or close it.";
|
"AppInactiveOtherClientDescription" = "Please, keep only one version of the app open.\nReload this page to continue using this tab or close it.";
|
||||||
"TooManyTabsReload" = "Reload app";
|
"AppInactiveAuthTitle" = "Many logins. So conflict.";
|
||||||
|
"AppInactiveAuthDescription" = "Please, keep only one tab open during login.\nReload this page to continue using this tab or close it.";
|
||||||
|
"AppInactiveReload" = "Reload App";
|
||||||
"SlowmodeEnabled" = "Slowmode enabled";
|
"SlowmodeEnabled" = "Slowmode enabled";
|
||||||
"SomethingWentWrong" = "Something went wrong";
|
"SomethingWentWrong" = "Something went wrong";
|
||||||
"MediaViewDownloading" = "{count}% downloading...";
|
"MediaViewDownloading" = "{count}% downloading...";
|
||||||
|
|||||||
@ -23,8 +23,7 @@ import { updateSizes } from '../util/windowSize';
|
|||||||
|
|
||||||
import useTauriDrag from '../hooks/tauri/useTauriDrag';
|
import useTauriDrag from '../hooks/tauri/useTauriDrag';
|
||||||
import useAppLayout from '../hooks/useAppLayout';
|
import useAppLayout from '../hooks/useAppLayout';
|
||||||
import useFlag from '../hooks/useFlag';
|
import usePrevious from '../hooks/usePrevious';
|
||||||
import usePreviousDeprecated from '../hooks/usePreviousDeprecated';
|
|
||||||
|
|
||||||
// import Test from './test/TestLocale';
|
// import Test from './test/TestLocale';
|
||||||
import Auth from './auth/Auth';
|
import Auth from './auth/Auth';
|
||||||
@ -40,7 +39,7 @@ type StateProps = {
|
|||||||
authState: GlobalState['authState'];
|
authState: GlobalState['authState'];
|
||||||
isScreenLocked?: boolean;
|
isScreenLocked?: boolean;
|
||||||
hasPasscode?: boolean;
|
hasPasscode?: boolean;
|
||||||
isInactiveAuth?: boolean;
|
inactiveReason?: 'auth' | 'otherClient';
|
||||||
hasWebAuthTokenFailed?: boolean;
|
hasWebAuthTokenFailed?: boolean;
|
||||||
isTestServer?: boolean;
|
isTestServer?: boolean;
|
||||||
theme: ThemeKey;
|
theme: ThemeKey;
|
||||||
@ -61,12 +60,11 @@ const App: FC<StateProps> = ({
|
|||||||
authState,
|
authState,
|
||||||
isScreenLocked,
|
isScreenLocked,
|
||||||
hasPasscode,
|
hasPasscode,
|
||||||
isInactiveAuth,
|
inactiveReason,
|
||||||
hasWebAuthTokenFailed,
|
hasWebAuthTokenFailed,
|
||||||
isTestServer,
|
isTestServer,
|
||||||
theme,
|
theme,
|
||||||
}) => {
|
}) => {
|
||||||
const [isInactive, markInactive, unmarkInactive] = useFlag(false);
|
|
||||||
const { isMobile } = useAppLayout();
|
const { isMobile } = useAppLayout();
|
||||||
const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android';
|
const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android';
|
||||||
|
|
||||||
@ -135,7 +133,7 @@ const App: FC<StateProps> = ({
|
|||||||
let activeKey: AppScreens;
|
let activeKey: AppScreens;
|
||||||
let page: UiLoaderPage | undefined;
|
let page: UiLoaderPage | undefined;
|
||||||
|
|
||||||
if (isInactive) {
|
if (inactiveReason) {
|
||||||
activeKey = AppScreens.inactive;
|
activeKey = AppScreens.inactive;
|
||||||
} else if (isScreenLocked) {
|
} else if (isScreenLocked) {
|
||||||
page = 'lock';
|
page = 'lock';
|
||||||
@ -193,16 +191,14 @@ const App: FC<StateProps> = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInactiveAuth) {
|
if (inactiveReason) {
|
||||||
document.title = INACTIVE_PAGE_TITLE;
|
document.title = INACTIVE_PAGE_TITLE;
|
||||||
markInactive();
|
|
||||||
} else {
|
} else {
|
||||||
document.title = ACTIVE_PAGE_TITLE;
|
document.title = ACTIVE_PAGE_TITLE;
|
||||||
unmarkInactive();
|
|
||||||
}
|
}
|
||||||
}, [isInactiveAuth, markInactive, unmarkInactive]);
|
}, [inactiveReason]);
|
||||||
|
|
||||||
const prevActiveKey = usePreviousDeprecated(activeKey);
|
const prevActiveKey = usePrevious(activeKey);
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
switch (activeKey) {
|
switch (activeKey) {
|
||||||
@ -213,7 +209,7 @@ const App: FC<StateProps> = ({
|
|||||||
case AppScreens.lock:
|
case AppScreens.lock:
|
||||||
return <LockScreen isLocked={isScreenLocked} />;
|
return <LockScreen isLocked={isScreenLocked} />;
|
||||||
case AppScreens.inactive:
|
case AppScreens.inactive:
|
||||||
return <AppInactive />;
|
return <AppInactive inactiveReason={inactiveReason!} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +251,7 @@ export default withGlobal(
|
|||||||
authState: global.authState,
|
authState: global.authState,
|
||||||
isScreenLocked: global.passcode?.isScreenLocked,
|
isScreenLocked: global.passcode?.isScreenLocked,
|
||||||
hasPasscode: global.passcode?.hasPasscode,
|
hasPasscode: global.passcode?.hasPasscode,
|
||||||
isInactiveAuth: selectTabState(global).isInactive,
|
inactiveReason: selectTabState(global).inactiveReason,
|
||||||
hasWebAuthTokenFailed: global.hasWebAuthTokenFailed || global.hasWebAuthTokenPasswordRequired,
|
hasWebAuthTokenFailed: global.hasWebAuthTokenFailed || global.hasWebAuthTokenPasswordRequired,
|
||||||
theme: selectTheme(global),
|
theme: selectTheme(global),
|
||||||
isTestServer: global.config?.isTestServer,
|
isTestServer: global.config?.isTestServer,
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import type { FC } from '../../lib/teact/teact';
|
import renderText from '../common/helpers/renderText';
|
||||||
import { useCallback } from '../../lib/teact/teact';
|
|
||||||
|
|
||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useLastCallback from '../../hooks/useLastCallback';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
|
|
||||||
@ -9,10 +10,16 @@ import './AppInactive.scss';
|
|||||||
|
|
||||||
import appInactivePath from '../../assets/app-inactive.png';
|
import appInactivePath from '../../assets/app-inactive.png';
|
||||||
|
|
||||||
const AppInactive: FC = () => {
|
type OwnProps = {
|
||||||
const handleReload = useCallback(() => {
|
inactiveReason: 'auth' | 'otherClient';
|
||||||
|
};
|
||||||
|
|
||||||
|
const AppInactive = ({ inactiveReason }: OwnProps) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const handleReload = useLastCallback(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}, []);
|
});
|
||||||
|
|
||||||
useHistoryBack({
|
useHistoryBack({
|
||||||
isActive: true,
|
isActive: true,
|
||||||
@ -24,15 +31,18 @@ const AppInactive: FC = () => {
|
|||||||
<div id="AppInactive">
|
<div id="AppInactive">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<img src={appInactivePath} alt="" />
|
<img src={appInactivePath} alt="" />
|
||||||
<h3 className="title">Such error, many tabs</h3>
|
<h3 className="title">
|
||||||
|
{inactiveReason === 'auth' ? lang('AppInactiveAuthTitle') : lang('AppInactiveOtherClientTitle')}
|
||||||
|
</h3>
|
||||||
<div className="description">
|
<div className="description">
|
||||||
Telegram supports only one active tab with the app.
|
{renderText(
|
||||||
<br />
|
lang(inactiveReason === 'auth' ? 'AppInactiveAuthDescription' : 'AppInactiveOtherClientDescription'),
|
||||||
Please reload this page to continue using this tab or close it.
|
['br'],
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<Button isText ripple onClick={handleReload}>
|
<Button isText ripple onClick={handleReload}>
|
||||||
Reload app
|
{lang('AppInactiveReload')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -67,6 +67,7 @@ export const AUTODOWNLOAD_FILESIZE_MB_LIMITS = [1, 5, 10, 50, 100, 500];
|
|||||||
export const DATA_BROADCAST_CHANNEL_PREFIX = 'tt-global';
|
export const DATA_BROADCAST_CHANNEL_PREFIX = 'tt-global';
|
||||||
export const ESTABLISH_BROADCAST_CHANNEL_PREFIX = 'tt-establish';
|
export const ESTABLISH_BROADCAST_CHANNEL_PREFIX = 'tt-establish';
|
||||||
export const MULTITAB_LOCALSTORAGE_KEY_PREFIX = 'tt-multitab';
|
export const MULTITAB_LOCALSTORAGE_KEY_PREFIX = 'tt-multitab';
|
||||||
|
export const INTERCLIENT_BROADCAST_CHANNEL = 'tgweb';
|
||||||
export const DC_IDS = [1, 2, 3, 4, 5] as const;
|
export const DC_IDS = [1, 2, 3, 4, 5] as const;
|
||||||
|
|
||||||
export const DOWNLOAD_WORKERS = 16;
|
export const DOWNLOAD_WORKERS = 16;
|
||||||
|
|||||||
@ -183,7 +183,7 @@ function onUpdateAuthorizationState<T extends GlobalState>(global: T, update: Ap
|
|||||||
};
|
};
|
||||||
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
Object.values(global.byTabId).forEach(({ id: tabId }) => {
|
||||||
global = updateTabState(global, {
|
global = updateTabState(global, {
|
||||||
isInactive: false,
|
inactiveReason: undefined,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|||||||
@ -115,7 +115,7 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => {
|
|||||||
Object.values(global.byTabId).forEach(({ id: otherTabId }) => {
|
Object.values(global.byTabId).forEach(({ id: otherTabId }) => {
|
||||||
if (otherTabId === tabId) return;
|
if (otherTabId === tabId) return;
|
||||||
global = updateTabState(global, {
|
global = updateTabState(global, {
|
||||||
isInactive: true,
|
inactiveReason: 'auth',
|
||||||
}, otherTabId);
|
}, otherTabId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ export type TabState = {
|
|||||||
id: number;
|
id: number;
|
||||||
isBlurred?: boolean;
|
isBlurred?: boolean;
|
||||||
isMasterTab: boolean;
|
isMasterTab: boolean;
|
||||||
isInactive?: boolean;
|
inactiveReason?: 'auth' | 'otherClient';
|
||||||
shouldPreventComposerAnimation?: boolean;
|
shouldPreventComposerAnimation?: boolean;
|
||||||
inviteHash?: string;
|
inviteHash?: string;
|
||||||
canInstall?: boolean;
|
canInstall?: boolean;
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { selectTabState } from './global/selectors';
|
|||||||
import { selectSharedSettings } from './global/selectors/sharedState';
|
import { selectSharedSettings } from './global/selectors/sharedState';
|
||||||
import { betterView } from './util/betterView';
|
import { betterView } from './util/betterView';
|
||||||
import { IS_TAURI } from './util/browser/globalEnvironment';
|
import { IS_TAURI } from './util/browser/globalEnvironment';
|
||||||
|
import listenOtherClients from './util/browser/listenOtherClients';
|
||||||
import { requestGlobal, subscribeToMultitabBroadcastChannel } from './util/browser/multitab';
|
import { requestGlobal, subscribeToMultitabBroadcastChannel } from './util/browser/multitab';
|
||||||
import { establishMultitabRole, subscribeToMasterChange } from './util/establishMultitabRole';
|
import { establishMultitabRole, subscribeToMasterChange } from './util/establishMultitabRole';
|
||||||
import { initGlobal } from './util/init';
|
import { initGlobal } from './util/init';
|
||||||
@ -51,6 +52,7 @@ async function init() {
|
|||||||
if (!(window as any).isCompatTestPassed) return;
|
if (!(window as any).isCompatTestPassed) return;
|
||||||
|
|
||||||
checkAndAssignPermanentWebVersion();
|
checkAndAssignPermanentWebVersion();
|
||||||
|
listenOtherClients();
|
||||||
|
|
||||||
subscribeToMultitabBroadcastChannel();
|
subscribeToMultitabBroadcastChannel();
|
||||||
await requestGlobal(APP_VERSION);
|
await requestGlobal(APP_VERSION);
|
||||||
|
|||||||
8
src/types/language.d.ts
vendored
8
src/types/language.d.ts
vendored
@ -1102,9 +1102,11 @@ export interface LangPair {
|
|||||||
'SettingsPasscodeStart2': undefined;
|
'SettingsPasscodeStart2': undefined;
|
||||||
'CurrentPasswordPlaceholder': undefined;
|
'CurrentPasswordPlaceholder': undefined;
|
||||||
'ChangeYourProfilePicture': undefined;
|
'ChangeYourProfilePicture': undefined;
|
||||||
'TooManyTabsTitle': undefined;
|
'AppInactiveOtherClientTitle': undefined;
|
||||||
'TooManyTabsDescription': undefined;
|
'AppInactiveOtherClientDescription': undefined;
|
||||||
'TooManyTabsReload': undefined;
|
'AppInactiveAuthTitle': undefined;
|
||||||
|
'AppInactiveAuthDescription': undefined;
|
||||||
|
'AppInactiveReload': undefined;
|
||||||
'SlowmodeEnabled': undefined;
|
'SlowmodeEnabled': undefined;
|
||||||
'SomethingWentWrong': undefined;
|
'SomethingWentWrong': undefined;
|
||||||
'VideoPlayerBuffering': undefined;
|
'VideoPlayerBuffering': undefined;
|
||||||
|
|||||||
22
src/util/browser/listenOtherClients.ts
Normal file
22
src/util/browser/listenOtherClients.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { getGlobal, setGlobal } from '../../global';
|
||||||
|
|
||||||
|
import { APP_CODE_NAME, INTERCLIENT_BROADCAST_CHANNEL } from '../../config';
|
||||||
|
import { updateTabState } from '../../global/reducers/tabs';
|
||||||
|
import { selectTabState } from '../../global/selectors';
|
||||||
|
|
||||||
|
export default function listenOtherClients() {
|
||||||
|
const channel = new BroadcastChannel(INTERCLIENT_BROADCAST_CHANNEL);
|
||||||
|
|
||||||
|
channel.addEventListener('message', (event) => {
|
||||||
|
if (event.data !== APP_CODE_NAME) {
|
||||||
|
let global = getGlobal();
|
||||||
|
const tabState = selectTabState(global);
|
||||||
|
global = updateTabState(global, {
|
||||||
|
inactiveReason: 'otherClient',
|
||||||
|
}, tabState.id);
|
||||||
|
setGlobal(global);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
channel.postMessage(APP_CODE_NAME);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user