import { useEffect, useLayoutEffect } from '../lib/teact/teact';
import { withGlobal } from '../global';
import type { GlobalState } from '../global/types';
import type { ThemeKey } from '../types';
import type { UiLoaderPage } from './common/UiLoader';
import { DARK_THEME_BG_COLOR, INACTIVE_MARKER, LIGHT_THEME_BG_COLOR, PAGE_TITLE, PAGE_TITLE_TAURI } from '../config';
import { forceMutation } from '../lib/fasterdom/stricterdom.ts';
import { selectActionMessageBg, selectTabState, selectTheme } from '../global/selectors';
import { IS_TAURI } from '../util/browser/globalEnvironment';
import { IS_INSTALL_PROMPT_SUPPORTED, PLATFORM_ENV } from '../util/browser/windowEnvironment';
import buildClassName from '../util/buildClassName';
import { setupBeforeInstallPrompt } from '../util/installPrompt';
import { ACCOUNT_SLOT, getAccountsInfo, getAccountSlotUrl } from '../util/multiaccount';
import { hasEncryptedSession } from '../util/passcode';
import { getInitialLocationHash, parseInitialLocationHash } from '../util/routing';
import { checkSessionLocked, hasStoredSession } from '../util/sessions';
import { updateSizes } from '../util/windowSize';
import useTauriDrag from '../hooks/tauri/useTauriDrag';
import useAppLayout from '../hooks/useAppLayout';
import usePrevious from '../hooks/usePrevious';
import { useSignalEffect } from '../hooks/useSignalEffect';
import { getIsInBackground } from '../hooks/window/useBackgroundMode';
// import Test from './test/TestLocale';
import Auth from './auth/Auth';
import Notifications from './common/Notifications';
import UiLoader from './common/UiLoader';
import AppInactive from './main/AppInactive';
import LockScreen from './main/LockScreen.async';
import Main from './main/Main.async';
import Transition from './ui/Transition';
import styles from './App.module.scss';
type StateProps = {
authState: GlobalState['auth']['state'];
isScreenLocked?: boolean;
hasPasscode?: boolean;
inactiveReason?: 'auth' | 'otherClient';
hasWebAuthTokenFailed?: boolean;
isTestServer?: boolean;
theme: ThemeKey;
actionMessageBg?: string;
};
enum AppScreens {
auth,
main,
lock,
inactive,
}
const TRANSITION_RENDER_COUNT = Object.keys(AppScreens).length / 2;
const ACTIVE_PAGE_TITLE = IS_TAURI ? PAGE_TITLE_TAURI : PAGE_TITLE;
const INACTIVE_PAGE_TITLE = `${ACTIVE_PAGE_TITLE} ${INACTIVE_MARKER}`;
const App = ({
authState,
isScreenLocked,
hasPasscode,
inactiveReason,
hasWebAuthTokenFailed,
isTestServer,
theme,
actionMessageBg,
}: StateProps) => {
const { isMobile } = useAppLayout();
const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android';
useEffect(() => {
if (IS_INSTALL_PROMPT_SUPPORTED) {
setupBeforeInstallPrompt();
}
}, []);
useEffect(() => {
const hash = getInitialLocationHash();
// If there is no stored session on first slot, navigate to any other slot with stored session
if (!hasStoredSession() && !ACCOUNT_SLOT && !hash) {
const accounts = getAccountsInfo();
Object.keys(accounts)
.map(Number)
.sort((a, b) => b - a)
.forEach((key) => {
const slot = Number(key);
const account = accounts[slot];
if (account) {
const url = getAccountSlotUrl(slot);
window.location.href = `${url}#${hash || 'login'}`;
}
});
}
// TODO[Passcode]: Remove when multiacc passcode is implemented
const checkMultiaccPasscode = async () => {
if (checkSessionLocked() && ACCOUNT_SLOT && await hasEncryptedSession()) {
const url = getAccountSlotUrl(1);
window.location.href = url;
}
};
checkMultiaccPasscode();
}, []);
// Prevent drop on elements that do not accept it
useEffect(() => {
const body = document.body;
const handleDrag = (e: DragEvent) => {
e.preventDefault();
if (!e.dataTransfer) return;
if (!(e.target as HTMLElement).dataset.dropzone) {
e.dataTransfer.dropEffect = 'none';
} else {
e.dataTransfer.dropEffect = 'copy';
}
};
const handleDrop = (e: DragEvent) => {
e.preventDefault();
};
body.addEventListener('drop', handleDrop);
body.addEventListener('dragover', handleDrag);
body.addEventListener('dragenter', handleDrag);
return () => {
body.removeEventListener('drop', handleDrop);
body.removeEventListener('dragover', handleDrag);
body.removeEventListener('dragenter', handleDrag);
};
}, []);
// return ;
let activeKey: AppScreens;
let page: UiLoaderPage | undefined;
if (inactiveReason) {
activeKey = AppScreens.inactive;
} else if (isScreenLocked) {
page = 'lock';
activeKey = AppScreens.lock;
} else if (authState) {
switch (authState) {
case 'authorizationStateWaitPhoneNumber':
page = 'authPhoneNumber';
activeKey = AppScreens.auth;
break;
case 'authorizationStateWaitCode':
page = 'authCode';
activeKey = AppScreens.auth;
break;
case 'authorizationStateWaitPassword':
page = 'authPassword';
activeKey = AppScreens.auth;
break;
case 'authorizationStateWaitRegistration':
activeKey = AppScreens.auth;
break;
case 'authorizationStateWaitQrCode':
page = 'authQrCode';
activeKey = AppScreens.auth;
break;
case 'authorizationStateClosed':
case 'authorizationStateClosing':
case 'authorizationStateLoggingOut':
case 'authorizationStateReady':
page = 'main';
activeKey = AppScreens.main;
break;
}
} else if (hasStoredSession()) {
page = 'main';
activeKey = AppScreens.main;
} else if (hasPasscode) {
activeKey = AppScreens.lock;
} else {
page = isMobileOs ? 'authPhoneNumber' : 'authQrCode';
activeKey = AppScreens.auth;
}
if (activeKey !== AppScreens.lock
&& activeKey !== AppScreens.inactive
&& activeKey !== AppScreens.main
&& parseInitialLocationHash()?.tgWebAuthToken
&& !hasWebAuthTokenFailed) {
page = 'main';
activeKey = AppScreens.main;
}
useEffect(() => {
updateSizes();
}, []);
useEffect(() => {
if (inactiveReason) {
document.title = INACTIVE_PAGE_TITLE;
} else {
document.title = ACTIVE_PAGE_TITLE;
}
}, [inactiveReason]);
const prevActiveKey = usePrevious(activeKey);
function renderContent() {
switch (activeKey) {
case AppScreens.auth:
return ;
case AppScreens.main:
return ;
case AppScreens.lock:
return ;
case AppScreens.inactive:
return ;
}
}
useTauriDrag();
useLayoutEffect(() => {
document.body.classList.add(styles.bg);
}, []);
useLayoutEffect(() => {
document.body.style.setProperty(
'--theme-background-color',
theme === 'dark' ? DARK_THEME_BG_COLOR : LIGHT_THEME_BG_COLOR,
);
}, [theme]);
useLayoutEffect(() => {
if (actionMessageBg) {
document.body.style.setProperty('--action-message-bg', actionMessageBg);
}
}, [actionMessageBg]);
const getIsInBackgroundLocal = getIsInBackground;
useSignalEffect(() => {
// Mutation forced to avoid RAF throttling in background
forceMutation(() => {
document.body.classList.toggle('in-background', getIsInBackgroundLocal());
}, document.body);
}, [getIsInBackgroundLocal]);
return (
{renderContent}
{activeKey === AppScreens.auth && isTestServer && Test server
}
);
};
export default withGlobal(
(global): Complete => {
const { state: authState, hasWebAuthTokenFailed, hasWebAuthTokenPasswordRequired } = global.auth;
return {
authState,
isScreenLocked: global.passcode?.isScreenLocked,
hasPasscode: global.passcode?.hasPasscode,
inactiveReason: selectTabState(global).inactiveReason,
hasWebAuthTokenFailed: hasWebAuthTokenFailed || hasWebAuthTokenPasswordRequired,
theme: selectTheme(global),
isTestServer: global.config?.isTestServer,
actionMessageBg: selectActionMessageBg(global),
};
},
)(App);