Re-write browser history; Introduce playwright tests (another attempt) (#1809)
This commit is contained in:
parent
0c5b3e560e
commit
697b709d89
@ -14,3 +14,4 @@ src/lib/music-metadata-browser
|
|||||||
webpack.config.js
|
webpack.config.js
|
||||||
jest.config.js
|
jest.config.js
|
||||||
src/lib/secret-sauce/
|
src/lib/secret-sauce/
|
||||||
|
playwright.config.ts
|
||||||
|
|||||||
@ -5,6 +5,7 @@ module.exports = {
|
|||||||
'<rootDir>/tests/staticFileMock.js',
|
'<rootDir>/tests/staticFileMock.js',
|
||||||
},
|
},
|
||||||
testPathIgnorePatterns: [
|
testPathIgnorePatterns: [
|
||||||
|
'<rootDir>/tests/playwright/',
|
||||||
'<rootDir>/node_modules/',
|
'<rootDir>/node_modules/',
|
||||||
'<rootDir>/legacy_notes_and_workbook/',
|
'<rootDir>/legacy_notes_and_workbook/',
|
||||||
'<rootDir>/client/src/stylesheets/',
|
'<rootDir>/client/src/stylesheets/',
|
||||||
|
|||||||
2276
package-lock.json
generated
2276
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,9 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env APP_ENV=development webpack serve --env mode=dev --env isDevServer --mode development --config ./webpack.config.js",
|
"dev": "cross-env APP_ENV=development webpack serve --env mode=dev --env isDevServer --mode development --config ./webpack.config.js",
|
||||||
|
"dev:mocked": "cross-env APP_ENV=test APP_MOCKED_CLIENT=1 webpack serve --env mode=dev --env isDevServer --mode development --config ./webpack.config.js --port 1235",
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
|
"build:mocked": "cross-env APP_ENV=test APP_MOCKED_CLIENT=1 webpack --env mode=dev --mode development --config ./webpack.config.js",
|
||||||
"build:staging": "rm -rf dist/ && APP_ENV=staging APP_VERSION=$(npm run print_version --silent) npm run build && ./deploy/copy_to_dist.sh",
|
"build:staging": "rm -rf dist/ && APP_ENV=staging APP_VERSION=$(npm run print_version --silent) npm run build && ./deploy/copy_to_dist.sh",
|
||||||
"build:production": "npm i && rm -rf dist/ && APP_VERSION=$(npm run inc_version --silent) APP_ENV=production npm run build && ./deploy/copy_to_dist.sh",
|
"build:production": "npm i && rm -rf dist/ && APP_VERSION=$(npm run inc_version --silent) APP_ENV=production npm run build && ./deploy/copy_to_dist.sh",
|
||||||
"deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push",
|
"deploy:production": "npm run build:production && git add -A && git commit -a -m '[Build]' --no-verify && git push",
|
||||||
@ -17,6 +19,8 @@
|
|||||||
"gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js",
|
"gramjs:tl": "node ./src/lib/gramjs/tl/generateModules.js",
|
||||||
"gramjs:lint:fix": "eslint ./src/lib/gramjs --fix",
|
"gramjs:lint:fix": "eslint ./src/lib/gramjs --fix",
|
||||||
"test": "cross-env APP_ENV=test jest --verbose --forceExit",
|
"test": "cross-env APP_ENV=test jest --verbose --forceExit",
|
||||||
|
"test:playwright": "playwright test",
|
||||||
|
"test:record": "playwright codegen localhost:1235",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"statoscope:validate": "statoscope validate --input public/build-stats.json",
|
"statoscope:validate": "statoscope validate --input public/build-stats.json",
|
||||||
"statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json"
|
"statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json"
|
||||||
@ -47,6 +51,7 @@
|
|||||||
"@peculiar/webcrypto": "^1.3.3",
|
"@peculiar/webcrypto": "^1.3.3",
|
||||||
"@statoscope/cli": "^5.20.1",
|
"@statoscope/cli": "^5.20.1",
|
||||||
"@statoscope/webpack-plugin": "^5.20.1",
|
"@statoscope/webpack-plugin": "^5.20.1",
|
||||||
|
"@playwright/test": "^1.18.1",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@types/croppie": "^2.6.1",
|
"@types/croppie": "^2.6.1",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
@ -92,6 +97,7 @@
|
|||||||
"replace-in-file": "^6.3.2",
|
"replace-in-file": "^6.3.2",
|
||||||
"sass": "^1.50.0",
|
"sass": "^1.50.0",
|
||||||
"sass-loader": "^12.6.0",
|
"sass-loader": "^12.6.0",
|
||||||
|
"serve": "^13.0.2",
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"stylelint": "^14.6.1",
|
"stylelint": "^14.6.1",
|
||||||
"stylelint-config-recommended-scss": "^6.0.0",
|
"stylelint-config-recommended-scss": "^6.0.0",
|
||||||
|
|||||||
38
playwright.config.ts
Normal file
38
playwright.config.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { PlaywrightTestConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
testDir: 'tests/playwright',
|
||||||
|
timeout: process.env.CI ? 60 * 5 * 1000 : 30 * 1000,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
webServer: {
|
||||||
|
command: 'npm run build:mocked && serve -l 1235 dist',
|
||||||
|
port: 1235,
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:1235/',
|
||||||
|
video: 'retain-on-failure',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: { ...devices['Desktop Firefox'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webkit',
|
||||||
|
use: { ...devices['Desktop Safari'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ios',
|
||||||
|
use: { ...devices['iPhone X'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
TelegramClient, sessions, Api as GramJs, connection,
|
sessions, Api as GramJs, connection,
|
||||||
} from '../../../lib/gramjs';
|
} from '../../../lib/gramjs';
|
||||||
|
import TelegramClient from '../../../lib/gramjs/client/TelegramClient';
|
||||||
|
|
||||||
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
||||||
import { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
import { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
||||||
|
|
||||||
|
|||||||
@ -42,10 +42,11 @@ const Auth: FC<StateProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useHistoryBack(
|
useHistoryBack({
|
||||||
(!isMobile && authState === 'authorizationStateWaitPhoneNumber')
|
isActive: (!isMobile && authState === 'authorizationStateWaitPhoneNumber')
|
||||||
|| (isMobile && authState === 'authorizationStateWaitQrCode'), handleChangeAuthorizationMethod,
|
|| (isMobile && authState === 'authorizationStateWaitQrCode'),
|
||||||
);
|
onBack: handleChangeAuthorizationMethod,
|
||||||
|
});
|
||||||
|
|
||||||
// Prevent refresh when rotating device
|
// Prevent refresh when rotating device
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -45,7 +45,10 @@ const AuthCode: FC<StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useHistoryBack(true, returnToAuthPhoneNumber);
|
useHistoryBack({
|
||||||
|
isActive: true,
|
||||||
|
onBack: returnToAuthPhoneNumber,
|
||||||
|
});
|
||||||
|
|
||||||
const onCodeChange = useCallback((e: FormEvent<HTMLInputElement>) => {
|
const onCodeChange = useCallback((e: FormEvent<HTMLInputElement>) => {
|
||||||
if (authError) {
|
if (authError) {
|
||||||
|
|||||||
@ -15,10 +15,13 @@ export type OwnProps = {
|
|||||||
onContentChange: (content: LeftColumnContent) => void;
|
onContentChange: (content: LeftColumnContent) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArchivedChats: FC<OwnProps> = ({ isActive, onReset, onContentChange }) => {
|
const ArchivedChats: FC<OwnProps> = ({ isActive, onReset }) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onContentChange, LeftColumnContent.Archived);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ArchivedChats">
|
<div className="ArchivedChats">
|
||||||
|
|||||||
@ -134,7 +134,10 @@ const ChatFolders: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}) : undefined), [activeChatFolder, setActiveChatFolder]);
|
}) : undefined), [activeChatFolder, setActiveChatFolder]);
|
||||||
|
|
||||||
useHistoryBack(activeChatFolder !== 0, () => setActiveChatFolder(0, { forceOnHeavyAnimation: true }));
|
useHistoryBack({
|
||||||
|
isActive: activeChatFolder !== 0,
|
||||||
|
onBack: () => setActiveChatFolder(0, { forceOnHeavyAnimation: true }),
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
|||||||
@ -58,7 +58,10 @@ const ContactList: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const handleClick = useCallback((id: string) => {
|
const handleClick = useCallback((id: string) => {
|
||||||
openChat({ id, shouldReplaceHistory: true });
|
openChat({ id, shouldReplaceHistory: true });
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
DEBUG,
|
DEBUG,
|
||||||
FEEDBACK_URL,
|
FEEDBACK_URL,
|
||||||
IS_BETA,
|
IS_BETA,
|
||||||
|
IS_TEST,
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
@ -26,7 +27,6 @@ import { clearWebsync } from '../../../util/websync';
|
|||||||
import { selectCurrentMessageList, selectTheme } from '../../../global/selectors';
|
import { selectCurrentMessageList, selectTheme } from '../../../global/selectors';
|
||||||
import { isChatArchived } from '../../../global/helpers';
|
import { isChatArchived } from '../../../global/helpers';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import { disableHistoryBack } from '../../../hooks/useHistoryBack';
|
|
||||||
import useConnectionStatus from '../../../hooks/useConnectionStatus';
|
import useConnectionStatus from '../../../hooks/useConnectionStatus';
|
||||||
|
|
||||||
import DropdownMenu from '../../ui/DropdownMenu';
|
import DropdownMenu from '../../ui/DropdownMenu';
|
||||||
@ -129,7 +129,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
|||||||
lang, connectionState, isSyncing, isMessageListOpen, isConnectionStatusMinimized, !areChatsLoaded,
|
lang, connectionState, isSyncing, isMessageListOpen, isConnectionStatusMinimized, !areChatsLoaded,
|
||||||
);
|
);
|
||||||
|
|
||||||
const withOtherVersions = window.location.hostname === PRODUCTION_HOSTNAME;
|
const withOtherVersions = window.location.hostname === PRODUCTION_HOSTNAME || IS_TEST;
|
||||||
|
|
||||||
const MainButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
const MainButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => {
|
||||||
return ({ onTrigger, isOpen }) => (
|
return ({ onTrigger, isOpen }) => (
|
||||||
@ -191,7 +191,6 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
|||||||
const handleSwitchToWebK = useCallback(() => {
|
const handleSwitchToWebK = useCallback(() => {
|
||||||
setPermanentWebVersion('K');
|
setPermanentWebVersion('K');
|
||||||
clearWebsync();
|
clearWebsync();
|
||||||
disableHistoryBack();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleOpenTipsChat = useCallback(() => {
|
const handleOpenTipsChat = useCallback(() => {
|
||||||
@ -302,7 +301,6 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
icon="char-W"
|
icon="char-W"
|
||||||
href={LEGACY_VERSION_URL}
|
href={LEGACY_VERSION_URL}
|
||||||
onClick={disableHistoryBack}
|
|
||||||
>
|
>
|
||||||
Switch to Old Version
|
Switch to Old Version
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@ -64,7 +64,10 @@ const NewChatStep1: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const handleFilterChange = useCallback((query: string) => {
|
const handleFilterChange = useCallback((query: string) => {
|
||||||
setGlobalSearchQuery({ query });
|
setGlobalSearchQuery({ query });
|
||||||
|
|||||||
@ -46,7 +46,10 @@ const NewChatStep2: FC<OwnProps & StateProps > = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [about, setAbout] = useState('');
|
const [about, setAbout] = useState('');
|
||||||
|
|||||||
@ -76,7 +76,10 @@ const LeftSearch: FC<OwnProps & StateProps> = ({
|
|||||||
setGlobalSearchDate({ date: value.getTime() / 1000 });
|
setGlobalSearchDate({ date: value.getTime() / 1000 });
|
||||||
}, [setGlobalSearchDate]);
|
}, [setGlobalSearchDate]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, undefined, undefined, true);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|||||||
@ -169,7 +169,6 @@ const Settings: FC<OwnProps> = ({
|
|||||||
case SettingsScreens.EditProfile:
|
case SettingsScreens.EditProfile:
|
||||||
return (
|
return (
|
||||||
<SettingsEditProfile
|
<SettingsEditProfile
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive && isScreenActive}
|
isActive={isActive && isScreenActive}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -188,15 +187,15 @@ const Settings: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
case SettingsScreens.QuickReaction:
|
case SettingsScreens.QuickReaction:
|
||||||
return (
|
return (
|
||||||
<SettingsQuickReaction onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
<SettingsQuickReaction isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.Notifications:
|
case SettingsScreens.Notifications:
|
||||||
return (
|
return (
|
||||||
<SettingsNotifications onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
<SettingsNotifications isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.DataStorage:
|
case SettingsScreens.DataStorage:
|
||||||
return (
|
return (
|
||||||
<SettingsDataStorage onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
<SettingsDataStorage isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.Privacy:
|
case SettingsScreens.Privacy:
|
||||||
return (
|
return (
|
||||||
@ -208,7 +207,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
case SettingsScreens.Language:
|
case SettingsScreens.Language:
|
||||||
return (
|
return (
|
||||||
<SettingsLanguage onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
<SettingsLanguage isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.GeneralChatBackground:
|
case SettingsScreens.GeneralChatBackground:
|
||||||
return (
|
return (
|
||||||
@ -221,7 +220,6 @@ const Settings: FC<OwnProps> = ({
|
|||||||
case SettingsScreens.GeneralChatBackgroundColor:
|
case SettingsScreens.GeneralChatBackgroundColor:
|
||||||
return (
|
return (
|
||||||
<SettingsGeneralBackgroundColor
|
<SettingsGeneralBackgroundColor
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isScreenActive}
|
isActive={isScreenActive}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -229,7 +227,6 @@ const Settings: FC<OwnProps> = ({
|
|||||||
case SettingsScreens.ActiveSessions:
|
case SettingsScreens.ActiveSessions:
|
||||||
return (
|
return (
|
||||||
<SettingsActiveSessions
|
<SettingsActiveSessions
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isScreenActive}
|
isActive={isScreenActive}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -237,7 +234,6 @@ const Settings: FC<OwnProps> = ({
|
|||||||
case SettingsScreens.PrivacyBlockedUsers:
|
case SettingsScreens.PrivacyBlockedUsers:
|
||||||
return (
|
return (
|
||||||
<SettingsPrivacyBlockedUsers
|
<SettingsPrivacyBlockedUsers
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isScreenActive}
|
isActive={isScreenActive}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import React, {
|
|||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { ApiSession } from '../../../api/types';
|
import { ApiSession } from '../../../api/types';
|
||||||
import { SettingsScreens } from '../../../types';
|
|
||||||
|
|
||||||
import { formatPastTimeShort } from '../../../util/dateFormat';
|
import { formatPastTimeShort } from '../../../util/dateFormat';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
@ -22,7 +21,6 @@ import RadioGroup from '../../ui/RadioGroup';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,7 +32,6 @@ type StateProps = {
|
|||||||
|
|
||||||
const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
|
const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
byHash,
|
byHash,
|
||||||
orderedHashes,
|
orderedHashes,
|
||||||
@ -119,7 +116,10 @@ const SettingsActiveSessions: FC<OwnProps & StateProps> = ({
|
|||||||
}, [byHash, orderedHashes]);
|
}, [byHash, orderedHashes]);
|
||||||
const hasOtherSessions = Boolean(otherSessionHashes.length);
|
const hasOtherSessions = Boolean(otherSessionHashes.length);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.ActiveSessions);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
function renderCurrentSession(session: ApiSession) {
|
function renderCurrentSession(session: ApiSession) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
|
import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { SettingsScreens, ISettings } from '../../../types';
|
import { ISettings } from '../../../types';
|
||||||
|
|
||||||
import { AUTODOWNLOAD_FILESIZE_MB_LIMITS } from '../../../config';
|
import { AUTODOWNLOAD_FILESIZE_MB_LIMITS } from '../../../config';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
@ -13,7 +13,6 @@ import RangeSlider from '../../ui/RangeSlider';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,7 +36,6 @@ type StateProps = Pick<ISettings, (
|
|||||||
|
|
||||||
const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
canAutoLoadPhotoFromContacts,
|
canAutoLoadPhotoFromContacts,
|
||||||
canAutoLoadPhotoInPrivateChats,
|
canAutoLoadPhotoInPrivateChats,
|
||||||
@ -59,7 +57,10 @@ const SettingsDataStorage: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.General);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const renderFileSizeCallback = useCallback((value: number) => {
|
const renderFileSizeCallback = useCallback((value: number) => {
|
||||||
return lang('AutodownloadSizeLimitUpTo', lang('FileSize.MB', String(AUTODOWNLOAD_FILESIZE_MB_LIMITS[value]), 'i'));
|
return lang('AutodownloadSizeLimitUpTo', lang('FileSize.MB', String(AUTODOWNLOAD_FILESIZE_MB_LIMITS[value]), 'i'));
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import React, {
|
|||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { ApiMediaFormat } from '../../../api/types';
|
import { ApiMediaFormat } from '../../../api/types';
|
||||||
import { ProfileEditProgress, SettingsScreens } from '../../../types';
|
import { ProfileEditProgress } from '../../../types';
|
||||||
|
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import { selectUser } from '../../../global/selectors';
|
import { selectUser } from '../../../global/selectors';
|
||||||
@ -23,7 +23,6 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,7 +45,6 @@ const ERROR_BIO_TOO_LONG = 'Bio can\' be longer than 70 characters';
|
|||||||
|
|
||||||
const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
currentAvatarHash,
|
currentAvatarHash,
|
||||||
currentFirstName,
|
currentFirstName,
|
||||||
@ -87,7 +85,10 @@ const SettingsEditProfile: FC<OwnProps & StateProps> = ({
|
|||||||
return Boolean(photo) || isProfileFieldsTouched || isUsernameAvailable === true;
|
return Boolean(photo) || isProfileFieldsTouched || isUsernameAvailable === true;
|
||||||
}, [photo, isProfileFieldsTouched, isUsernameError, isUsernameAvailable]);
|
}, [photo, isProfileFieldsTouched, isUsernameError, isUsernameAvailable]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.EditProfile);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
// Due to the parent Transition, this component never gets unmounted,
|
// Due to the parent Transition, this component never gets unmounted,
|
||||||
// that's why we use throttled API call on every update.
|
// that's why we use throttled API call on every update.
|
||||||
|
|||||||
@ -167,7 +167,10 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
|||||||
return stickerSetsById?.[id]?.installedDate ? stickerSetsById[id] : false;
|
return stickerSetsById?.[id]?.installedDate ? stickerSetsById[id] : false;
|
||||||
}).filter<ApiStickerSet>(Boolean as any);
|
}).filter<ApiStickerSet>(Boolean as any);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.General);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
|
|||||||
@ -108,7 +108,10 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.GeneralChatBackground);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const isUploading = loadedWallpapers?.[0] && loadedWallpapers[0].slug === UPLOADING_WALLPAPER_SLUG;
|
const isUploading = loadedWallpapers?.[0] && loadedWallpapers[0].slug === UPLOADING_WALLPAPER_SLUG;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { SettingsScreens, ThemeKey } from '../../../types';
|
import { ThemeKey } from '../../../types';
|
||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import {
|
import {
|
||||||
@ -22,7 +22,6 @@ import './SettingsGeneralBackgroundColor.scss';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -52,7 +51,6 @@ const PREDEFINED_COLORS = [
|
|||||||
|
|
||||||
const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
theme,
|
theme,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
@ -205,7 +203,10 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
|||||||
isDragging && 'is-dragging',
|
isDragging && 'is-dragging',
|
||||||
);
|
);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.GeneralChatBackgroundColor);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className={className}>
|
<div ref={containerRef} className={className}>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { ISettings, LangCode, SettingsScreens } from '../../../types';
|
import { ISettings, LangCode } from '../../../types';
|
||||||
import { ApiLanguage } from '../../../api/types';
|
import { ApiLanguage } from '../../../api/types';
|
||||||
|
|
||||||
import { setLanguage } from '../../../util/langProvider';
|
import { setLanguage } from '../../../util/langProvider';
|
||||||
@ -15,7 +15,6 @@ import useHistoryBack from '../../../hooks/useHistoryBack';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ type StateProps = Pick<ISettings, 'languages' | 'language'>;
|
|||||||
|
|
||||||
const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
languages,
|
languages,
|
||||||
language,
|
language,
|
||||||
@ -56,7 +54,10 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
|||||||
return languages ? buildOptions(languages) : undefined;
|
return languages ? buildOptions(languages) : undefined;
|
||||||
}, [languages]);
|
}, [languages]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Language);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content settings-item settings-language custom-scroll settings-item--first">
|
<div className="settings-content settings-item settings-language custom-scroll settings-item--first">
|
||||||
|
|||||||
@ -43,7 +43,10 @@ const SettingsMain: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [lastSyncTime, profileId, loadProfilePhotos]);
|
}, [lastSyncTime, profileId, loadProfilePhotos]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Main);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastSyncTime) {
|
if (lastSyncTime) {
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { SettingsScreens } from '../../../types';
|
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import { playNotifySound } from '../../../util/notifications';
|
import { playNotifySound } from '../../../util/notifications';
|
||||||
@ -16,7 +14,6 @@ import RangeSlider from '../../ui/RangeSlider';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,7 +32,6 @@ type StateProps = {
|
|||||||
|
|
||||||
const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
hasPrivateChatsNotifications,
|
hasPrivateChatsNotifications,
|
||||||
hasPrivateChatsMessagePreview,
|
hasPrivateChatsMessagePreview,
|
||||||
@ -136,7 +132,10 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Notifications);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
|
|||||||
@ -59,7 +59,10 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Privacy);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
function getVisibilityValue(visibility?: PrivacyVisibility) {
|
function getVisibilityValue(visibility?: PrivacyVisibility) {
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import React, {
|
|||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { ApiChat, ApiCountryCode, ApiUser } from '../../../api/types';
|
import { ApiChat, ApiCountryCode, ApiUser } from '../../../api/types';
|
||||||
import { SettingsScreens } from '../../../types';
|
|
||||||
|
|
||||||
import { CHAT_HEIGHT_PX } from '../../../config';
|
import { CHAT_HEIGHT_PX } from '../../../config';
|
||||||
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||||
@ -25,7 +24,6 @@ import BlockUserModal from './BlockUserModal';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,7 +36,6 @@ type StateProps = {
|
|||||||
|
|
||||||
const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
chatsByIds,
|
chatsByIds,
|
||||||
usersByIds,
|
usersByIds,
|
||||||
@ -53,7 +50,10 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
|||||||
unblockContact({ contactId });
|
unblockContact({ contactId });
|
||||||
}, [unblockContact]);
|
}, [unblockContact]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.PrivacyBlockedUsers);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
function renderContact(contactId: string, i: number, viewportOffset: number) {
|
function renderContact(contactId: string, i: number, viewportOffset: number) {
|
||||||
const isPrivate = isUserId(contactId);
|
const isPrivate = isUserId(contactId);
|
||||||
|
|||||||
@ -84,7 +84,10 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [lang, screen]);
|
}, [lang, screen]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const descriptionText = useMemo(() => {
|
const descriptionText = useMemo(() => {
|
||||||
switch (screen) {
|
switch (screen) {
|
||||||
|
|||||||
@ -92,7 +92,10 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps> = ({
|
|||||||
onScreenSelect(SettingsScreens.Privacy);
|
onScreenSelect(SettingsScreens.Privacy);
|
||||||
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
|
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NewChat-inner step-1">
|
<div className="NewChat-inner step-1">
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
|
import React, { FC, memo, useCallback } from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import { SettingsScreens } from '../../../types';
|
|
||||||
import { ApiAvailableReaction } from '../../../api/types';
|
import { ApiAvailableReaction } from '../../../api/types';
|
||||||
|
|
||||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
@ -11,7 +10,6 @@ import RadioGroup from '../../ui/RadioGroup';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,12 +21,15 @@ type StateProps = {
|
|||||||
const SettingsQuickReaction: FC<OwnProps & StateProps> = ({
|
const SettingsQuickReaction: FC<OwnProps & StateProps> = ({
|
||||||
isActive,
|
isActive,
|
||||||
onReset,
|
onReset,
|
||||||
onScreenSelect,
|
|
||||||
availableReactions,
|
availableReactions,
|
||||||
selectedReaction,
|
selectedReaction,
|
||||||
}) => {
|
}) => {
|
||||||
const { setDefaultReaction } = getActions();
|
const { setDefaultReaction } = getActions();
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.General);
|
|
||||||
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const options = availableReactions?.filter((l) => !l.isInactive).map((l) => {
|
const options = availableReactions?.filter((l) => !l.isInactive).map((l) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -91,7 +91,6 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
<SettingsFoldersMain
|
<SettingsFoldersMain
|
||||||
onCreateFolder={handleCreateFolder}
|
onCreateFolder={handleCreateFolder}
|
||||||
onEditFolder={handleEditFolder}
|
onEditFolder={handleEditFolder}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.FoldersCreateFolder,
|
SettingsScreens.FoldersCreateFolder,
|
||||||
SettingsScreens.FoldersEditFolder,
|
SettingsScreens.FoldersEditFolder,
|
||||||
@ -111,7 +110,6 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
onAddIncludedChats={handleAddIncludedChats}
|
onAddIncludedChats={handleAddIncludedChats}
|
||||||
onAddExcludedChats={handleAddExcludedChats}
|
onAddExcludedChats={handleAddExcludedChats}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.FoldersIncludedChats,
|
SettingsScreens.FoldersIncludedChats,
|
||||||
SettingsScreens.FoldersExcludedChats,
|
SettingsScreens.FoldersExcludedChats,
|
||||||
@ -127,7 +125,6 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
state={state}
|
state={state}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -139,7 +136,6 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
state={state}
|
state={state}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import React, {
|
|||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getGlobal } from '../../../../global';
|
import { getGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { unique } from '../../../../util/iteratees';
|
import { unique } from '../../../../util/iteratees';
|
||||||
|
|
||||||
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID } from '../../../../config';
|
||||||
@ -26,7 +24,6 @@ type OwnProps = {
|
|||||||
state: FoldersState;
|
state: FoldersState;
|
||||||
dispatch: FolderEditDispatch;
|
dispatch: FolderEditDispatch;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,7 +32,6 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const { chatFilter } = state;
|
const { chatFilter } = state;
|
||||||
@ -103,12 +99,10 @@ const SettingsFoldersChatFilters: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}, [mode, selectedChatIds, dispatch]);
|
}, [mode, selectedChatIds, dispatch]);
|
||||||
|
|
||||||
useHistoryBack(
|
useHistoryBack({
|
||||||
isActive,
|
isActive,
|
||||||
onReset,
|
onBack: onReset,
|
||||||
onScreenSelect,
|
});
|
||||||
mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!displayedIds) {
|
if (!displayedIds) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import React, {
|
|||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../../global';
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
||||||
import { findIntersectionWithSet } from '../../../../util/iteratees';
|
import { findIntersectionWithSet } from '../../../../util/iteratees';
|
||||||
import { isUserId } from '../../../../global/helpers';
|
import { isUserId } from '../../../../global/helpers';
|
||||||
@ -34,7 +32,6 @@ type OwnProps = {
|
|||||||
onAddIncludedChats: () => void;
|
onAddIncludedChats: () => void;
|
||||||
onAddExcludedChats: () => void;
|
onAddExcludedChats: () => void;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
@ -57,7 +54,6 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
onAddIncludedChats,
|
onAddIncludedChats,
|
||||||
onAddExcludedChats,
|
onAddExcludedChats,
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
onBack,
|
onBack,
|
||||||
loadedActiveChatIds,
|
loadedActiveChatIds,
|
||||||
@ -120,9 +116,10 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onBack, onScreenSelect, state.mode === 'edit'
|
useHistoryBack({
|
||||||
? SettingsScreens.FoldersEditFolder
|
isActive,
|
||||||
: SettingsScreens.FoldersCreateFolder);
|
onBack,
|
||||||
|
});
|
||||||
|
|
||||||
const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { currentTarget } = event;
|
const { currentTarget } = event;
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import React, {
|
|||||||
import { getActions, withGlobal } from '../../../../global';
|
import { getActions, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { ApiChatFolder } from '../../../../api/types';
|
import { ApiChatFolder } from '../../../../api/types';
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
||||||
import { throttle } from '../../../../util/schedulers';
|
import { throttle } from '../../../../util/schedulers';
|
||||||
@ -23,7 +22,6 @@ type OwnProps = {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onCreateFolder: () => void;
|
onCreateFolder: () => void;
|
||||||
onEditFolder: (folder: ApiChatFolder) => void;
|
onEditFolder: (folder: ApiChatFolder) => void;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -41,7 +39,6 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
|||||||
isActive,
|
isActive,
|
||||||
onCreateFolder,
|
onCreateFolder,
|
||||||
onEditFolder,
|
onEditFolder,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
orderedFolderIds,
|
orderedFolderIds,
|
||||||
foldersById,
|
foldersById,
|
||||||
@ -88,7 +85,10 @@ const SettingsFoldersMain: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Folders);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const chatsCountByFolderId = useFolderManagerForChatsCount();
|
const chatsCountByFolderId = useFolderManagerForChatsCount();
|
||||||
const userFolders = useMemo(() => {
|
const userFolders = useMemo(() => {
|
||||||
|
|||||||
@ -161,7 +161,6 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SettingsTwoFaStart
|
<SettingsTwoFaStart
|
||||||
onStart={handleStartWizard}
|
onStart={handleStartWizard}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaNewPassword,
|
SettingsScreens.TwoFaNewPassword,
|
||||||
SettingsScreens.TwoFaNewPasswordConfirm,
|
SettingsScreens.TwoFaNewPasswordConfirm,
|
||||||
@ -177,11 +176,9 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaNewPassword:
|
case SettingsScreens.TwoFaNewPassword:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
placeholder={lang('PleaseEnterPassword')}
|
placeholder={lang('PleaseEnterPassword')}
|
||||||
submitLabel={lang('Continue')}
|
submitLabel={lang('Continue')}
|
||||||
onSubmit={handleNewPassword}
|
onSubmit={handleNewPassword}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaNewPasswordConfirm,
|
SettingsScreens.TwoFaNewPasswordConfirm,
|
||||||
SettingsScreens.TwoFaNewPasswordHint,
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
@ -196,12 +193,10 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaNewPasswordConfirm:
|
case SettingsScreens.TwoFaNewPasswordConfirm:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
expectedPassword={state.password}
|
expectedPassword={state.password}
|
||||||
placeholder={lang('PleaseReEnterPassword')}
|
placeholder={lang('PleaseReEnterPassword')}
|
||||||
submitLabel={lang('Continue')}
|
submitLabel={lang('Continue')}
|
||||||
onSubmit={handleNewPasswordConfirm}
|
onSubmit={handleNewPasswordConfirm}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaNewPasswordHint,
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
SettingsScreens.TwoFaNewPasswordEmail,
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
@ -218,8 +213,6 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
icon="hint"
|
icon="hint"
|
||||||
placeholder={lang('PasswordHintPlaceholder')}
|
placeholder={lang('PasswordHintPlaceholder')}
|
||||||
onSubmit={handleNewPasswordHint}
|
onSubmit={handleNewPasswordHint}
|
||||||
screen={currentScreen}
|
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaNewPasswordEmail,
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
SettingsScreens.TwoFaNewPasswordEmailCode,
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
@ -240,8 +233,6 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
placeholder={lang('RecoveryEmailTitle')}
|
placeholder={lang('RecoveryEmailTitle')}
|
||||||
shouldConfirm
|
shouldConfirm
|
||||||
onSubmit={handleNewPasswordEmail}
|
onSubmit={handleNewPasswordEmail}
|
||||||
screen={currentScreen}
|
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaNewPasswordEmailCode,
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
SettingsScreens.TwoFaCongratulations,
|
SettingsScreens.TwoFaCongratulations,
|
||||||
@ -257,8 +248,6 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
error={error}
|
error={error}
|
||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
onSubmit={handleEmailCode}
|
onSubmit={handleEmailCode}
|
||||||
screen={currentScreen}
|
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
@ -295,13 +284,11 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaChangePasswordCurrent:
|
case SettingsScreens.TwoFaChangePasswordCurrent:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
error={error}
|
error={error}
|
||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
hint={hint}
|
hint={hint}
|
||||||
onSubmit={handleChangePasswordCurrent}
|
onSubmit={handleChangePasswordCurrent}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaChangePasswordNew,
|
SettingsScreens.TwoFaChangePasswordNew,
|
||||||
SettingsScreens.TwoFaChangePasswordConfirm,
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
@ -315,10 +302,8 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaChangePasswordNew:
|
case SettingsScreens.TwoFaChangePasswordNew:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
placeholder={lang('PleaseEnterNewFirstPassword')}
|
placeholder={lang('PleaseEnterNewFirstPassword')}
|
||||||
onSubmit={handleChangePasswordNew}
|
onSubmit={handleChangePasswordNew}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaChangePasswordConfirm,
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
SettingsScreens.TwoFaChangePasswordHint,
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
@ -331,11 +316,9 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaChangePasswordConfirm:
|
case SettingsScreens.TwoFaChangePasswordConfirm:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
expectedPassword={state.password}
|
expectedPassword={state.password}
|
||||||
placeholder={lang('PleaseReEnterPassword')}
|
placeholder={lang('PleaseReEnterPassword')}
|
||||||
onSubmit={handleChangePasswordConfirm}
|
onSubmit={handleChangePasswordConfirm}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaChangePasswordHint,
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
SettingsScreens.TwoFaCongratulations,
|
SettingsScreens.TwoFaCongratulations,
|
||||||
@ -353,10 +336,8 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
icon="hint"
|
icon="hint"
|
||||||
placeholder={lang('PasswordHintPlaceholder')}
|
placeholder={lang('PasswordHintPlaceholder')}
|
||||||
onSubmit={handleChangePasswordHint}
|
onSubmit={handleChangePasswordHint}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
screen={currentScreen}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -368,23 +349,19 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
hint={hint}
|
hint={hint}
|
||||||
onSubmit={handleTurnOff}
|
onSubmit={handleTurnOff}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
screen={currentScreen}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case SettingsScreens.TwoFaRecoveryEmailCurrentPassword:
|
case SettingsScreens.TwoFaRecoveryEmailCurrentPassword:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
screen={currentScreen}
|
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
error={error}
|
error={error}
|
||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
hint={hint}
|
hint={hint}
|
||||||
onSubmit={handleRecoveryEmailCurrentPassword}
|
onSubmit={handleRecoveryEmailCurrentPassword}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaRecoveryEmail,
|
SettingsScreens.TwoFaRecoveryEmail,
|
||||||
SettingsScreens.TwoFaRecoveryEmailCode,
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
@ -397,12 +374,10 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaRecoveryEmail:
|
case SettingsScreens.TwoFaRecoveryEmail:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaSkippableForm
|
<SettingsTwoFaSkippableForm
|
||||||
screen={currentScreen}
|
|
||||||
icon="email"
|
icon="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder={lang('RecoveryEmailTitle')}
|
placeholder={lang('RecoveryEmailTitle')}
|
||||||
onSubmit={handleRecoveryEmail}
|
onSubmit={handleRecoveryEmail}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || [
|
isActive={isActive || [
|
||||||
SettingsScreens.TwoFaRecoveryEmailCode,
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
SettingsScreens.TwoFaCongratulations,
|
SettingsScreens.TwoFaCongratulations,
|
||||||
@ -414,12 +389,10 @@ const SettingsTwoFa: FC<OwnProps & StateProps> = ({
|
|||||||
case SettingsScreens.TwoFaRecoveryEmailCode:
|
case SettingsScreens.TwoFaRecoveryEmailCode:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaEmailCode
|
<SettingsTwoFaEmailCode
|
||||||
screen={currentScreen}
|
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
error={error}
|
error={error}
|
||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
onSubmit={handleEmailCode}
|
onSubmit={handleEmailCode}
|
||||||
onScreenSelect={onScreenSelect}
|
|
||||||
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -30,7 +30,10 @@ const SettingsTwoFaCongratulations: FC<OwnProps & StateProps> = ({
|
|||||||
onScreenSelect(SettingsScreens.Privacy);
|
onScreenSelect(SettingsScreens.Privacy);
|
||||||
}, [onScreenSelect]);
|
}, [onScreenSelect]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaCongratulations);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import React, {
|
|||||||
import { withGlobal } from '../../../../global';
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { ApiSticker } from '../../../../api/types';
|
import { ApiSticker } from '../../../../api/types';
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../../util/environment';
|
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../../util/environment';
|
||||||
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
||||||
@ -21,9 +20,7 @@ type OwnProps = {
|
|||||||
clearError: NoneToVoidFunction;
|
clearError: NoneToVoidFunction;
|
||||||
onSubmit: (hint: string) => void;
|
onSubmit: (hint: string) => void;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
screen: SettingsScreens;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -41,9 +38,7 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
clearError,
|
clearError,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
screen,
|
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@ -60,7 +55,10 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (error && clearError) {
|
if (error && clearError) {
|
||||||
|
|||||||
@ -27,7 +27,10 @@ const SettingsTwoFaEnabled: FC<OwnProps & StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaEnabled);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import React, {
|
|||||||
FC, memo, useCallback, useState,
|
FC, memo, useCallback, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
|
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
@ -11,7 +9,6 @@ import PasswordMonkey from '../../../common/PasswordMonkey';
|
|||||||
import PasswordForm from '../../../common/PasswordForm';
|
import PasswordForm from '../../../common/PasswordForm';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
screen: SettingsScreens;
|
|
||||||
error?: string;
|
error?: string;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
expectedPassword?: string;
|
expectedPassword?: string;
|
||||||
@ -21,16 +18,13 @@ type OwnProps = {
|
|||||||
clearError?: NoneToVoidFunction;
|
clearError?: NoneToVoidFunction;
|
||||||
onSubmit: (password: string) => void;
|
onSubmit: (password: string) => void;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const EQUAL_PASSWORD_ERROR = 'Passwords Should Be Equal';
|
const EQUAL_PASSWORD_ERROR = 'Passwords Should Be Equal';
|
||||||
|
|
||||||
const SettingsTwoFaPassword: FC<OwnProps> = ({
|
const SettingsTwoFaPassword: FC<OwnProps> = ({
|
||||||
screen,
|
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
@ -61,7 +55,10 @@ const SettingsTwoFaPassword: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import React, {
|
|||||||
import { withGlobal } from '../../../../global';
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { ApiSticker } from '../../../../api/types';
|
import { ApiSticker } from '../../../../api/types';
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../../util/environment';
|
import { IS_SINGLE_COLUMN_LAYOUT, IS_TOUCH_ENV } from '../../../../util/environment';
|
||||||
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
||||||
@ -28,9 +27,7 @@ type OwnProps = {
|
|||||||
clearError?: NoneToVoidFunction;
|
clearError?: NoneToVoidFunction;
|
||||||
onSubmit: (value?: string) => void;
|
onSubmit: (value?: string) => void;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
screen: SettingsScreens;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -49,9 +46,7 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
clearError,
|
clearError,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
isActive,
|
isActive,
|
||||||
onScreenSelect,
|
|
||||||
onReset,
|
onReset,
|
||||||
screen,
|
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@ -96,7 +91,10 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import React, { FC, memo } from '../../../../lib/teact/teact';
|
|||||||
import { withGlobal } from '../../../../global';
|
import { withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import { ApiSticker } from '../../../../api/types';
|
import { ApiSticker } from '../../../../api/types';
|
||||||
import { SettingsScreens } from '../../../../types';
|
|
||||||
|
|
||||||
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
@ -14,7 +13,6 @@ import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
onStart: NoneToVoidFunction;
|
onStart: NoneToVoidFunction;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,11 +21,14 @@ type StateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SettingsTwoFaStart: FC<OwnProps & StateProps> = ({
|
const SettingsTwoFaStart: FC<OwnProps & StateProps> = ({
|
||||||
isActive, onScreenSelect, onReset, animatedEmoji, onStart,
|
isActive, onReset, animatedEmoji, onStart,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaDisabled);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onReset,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
|
|||||||
@ -54,6 +54,7 @@ import HistoryCalendar from './HistoryCalendar.async';
|
|||||||
import GroupCall from '../calls/group/GroupCall.async';
|
import GroupCall from '../calls/group/GroupCall.async';
|
||||||
import ActiveCallHeader from '../calls/ActiveCallHeader.async';
|
import ActiveCallHeader from '../calls/ActiveCallHeader.async';
|
||||||
import PhoneCall from '../calls/phone/PhoneCall.async';
|
import PhoneCall from '../calls/phone/PhoneCall.async';
|
||||||
|
import MessageListHistoryHandler from '../middle/MessageListHistoryHandler';
|
||||||
import NewContactModal from './NewContactModal.async';
|
import NewContactModal from './NewContactModal.async';
|
||||||
import RatePhoneCallModal from '../calls/phone/RatePhoneCallModal.async';
|
import RatePhoneCallModal from '../calls/phone/RatePhoneCallModal.async';
|
||||||
import WebAppModal from './WebAppModal.async';
|
import WebAppModal from './WebAppModal.async';
|
||||||
@ -387,6 +388,7 @@ const Main: FC<StateProps> = ({
|
|||||||
<RatePhoneCallModal isOpen={isRatePhoneCallModalOpen} />
|
<RatePhoneCallModal isOpen={isRatePhoneCallModalOpen} />
|
||||||
<BotTrustModal bot={botTrustRequest?.bot} type={botTrustRequest?.type} />
|
<BotTrustModal bot={botTrustRequest?.bot} type={botTrustRequest?.type} />
|
||||||
<BotAttachModal bot={botAttachRequest?.bot} />
|
<BotAttachModal bot={botAttachRequest?.bot} />
|
||||||
|
<MessageListHistoryHandler />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -397,12 +397,9 @@ const MediaViewer: FC<StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isOpen, closeMediaViewer, openMediaViewer, {
|
useHistoryBack({
|
||||||
chatId,
|
isActive: isOpen,
|
||||||
threadId,
|
onBack: closeMediaViewer,
|
||||||
messageId,
|
|
||||||
origin,
|
|
||||||
avatarOwnerId: avatarOwner && avatarOwner.id,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
49
src/components/middle/MessageListHistoryHandler.tsx
Normal file
49
src/components/middle/MessageListHistoryHandler.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import React, { FC, memo } from '../../lib/teact/teact';
|
||||||
|
import { getActions, withGlobal } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
|
import { createMessageHash } from '../../util/routing';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
import { MessageList as GlobalMessageList } from '../../global/types';
|
||||||
|
|
||||||
|
type StateProps = {
|
||||||
|
messageLists?: GlobalMessageList[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Actual `MessageList` components are unmounted when deep in the history,
|
||||||
|
// so we need a separate component just for handling history
|
||||||
|
const MessageListHistoryHandler: FC<StateProps> = ({ messageLists }) => {
|
||||||
|
const { openChat } = getActions();
|
||||||
|
|
||||||
|
const closeChat = () => {
|
||||||
|
openChat({ id: undefined }, { forceSyncOnIOs: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const MessageHistoryRecord: FC<GlobalMessageList> = ({ chatId, type, threadId }) => {
|
||||||
|
useHistoryBack({
|
||||||
|
isActive: true,
|
||||||
|
hash: createMessageHash(chatId, type, threadId),
|
||||||
|
onBack: closeChat,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{messageLists?.map((messageList, i) => (
|
||||||
|
<MessageHistoryRecord
|
||||||
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
|
key={`${messageList.chatId}_${messageList.threadId}_${messageList.type}_${i}`}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...messageList}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(withGlobal(
|
||||||
|
(global): StateProps => {
|
||||||
|
return {
|
||||||
|
messageLists: global.messages.messageLists,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
)(MessageListHistoryHandler));
|
||||||
@ -6,7 +6,6 @@ import { getActions, withGlobal } from '../../global';
|
|||||||
import { ApiChatBannedRights, MAIN_THREAD_ID } from '../../api/types';
|
import { ApiChatBannedRights, MAIN_THREAD_ID } from '../../api/types';
|
||||||
import {
|
import {
|
||||||
MessageListType,
|
MessageListType,
|
||||||
MessageList as GlobalMessageList,
|
|
||||||
ActiveEmojiInteraction,
|
ActiveEmojiInteraction,
|
||||||
} from '../../global/types';
|
} from '../../global/types';
|
||||||
import { ThemeKey } from '../../types';
|
import { ThemeKey } from '../../types';
|
||||||
@ -48,7 +47,6 @@ import {
|
|||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { createMessageHash } from '../../util/routing';
|
|
||||||
import useCustomBackground from '../../hooks/useCustomBackground';
|
import useCustomBackground from '../../hooks/useCustomBackground';
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
||||||
@ -104,7 +102,6 @@ type StateProps = {
|
|||||||
animationLevel?: number;
|
animationLevel?: number;
|
||||||
shouldSkipHistoryAnimations?: boolean;
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
currentTransitionKey: number;
|
currentTransitionKey: number;
|
||||||
messageLists?: GlobalMessageList[];
|
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
areChatSettingsLoaded?: boolean;
|
areChatSettingsLoaded?: boolean;
|
||||||
canSubscribe?: boolean;
|
canSubscribe?: boolean;
|
||||||
@ -126,7 +123,6 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
messageListType,
|
messageListType,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
isPinnedMessageList,
|
isPinnedMessageList,
|
||||||
messageLists,
|
|
||||||
canPost,
|
canPost,
|
||||||
currentUserBannedRights,
|
currentUserBannedRights,
|
||||||
defaultBannedRights,
|
defaultBannedRights,
|
||||||
@ -158,6 +154,7 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
openChat,
|
openChat,
|
||||||
|
openPreviousChat,
|
||||||
unpinAllMessages,
|
unpinAllMessages,
|
||||||
loadUser,
|
loadUser,
|
||||||
loadChatSettings,
|
loadChatSettings,
|
||||||
@ -293,8 +290,8 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
const handleUnpinAllMessages = useCallback(() => {
|
const handleUnpinAllMessages = useCallback(() => {
|
||||||
unpinAllMessages({ chatId });
|
unpinAllMessages({ chatId });
|
||||||
closeUnpinModal();
|
closeUnpinModal();
|
||||||
openChat({ id: chatId });
|
openPreviousChat();
|
||||||
}, [unpinAllMessages, openChat, closeUnpinModal, chatId]);
|
}, [unpinAllMessages, chatId, closeUnpinModal, openPreviousChat]);
|
||||||
|
|
||||||
const handleTabletFocus = useCallback(() => {
|
const handleTabletFocus = useCallback(() => {
|
||||||
openChat({ id: chatId });
|
openChat({ id: chatId });
|
||||||
@ -347,21 +344,15 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
renderingCanPost && isNotchShown && !isSelectModeActive && 'with-notch',
|
renderingCanPost && isNotchShown && !isSelectModeActive && 'with-notch',
|
||||||
);
|
);
|
||||||
|
|
||||||
const closeChat = () => {
|
useHistoryBack({
|
||||||
openChat({ id: undefined }, { forceSyncOnIOs: true });
|
isActive: isSelectModeActive,
|
||||||
};
|
onBack: exitMessageSelectMode,
|
||||||
|
});
|
||||||
|
|
||||||
useHistoryBack(
|
useHistoryBack({
|
||||||
renderingChatId && renderingThreadId,
|
isActive: isMobileSearchActive,
|
||||||
closeChat,
|
onBack: closeLocalTextSearch,
|
||||||
undefined,
|
});
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
messageLists?.map(createMessageHash) || [],
|
|
||||||
);
|
|
||||||
|
|
||||||
useHistoryBack(isMobileSearchActive, closeLocalTextSearch);
|
|
||||||
useHistoryBack(isSelectModeActive, exitMessageSelectMode);
|
|
||||||
|
|
||||||
const isMessagingDisabled = Boolean(
|
const isMessagingDisabled = Boolean(
|
||||||
!isPinnedMessageList && !renderingCanPost && !renderingCanRestartBot && !renderingCanStartBot
|
!isPinnedMessageList && !renderingCanPost && !renderingCanRestartBot && !renderingCanStartBot
|
||||||
@ -573,7 +564,7 @@ export default memo(withGlobal(
|
|||||||
isSeenByModalOpen: Boolean(global.seenByModal),
|
isSeenByModalOpen: Boolean(global.seenByModal),
|
||||||
isReactorListModalOpen: Boolean(global.reactorModal),
|
isReactorListModalOpen: Boolean(global.reactorModal),
|
||||||
animationLevel: global.settings.byKey.animationLevel,
|
animationLevel: global.settings.byKey.animationLevel,
|
||||||
currentTransitionKey: Math.max(0, global.messages.messageLists.length - 1),
|
currentTransitionKey: Math.max(0, messageLists.length - 1),
|
||||||
activeEmojiInteractions,
|
activeEmojiInteractions,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
};
|
};
|
||||||
@ -620,7 +611,6 @@ export default memo(withGlobal(
|
|||||||
),
|
),
|
||||||
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
||||||
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
messageLists,
|
|
||||||
isChannel,
|
isChannel,
|
||||||
canSubscribe,
|
canSubscribe,
|
||||||
canStartBot,
|
canStartBot,
|
||||||
|
|||||||
@ -73,7 +73,10 @@ const AddChatMembers: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [connectionState, isActive, loadContactList]);
|
}, [connectionState, isActive, loadContactList]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const memberIds = useMemo(() => {
|
const memberIds = useMemo(() => {
|
||||||
return members ? members.map((member) => member.userId) : [];
|
return members ? members.map((member) => member.userId) : [];
|
||||||
|
|||||||
@ -91,7 +91,10 @@ const GifSearch: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
if (query === undefined) {
|
if (query === undefined) {
|
||||||
|
|||||||
@ -33,7 +33,10 @@ const PollResults: FC<OwnProps & StateProps> = ({
|
|||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
if (!message || !chat) {
|
if (!message || !chat) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
|
|||||||
@ -223,11 +223,13 @@ const RightColumn: FC<StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [chatId]);
|
}, [chatId]);
|
||||||
|
|
||||||
useHistoryBack(isChatSelected && (
|
useHistoryBack({
|
||||||
contentKey === RightColumnContent.ChatInfo
|
isActive: isChatSelected && (
|
||||||
|| contentKey === RightColumnContent.Management
|
contentKey === RightColumnContent.ChatInfo
|
||||||
|| contentKey === RightColumnContent.AddingMembers
|
|| contentKey === RightColumnContent.Management
|
||||||
), () => close(false), toggleChatInfo);
|
|| contentKey === RightColumnContent.AddingMembers),
|
||||||
|
onBack: () => close(false),
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
function renderContent(isActive: boolean) {
|
function renderContent(isActive: boolean) {
|
||||||
|
|||||||
@ -66,7 +66,10 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
|||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const [viewportIds, getMore] = useInfiniteScroll(searchTextMessagesLocal, foundIds);
|
const [viewportIds, getMore] = useInfiniteScroll(searchTextMessagesLocal, foundIds);
|
||||||
|
|
||||||
|
|||||||
@ -57,7 +57,10 @@ const StickerSearch: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
if (query === undefined) {
|
if (query === undefined) {
|
||||||
|
|||||||
@ -84,7 +84,10 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
|||||||
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastSyncTime) {
|
if (lastSyncTime) {
|
||||||
|
|||||||
@ -40,7 +40,10 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const handleRecentActionsClick = useCallback(() => {
|
const handleRecentActionsClick = useCallback(() => {
|
||||||
onScreenSelect(ManagementScreens.GroupRecentActions);
|
onScreenSelect(ManagementScreens.GroupRecentActions);
|
||||||
|
|||||||
@ -65,7 +65,10 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps> = ({
|
|||||||
|| (privacyType === 'private' && isPublic),
|
|| (privacyType === 'private' && isPublic),
|
||||||
);
|
);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (privacyType && !privateLink) {
|
if (privacyType && !privateLink) {
|
||||||
|
|||||||
@ -42,7 +42,10 @@ const ManageChatRemovedUsers: FC<OwnProps & StateProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const [isRemoveUserModalOpen, openRemoveUserModal, closeRemoveUserModal] = useFlag();
|
const [isRemoveUserModalOpen, openRemoveUserModal, closeRemoveUserModal] = useFlag();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const removedMembers = useMemo(() => {
|
const removedMembers = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.kickedMembers) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.kickedMembers) {
|
||||||
|
|||||||
@ -63,7 +63,10 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const linkedChatId = linkedChat?.id;
|
const linkedChatId = linkedChat?.id;
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadGroupsForDiscussion();
|
loadGroupsForDiscussion();
|
||||||
|
|||||||
@ -96,7 +96,10 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
|||||||
const isPublicGroup = chat.username || hasLinkedChannel;
|
const isPublicGroup = chat.username || hasLinkedChannel;
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (lastSyncTime && canInvite) {
|
if (lastSyncTime && canInvite) {
|
||||||
|
|||||||
@ -63,7 +63,10 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
|
|||||||
const [customTitle, setCustomTitle] = useState('');
|
const [customTitle, setCustomTitle] = useState('');
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember = useMemo(() => {
|
||||||
const selectedAdminMember = chat.fullInfo?.adminMembers?.find(({ userId }) => userId === selectedUserId);
|
const selectedAdminMember = chat.fullInfo?.adminMembers?.find(({ userId }) => userId === selectedUserId);
|
||||||
|
|||||||
@ -139,7 +139,10 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, '.ListItem-button', true);
|
}, '.ListItem-button', true);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
function renderSearchField() {
|
function renderSearchField() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -69,7 +69,10 @@ const ManageGroupPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const handleRemovedUsersClick = useCallback(() => {
|
const handleRemovedUsersClick = useCallback(() => {
|
||||||
onScreenSelect(ManagementScreens.GroupRemovedUsers);
|
onScreenSelect(ManagementScreens.GroupRemovedUsers);
|
||||||
|
|||||||
@ -25,7 +25,10 @@ type StateProps = {
|
|||||||
const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat, onClose, isActive }) => {
|
const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat, onClose, isActive }) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const adminMembers = useMemo(() => {
|
const adminMembers = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.adminMembers) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.adminMembers) {
|
||||||
|
|||||||
@ -48,7 +48,10 @@ const ManageGroupUserPermissions: FC<OwnProps & StateProps> = ({
|
|||||||
const [isBanConfirmationDialogOpen, openBanConfirmationDialog, closeBanConfirmationDialog] = useFlag();
|
const [isBanConfirmationDialogOpen, openBanConfirmationDialog, closeBanConfirmationDialog] = useFlag();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.members) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.members) {
|
||||||
|
|||||||
@ -41,7 +41,10 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
isActive,
|
isActive,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
}) => {
|
}) => {
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const memberIds = useMemo(() => {
|
const memberIds = useMemo(() => {
|
||||||
if (!members || !usersById) {
|
if (!members || !usersById) {
|
||||||
|
|||||||
@ -61,7 +61,10 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
|||||||
const [selectedUsageOption, setSelectedUsageOption] = useState('0');
|
const [selectedUsageOption, setSelectedUsageOption] = useState('0');
|
||||||
const [isSubmitBlocked, setIsSubmitBlocked] = useState(false);
|
const [isSubmitBlocked, setIsSubmitBlocked] = useState(false);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useOnChange(([oldEditingInvite]) => {
|
useOnChange(([oldEditingInvite]) => {
|
||||||
if (oldEditingInvite === editingInvite) return;
|
if (oldEditingInvite === editingInvite) return;
|
||||||
|
|||||||
@ -71,7 +71,10 @@ const ManageInviteInfo: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
}, [invite, lang, showNotification]);
|
}, [invite, lang, showNotification]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const renderImporters = () => {
|
const renderImporters = () => {
|
||||||
if (!importers?.length && requesters?.length) return undefined;
|
if (!importers?.length && requesters?.length) return undefined;
|
||||||
|
|||||||
@ -91,7 +91,10 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [animationData]);
|
}, [animationData]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const hasDetailedCountdown = useMemo(() => {
|
const hasDetailedCountdown = useMemo(() => {
|
||||||
if (!exportedInvites) return undefined;
|
if (!exportedInvites) return undefined;
|
||||||
|
|||||||
@ -54,7 +54,10 @@ const ManageJoinRequests: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [animationData]);
|
}, [animationData]);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!chat?.joinRequests && !isUserId(chatId)) {
|
if (!chat?.joinRequests && !isUserId(chatId)) {
|
||||||
|
|||||||
@ -40,7 +40,10 @@ const ManageReactions: FC<OwnProps & StateProps> = ({
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [localEnabledReactions, setLocalEnabledReactions] = useState(enabledReactions || []);
|
const [localEnabledReactions, setLocalEnabledReactions] = useState(enabledReactions || []);
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const handleSaveReactions = useCallback(() => {
|
const handleSaveReactions = useCallback(() => {
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|||||||
@ -58,7 +58,10 @@ const ManageUser: FC<OwnProps & StateProps> = ({
|
|||||||
const [error, setError] = useState<string | undefined>();
|
const [error, setError] = useState<string | undefined>();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
useHistoryBack(isActive, onClose);
|
useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
onBack: onClose,
|
||||||
|
});
|
||||||
|
|
||||||
const currentFirstName = user ? (user.firstName || '') : '';
|
const currentFirstName = user ? (user.firstName || '') : '';
|
||||||
const currentLastName = user ? (user.lastName || '') : '';
|
const currentLastName = user ? (user.lastName || '') : '';
|
||||||
|
|||||||
@ -34,7 +34,7 @@ type OwnProps = {
|
|||||||
noCompact?: boolean;
|
noCompact?: boolean;
|
||||||
onKeyDown?: (e: React.KeyboardEvent<any>) => void;
|
onKeyDown?: (e: React.KeyboardEvent<any>) => void;
|
||||||
onCloseAnimationEnd?: () => void;
|
onCloseAnimationEnd?: () => void;
|
||||||
onClose?: () => void;
|
onClose: () => void;
|
||||||
onMouseEnter?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
onMouseEnter?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
onMouseLeave?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
onMouseLeave?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@ -84,11 +84,15 @@ const Menu: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => (isOpen && onClose ? captureEscKeyListener(onClose) : undefined),
|
() => (isOpen ? captureEscKeyListener(onClose) : undefined),
|
||||||
[isOpen, onClose],
|
[isOpen, onClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
useHistoryBack(isOpen, onClose, undefined, undefined, autoClose);
|
useHistoryBack({
|
||||||
|
isActive: isOpen,
|
||||||
|
onBack: onClose,
|
||||||
|
shouldBeReplaced: true,
|
||||||
|
});
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsOpen]) => {
|
useEffectWithPrevDeps(([prevIsOpen]) => {
|
||||||
if (isOpen || (!isOpen && prevIsOpen === true)) {
|
if (isOpen || (!isOpen && prevIsOpen === true)) {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { FC, useCallback } from '../../lib/teact/teact';
|
import React, { FC, useCallback } from '../../lib/teact/teact';
|
||||||
|
|
||||||
|
import { IS_TEST } from '../../config';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import { IS_COMPACT_MENU } from '../../util/environment';
|
import { IS_COMPACT_MENU } from '../../util/environment';
|
||||||
@ -91,7 +92,7 @@ const MenuItem: FC<OwnProps> = (props) => {
|
|||||||
download={download}
|
download={download}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
title={ariaLabel}
|
title={ariaLabel}
|
||||||
target={href.startsWith(window.location.origin) ? '_self' : '_blank'}
|
target={href.startsWith(window.location.origin) || IS_TEST ? '_self' : '_blank'}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
dir={lang.isRtl ? 'rtl' : undefined}
|
dir={lang.isRtl ? 'rtl' : undefined}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@ -67,17 +67,10 @@ const Modal: FC<OwnProps & StateProps> = ({
|
|||||||
: undefined), [isOpen, onClose, onEnter]);
|
: undefined), [isOpen, onClose, onEnter]);
|
||||||
useEffect(() => (isOpen && modalRef.current ? trapFocus(modalRef.current) : undefined), [isOpen]);
|
useEffect(() => (isOpen && modalRef.current ? trapFocus(modalRef.current) : undefined), [isOpen]);
|
||||||
|
|
||||||
const { forceClose } = useHistoryBack(isOpen, onClose);
|
useHistoryBack({
|
||||||
|
isActive: isOpen,
|
||||||
// For modals that are closed by unmounting without changing `isOpen` to `false`
|
onBack: onClose,
|
||||||
useEffect(() => {
|
});
|
||||||
return () => {
|
|
||||||
if (isOpen) {
|
|
||||||
forceClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsOpen]) => {
|
useEffectWithPrevDeps(([prevIsOpen]) => {
|
||||||
document.body.classList.toggle('has-open-dialog', isOpen);
|
document.body.classList.toggle('has-open-dialog', isOpen);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export const APP_VERSION = process.env.APP_VERSION!;
|
|||||||
export const DEBUG = process.env.APP_ENV !== 'production';
|
export const DEBUG = process.env.APP_ENV !== 'production';
|
||||||
export const DEBUG_MORE = false;
|
export const DEBUG_MORE = false;
|
||||||
|
|
||||||
|
export const IS_MOCKED_CLIENT = process.env.APP_MOCKED_CLIENT === '1';
|
||||||
export const IS_TEST = process.env.APP_ENV === 'test';
|
export const IS_TEST = process.env.APP_ENV === 'test';
|
||||||
export const IS_PERF = process.env.APP_ENV === 'perf';
|
export const IS_PERF = process.env.APP_ENV === 'perf';
|
||||||
export const IS_BETA = process.env.APP_ENV === 'staging';
|
export const IS_BETA = process.env.APP_ENV === 'staging';
|
||||||
|
|||||||
@ -3,10 +3,13 @@ import { addActionHandler } from './index';
|
|||||||
import { INITIAL_STATE } from './initialState';
|
import { INITIAL_STATE } from './initialState';
|
||||||
import { initCache, loadCache } from './cache';
|
import { initCache, loadCache } from './cache';
|
||||||
import { cloneDeep } from '../util/iteratees';
|
import { cloneDeep } from '../util/iteratees';
|
||||||
|
import { IS_MOCKED_CLIENT } from '../config';
|
||||||
|
|
||||||
initCache();
|
initCache();
|
||||||
|
|
||||||
addActionHandler('init', () => {
|
addActionHandler('init', () => {
|
||||||
const initial = cloneDeep(INITIAL_STATE);
|
const initial = cloneDeep(INITIAL_STATE);
|
||||||
return loadCache(initial) || initial;
|
const state = loadCache(initial) || initial;
|
||||||
|
if (IS_MOCKED_CLIENT) state.authState = 'authorizationStateReady';
|
||||||
|
return state;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
import { FocusDirection } from '../../types';
|
import { FocusDirection } from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IS_MOCKED_CLIENT,
|
||||||
IS_TEST, MESSAGE_LIST_SLICE, MESSAGE_LIST_VIEWPORT_LIMIT, TMP_CHAT_ID,
|
IS_TEST, MESSAGE_LIST_SLICE, MESSAGE_LIST_VIEWPORT_LIMIT, TMP_CHAT_ID,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
import {
|
import {
|
||||||
@ -41,7 +42,7 @@ export function updateCurrentMessageList(
|
|||||||
): GlobalState {
|
): GlobalState {
|
||||||
const { messageLists } = global.messages;
|
const { messageLists } = global.messages;
|
||||||
let newMessageLists: MessageList[] = messageLists;
|
let newMessageLists: MessageList[] = messageLists;
|
||||||
if (shouldReplaceHistory || IS_TEST) {
|
if (shouldReplaceHistory || (IS_TEST && !IS_MOCKED_CLIENT)) {
|
||||||
newMessageLists = chatId ? [{ chatId, threadId, type }] : [];
|
newMessageLists = chatId ? [{ chatId, threadId, type }] : [];
|
||||||
} else if (chatId) {
|
} else if (chatId) {
|
||||||
const last = messageLists[messageLists.length - 1];
|
const last = messageLists[messageLists.length - 1];
|
||||||
|
|||||||
@ -1,54 +1,78 @@
|
|||||||
import { useCallback, useEffect, useRef } from '../lib/teact/teact';
|
import useOnChange from './useOnChange';
|
||||||
|
import { useEffect, useRef } from '../lib/teact/teact';
|
||||||
|
import { IS_TEST } from '../config';
|
||||||
|
import { fastRaf } from '../util/schedulers';
|
||||||
import { IS_IOS } from '../util/environment';
|
import { IS_IOS } from '../util/environment';
|
||||||
import usePrevious from './usePrevious';
|
import { getActions } from '../lib/teact/teactn';
|
||||||
import { getActions } from '../global';
|
|
||||||
import { areSortedArraysEqual } from '../util/iteratees';
|
|
||||||
|
|
||||||
type HistoryState = {
|
|
||||||
currentIndex: number;
|
|
||||||
nextStateIndexToReplace: number;
|
|
||||||
isHistoryAltered: boolean;
|
|
||||||
isDisabled: boolean;
|
|
||||||
isEdge: boolean;
|
|
||||||
currentIndexes: number[];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export const LOCATION_HASH = window.location.hash;
|
||||||
|
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
||||||
// Carefully selected by swiping and observing visual changes
|
// Carefully selected by swiping and observing visual changes
|
||||||
// TODO: may be different on other devices such as iPad, maybe take dpi into account?
|
// TODO: may be different on other devices such as iPad, maybe take dpi into account?
|
||||||
const SAFARI_EDGE_BACK_GESTURE_LIMIT = 300;
|
const SAFARI_EDGE_BACK_GESTURE_LIMIT = 300;
|
||||||
const SAFARI_EDGE_BACK_GESTURE_DURATION = 350;
|
const SAFARI_EDGE_BACK_GESTURE_DURATION = 350;
|
||||||
export const LOCATION_HASH = window.location.hash;
|
|
||||||
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
|
||||||
|
|
||||||
const historyState: HistoryState = {
|
type HistoryRecord = {
|
||||||
currentIndex: 0,
|
index: number;
|
||||||
nextStateIndexToReplace: -1,
|
// Should this record be replaced by the next record (for example Menu)
|
||||||
isHistoryAltered: false,
|
shouldBeReplaced?: boolean;
|
||||||
isDisabled: false,
|
// Mark this record as replaced by the next record. Only used to check if needed to perform effectBack
|
||||||
isEdge: false,
|
markReplaced?: VoidFunction;
|
||||||
currentIndexes: [],
|
onBack?: VoidFunction;
|
||||||
|
// Set if the element is closed in the UI, but not in the real history
|
||||||
|
isClosed?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const disableHistoryBack = () => {
|
type HistoryOperationGo = {
|
||||||
historyState.isDisabled = true;
|
type: 'go';
|
||||||
|
delta: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchStart = (event: TouchEvent) => {
|
type HistoryOperationState = {
|
||||||
|
type: 'pushState' | 'replaceState';
|
||||||
|
data: any;
|
||||||
|
hash?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type HistoryOperation = HistoryOperationGo | HistoryOperationState;
|
||||||
|
|
||||||
|
// Needed to dismiss any 'trashed' history records from the previous page reloads.
|
||||||
|
const historyUniqueSessionId = Number(new Date());
|
||||||
|
// Reflects real history state, but also contains information on which records should be replaced by the next record and
|
||||||
|
// which records are deferred to close on the next operation
|
||||||
|
let historyState: HistoryRecord[];
|
||||||
|
// Reflects current real history index
|
||||||
|
let historyCursor: number;
|
||||||
|
// If we alter real history programmatically, the popstate event will be fired, which we don't need
|
||||||
|
let isAlteringHistory = false;
|
||||||
|
// Unfortunately Safari doesn't really like when there's 2+ consequent history operations in one frame, so we need
|
||||||
|
// to delay them to the next raf
|
||||||
|
let deferredHistoryOperations: HistoryOperation[] = [];
|
||||||
|
let isSafariGestureAnimation = false;
|
||||||
|
|
||||||
|
// Do not remove: used for history unit tests
|
||||||
|
if (IS_TEST) {
|
||||||
|
(window as any).TEST_getHistoryState = () => historyState;
|
||||||
|
(window as any).TEST_getHistoryCursor = () => historyCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTouchStart(event: TouchEvent) {
|
||||||
const x = event.touches[0].pageX;
|
const x = event.touches[0].pageX;
|
||||||
|
|
||||||
if (x <= SAFARI_EDGE_BACK_GESTURE_LIMIT || x >= window.innerWidth - SAFARI_EDGE_BACK_GESTURE_LIMIT) {
|
if (x <= SAFARI_EDGE_BACK_GESTURE_LIMIT || x >= window.innerWidth - SAFARI_EDGE_BACK_GESTURE_LIMIT) {
|
||||||
historyState.isEdge = true;
|
isSafariGestureAnimation = true;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleTouchEnd = () => {
|
function handleTouchEnd() {
|
||||||
if (historyState.isEdge) {
|
if (!isSafariGestureAnimation) {
|
||||||
setTimeout(() => {
|
return;
|
||||||
historyState.isEdge = false;
|
|
||||||
}, SAFARI_EDGE_BACK_GESTURE_DURATION);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
setTimeout(() => {
|
||||||
|
isSafariGestureAnimation = false;
|
||||||
|
}, SAFARI_EDGE_BACK_GESTURE_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
if (IS_IOS) {
|
if (IS_IOS) {
|
||||||
window.addEventListener('touchstart', handleTouchStart);
|
window.addEventListener('touchstart', handleTouchStart);
|
||||||
@ -56,197 +80,209 @@ if (IS_IOS) {
|
|||||||
window.addEventListener('popstate', handleTouchEnd);
|
window.addEventListener('popstate', handleTouchEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.history.replaceState({ index: historyState.currentIndex }, '', PATH_BASE);
|
function applyDeferredHistoryOperations() {
|
||||||
|
const goOperations = deferredHistoryOperations.filter((op) => op.type === 'go') as HistoryOperationGo[];
|
||||||
|
const stateOperations = deferredHistoryOperations.filter((op) => op.type !== 'go') as HistoryOperationState[];
|
||||||
|
const goCount = goOperations.reduce((acc, op) => acc + op.delta, 0);
|
||||||
|
if (goCount) {
|
||||||
|
window.history.go(goCount);
|
||||||
|
}
|
||||||
|
|
||||||
export default function useHistoryBack(
|
stateOperations.forEach((op) => window.history[op.type](op.data, '', op.hash));
|
||||||
isActive: boolean | undefined,
|
|
||||||
onBack: ((noDisableAnimation: boolean) => void) | undefined,
|
|
||||||
onForward?: (state: any) => void,
|
|
||||||
currentState?: any,
|
|
||||||
shouldReplaceNext = false,
|
|
||||||
hashes?: string[],
|
|
||||||
) {
|
|
||||||
const indexRef = useRef(-1);
|
|
||||||
const isForward = useRef(false);
|
|
||||||
const prevIsActive = usePrevious(isActive);
|
|
||||||
const isClosed = useRef(true);
|
|
||||||
const indexHashRef = useRef<{ index: number; hash: string }[]>([]);
|
|
||||||
const prevHashes = usePrevious(hashes);
|
|
||||||
const isHashChangedFromEvent = useRef<boolean>(false);
|
|
||||||
|
|
||||||
const handleChange = useCallback((isForceClose = false) => {
|
deferredHistoryOperations = [];
|
||||||
if (!hashes) {
|
}
|
||||||
if (isActive && !isForceClose) {
|
|
||||||
isClosed.current = false;
|
|
||||||
|
|
||||||
if (isForward.current) {
|
function deferHistoryOperation(historyOperation: HistoryOperation) {
|
||||||
isForward.current = false;
|
if (!deferredHistoryOperations.length) fastRaf(applyDeferredHistoryOperations);
|
||||||
historyState.currentIndexes.push(indexRef.current);
|
deferredHistoryOperations.push(historyOperation);
|
||||||
} else {
|
}
|
||||||
setTimeout(() => {
|
|
||||||
const index = ++historyState.currentIndex;
|
|
||||||
|
|
||||||
historyState.currentIndexes.push(index);
|
// Resets history to the `root` state
|
||||||
|
function resetHistory() {
|
||||||
|
historyCursor = 0;
|
||||||
|
historyState = [{
|
||||||
|
index: 0,
|
||||||
|
onBack: () => window.history.back(),
|
||||||
|
}];
|
||||||
|
|
||||||
window.history[(
|
window.history.replaceState({ index: 0, historyUniqueSessionId }, PATH_BASE);
|
||||||
(
|
}
|
||||||
historyState.currentIndexes.includes(historyState.nextStateIndexToReplace - 1)
|
|
||||||
&& window.history.state.index !== 0
|
|
||||||
&& historyState.nextStateIndexToReplace === index
|
|
||||||
&& !shouldReplaceNext
|
|
||||||
)
|
|
||||||
? 'replaceState'
|
|
||||||
: 'pushState'
|
|
||||||
)]({
|
|
||||||
index,
|
|
||||||
state: currentState,
|
|
||||||
}, '');
|
|
||||||
|
|
||||||
indexRef.current = index;
|
resetHistory();
|
||||||
|
|
||||||
if (shouldReplaceNext) {
|
function cleanupClosed(alreadyClosedCount = 1) {
|
||||||
historyState.nextStateIndexToReplace = historyState.currentIndex + 1;
|
let countClosed = alreadyClosedCount;
|
||||||
}
|
for (let i = historyCursor - 1; i > 0; i--) {
|
||||||
}, 0);
|
if (!historyState[i].isClosed) break;
|
||||||
}
|
countClosed++;
|
||||||
}
|
}
|
||||||
|
if (countClosed) {
|
||||||
|
isAlteringHistory = true;
|
||||||
|
deferHistoryOperation({
|
||||||
|
type: 'go',
|
||||||
|
delta: -countClosed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return countClosed;
|
||||||
|
}
|
||||||
|
|
||||||
if ((isForceClose || !isActive) && !isClosed.current) {
|
function cleanupTrashedState() {
|
||||||
if ((indexRef.current === historyState.currentIndex || !shouldReplaceNext)) {
|
// Navigation to previous page reload, state of which was trashed by reload
|
||||||
historyState.isHistoryAltered = true;
|
for (let i = historyState.length - 1; i > 0; i--) {
|
||||||
window.history.back();
|
if (historyState[i].isClosed) {
|
||||||
|
continue;
|
||||||
setTimeout(() => {
|
|
||||||
historyState.nextStateIndexToReplace = -1;
|
|
||||||
}, 400);
|
|
||||||
}
|
|
||||||
historyState.currentIndexes.splice(historyState.currentIndexes.indexOf(indexRef.current), 1);
|
|
||||||
|
|
||||||
isClosed.current = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const prev = prevHashes || [];
|
|
||||||
if (prev.length < hashes.length) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const index = ++historyState.currentIndex;
|
|
||||||
historyState.currentIndexes.push(index);
|
|
||||||
|
|
||||||
window.history.pushState({
|
|
||||||
index,
|
|
||||||
state: currentState,
|
|
||||||
}, '', `#${hashes[hashes.length - 1]}`);
|
|
||||||
|
|
||||||
indexHashRef.current.push({
|
|
||||||
index,
|
|
||||||
hash: hashes[hashes.length - 1],
|
|
||||||
});
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
const delta = prev.length - hashes.length;
|
|
||||||
if (isHashChangedFromEvent.current) {
|
|
||||||
isHashChangedFromEvent.current = false;
|
|
||||||
} else {
|
|
||||||
if (hashes.length !== indexHashRef.current.length) {
|
|
||||||
if (delta > 0) {
|
|
||||||
const last = indexHashRef.current[indexHashRef.current.length - delta - 1];
|
|
||||||
let realDelta = delta;
|
|
||||||
if (last) {
|
|
||||||
const indexLast = historyState.currentIndexes.findIndex(
|
|
||||||
(l) => l === last.index,
|
|
||||||
);
|
|
||||||
realDelta = historyState.currentIndexes.length - indexLast - 1;
|
|
||||||
}
|
|
||||||
historyState.isHistoryAltered = true;
|
|
||||||
window.history.go(-realDelta);
|
|
||||||
const removed = indexHashRef.current.splice(indexHashRef.current.length - delta - 1, delta);
|
|
||||||
removed.forEach(({ index }) => {
|
|
||||||
historyState.currentIndexes.splice(historyState.currentIndexes.indexOf(index), 1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hashes.length > 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const index = ++historyState.currentIndex;
|
|
||||||
historyState.currentIndexes[historyState.currentIndexes.length - 1] = index;
|
|
||||||
|
|
||||||
window.history.replaceState({
|
|
||||||
index,
|
|
||||||
state: currentState,
|
|
||||||
}, '', `${PATH_BASE}#${hashes[hashes.length - 1]}`);
|
|
||||||
|
|
||||||
indexHashRef.current[indexHashRef.current.length - 1] = {
|
|
||||||
index,
|
|
||||||
hash: hashes[hashes.length - 1],
|
|
||||||
};
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [currentState, hashes, isActive, prevHashes, shouldReplaceNext]);
|
if (isSafariGestureAnimation) {
|
||||||
|
getActions().disableHistoryAnimations();
|
||||||
|
}
|
||||||
|
historyState[i].onBack?.();
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
resetHistory();
|
||||||
const handlePopState = (event: PopStateEvent) => {
|
}
|
||||||
if (historyState.isHistoryAltered) {
|
|
||||||
setTimeout(() => {
|
window.addEventListener('popstate', ({ state }: PopStateEvent) => {
|
||||||
historyState.isHistoryAltered = false;
|
if (isAlteringHistory) {
|
||||||
}, 0);
|
isAlteringHistory = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
cleanupTrashedState();
|
||||||
|
|
||||||
|
if (!window.location.hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { index, historyUniqueSessionId: previousUniqueSessionId } = state;
|
||||||
|
if (previousUniqueSessionId !== historyUniqueSessionId) {
|
||||||
|
cleanupTrashedState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New real history state matches the old virtual one. Not possible in theory, but in practice we have Safari
|
||||||
|
if (index === historyCursor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < historyCursor) {
|
||||||
|
// Navigating back
|
||||||
|
let alreadyClosedCount = 0;
|
||||||
|
for (let i = historyCursor; i > index - alreadyClosedCount; i--) {
|
||||||
|
if (historyState[i].isClosed) {
|
||||||
|
alreadyClosedCount++;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
const { index: i } = event.state;
|
if (isSafariGestureAnimation) {
|
||||||
const index = i || 0;
|
getActions().disableHistoryAnimations();
|
||||||
try {
|
|
||||||
const currIndex = hashes ? indexHashRef.current[indexHashRef.current.length - 1].index : indexRef.current;
|
|
||||||
|
|
||||||
const prev = historyState.currentIndexes[historyState.currentIndexes.indexOf(currIndex) - 1];
|
|
||||||
|
|
||||||
if (historyState.isDisabled) return;
|
|
||||||
|
|
||||||
if ((!isClosed.current && (index === 0 || index === prev)) || (hashes && (index === 0 || index === prev))) {
|
|
||||||
if (hashes) {
|
|
||||||
isHashChangedFromEvent.current = true;
|
|
||||||
indexHashRef.current.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
historyState.currentIndexes.splice(historyState.currentIndexes.indexOf(currIndex), 1);
|
|
||||||
|
|
||||||
if (onBack) {
|
|
||||||
if (historyState.isEdge) {
|
|
||||||
getActions()
|
|
||||||
.disableHistoryAnimations();
|
|
||||||
}
|
|
||||||
onBack(!historyState.isEdge);
|
|
||||||
isClosed.current = true;
|
|
||||||
}
|
|
||||||
} else if (index === currIndex && isClosed.current && onForward && !hashes) {
|
|
||||||
isForward.current = true;
|
|
||||||
if (historyState.isEdge) {
|
|
||||||
getActions()
|
|
||||||
.disableHistoryAnimations();
|
|
||||||
}
|
|
||||||
onForward(event.state.state);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Forward navigation for hashed is not supported
|
|
||||||
}
|
}
|
||||||
|
historyState[i].onBack?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
const countClosed = cleanupClosed(alreadyClosedCount);
|
||||||
|
historyCursor += index - historyCursor - countClosed;
|
||||||
|
|
||||||
|
// Can happen when we have deferred a real back for some element (for example Menu), closed via UI,
|
||||||
|
// pressed back button and caused a pushState.
|
||||||
|
if (historyCursor < 0) {
|
||||||
|
historyCursor = 0;
|
||||||
|
}
|
||||||
|
} else if (index > historyCursor) {
|
||||||
|
// Forward navigation is not yet supported
|
||||||
|
isAlteringHistory = true;
|
||||||
|
deferHistoryOperation({
|
||||||
|
type: 'go',
|
||||||
|
delta: -(index - historyCursor),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function useHistoryBack({
|
||||||
|
isActive,
|
||||||
|
shouldBeReplaced,
|
||||||
|
hash,
|
||||||
|
onBack,
|
||||||
|
}: {
|
||||||
|
isActive?: boolean;
|
||||||
|
shouldBeReplaced?: boolean;
|
||||||
|
hash?: string;
|
||||||
|
title?: string;
|
||||||
|
onBack: VoidFunction;
|
||||||
|
}) {
|
||||||
|
// Active index of the record
|
||||||
|
const indexRef = useRef<number>();
|
||||||
|
const wasReplaced = useRef(false);
|
||||||
|
|
||||||
|
const isFirstRender = useRef(true);
|
||||||
|
|
||||||
|
const pushState = (forceReplace = false) => {
|
||||||
|
// Check if the old state should be replaced
|
||||||
|
const shouldReplace = forceReplace || historyState[historyCursor].shouldBeReplaced;
|
||||||
|
indexRef.current = shouldReplace ? historyCursor : ++historyCursor;
|
||||||
|
|
||||||
|
historyCursor = indexRef.current;
|
||||||
|
|
||||||
|
// Mark the previous record as replaced so effectBack doesn't perform back operation on the new record
|
||||||
|
const previousRecord = historyState[indexRef.current];
|
||||||
|
if (previousRecord && !previousRecord.isClosed) {
|
||||||
|
previousRecord.markReplaced?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
historyState[indexRef.current] = {
|
||||||
|
index: indexRef.current,
|
||||||
|
onBack,
|
||||||
|
shouldBeReplaced,
|
||||||
|
markReplaced: () => {
|
||||||
|
wasReplaced.current = true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasChanged = hashes
|
// Delete forward navigation in the virtual history. Not really needed, just looks better when debugging `logState`
|
||||||
? (!prevHashes || !areSortedArraysEqual(prevHashes, hashes))
|
for (let i = indexRef.current + 1; i < historyState.length; i++) {
|
||||||
: prevIsActive !== isActive;
|
delete historyState[i];
|
||||||
|
|
||||||
if (!historyState.isDisabled && hasChanged) {
|
|
||||||
handleChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('popstate', handlePopState);
|
deferHistoryOperation({
|
||||||
return () => window.removeEventListener('popstate', handlePopState);
|
type: shouldReplace ? 'replaceState' : 'pushState',
|
||||||
}, [
|
data: {
|
||||||
currentState, handleChange, hashes, isActive, onBack, onForward, prevHashes, prevIsActive, shouldReplaceNext,
|
index: indexRef.current,
|
||||||
]);
|
historyUniqueSessionId,
|
||||||
|
},
|
||||||
return {
|
hash: hash ? `#${hash}` : undefined,
|
||||||
forceClose: () => handleChange(true),
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const processBack = () => {
|
||||||
|
// Only process back on open records
|
||||||
|
if (indexRef.current && historyState[indexRef.current] && !wasReplaced.current) {
|
||||||
|
historyState[indexRef.current].isClosed = true;
|
||||||
|
wasReplaced.current = true;
|
||||||
|
if (indexRef.current === historyCursor && !shouldBeReplaced) {
|
||||||
|
historyCursor -= cleanupClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process back navigation when element is unmounted
|
||||||
|
useEffect(() => {
|
||||||
|
isFirstRender.current = false;
|
||||||
|
return () => {
|
||||||
|
if (!isActive || wasReplaced.current) return;
|
||||||
|
processBack();
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useOnChange(() => {
|
||||||
|
if (isFirstRender.current && !isActive) return;
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
pushState();
|
||||||
|
} else {
|
||||||
|
processBack();
|
||||||
|
}
|
||||||
|
}, [isActive]);
|
||||||
}
|
}
|
||||||
|
|||||||
331
src/lib/gramjs/client/MockClient.ts
Normal file
331
src/lib/gramjs/client/MockClient.ts
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
import BigInt from 'big-integer';
|
||||||
|
import { UpdateConnectionState } from '../network';
|
||||||
|
import Request from '../tl/api';
|
||||||
|
import { default as GramJs } from "../tl/api";
|
||||||
|
|
||||||
|
type Peer = {
|
||||||
|
peer: GramJs.Chat | GramJs.Channel | GramJs.User;
|
||||||
|
TEST_messages: GramJs.Message[];
|
||||||
|
TEST_sendMessage: (data: CreateMessageParams) => GramJs.Message | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreateMessageParams = {
|
||||||
|
fromId?: any;
|
||||||
|
repliesChannelId?: any;
|
||||||
|
replyingTo?: GramJs.MessageReplyHeader;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TelegramClient {
|
||||||
|
addEventHandler(callback: any, event: any) {
|
||||||
|
callback(event.build(new UpdateConnectionState(UpdateConnectionState.connected)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private lastId = 0;
|
||||||
|
|
||||||
|
private peers: Peer[] = [];
|
||||||
|
|
||||||
|
private dialogs: GramJs.Dialog[] = [];
|
||||||
|
|
||||||
|
start() {
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
const user = this.createUser({
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'Account',
|
||||||
|
});
|
||||||
|
user.TEST_sendMessage({});
|
||||||
|
|
||||||
|
const chat = this.createChat({});
|
||||||
|
chat.TEST_sendMessage({});
|
||||||
|
|
||||||
|
const channel = this.createChannel({
|
||||||
|
title: 'Test Channel',
|
||||||
|
username: 'testchannel',
|
||||||
|
});
|
||||||
|
|
||||||
|
const discussion = this.createChannel({
|
||||||
|
title: 'Test Discussion',
|
||||||
|
username: 'testdiscuss',
|
||||||
|
isMegagroup: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = channel.TEST_sendMessage({
|
||||||
|
repliesChannelId: discussion.peer.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { id } = discussion.TEST_sendMessage({})!;
|
||||||
|
|
||||||
|
discussion.TEST_sendMessage({
|
||||||
|
fromId: new GramJs.PeerUser({
|
||||||
|
userId: user.peer.id,
|
||||||
|
}),
|
||||||
|
replyingTo: new GramJs.MessageReplyHeader({
|
||||||
|
replyToMsgId: id,
|
||||||
|
replyToPeerId: new GramJs.PeerChannel({
|
||||||
|
channelId: channel.peer.id,
|
||||||
|
}),
|
||||||
|
replyToTopId: message!.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createDialog(peer: GramJs.TypePeer) {
|
||||||
|
return new GramJs.Dialog({
|
||||||
|
peer,
|
||||||
|
topMessage: 0,
|
||||||
|
readInboxMaxId: 0,
|
||||||
|
readOutboxMaxId: 0,
|
||||||
|
unreadCount: 0,
|
||||||
|
unreadMentionsCount: 0,
|
||||||
|
unreadReactionsCount: 0,
|
||||||
|
notifySettings: new GramJs.PeerNotifySettings({}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createMessage(peer: GramJs.TypePeer) {
|
||||||
|
return ({
|
||||||
|
fromId,
|
||||||
|
repliesChannelId,
|
||||||
|
replyingTo,
|
||||||
|
}: CreateMessageParams) => {
|
||||||
|
const pi = this.getPeerIndex(peer);
|
||||||
|
const p = this.getPeer(peer);
|
||||||
|
if (!p || pi === undefined) return;
|
||||||
|
|
||||||
|
const message = new GramJs.Message({
|
||||||
|
id: p.TEST_messages.length + 1,
|
||||||
|
fromId,
|
||||||
|
peerId: peer,
|
||||||
|
date: Number(new Date()) / 1000 + pi * 60,
|
||||||
|
message: 'lol @channel',
|
||||||
|
entities: [new GramJs.MessageEntityMention({
|
||||||
|
offset: 4,
|
||||||
|
length: 8,
|
||||||
|
})],
|
||||||
|
replyTo: replyingTo,
|
||||||
|
replies: new GramJs.MessageReplies({
|
||||||
|
comments: true,
|
||||||
|
replies: 0,
|
||||||
|
repliesPts: 0,
|
||||||
|
channelId: repliesChannelId ? BigInt(repliesChannelId) : undefined,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
this.peers[pi].TEST_messages.push(message);
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createChat({}) {
|
||||||
|
const chat = new GramJs.Chat({
|
||||||
|
id: BigInt(this.lastId++),
|
||||||
|
title: 'Some chat',
|
||||||
|
photo: new GramJs.ChatPhotoEmpty(),
|
||||||
|
participantsCount: 1,
|
||||||
|
date: 1000,
|
||||||
|
version: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const peerChat = new GramJs.PeerChat({
|
||||||
|
chatId: chat.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogs.push(this.createDialog(peerChat));
|
||||||
|
|
||||||
|
const testChat: Peer = { peer: chat, TEST_messages: [], TEST_sendMessage: this.createMessage(peerChat) };
|
||||||
|
|
||||||
|
this.peers.push(testChat);
|
||||||
|
|
||||||
|
return testChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
createChannel({ title, username, isMegagroup }: {
|
||||||
|
title: string;
|
||||||
|
username: string;
|
||||||
|
isMegagroup?: boolean;
|
||||||
|
}) {
|
||||||
|
const channel = new GramJs.Channel({
|
||||||
|
username,
|
||||||
|
id: BigInt(this.lastId++),
|
||||||
|
megagroup: isMegagroup ? true : undefined,
|
||||||
|
title,
|
||||||
|
photo: new GramJs.ChatPhotoEmpty(),
|
||||||
|
participantsCount: 1,
|
||||||
|
date: 1000,
|
||||||
|
creator: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const peerChannel = new GramJs.PeerChannel({
|
||||||
|
channelId: channel.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogs.push(this.createDialog(peerChannel));
|
||||||
|
|
||||||
|
const testChat: Peer = { peer: channel, TEST_messages: [], TEST_sendMessage: this.createMessage(peerChannel) };
|
||||||
|
|
||||||
|
this.peers.push(testChat);
|
||||||
|
|
||||||
|
return testChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
createUser({
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
}: {
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
}): Peer {
|
||||||
|
const user = new GramJs.User({
|
||||||
|
// self: true,
|
||||||
|
verified: true,
|
||||||
|
id: BigInt(this.lastId++),
|
||||||
|
// accessHash?: long;
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
username: 'man',
|
||||||
|
// phone?: string;
|
||||||
|
// photo?: Api.TypeUserProfilePhoto;
|
||||||
|
// status?: Api.TypeUserStatus;
|
||||||
|
// botInfoVersion?: int;
|
||||||
|
// restrictionReason?: Api.//TypeRestrictionReason[];
|
||||||
|
// botInlinePlaceholder?: string;
|
||||||
|
// langCode?: string;
|
||||||
|
});
|
||||||
|
|
||||||
|
const peerUser = new GramJs.PeerUser({
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialogs.push(this.createDialog(peerUser));
|
||||||
|
|
||||||
|
const testChat: Peer = { peer: user, TEST_messages: [], TEST_sendMessage: this.createMessage(peerUser) };
|
||||||
|
|
||||||
|
this.peers.push(testChat);
|
||||||
|
|
||||||
|
return testChat;
|
||||||
|
}
|
||||||
|
|
||||||
|
async invoke(request: Request) {
|
||||||
|
// await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
if (request instanceof GramJs.messages.GetDiscussionMessage) {
|
||||||
|
return new GramJs.messages.DiscussionMessage({
|
||||||
|
messages: [
|
||||||
|
this.peers[3].TEST_messages[0],
|
||||||
|
],
|
||||||
|
maxId: 2,
|
||||||
|
unreadCount: 1,
|
||||||
|
chats: [],
|
||||||
|
users: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (request instanceof GramJs.messages.GetHistory) {
|
||||||
|
const peer = this.getPeer(request.peer);
|
||||||
|
if (!peer) return;
|
||||||
|
|
||||||
|
return new GramJs.messages.Messages({
|
||||||
|
messages: peer.TEST_messages,
|
||||||
|
chats: [],
|
||||||
|
users: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (request instanceof GramJs.messages.GetReplies) {
|
||||||
|
const peer = this.peers[3];
|
||||||
|
if (!peer) return;
|
||||||
|
|
||||||
|
return new GramJs.messages.ChannelMessages({
|
||||||
|
messages: peer.TEST_messages,
|
||||||
|
pts: 0,
|
||||||
|
count: peer.TEST_messages.length,
|
||||||
|
chats: [],
|
||||||
|
users: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (request instanceof GramJs.messages.GetDialogFilters) {
|
||||||
|
return [new GramJs.DialogFilter({
|
||||||
|
contacts: true,
|
||||||
|
nonContacts: true,
|
||||||
|
groups: true,
|
||||||
|
broadcasts: true,
|
||||||
|
bots: true,
|
||||||
|
// excludeMuted?: true;
|
||||||
|
// excludeRead?: true;
|
||||||
|
// excludeArchived?: true;
|
||||||
|
id: 1,
|
||||||
|
title: 'Dialog Filter',
|
||||||
|
// emoticon?: string;
|
||||||
|
pinnedPeers: [],
|
||||||
|
includePeers: [],
|
||||||
|
excludePeers: [],
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
if (request instanceof GramJs.messages.GetPinnedDialogs) {
|
||||||
|
return new GramJs.messages.PeerDialogs({
|
||||||
|
dialogs: [],
|
||||||
|
chats: [],
|
||||||
|
messages: [],
|
||||||
|
users: [],
|
||||||
|
state: new GramJs.updates.State({
|
||||||
|
pts: 0,
|
||||||
|
qts: 0,
|
||||||
|
date: 0,
|
||||||
|
seq: 0,
|
||||||
|
unreadCount: 0,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (request instanceof GramJs.messages.GetDialogs) {
|
||||||
|
if (request.folderId || !(request.offsetPeer instanceof GramJs.InputPeerEmpty)) {
|
||||||
|
return new GramJs.messages.Dialogs({
|
||||||
|
dialogs: [],
|
||||||
|
users: [],
|
||||||
|
chats: [],
|
||||||
|
messages: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GramJs.messages.Dialogs({
|
||||||
|
dialogs: this.dialogs,
|
||||||
|
messages: this.getAllMessages(),
|
||||||
|
chats: this.getChats(),
|
||||||
|
users: this.getUsers(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// console.log(request.className, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPeerIndex(peer: GramJs.TypeInputPeer) {
|
||||||
|
const id = 'channelId' in peer ? peer.channelId : (
|
||||||
|
'userId' in peer ? peer.userId : (
|
||||||
|
'chatId' in peer ? peer.chatId : undefined
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!id) return undefined;
|
||||||
|
|
||||||
|
return this.peers.findIndex((l) => l.peer.id.toString() === id.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPeer(peer: GramJs.TypeInputPeer) {
|
||||||
|
const index = this.getPeerIndex(peer);
|
||||||
|
if (index === undefined) return undefined;
|
||||||
|
|
||||||
|
return this.peers[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAllMessages() {
|
||||||
|
return this.peers.reduce((acc: GramJs.Message[], el) => {
|
||||||
|
acc.push(...el.TEST_messages);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getChats() {
|
||||||
|
return this.peers.filter((l) => !(l.peer instanceof GramJs.User)).map((l) => l.peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUsers() {
|
||||||
|
return this.peers.filter((l) => l.peer instanceof GramJs.User).map((l) => l.peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TelegramClient;
|
||||||
@ -1,17 +1,12 @@
|
|||||||
import { MessageList, MessageListType } from '../global/types';
|
import { MessageListType } from '../global/types';
|
||||||
import { MAIN_THREAD_ID } from '../api/types';
|
import { MAIN_THREAD_ID } from '../api/types';
|
||||||
|
|
||||||
import { LOCATION_HASH } from '../hooks/useHistoryBack';
|
import { LOCATION_HASH } from '../hooks/useHistoryBack';
|
||||||
|
|
||||||
export function createMessageHash(messageList: MessageList) {
|
export const createMessageHash = (chatId: string, type: string, threadId: number): string => (
|
||||||
const typeOrThreadId = messageList.type !== 'thread' ? (
|
chatId.toString()
|
||||||
`_${messageList.type}`
|
+ (type !== 'thread' ? `_${type}`
|
||||||
) : messageList.threadId !== -1 ? (
|
: (threadId !== -1 ? `_${threadId}` : ''))
|
||||||
`_${messageList.threadId}`
|
);
|
||||||
) : '';
|
|
||||||
|
|
||||||
return `${messageList.chatId}${typeOrThreadId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseLocationHash() {
|
export function parseLocationHash() {
|
||||||
if (!LOCATION_HASH) return undefined;
|
if (!LOCATION_HASH) return undefined;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DEBUG, DEBUG_MORE } from '../config';
|
import { DEBUG, DEBUG_MORE, IS_TEST } from '../config';
|
||||||
import { getActions } from '../global';
|
import { getActions } from '../global';
|
||||||
import { IS_ANDROID, IS_IOS, IS_SERVICE_WORKER_SUPPORTED } from './environment';
|
import { IS_ANDROID, IS_IOS, IS_SERVICE_WORKER_SUPPORTED } from './environment';
|
||||||
import { notifyClientReady, playNotifySoundDebounced } from './notifications';
|
import { notifyClientReady, playNotifySoundDebounced } from './notifications';
|
||||||
@ -77,7 +77,7 @@ if (IS_SERVICE_WORKER_SUPPORTED) {
|
|||||||
console.error('[SW] ServiceWorker not available');
|
console.error('[SW] ServiceWorker not available');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IS_IOS && !IS_ANDROID) {
|
if (!IS_IOS && !IS_ANDROID && !IS_TEST) {
|
||||||
getActions().showDialog({ data: { message: 'SERVICE_WORKER_DISABLED', hasErrorKey: true } });
|
getActions().showDialog({ data: { message: 'SERVICE_WORKER_DISABLED', hasErrorKey: true } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { APP_VERSION, DEBUG } from '../config';
|
import { APP_VERSION, DEBUG, IS_MOCKED_CLIENT } from '../config';
|
||||||
import { getGlobal } from '../global';
|
import { getGlobal } from '../global';
|
||||||
import { hasStoredSession } from './sessions';
|
import { hasStoredSession } from './sessions';
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ const saveSync = (authed: boolean) => {
|
|||||||
let lastTimeout: NodeJS.Timeout | undefined;
|
let lastTimeout: NodeJS.Timeout | undefined;
|
||||||
|
|
||||||
export const forceWebsync = (authed: boolean) => {
|
export const forceWebsync = (authed: boolean) => {
|
||||||
|
if (IS_MOCKED_CLIENT) return undefined;
|
||||||
const currentTs = getTs();
|
const currentTs = getTs();
|
||||||
|
|
||||||
const { canRedirect, ts } = JSON.parse(localStorage.getItem(WEBSYNC_KEY) || '{}');
|
const { canRedirect, ts } = JSON.parse(localStorage.getItem(WEBSYNC_KEY) || '{}');
|
||||||
|
|||||||
@ -5,6 +5,8 @@ const {
|
|||||||
DefinePlugin,
|
DefinePlugin,
|
||||||
EnvironmentPlugin,
|
EnvironmentPlugin,
|
||||||
ProvidePlugin,
|
ProvidePlugin,
|
||||||
|
|
||||||
|
NormalModuleReplacementPlugin,
|
||||||
} = require('webpack');
|
} = require('webpack');
|
||||||
const HtmlWebackPlugin = require('html-webpack-plugin');
|
const HtmlWebackPlugin = require('html-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
@ -118,6 +120,10 @@ module.exports = (env = {}, argv = {}) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
...(process.env.APP_MOCKED_CLIENT === '1' ? [new NormalModuleReplacementPlugin(
|
||||||
|
/src\/lib\/gramjs\/client\/TelegramClient\.js/,
|
||||||
|
'./MockClient.ts'
|
||||||
|
)] : []),
|
||||||
new HtmlWebackPlugin({
|
new HtmlWebackPlugin({
|
||||||
appName: process.env.APP_ENV === 'production' ? 'Telegram Web' : 'Telegram Web Beta',
|
appName: process.env.APP_ENV === 'production' ? 'Telegram Web' : 'Telegram Web Beta',
|
||||||
appleIcon: process.env.APP_ENV === 'production' ? 'apple-touch-icon' : './apple-touch-icon-dev',
|
appleIcon: process.env.APP_ENV === 'production' ? 'apple-touch-icon' : './apple-touch-icon-dev',
|
||||||
@ -130,6 +136,7 @@ module.exports = (env = {}, argv = {}) => {
|
|||||||
}),
|
}),
|
||||||
new EnvironmentPlugin({
|
new EnvironmentPlugin({
|
||||||
APP_ENV: 'production',
|
APP_ENV: 'production',
|
||||||
|
APP_MOCKED_CLIENT: '',
|
||||||
APP_NAME: null,
|
APP_NAME: null,
|
||||||
APP_VERSION: appVersion,
|
APP_VERSION: appVersion,
|
||||||
TELEGRAM_T_API_ID: undefined,
|
TELEGRAM_T_API_ID: undefined,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user