Full browser history support (#1181)
This commit is contained in:
parent
c4e3a41ff1
commit
0a594a84e1
@ -6,6 +6,7 @@ import { GlobalActions, GlobalState } from '../../global/types';
|
|||||||
import '../../modules/actions/initial';
|
import '../../modules/actions/initial';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import { PLATFORM_ENV } from '../../util/environment';
|
import { PLATFORM_ENV } from '../../util/environment';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import UiLoader from '../common/UiLoader';
|
import UiLoader from '../common/UiLoader';
|
||||||
import AuthPhoneNumber from './AuthPhoneNumber';
|
import AuthPhoneNumber from './AuthPhoneNumber';
|
||||||
@ -17,14 +18,31 @@ import AuthQrCode from './AuthQrCode';
|
|||||||
import './Auth.scss';
|
import './Auth.scss';
|
||||||
|
|
||||||
type StateProps = Pick<GlobalState, 'authState'>;
|
type StateProps = Pick<GlobalState, 'authState'>;
|
||||||
type DispatchProps = Pick<GlobalActions, 'reset' | 'initApi'>;
|
type DispatchProps = Pick<GlobalActions, 'reset' | 'initApi' | 'returnToAuthPhoneNumber' | 'goToAuthQrCode'>;
|
||||||
|
|
||||||
const Auth: FC<StateProps & DispatchProps> = ({ authState, reset, initApi }) => {
|
const Auth: FC<StateProps & DispatchProps> = ({
|
||||||
|
authState, reset, initApi, returnToAuthPhoneNumber, goToAuthQrCode,
|
||||||
|
}) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reset();
|
reset();
|
||||||
initApi();
|
initApi();
|
||||||
}, [reset, initApi]);
|
}, [reset, initApi]);
|
||||||
|
|
||||||
|
const isMobile = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android';
|
||||||
|
|
||||||
|
const handleChangeAuthorizationMethod = () => {
|
||||||
|
if (!isMobile) {
|
||||||
|
goToAuthQrCode();
|
||||||
|
} else {
|
||||||
|
returnToAuthPhoneNumber();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useHistoryBack(
|
||||||
|
(!isMobile && authState === 'authorizationStateWaitPhoneNumber')
|
||||||
|
|| (isMobile && authState === 'authorizationStateWaitQrCode'), handleChangeAuthorizationMethod,
|
||||||
|
);
|
||||||
|
|
||||||
switch (authState) {
|
switch (authState) {
|
||||||
case 'authorizationStateWaitCode':
|
case 'authorizationStateWaitCode':
|
||||||
return <UiLoader page="authCode" key="authCode"><AuthCode /></UiLoader>;
|
return <UiLoader page="authCode" key="authCode"><AuthCode /></UiLoader>;
|
||||||
@ -37,7 +55,7 @@ const Auth: FC<StateProps & DispatchProps> = ({ authState, reset, initApi }) =>
|
|||||||
case 'authorizationStateWaitQrCode':
|
case 'authorizationStateWaitQrCode':
|
||||||
return <UiLoader page="authQrCode" key="authQrCode"><AuthQrCode /></UiLoader>;
|
return <UiLoader page="authQrCode" key="authQrCode"><AuthQrCode /></UiLoader>;
|
||||||
default:
|
default:
|
||||||
return PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android'
|
return isMobile
|
||||||
? <UiLoader page="authPhoneNumber" key="authPhoneNumber"><AuthPhoneNumber /></UiLoader>
|
? <UiLoader page="authPhoneNumber" key="authPhoneNumber"><AuthPhoneNumber /></UiLoader>
|
||||||
: <UiLoader page="authQrCode" key="authQrCode"><AuthQrCode /></UiLoader>;
|
: <UiLoader page="authQrCode" key="authQrCode"><AuthQrCode /></UiLoader>;
|
||||||
}
|
}
|
||||||
@ -45,5 +63,5 @@ const Auth: FC<StateProps & DispatchProps> = ({ authState, reset, initApi }) =>
|
|||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal(
|
||||||
(global): StateProps => pick(global, ['authState']),
|
(global): StateProps => pick(global, ['authState']),
|
||||||
(global, actions): DispatchProps => pick(actions, ['reset', 'initApi']),
|
(global, actions): DispatchProps => pick(actions, ['reset', 'initApi', 'returnToAuthPhoneNumber', 'goToAuthQrCode']),
|
||||||
)(Auth));
|
)(Auth));
|
||||||
|
|||||||
@ -7,19 +7,27 @@ import { GlobalState, GlobalActions } from '../../global/types';
|
|||||||
|
|
||||||
import { IS_TOUCH_ENV } from '../../util/environment';
|
import { IS_TOUCH_ENV } from '../../util/environment';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InputText from '../ui/InputText';
|
import InputText from '../ui/InputText';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import TrackingMonkey from '../common/TrackingMonkey';
|
import TrackingMonkey from '../common/TrackingMonkey';
|
||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
|
||||||
|
|
||||||
type StateProps = Pick<GlobalState, 'authPhoneNumber' | 'authIsCodeViaApp' | 'authIsLoading' | 'authError'>;
|
type StateProps = Pick<GlobalState, 'authPhoneNumber' | 'authIsCodeViaApp' | 'authIsLoading' | 'authError'>;
|
||||||
type DispatchProps = Pick<GlobalActions, 'setAuthCode' | 'returnToAuthPhoneNumber' | 'clearAuthError'>;
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
|
'setAuthCode' | 'returnToAuthPhoneNumber' | 'clearAuthError'
|
||||||
|
)>;
|
||||||
|
|
||||||
const CODE_LENGTH = 5;
|
const CODE_LENGTH = 5;
|
||||||
|
|
||||||
const AuthCode: FC<StateProps & DispatchProps> = ({
|
const AuthCode: FC<StateProps & DispatchProps> = ({
|
||||||
authPhoneNumber, authIsCodeViaApp, authIsLoading, authError, setAuthCode, returnToAuthPhoneNumber, clearAuthError,
|
authPhoneNumber,
|
||||||
|
authIsCodeViaApp,
|
||||||
|
authIsLoading,
|
||||||
|
authError,
|
||||||
|
setAuthCode,
|
||||||
|
returnToAuthPhoneNumber,
|
||||||
|
clearAuthError,
|
||||||
}) => {
|
}) => {
|
||||||
// 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);
|
||||||
@ -34,7 +42,7 @@ const AuthCode: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useHistoryBack(returnToAuthPhoneNumber);
|
useHistoryBack(true, returnToAuthPhoneNumber);
|
||||||
|
|
||||||
const onCodeChange = useCallback((e: FormEvent<HTMLInputElement>) => {
|
const onCodeChange = useCallback((e: FormEvent<HTMLInputElement>) => {
|
||||||
if (authError) {
|
if (authError) {
|
||||||
@ -119,5 +127,9 @@ const AuthCode: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal(
|
||||||
(global): StateProps => pick(global, ['authPhoneNumber', 'authIsCodeViaApp', 'authIsLoading', 'authError']),
|
(global): StateProps => pick(global, ['authPhoneNumber', 'authIsCodeViaApp', 'authIsLoading', 'authError']),
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, ['setAuthCode', 'returnToAuthPhoneNumber', 'clearAuthError']),
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
|
'setAuthCode',
|
||||||
|
'returnToAuthPhoneNumber',
|
||||||
|
'clearAuthError',
|
||||||
|
]),
|
||||||
)(AuthCode));
|
)(AuthCode));
|
||||||
|
|||||||
@ -8,7 +8,9 @@ import React, {
|
|||||||
FC, memo, useCallback, useEffect, useLayoutEffect, useRef, useState,
|
FC, memo, useCallback, useEffect, useLayoutEffect, useRef, useState,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../lib/teact/teactn';
|
import { withGlobal } from '../../lib/teact/teactn';
|
||||||
import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/environment';
|
import {
|
||||||
|
IS_SAFARI, IS_TOUCH_ENV,
|
||||||
|
} from '../../util/environment';
|
||||||
import { preloadImage } from '../../util/files';
|
import { preloadImage } from '../../util/files';
|
||||||
import preloadFonts from '../../util/fonts';
|
import preloadFonts from '../../util/fonts';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { pick } from '../../util/iteratees';
|
|||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
|
||||||
|
|
||||||
type StateProps = Pick<GlobalState, 'connectionState' | 'authState' | 'authQrCode'>;
|
type StateProps = Pick<GlobalState, 'connectionState' | 'authState' | 'authQrCode'>;
|
||||||
type DispatchProps = Pick<GlobalActions, 'returnToAuthPhoneNumber'>;
|
type DispatchProps = Pick<GlobalActions, 'returnToAuthPhoneNumber'>;
|
||||||
@ -17,7 +16,10 @@ type DispatchProps = Pick<GlobalActions, 'returnToAuthPhoneNumber'>;
|
|||||||
const DATA_PREFIX = 'tg://login?token=';
|
const DATA_PREFIX = 'tg://login?token=';
|
||||||
|
|
||||||
const AuthCode: FC<StateProps & DispatchProps> = ({
|
const AuthCode: FC<StateProps & DispatchProps> = ({
|
||||||
connectionState, authState, authQrCode, returnToAuthPhoneNumber,
|
connectionState,
|
||||||
|
authState,
|
||||||
|
authQrCode,
|
||||||
|
returnToAuthPhoneNumber,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const qrCodeRef = useRef<HTMLDivElement>(null);
|
const qrCodeRef = useRef<HTMLDivElement>(null);
|
||||||
@ -41,8 +43,6 @@ const AuthCode: FC<StateProps & DispatchProps> = ({
|
|||||||
}, container);
|
}, container);
|
||||||
}, [connectionState, authQrCode]);
|
}, [connectionState, authQrCode]);
|
||||||
|
|
||||||
useHistoryBack(returnToAuthPhoneNumber);
|
|
||||||
|
|
||||||
const isAuthReady = authState === 'authorizationStateWaitQrCode';
|
const isAuthReady = authState === 'authorizationStateWaitQrCode';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -28,7 +28,7 @@ type OwnProps = {
|
|||||||
children: any;
|
children: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = Pick<GlobalState, 'uiReadyState'> & {
|
type StateProps = Pick<GlobalState, 'uiReadyState' | 'shouldSkipHistoryAnimations'> & {
|
||||||
hasCustomBackground?: boolean;
|
hasCustomBackground?: boolean;
|
||||||
hasCustomBackgroundColor: boolean;
|
hasCustomBackgroundColor: boolean;
|
||||||
isRightColumnShown?: boolean;
|
isRightColumnShown?: boolean;
|
||||||
@ -82,6 +82,7 @@ const UiLoader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
hasCustomBackground,
|
hasCustomBackground,
|
||||||
hasCustomBackgroundColor,
|
hasCustomBackgroundColor,
|
||||||
isRightColumnShown,
|
isRightColumnShown,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
setIsUiReady,
|
setIsUiReady,
|
||||||
}) => {
|
}) => {
|
||||||
const [isReady, markReady] = useFlag();
|
const [isReady, markReady] = useFlag();
|
||||||
@ -126,7 +127,7 @@ const UiLoader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div id="UiLoader">
|
<div id="UiLoader">
|
||||||
{children}
|
{children}
|
||||||
{shouldRenderMask && (
|
{shouldRenderMask && !shouldSkipHistoryAnimations && (
|
||||||
<div className={buildClassName('mask', transitionClassNames)}>
|
<div className={buildClassName('mask', transitionClassNames)}>
|
||||||
{page === 'main' ? (
|
{page === 'main' ? (
|
||||||
<>
|
<>
|
||||||
@ -156,6 +157,7 @@ export default withGlobal<OwnProps>(
|
|||||||
const { background, backgroundColor } = global.settings.themes[theme] || {};
|
const { background, backgroundColor } = global.settings.themes[theme] || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
uiReadyState: global.uiReadyState,
|
uiReadyState: global.uiReadyState,
|
||||||
hasCustomBackground: Boolean(background),
|
hasCustomBackground: Boolean(background),
|
||||||
hasCustomBackgroundColor: Boolean(backgroundColor),
|
hasCustomBackgroundColor: Boolean(backgroundColor),
|
||||||
|
|||||||
@ -1,20 +1,25 @@
|
|||||||
import React, { FC, memo } from '../../lib/teact/teact';
|
import React, { FC, memo } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Button from '../ui/Button';
|
import Button from '../ui/Button';
|
||||||
import ChatList from './main/ChatList';
|
import ChatList from './main/ChatList';
|
||||||
|
import { LeftColumnContent } from '../../types';
|
||||||
|
|
||||||
import './ArchivedChats.scss';
|
import './ArchivedChats.scss';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
|
onContentChange: (content: LeftColumnContent) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ArchivedChats: FC<OwnProps> = ({ isActive, onReset }) => {
|
const ArchivedChats: FC<OwnProps> = ({ isActive, onReset, onContentChange }) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onContentChange, LeftColumnContent.Archived);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ArchivedChats">
|
<div className="ArchivedChats">
|
||||||
<div className="left-header">
|
<div className="left-header">
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type StateProps = {
|
|||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
searchDate?: number;
|
searchDate?: number;
|
||||||
activeChatFolder: number;
|
activeChatFolder: number;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, (
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
@ -47,6 +48,7 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
searchQuery,
|
searchQuery,
|
||||||
searchDate,
|
searchDate,
|
||||||
activeChatFolder,
|
activeChatFolder,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
setGlobalSearchQuery,
|
setGlobalSearchQuery,
|
||||||
setGlobalSearchChatId,
|
setGlobalSearchChatId,
|
||||||
resetChatCreation,
|
resetChatCreation,
|
||||||
@ -80,14 +82,20 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleReset = useCallback((forceReturnToChatList?: boolean) => {
|
const handleReset = useCallback((forceReturnToChatList?: boolean) => {
|
||||||
if (
|
if (content === LeftColumnContent.NewGroupStep2
|
||||||
content === LeftColumnContent.NewGroupStep2
|
|
||||||
&& !forceReturnToChatList
|
&& !forceReturnToChatList
|
||||||
) {
|
) {
|
||||||
setContent(LeftColumnContent.NewGroupStep1);
|
setContent(LeftColumnContent.NewGroupStep1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (content === LeftColumnContent.NewChannelStep2
|
||||||
|
&& !forceReturnToChatList
|
||||||
|
) {
|
||||||
|
setContent(LeftColumnContent.NewChannelStep1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (content === LeftColumnContent.NewGroupStep1) {
|
if (content === LeftColumnContent.NewGroupStep1) {
|
||||||
const pickerSearchInput = document.getElementById('new-group-picker-search');
|
const pickerSearchInput = document.getElementById('new-group-picker-search');
|
||||||
if (pickerSearchInput) {
|
if (pickerSearchInput) {
|
||||||
@ -205,8 +213,8 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
setLastResetTime(Date.now());
|
setLastResetTime(Date.now());
|
||||||
}, RESET_TRANSITION_DELAY_MS);
|
}, RESET_TRANSITION_DELAY_MS);
|
||||||
}, [
|
}, [
|
||||||
content, activeChatFolder, setGlobalSearchQuery, setGlobalSearchDate, setGlobalSearchChatId, resetChatCreation,
|
content, activeChatFolder, settingsScreen, setGlobalSearchQuery, setGlobalSearchDate, setGlobalSearchChatId,
|
||||||
settingsScreen,
|
resetChatCreation,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleSearchQuery = useCallback((query: string) => {
|
const handleSearchQuery = useCallback((query: string) => {
|
||||||
@ -220,7 +228,7 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
if (query !== searchQuery) {
|
if (query !== searchQuery) {
|
||||||
setGlobalSearchQuery({ query });
|
setGlobalSearchQuery({ query });
|
||||||
}
|
}
|
||||||
}, [content, setGlobalSearchQuery, searchQuery]);
|
}, [content, searchQuery, setGlobalSearchQuery]);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => (content !== LeftColumnContent.ChatList || activeChatFolder === 0
|
() => (content !== LeftColumnContent.ChatList || activeChatFolder === 0
|
||||||
@ -237,10 +245,15 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [clearTwoFaError, loadPasswordInfo, settingsScreen]);
|
}, [clearTwoFaError, loadPasswordInfo, settingsScreen]);
|
||||||
|
|
||||||
|
const handleSettingsScreenSelect = (screen: SettingsScreens) => {
|
||||||
|
setContent(LeftColumnContent.Settings);
|
||||||
|
setSettingsScreen(screen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition
|
<Transition
|
||||||
id="LeftColumn"
|
id="LeftColumn"
|
||||||
name={LAYERS_ANIMATION_NAME}
|
name={shouldSkipHistoryAnimations ? 'none' : LAYERS_ANIMATION_NAME}
|
||||||
renderCount={RENDER_COUNT}
|
renderCount={RENDER_COUNT}
|
||||||
activeKey={contentType}
|
activeKey={contentType}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
@ -253,20 +266,24 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
<ArchivedChats
|
<ArchivedChats
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
|
onContentChange={setContent}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ContentType.Settings:
|
case ContentType.Settings:
|
||||||
return (
|
return (
|
||||||
<Settings
|
<Settings
|
||||||
|
isActive={isActive}
|
||||||
currentScreen={settingsScreen}
|
currentScreen={settingsScreen}
|
||||||
onScreenSelect={setSettingsScreen}
|
onScreenSelect={handleSettingsScreenSelect}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
|
shouldSkipTransition={shouldSkipHistoryAnimations}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ContentType.NewChannel:
|
case ContentType.NewChannel:
|
||||||
return (
|
return (
|
||||||
<NewChat
|
<NewChat
|
||||||
key={lastResetTime}
|
key={lastResetTime}
|
||||||
|
isActive={isActive}
|
||||||
isChannel
|
isChannel
|
||||||
content={content}
|
content={content}
|
||||||
onContentChange={setContent}
|
onContentChange={setContent}
|
||||||
@ -277,6 +294,7 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<NewChat
|
<NewChat
|
||||||
key={lastResetTime}
|
key={lastResetTime}
|
||||||
|
isActive={isActive}
|
||||||
content={content}
|
content={content}
|
||||||
onContentChange={setContent}
|
onContentChange={setContent}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
@ -292,6 +310,7 @@ const LeftColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
onContentChange={setContent}
|
onContentChange={setContent}
|
||||||
onSearchQuery={handleSearchQuery}
|
onSearchQuery={handleSearchQuery}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
|
shouldSkipTransition={shouldSkipHistoryAnimations}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -310,8 +329,11 @@ export default memo(withGlobal(
|
|||||||
chatFolders: {
|
chatFolders: {
|
||||||
activeChatFolder,
|
activeChatFolder,
|
||||||
},
|
},
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
} = global;
|
} = global;
|
||||||
return { searchQuery: query, searchDate: date, activeChatFolder };
|
return {
|
||||||
|
searchQuery: query, searchDate: date, activeChatFolder, shouldSkipHistoryAnimations,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
'setGlobalSearchQuery', 'setGlobalSearchChatId', 'resetChatCreation', 'setGlobalSearchDate',
|
'setGlobalSearchQuery', 'setGlobalSearchChatId', 'resetChatCreation', 'setGlobalSearchDate',
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import useShowTransition from '../../../hooks/useShowTransition';
|
|||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import useThrottledMemo from '../../../hooks/useThrottledMemo';
|
import useThrottledMemo from '../../../hooks/useThrottledMemo';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||||
|
|
||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
@ -144,6 +145,8 @@ const ChatFolders: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}) : undefined), [activeChatFolder, setActiveChatFolder]);
|
}) : undefined), [activeChatFolder, setActiveChatFolder]);
|
||||||
|
|
||||||
|
useHistoryBack(activeChatFolder !== 0, () => setActiveChatFolder(0));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.metaKey && e.code.startsWith('Digit') && folderTabs) {
|
if (e.metaKey && e.code.startsWith('Digit') && folderTabs) {
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import searchWords from '../../../util/searchWords';
|
|||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { getUserFullName, sortUserIds } from '../../../modules/helpers';
|
import { getUserFullName, sortUserIds } from '../../../modules/helpers';
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import InfiniteScroll from '../../ui/InfiniteScroll';
|
import InfiniteScroll from '../../ui/InfiniteScroll';
|
||||||
@ -20,6 +21,8 @@ import Loading from '../../ui/Loading';
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
filter: string;
|
filter: string;
|
||||||
|
isActive: boolean;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -33,6 +36,7 @@ type DispatchProps = Pick<GlobalActions, 'loadContactList' | 'openChat'>;
|
|||||||
const runThrottled = throttle((cb) => cb(), 60000, true);
|
const runThrottled = throttle((cb) => cb(), 60000, true);
|
||||||
|
|
||||||
const ContactList: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ContactList: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive, onReset,
|
||||||
filter, usersById, contactIds, loadContactList, openChat, serverTimeOffset,
|
filter, usersById, contactIds, loadContactList, openChat, serverTimeOffset,
|
||||||
}) => {
|
}) => {
|
||||||
// Due to the parent Transition, this component never gets unmounted,
|
// Due to the parent Transition, this component never gets unmounted,
|
||||||
@ -43,6 +47,8 @@ const ContactList: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset);
|
||||||
|
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(id: number) => {
|
(id: number) => {
|
||||||
openChat({ id });
|
openChat({ id });
|
||||||
|
|||||||
@ -31,6 +31,7 @@ type OwnProps = {
|
|||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
searchDate?: number;
|
searchDate?: number;
|
||||||
contactsFilter: string;
|
contactsFilter: string;
|
||||||
|
shouldSkipTransition?: boolean;
|
||||||
onSearchQuery: (query: string) => void;
|
onSearchQuery: (query: string) => void;
|
||||||
onContentChange: (content: LeftColumnContent) => void;
|
onContentChange: (content: LeftColumnContent) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
@ -49,6 +50,7 @@ const LeftMain: FC<OwnProps & StateProps> = ({
|
|||||||
searchQuery,
|
searchQuery,
|
||||||
searchDate,
|
searchDate,
|
||||||
contactsFilter,
|
contactsFilter,
|
||||||
|
shouldSkipTransition,
|
||||||
onSearchQuery,
|
onSearchQuery,
|
||||||
onContentChange,
|
onContentChange,
|
||||||
onReset,
|
onReset,
|
||||||
@ -140,12 +142,13 @@ const LeftMain: FC<OwnProps & StateProps> = ({
|
|||||||
onSelectContacts={handleSelectContacts}
|
onSelectContacts={handleSelectContacts}
|
||||||
onSelectArchived={handleSelectArchived}
|
onSelectArchived={handleSelectArchived}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
|
shouldSkipTransition={shouldSkipTransition}
|
||||||
/>
|
/>
|
||||||
<ShowTransition isOpen={isConnecting} isCustom className="connection-state-wrapper opacity-transition slow">
|
<ShowTransition isOpen={isConnecting} isCustom className="connection-state-wrapper opacity-transition slow">
|
||||||
{() => <ConnectionState />}
|
{() => <ConnectionState />}
|
||||||
</ShowTransition>
|
</ShowTransition>
|
||||||
<Transition
|
<Transition
|
||||||
name="zoom-fade"
|
name={shouldSkipTransition ? 'none' : 'zoom-fade'}
|
||||||
renderCount={TRANSITION_RENDER_COUNT}
|
renderCount={TRANSITION_RENDER_COUNT}
|
||||||
activeKey={content}
|
activeKey={content}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
@ -166,7 +169,7 @@ const LeftMain: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case LeftColumnContent.Contacts:
|
case LeftColumnContent.Contacts:
|
||||||
return <ContactList filter={contactsFilter} />;
|
return <ContactList filter={contactsFilter} isActive={isActive} onReset={onReset} />;
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,6 +40,14 @@
|
|||||||
transform: rotate(-45deg) scaleX(0.75) translate(0.375rem, 0.1875rem);
|
transform: rotate(-45deg) scaleX(0.75) translate(0.375rem, 0.1875rem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.no-animation {
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&::before, &::after {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.archived-badge {
|
.archived-badge {
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import './LeftMainHeader.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
content: LeftColumnContent;
|
content: LeftColumnContent;
|
||||||
contactsFilter: string;
|
contactsFilter: string;
|
||||||
|
shouldSkipTransition?: boolean;
|
||||||
onSearchQuery: (query: string) => void;
|
onSearchQuery: (query: string) => void;
|
||||||
onSelectSettings: () => void;
|
onSelectSettings: () => void;
|
||||||
onSelectContacts: () => void;
|
onSelectContacts: () => void;
|
||||||
@ -71,6 +72,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onReset,
|
onReset,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
shouldSkipTransition,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
globalSearchChatId,
|
globalSearchChatId,
|
||||||
searchDate,
|
searchDate,
|
||||||
@ -118,10 +120,15 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onClick={hasMenu ? onTrigger : () => onReset()}
|
onClick={hasMenu ? onTrigger : () => onReset()}
|
||||||
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
|
ariaLabel={hasMenu ? lang('AccDescrOpenMenu2') : 'Return to chat list'}
|
||||||
>
|
>
|
||||||
<div className={buildClassName('animated-menu-icon', !hasMenu && 'state-back')} />
|
<div className={buildClassName(
|
||||||
|
'animated-menu-icon',
|
||||||
|
!hasMenu && 'state-back',
|
||||||
|
shouldSkipTransition && 'no-animation',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}, [hasMenu, lang, onReset]);
|
}, [hasMenu, lang, onReset, shouldSkipTransition]);
|
||||||
|
|
||||||
const handleSearchFocus = useCallback(() => {
|
const handleSearchFocus = useCallback(() => {
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import NewChatStep2 from './NewChatStep2';
|
|||||||
import './NewChat.scss';
|
import './NewChat.scss';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
|
isActive: boolean;
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
content: LeftColumnContent;
|
content: LeftColumnContent;
|
||||||
onContentChange: (content: LeftColumnContent) => void;
|
onContentChange: (content: LeftColumnContent) => void;
|
||||||
@ -22,6 +23,7 @@ export type OwnProps = {
|
|||||||
const RENDER_COUNT = Object.keys(LeftColumnContent).length / 2;
|
const RENDER_COUNT = Object.keys(LeftColumnContent).length / 2;
|
||||||
|
|
||||||
const NewChat: FC<OwnProps> = ({
|
const NewChat: FC<OwnProps> = ({
|
||||||
|
isActive,
|
||||||
isChannel = false,
|
isChannel = false,
|
||||||
content,
|
content,
|
||||||
onContentChange,
|
onContentChange,
|
||||||
@ -40,13 +42,14 @@ const NewChat: FC<OwnProps> = ({
|
|||||||
renderCount={RENDER_COUNT}
|
renderCount={RENDER_COUNT}
|
||||||
activeKey={content}
|
activeKey={content}
|
||||||
>
|
>
|
||||||
{() => {
|
{(isStepActive) => {
|
||||||
switch (content) {
|
switch (content) {
|
||||||
case LeftColumnContent.NewChannelStep1:
|
case LeftColumnContent.NewChannelStep1:
|
||||||
case LeftColumnContent.NewGroupStep1:
|
case LeftColumnContent.NewGroupStep1:
|
||||||
return (
|
return (
|
||||||
<NewChatStep1
|
<NewChatStep1
|
||||||
isChannel={isChannel}
|
isChannel={isChannel}
|
||||||
|
isActive={isActive}
|
||||||
selectedMemberIds={newChatMemberIds}
|
selectedMemberIds={newChatMemberIds}
|
||||||
onSelectedMemberIdsChange={setNewChatMemberIds}
|
onSelectedMemberIdsChange={setNewChatMemberIds}
|
||||||
onNextStep={handleNextStep}
|
onNextStep={handleNextStep}
|
||||||
@ -58,6 +61,7 @@ const NewChat: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<NewChatStep2
|
<NewChatStep2
|
||||||
isChannel={isChannel}
|
isChannel={isChannel}
|
||||||
|
isActive={isStepActive && isActive}
|
||||||
memberIds={newChatMemberIds}
|
memberIds={newChatMemberIds}
|
||||||
onReset={onReset}
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { throttle } from '../../../util/schedulers';
|
|||||||
import searchWords from '../../../util/searchWords';
|
import searchWords from '../../../util/searchWords';
|
||||||
import { getUserFullName, sortChatIds } from '../../../modules/helpers';
|
import { getUserFullName, sortChatIds } from '../../../modules/helpers';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Picker from '../../common/Picker';
|
import Picker from '../../common/Picker';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -18,6 +19,7 @@ import Button from '../../ui/Button';
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
|
isActive: boolean;
|
||||||
selectedMemberIds: number[];
|
selectedMemberIds: number[];
|
||||||
onSelectedMemberIdsChange: (ids: number[]) => void;
|
onSelectedMemberIdsChange: (ids: number[]) => void;
|
||||||
onNextStep: () => void;
|
onNextStep: () => void;
|
||||||
@ -41,6 +43,7 @@ const runThrottled = throttle((cb) => cb(), 60000, true);
|
|||||||
|
|
||||||
const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
isChannel,
|
isChannel,
|
||||||
|
isActive,
|
||||||
selectedMemberIds,
|
selectedMemberIds,
|
||||||
onSelectedMemberIdsChange,
|
onSelectedMemberIdsChange,
|
||||||
onNextStep,
|
onNextStep,
|
||||||
@ -64,6 +67,10 @@ const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset);
|
||||||
|
|
||||||
const handleFilterChange = useCallback((query: string) => {
|
const handleFilterChange = useCallback((query: string) => {
|
||||||
setGlobalSearchQuery({ query });
|
setGlobalSearchQuery({ query });
|
||||||
}, [setGlobalSearchQuery]);
|
}, [setGlobalSearchQuery]);
|
||||||
@ -108,8 +115,6 @@ const NewChatStep1: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [selectedMemberIds.length, isChannel, setGlobalSearchQuery, onNextStep]);
|
}, [selectedMemberIds.length, isChannel, setGlobalSearchQuery, onNextStep]);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NewChat step-1">
|
<div className="NewChat step-1">
|
||||||
<div className="left-header">
|
<div className="left-header">
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { ChatCreationProgress } from '../../../types';
|
|||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -19,6 +20,7 @@ import PrivateChatInfo from '../../common/PrivateChatInfo';
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
|
isActive: boolean;
|
||||||
memberIds: number[];
|
memberIds: number[];
|
||||||
onReset: (forceReturnToChatList?: boolean) => void;
|
onReset: (forceReturnToChatList?: boolean) => void;
|
||||||
};
|
};
|
||||||
@ -35,6 +37,7 @@ const MAX_USERS_FOR_LEGACY_CHAT = 199; // Accounting for current user
|
|||||||
|
|
||||||
const NewChatStep2: FC<OwnProps & StateProps & DispatchProps> = ({
|
const NewChatStep2: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
isChannel,
|
isChannel,
|
||||||
|
isActive,
|
||||||
memberIds,
|
memberIds,
|
||||||
onReset,
|
onReset,
|
||||||
creationProgress,
|
creationProgress,
|
||||||
@ -44,6 +47,8 @@ const NewChatStep2: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset);
|
||||||
|
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [about, setAbout] = useState('');
|
const [about, setAbout] = useState('');
|
||||||
const [photo, setPhoto] = useState<File | undefined>();
|
const [photo, setPhoto] = useState<File | undefined>();
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import { parseDateString } from '../../../util/dateFormat';
|
import { parseDateString } from '../../../util/dateFormat';
|
||||||
import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation';
|
import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import TabList from '../../ui/TabList';
|
import TabList from '../../ui/TabList';
|
||||||
import Transition from '../../ui/Transition';
|
import Transition from '../../ui/Transition';
|
||||||
@ -76,6 +77,8 @@ const LeftSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
setGlobalSearchDate({ date: value.getTime() / 1000 });
|
setGlobalSearchDate({ date: value.getTime() / 1000 });
|
||||||
}, [setGlobalSearchDate]);
|
}, [setGlobalSearchDate]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, undefined, undefined, true);
|
||||||
|
|
||||||
// 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);
|
||||||
const handleKeyDown = useKeyboardListNavigation(containerRef, isActive, undefined, '.ListItem-button', true);
|
const handleKeyDown = useKeyboardListNavigation(containerRef, isActive, undefined, '.ListItem-button', true);
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-content {
|
.settings-content {
|
||||||
|
background: var(--color-background);
|
||||||
height: calc(100% - var(--header-height));
|
height: calc(100% - var(--header-height));
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
|||||||
@ -28,16 +28,77 @@ import './Settings.scss';
|
|||||||
const TRANSITION_RENDER_COUNT = Object.keys(SettingsScreens).length / 2;
|
const TRANSITION_RENDER_COUNT = Object.keys(SettingsScreens).length / 2;
|
||||||
const TRANSITION_DURATION = 200;
|
const TRANSITION_DURATION = 200;
|
||||||
|
|
||||||
|
const TWO_FA_SCREENS = [
|
||||||
|
SettingsScreens.TwoFaDisabled,
|
||||||
|
SettingsScreens.TwoFaNewPassword,
|
||||||
|
SettingsScreens.TwoFaNewPasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
SettingsScreens.TwoFaEnabled,
|
||||||
|
SettingsScreens.TwoFaChangePasswordCurrent,
|
||||||
|
SettingsScreens.TwoFaChangePasswordNew,
|
||||||
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
|
SettingsScreens.TwoFaTurnOff,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCurrentPassword,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmail,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
|
];
|
||||||
|
|
||||||
|
const FOLDERS_SCREENS = [
|
||||||
|
SettingsScreens.Folders,
|
||||||
|
SettingsScreens.FoldersCreateFolder,
|
||||||
|
SettingsScreens.FoldersEditFolder,
|
||||||
|
SettingsScreens.FoldersIncludedChats,
|
||||||
|
SettingsScreens.FoldersExcludedChats,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyBlockedUsers,
|
||||||
|
SettingsScreens.PrivacyActiveSessions,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_PHONE_NUMBER_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyPhoneNumberAllowedContacts,
|
||||||
|
SettingsScreens.PrivacyPhoneNumberDeniedContacts,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_LAST_SEEN_PHONE_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyLastSeenAllowedContacts,
|
||||||
|
SettingsScreens.PrivacyLastSeenDeniedContacts,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_PROFILE_PHOTO_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyProfilePhotoAllowedContacts,
|
||||||
|
SettingsScreens.PrivacyProfilePhotoDeniedContacts,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_FORWARDING_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyForwardingAllowedContacts,
|
||||||
|
SettingsScreens.PrivacyForwardingDeniedContacts,
|
||||||
|
];
|
||||||
|
|
||||||
|
const PRIVACY_GROUP_CHATS_SCREENS = [
|
||||||
|
SettingsScreens.PrivacyGroupChatsAllowedContacts,
|
||||||
|
SettingsScreens.PrivacyGroupChatsDeniedContacts,
|
||||||
|
];
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
|
isActive: boolean;
|
||||||
currentScreen: SettingsScreens;
|
currentScreen: SettingsScreens;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
shouldSkipTransition?: boolean;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Settings: FC<OwnProps> = ({
|
const Settings: FC<OwnProps> = ({
|
||||||
|
isActive,
|
||||||
currentScreen,
|
currentScreen,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
onReset,
|
onReset,
|
||||||
|
shouldSkipTransition,
|
||||||
}) => {
|
}) => {
|
||||||
const [foldersState, foldersDispatch] = useFoldersReducer();
|
const [foldersState, foldersDispatch] = useFoldersReducer();
|
||||||
const [twoFaState, twoFaDispatch] = useTwoFaReducer();
|
const [twoFaState, twoFaDispatch] = useTwoFaReducer();
|
||||||
@ -75,47 +136,93 @@ const Settings: FC<OwnProps> = ({
|
|||||||
handleReset();
|
handleReset();
|
||||||
}, [foldersDispatch, handleReset]);
|
}, [foldersDispatch, handleReset]);
|
||||||
|
|
||||||
function renderCurrentSectionContent() {
|
function renderCurrentSectionContent(isScreenActive: boolean, screen: SettingsScreens) {
|
||||||
|
const privacyAllowScreens: Record<number, boolean> = {
|
||||||
|
[SettingsScreens.PrivacyPhoneNumber]: PRIVACY_PHONE_NUMBER_SCREENS.includes(screen),
|
||||||
|
[SettingsScreens.PrivacyLastSeen]: PRIVACY_LAST_SEEN_PHONE_SCREENS.includes(screen),
|
||||||
|
[SettingsScreens.PrivacyProfilePhoto]: PRIVACY_PROFILE_PHOTO_SCREENS.includes(screen),
|
||||||
|
[SettingsScreens.PrivacyForwarding]: PRIVACY_FORWARDING_SCREENS.includes(screen),
|
||||||
|
[SettingsScreens.PrivacyGroupChats]: PRIVACY_GROUP_CHATS_SCREENS.includes(screen),
|
||||||
|
};
|
||||||
|
|
||||||
|
const isTwoFaScreen = TWO_FA_SCREENS.includes(screen);
|
||||||
|
const isFoldersScreen = FOLDERS_SCREENS.includes(screen);
|
||||||
|
const isPrivacyScreen = PRIVACY_SCREENS.includes(screen)
|
||||||
|
|| isTwoFaScreen
|
||||||
|
|| Object.keys(privacyAllowScreens).includes(screen.toString())
|
||||||
|
|| Object.values(privacyAllowScreens).find((key) => key === true);
|
||||||
|
|
||||||
switch (currentScreen) {
|
switch (currentScreen) {
|
||||||
case SettingsScreens.Main:
|
case SettingsScreens.Main:
|
||||||
return (
|
return (
|
||||||
<SettingsMain onScreenSelect={onScreenSelect} />
|
<SettingsMain onScreenSelect={onScreenSelect} isActive={isActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.EditProfile:
|
case SettingsScreens.EditProfile:
|
||||||
return (
|
return (
|
||||||
<SettingsEditProfile />
|
<SettingsEditProfile
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive && isScreenActive}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.General:
|
case SettingsScreens.General:
|
||||||
return (
|
return (
|
||||||
<SettingsGeneral onScreenSelect={onScreenSelect} />
|
<SettingsGeneral
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive
|
||||||
|
|| screen === SettingsScreens.GeneralChatBackgroundColor
|
||||||
|
|| screen === SettingsScreens.GeneralChatBackground
|
||||||
|
|| isPrivacyScreen || isFoldersScreen}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.Notifications:
|
case SettingsScreens.Notifications:
|
||||||
return (
|
return (
|
||||||
<SettingsNotifications />
|
<SettingsNotifications onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.Privacy:
|
case SettingsScreens.Privacy:
|
||||||
return (
|
return (
|
||||||
<SettingsPrivacy onScreenSelect={onScreenSelect} />
|
<SettingsPrivacy
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive || isPrivacyScreen || isTwoFaScreen}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.Language:
|
case SettingsScreens.Language:
|
||||||
return (
|
return (
|
||||||
<SettingsLanguage />
|
<SettingsLanguage onScreenSelect={onScreenSelect} isActive={isScreenActive} onReset={handleReset} />
|
||||||
);
|
);
|
||||||
case SettingsScreens.GeneralChatBackground:
|
case SettingsScreens.GeneralChatBackground:
|
||||||
return (
|
return (
|
||||||
<SettingsGeneralBackground onScreenSelect={onScreenSelect} />
|
<SettingsGeneralBackground
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive || screen === SettingsScreens.GeneralChatBackgroundColor}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.GeneralChatBackgroundColor:
|
case SettingsScreens.GeneralChatBackgroundColor:
|
||||||
return (
|
return (
|
||||||
<SettingsGeneralBackgroundColor onScreenSelect={onScreenSelect} />
|
<SettingsGeneralBackgroundColor
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.PrivacyActiveSessions:
|
case SettingsScreens.PrivacyActiveSessions:
|
||||||
return (
|
return (
|
||||||
<SettingsPrivacyActiveSessions />
|
<SettingsPrivacyActiveSessions
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.PrivacyBlockedUsers:
|
case SettingsScreens.PrivacyBlockedUsers:
|
||||||
return (
|
return (
|
||||||
<SettingsPrivacyBlockedUsers />
|
<SettingsPrivacyBlockedUsers
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.PrivacyPhoneNumber:
|
case SettingsScreens.PrivacyPhoneNumber:
|
||||||
case SettingsScreens.PrivacyLastSeen:
|
case SettingsScreens.PrivacyLastSeen:
|
||||||
@ -123,7 +230,12 @@ const Settings: FC<OwnProps> = ({
|
|||||||
case SettingsScreens.PrivacyForwarding:
|
case SettingsScreens.PrivacyForwarding:
|
||||||
case SettingsScreens.PrivacyGroupChats:
|
case SettingsScreens.PrivacyGroupChats:
|
||||||
return (
|
return (
|
||||||
<SettingsPrivacyVisibility screen={currentScreen} onScreenSelect={onScreenSelect} />
|
<SettingsPrivacyVisibility
|
||||||
|
screen={currentScreen}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive || privacyAllowScreens[currentScreen]}
|
||||||
|
onReset={handleReset}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case SettingsScreens.PrivacyPhoneNumberAllowedContacts:
|
case SettingsScreens.PrivacyPhoneNumberAllowedContacts:
|
||||||
@ -136,6 +248,8 @@ const Settings: FC<OwnProps> = ({
|
|||||||
isAllowList
|
isAllowList
|
||||||
screen={currentScreen}
|
screen={currentScreen}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive || privacyAllowScreens[currentScreen]}
|
||||||
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -148,6 +262,8 @@ const Settings: FC<OwnProps> = ({
|
|||||||
<SettingsPrivacyVisibilityExceptionList
|
<SettingsPrivacyVisibilityExceptionList
|
||||||
screen={currentScreen}
|
screen={currentScreen}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isScreenActive}
|
||||||
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -159,8 +275,10 @@ const Settings: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SettingsFolders
|
<SettingsFolders
|
||||||
currentScreen={currentScreen}
|
currentScreen={currentScreen}
|
||||||
|
shownScreen={screen}
|
||||||
state={foldersState}
|
state={foldersState}
|
||||||
dispatch={foldersDispatch}
|
dispatch={foldersDispatch}
|
||||||
|
isActive={isScreenActive}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
@ -187,7 +305,10 @@ const Settings: FC<OwnProps> = ({
|
|||||||
currentScreen={currentScreen}
|
currentScreen={currentScreen}
|
||||||
state={twoFaState}
|
state={twoFaState}
|
||||||
dispatch={twoFaDispatch}
|
dispatch={twoFaDispatch}
|
||||||
|
shownScreen={screen}
|
||||||
|
isActive={isScreenActive}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
onReset={handleReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -196,7 +317,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCurrentSection() {
|
function renderCurrentSection(isScreenActive: boolean, isFrom: boolean, currentKey: SettingsScreens) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SettingsHeader
|
<SettingsHeader
|
||||||
@ -205,7 +326,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
onSaveFilter={handleSaveFilter}
|
onSaveFilter={handleSaveFilter}
|
||||||
editedFolderId={foldersState.folderId}
|
editedFolderId={foldersState.folderId}
|
||||||
/>
|
/>
|
||||||
{renderCurrentSectionContent()}
|
{renderCurrentSectionContent(isScreenActive, currentKey)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -213,7 +334,7 @@ const Settings: FC<OwnProps> = ({
|
|||||||
return (
|
return (
|
||||||
<Transition
|
<Transition
|
||||||
id="Settings"
|
id="Settings"
|
||||||
name={LAYERS_ANIMATION_NAME}
|
name={shouldSkipTransition ? 'none' : LAYERS_ANIMATION_NAME}
|
||||||
activeKey={currentScreen}
|
activeKey={currentScreen}
|
||||||
renderCount={TRANSITION_RENDER_COUNT}
|
renderCount={TRANSITION_RENDER_COUNT}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
|||||||
|
|
||||||
import { ApiMediaFormat } from '../../../api/types';
|
import { ApiMediaFormat } from '../../../api/types';
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { ProfileEditProgress } from '../../../types';
|
import { ProfileEditProgress, SettingsScreens } from '../../../types';
|
||||||
|
|
||||||
import { throttle } from '../../../util/schedulers';
|
import { throttle } from '../../../util/schedulers';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
@ -21,6 +21,13 @@ import Spinner from '../../ui/Spinner';
|
|||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
import UsernameInput from '../../common/UsernameInput';
|
import UsernameInput from '../../common/UsernameInput';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
currentAvatarHash?: string;
|
currentAvatarHash?: string;
|
||||||
@ -43,7 +50,10 @@ const MAX_BIO_LENGTH = 70;
|
|||||||
const ERROR_FIRST_NAME_MISSING = 'Please provide your first name';
|
const ERROR_FIRST_NAME_MISSING = 'Please provide your first name';
|
||||||
const ERROR_BIO_TOO_LONG = 'Bio can\' be longer than 70 characters';
|
const ERROR_BIO_TOO_LONG = 'Bio can\' be longer than 70 characters';
|
||||||
|
|
||||||
const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
const SettingsEditProfile: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
currentAvatarHash,
|
currentAvatarHash,
|
||||||
currentFirstName,
|
currentFirstName,
|
||||||
currentLastName,
|
currentLastName,
|
||||||
@ -55,6 +65,8 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
updateProfile,
|
updateProfile,
|
||||||
checkUsername,
|
checkUsername,
|
||||||
}) => {
|
}) => {
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
const [isUsernameTouched, setIsUsernameTouched] = useState(false);
|
const [isUsernameTouched, setIsUsernameTouched] = useState(false);
|
||||||
const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false);
|
const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false);
|
||||||
const [error, setError] = useState<string | undefined>();
|
const [error, setError] = useState<string | undefined>();
|
||||||
@ -78,6 +90,8 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
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);
|
||||||
|
|
||||||
// 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.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -165,8 +179,6 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
updateProfile,
|
updateProfile,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const lang = useLang();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-fab-wrapper">
|
<div className="settings-fab-wrapper">
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
@ -242,7 +254,7 @@ const SettingsEditProfile: FC<StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const { currentUserId } = global;
|
const { currentUserId } = global;
|
||||||
const { progress, isUsernameAvailable } = global.profileEdit || {};
|
const { progress, isUsernameAvailable } = global.profileEdit || {};
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RangeSlider from '../../ui/RangeSlider';
|
import RangeSlider from '../../ui/RangeSlider';
|
||||||
@ -21,7 +22,9 @@ import SettingsStickerSet from './SettingsStickerSet';
|
|||||||
import StickerSetModal from '../../common/StickerSetModal.async';
|
import StickerSetModal from '../../common/StickerSetModal.async';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = Pick<ISettings, (
|
type StateProps = Pick<ISettings, (
|
||||||
@ -52,7 +55,9 @@ const ANIMATION_LEVEL_OPTIONS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
stickerSetIds,
|
stickerSetIds,
|
||||||
stickerSetsById,
|
stickerSetsById,
|
||||||
messageTextSize,
|
messageTextSize,
|
||||||
@ -120,6 +125,8 @@ const SettingsGeneral: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return stickerSetsById && stickerSetsById[id] && stickerSetsById[id].installedDate ? stickerSetsById[id] : false;
|
return stickerSetsById && stickerSetsById[id] && stickerSetsById[id].installedDate ? stickerSetsById[id] : false;
|
||||||
}).filter<ApiStickerSet>(Boolean as any);
|
}).filter<ApiStickerSet>(Boolean as any);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.General);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item pt-3">
|
<div className="settings-item pt-3">
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
|||||||
import { getAverageColor, getPatternColor, rgb2hex } from '../../../util/colors';
|
import { getAverageColor, getPatternColor, rgb2hex } from '../../../util/colors';
|
||||||
import { selectTheme } from '../../../modules/selectors';
|
import { selectTheme } from '../../../modules/selectors';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
@ -23,7 +24,9 @@ import WallpaperTile from './WallpaperTile';
|
|||||||
import './SettingsGeneralBackground.scss';
|
import './SettingsGeneralBackground.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -42,7 +45,9 @@ const SUPPORTED_TYPES = 'image/jpeg';
|
|||||||
const runThrottled = throttle((cb) => cb(), 60000, true);
|
const runThrottled = throttle((cb) => cb(), 60000, true);
|
||||||
|
|
||||||
const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
background,
|
background,
|
||||||
isBlurred,
|
isBlurred,
|
||||||
loadedWallpapers,
|
loadedWallpapers,
|
||||||
@ -106,6 +111,8 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.GeneralChatBackground);
|
||||||
|
|
||||||
const isUploading = loadedWallpapers && loadedWallpapers[0] && loadedWallpapers[0].slug === UPLOADING_WALLPAPER_SLUG;
|
const isUploading = loadedWallpapers && loadedWallpapers[0] && loadedWallpapers[0].slug === UPLOADING_WALLPAPER_SLUG;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -15,13 +15,16 @@ import { captureEvents, RealTouchEvent } from '../../../util/captureEvents';
|
|||||||
import { selectTheme } from '../../../modules/selectors';
|
import { selectTheme } from '../../../modules/selectors';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
|
|
||||||
import './SettingsGeneralBackgroundColor.scss';
|
import './SettingsGeneralBackgroundColor.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -51,6 +54,9 @@ const PREDEFINED_COLORS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
theme,
|
theme,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
setThemeSettings,
|
setThemeSettings,
|
||||||
@ -195,6 +201,8 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
isDragging && 'is-dragging',
|
isDragging && 'is-dragging',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.GeneralChatBackgroundColor);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} className={className}>
|
<div ref={containerRef} className={className}>
|
||||||
<div className="settings-item pt-3">
|
<div className="settings-item pt-3">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import React, {
|
|||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { ISettings } from '../../../types';
|
import { ISettings, SettingsScreens } from '../../../types';
|
||||||
import { ApiLanguage } from '../../../api/types';
|
import { ApiLanguage } from '../../../api/types';
|
||||||
|
|
||||||
import { setLanguage } from '../../../util/langProvider';
|
import { setLanguage } from '../../../util/langProvider';
|
||||||
@ -13,12 +13,22 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = Pick<ISettings, 'languages' | 'language'>;
|
type StateProps = Pick<ISettings, 'languages' | 'language'>;
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'loadLanguages' | 'setSettingOption'>;
|
type DispatchProps = Pick<GlobalActions, 'loadLanguages' | 'setSettingOption'>;
|
||||||
|
|
||||||
const SettingsLanguage: FC<StateProps & DispatchProps> = ({
|
const SettingsLanguage: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
languages,
|
languages,
|
||||||
language,
|
language,
|
||||||
loadLanguages,
|
loadLanguages,
|
||||||
@ -47,6 +57,8 @@ const SettingsLanguage: FC<StateProps & DispatchProps> = ({
|
|||||||
return languages ? buildOptions(languages) : undefined;
|
return languages ? buildOptions(languages) : undefined;
|
||||||
}, [languages]);
|
}, [languages]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Language);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content settings-item settings-language custom-scroll">
|
<div className="settings-content settings-item settings-language custom-scroll">
|
||||||
{options ? (
|
{options ? (
|
||||||
@ -77,7 +89,7 @@ function buildOptions(languages: ApiLanguage[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
return {
|
return {
|
||||||
languages: global.settings.byKey.languages,
|
languages: global.settings.byKey.languages,
|
||||||
|
|||||||
@ -8,12 +8,15 @@ import { selectUser } from '../../../modules/selectors';
|
|||||||
import { getUserFullName } from '../../../modules/helpers';
|
import { getUserFullName } from '../../../modules/helpers';
|
||||||
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
import { formatPhoneNumberWithCode } from '../../../util/phoneNumber';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -21,11 +24,15 @@ type StateProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const SettingsMain: FC<OwnProps & StateProps> = ({
|
const SettingsMain: FC<OwnProps & StateProps> = ({
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
currentUser,
|
currentUser,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Main);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-main-menu">
|
<div className="settings-main-menu">
|
||||||
|
|||||||
@ -5,12 +5,20 @@ import React, {
|
|||||||
import { withGlobal } from '../../../lib/teact/teactn';
|
import { withGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
|
import { SettingsScreens } from '../../../types';
|
||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
hasPrivateChatsNotifications: boolean;
|
hasPrivateChatsNotifications: boolean;
|
||||||
hasPrivateChatsMessagePreview: boolean;
|
hasPrivateChatsMessagePreview: boolean;
|
||||||
@ -25,7 +33,10 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
'loadNotificationSettings' | 'updateContactSignUpNotification' | 'updateNotificationSettings'
|
'loadNotificationSettings' | 'updateContactSignUpNotification' | 'updateNotificationSettings'
|
||||||
)>;
|
)>;
|
||||||
|
|
||||||
const SettingsNotifications: FC<StateProps & DispatchProps> = ({
|
const SettingsNotifications: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
hasPrivateChatsNotifications,
|
hasPrivateChatsNotifications,
|
||||||
hasPrivateChatsMessagePreview,
|
hasPrivateChatsMessagePreview,
|
||||||
hasGroupNotifications,
|
hasGroupNotifications,
|
||||||
@ -73,6 +84,8 @@ const SettingsNotifications: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Notifications);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content custom-scroll">
|
<div className="settings-content custom-scroll">
|
||||||
<div className="settings-item">
|
<div className="settings-item">
|
||||||
@ -145,7 +158,7 @@ const SettingsNotifications: FC<StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(withGlobal((global): StateProps => {
|
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||||
return {
|
return {
|
||||||
hasPrivateChatsNotifications: Boolean(global.settings.byKey.hasPrivateChatsNotifications),
|
hasPrivateChatsNotifications: Boolean(global.settings.byKey.hasPrivateChatsNotifications),
|
||||||
hasPrivateChatsMessagePreview: Boolean(global.settings.byKey.hasPrivateChatsMessagePreview),
|
hasPrivateChatsMessagePreview: Boolean(global.settings.byKey.hasPrivateChatsMessagePreview),
|
||||||
|
|||||||
@ -6,12 +6,15 @@ import { PrivacyVisibility, SettingsScreens } from '../../../types';
|
|||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -32,7 +35,9 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
)>;
|
)>;
|
||||||
|
|
||||||
const SettingsPrivacy: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsPrivacy: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
hasPassword,
|
hasPassword,
|
||||||
blockedCount,
|
blockedCount,
|
||||||
sessionsCount,
|
sessionsCount,
|
||||||
@ -58,6 +63,8 @@ const SettingsPrivacy: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Privacy);
|
||||||
|
|
||||||
function getVisibilityValue(visibility?: PrivacyVisibility) {
|
function getVisibilityValue(visibility?: PrivacyVisibility) {
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
case 'everybody':
|
case 'everybody':
|
||||||
|
|||||||
@ -5,15 +5,23 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
|||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { ApiSession } from '../../../api/types';
|
import { ApiSession } from '../../../api/types';
|
||||||
|
import { SettingsScreens } from '../../../types';
|
||||||
|
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import { formatPastTimeShort } from '../../../util/dateFormat';
|
import { formatPastTimeShort } from '../../../util/dateFormat';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
activeSessions: ApiSession[];
|
activeSessions: ApiSession[];
|
||||||
};
|
};
|
||||||
@ -22,7 +30,10 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
'loadAuthorizations' | 'terminateAuthorization' | 'terminateAllAuthorizations'
|
'loadAuthorizations' | 'terminateAuthorization' | 'terminateAllAuthorizations'
|
||||||
)>;
|
)>;
|
||||||
|
|
||||||
const SettingsPrivacyActiveSessions: FC<StateProps & DispatchProps> = ({
|
const SettingsPrivacyActiveSessions: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
activeSessions,
|
activeSessions,
|
||||||
loadAuthorizations,
|
loadAuthorizations,
|
||||||
terminateAuthorization,
|
terminateAuthorization,
|
||||||
@ -52,6 +63,8 @@ const SettingsPrivacyActiveSessions: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.PrivacyActiveSessions);
|
||||||
|
|
||||||
function renderCurrentSession(session: ApiSession) {
|
function renderCurrentSession(session: ApiSession) {
|
||||||
return (
|
return (
|
||||||
<div className="settings-item">
|
<div className="settings-item">
|
||||||
@ -140,7 +153,7 @@ function getDeviceEnvironment(session: ApiSession) {
|
|||||||
return `${session.deviceModel}${session.deviceModel ? ', ' : ''} ${session.platform} ${session.systemVersion}`;
|
return `${session.deviceModel}${session.deviceModel ? ', ' : ''} ${session.platform} ${session.systemVersion}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
return {
|
return {
|
||||||
activeSessions: global.activeSessions,
|
activeSessions: global.activeSessions,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
|||||||
|
|
||||||
import { GlobalActions } from '../../../global/types';
|
import { GlobalActions } from '../../../global/types';
|
||||||
import { ApiChat, ApiUser } from '../../../api/types';
|
import { ApiChat, 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';
|
||||||
@ -15,12 +16,19 @@ import {
|
|||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import Avatar from '../../common/Avatar';
|
import Avatar from '../../common/Avatar';
|
||||||
import Loading from '../../ui/Loading';
|
import Loading from '../../ui/Loading';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
chatsByIds: Record<number, ApiChat>;
|
chatsByIds: Record<number, ApiChat>;
|
||||||
usersByIds: Record<number, ApiUser>;
|
usersByIds: Record<number, ApiUser>;
|
||||||
@ -29,7 +37,10 @@ type StateProps = {
|
|||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'unblockContact'>;
|
type DispatchProps = Pick<GlobalActions, 'unblockContact'>;
|
||||||
|
|
||||||
const SettingsPrivacyBlockedUsers: FC<StateProps & DispatchProps> = ({
|
const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
chatsByIds,
|
chatsByIds,
|
||||||
usersByIds,
|
usersByIds,
|
||||||
blockedIds,
|
blockedIds,
|
||||||
@ -41,6 +52,8 @@ const SettingsPrivacyBlockedUsers: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.PrivacyBlockedUsers);
|
||||||
|
|
||||||
function renderContact(contactId: number, i: number, viewportOffset: number) {
|
function renderContact(contactId: number, i: number, viewportOffset: number) {
|
||||||
const isPrivate = isChatPrivate(contactId);
|
const isPrivate = isChatPrivate(contactId);
|
||||||
const user = isPrivate ? usersByIds[contactId] : undefined;
|
const user = isPrivate ? usersByIds[contactId] : undefined;
|
||||||
@ -118,7 +131,7 @@ const SettingsPrivacyBlockedUsers: FC<StateProps & DispatchProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global): StateProps => {
|
(global): StateProps => {
|
||||||
const {
|
const {
|
||||||
chats: {
|
chats: {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { ApiPrivacySettings, SettingsScreens } from '../../../types';
|
|||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
@ -16,7 +17,9 @@ import { getPrivacyKey } from './helper/privacy';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
screen: SettingsScreens;
|
screen: SettingsScreens;
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = Partial<ApiPrivacySettings> & {
|
type StateProps = Partial<ApiPrivacySettings> & {
|
||||||
@ -28,7 +31,9 @@ type DispatchProps = Pick<GlobalActions, 'setPrivacyVisibility'>;
|
|||||||
|
|
||||||
const SettingsPrivacyVisibility: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsPrivacyVisibility: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
screen,
|
screen,
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
visibility,
|
visibility,
|
||||||
allowUserIds,
|
allowUserIds,
|
||||||
allowChatIds,
|
allowChatIds,
|
||||||
@ -81,6 +86,8 @@ const SettingsPrivacyVisibility: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [lang, screen]);
|
}, [lang, screen]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||||
|
|
||||||
const descriptionText = useMemo(() => {
|
const descriptionText = useMemo(() => {
|
||||||
switch (screen) {
|
switch (screen) {
|
||||||
case SettingsScreens.PrivacyLastSeen:
|
case SettingsScreens.PrivacyLastSeen:
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { getPrivacyKey } from './helper/privacy';
|
|||||||
import {
|
import {
|
||||||
getChatTitle, isChatGroup, isChatPrivate, prepareChatList,
|
getChatTitle, isChatGroup, isChatPrivate, prepareChatList,
|
||||||
} from '../../../modules/helpers';
|
} from '../../../modules/helpers';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Picker from '../../common/Picker';
|
import Picker from '../../common/Picker';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
@ -21,7 +22,9 @@ import FloatingActionButton from '../../ui/FloatingActionButton';
|
|||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
isAllowList?: boolean;
|
isAllowList?: boolean;
|
||||||
screen: SettingsScreens;
|
screen: SettingsScreens;
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -47,7 +50,9 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps & Dispatc
|
|||||||
archivedListIds,
|
archivedListIds,
|
||||||
archivedPinnedIds,
|
archivedPinnedIds,
|
||||||
setPrivacySettings,
|
setPrivacySettings,
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
@ -122,6 +127,9 @@ const SettingsPrivacyVisibilityExceptionList: FC<OwnProps & StateProps & Dispatc
|
|||||||
onScreenSelect(SettingsScreens.Privacy);
|
onScreenSelect(SettingsScreens.Privacy);
|
||||||
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
|
}, [isAllowList, newSelectedContactIds, onScreenSelect, screen, setPrivacySettings]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="NewChat-inner step-1">
|
<div className="NewChat-inner step-1">
|
||||||
<Picker
|
<Picker
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, { FC, memo, useCallback } from '../../../../lib/teact/teact';
|
|||||||
import { ApiChatFolder } from '../../../../api/types';
|
import { ApiChatFolder } from '../../../../api/types';
|
||||||
import { SettingsScreens } from '../../../../types';
|
import { SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
import { FoldersState, FolderEditDispatch } from '../../../../hooks/reducers/useFoldersReducer';
|
import { FolderEditDispatch, FoldersState } from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
|
|
||||||
import SettingsFoldersMain from './SettingsFoldersMain';
|
import SettingsFoldersMain from './SettingsFoldersMain';
|
||||||
import SettingsFoldersEdit from './SettingsFoldersEdit';
|
import SettingsFoldersEdit from './SettingsFoldersEdit';
|
||||||
@ -15,16 +15,20 @@ const TRANSITION_DURATION = 200;
|
|||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
currentScreen: SettingsScreens;
|
currentScreen: SettingsScreens;
|
||||||
|
shownScreen: SettingsScreens;
|
||||||
state: FoldersState;
|
state: FoldersState;
|
||||||
dispatch: FolderEditDispatch;
|
dispatch: FolderEditDispatch;
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsFolders: FC<OwnProps> = ({
|
const SettingsFolders: FC<OwnProps> = ({
|
||||||
currentScreen,
|
currentScreen,
|
||||||
|
shownScreen,
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
onReset,
|
onReset,
|
||||||
}) => {
|
}) => {
|
||||||
@ -82,6 +86,14 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
<SettingsFoldersMain
|
<SettingsFoldersMain
|
||||||
onCreateFolder={handleCreateFolder}
|
onCreateFolder={handleCreateFolder}
|
||||||
onEditFolder={handleEditFolder}
|
onEditFolder={handleEditFolder}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.FoldersCreateFolder,
|
||||||
|
SettingsScreens.FoldersEditFolder,
|
||||||
|
SettingsScreens.FoldersIncludedChats,
|
||||||
|
SettingsScreens.FoldersExcludedChats,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.FoldersCreateFolder:
|
case SettingsScreens.FoldersCreateFolder:
|
||||||
@ -93,6 +105,12 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
onAddIncludedChats={handleAddIncludedChats}
|
onAddIncludedChats={handleAddIncludedChats}
|
||||||
onAddExcludedChats={handleAddExcludedChats}
|
onAddExcludedChats={handleAddExcludedChats}
|
||||||
onReset={handleReset}
|
onReset={handleReset}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.FoldersIncludedChats,
|
||||||
|
SettingsScreens.FoldersExcludedChats,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onBack={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.FoldersIncludedChats:
|
case SettingsScreens.FoldersIncludedChats:
|
||||||
@ -101,6 +119,9 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
mode="included"
|
mode="included"
|
||||||
state={state}
|
state={state}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
onReset={handleReset}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case SettingsScreens.FoldersExcludedChats:
|
case SettingsScreens.FoldersExcludedChats:
|
||||||
@ -109,6 +130,9 @@ const SettingsFolders: FC<OwnProps> = ({
|
|||||||
mode="excluded"
|
mode="excluded"
|
||||||
state={state}
|
state={state}
|
||||||
dispatch={dispatch}
|
dispatch={dispatch}
|
||||||
|
onReset={handleReset}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { withGlobal } from '../../../../lib/teact/teactn';
|
|||||||
|
|
||||||
import { GlobalActions } from '../../../../global/types';
|
import { GlobalActions } from '../../../../global/types';
|
||||||
import { ApiChat } from '../../../../api/types';
|
import { ApiChat } from '../../../../api/types';
|
||||||
|
import { SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
import { pick } from '../../../../util/iteratees';
|
import { pick } from '../../../../util/iteratees';
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
FolderEditDispatch,
|
FolderEditDispatch,
|
||||||
selectChatFilters,
|
selectChatFilters,
|
||||||
} from '../../../../hooks/reducers/useFoldersReducer';
|
} from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import SettingsFoldersChatsPicker from './SettingsFoldersChatsPicker';
|
import SettingsFoldersChatsPicker from './SettingsFoldersChatsPicker';
|
||||||
|
|
||||||
@ -24,6 +26,9 @@ type OwnProps = {
|
|||||||
mode: 'included' | 'excluded';
|
mode: 'included' | 'excluded';
|
||||||
state: FoldersState;
|
state: FoldersState;
|
||||||
dispatch: FolderEditDispatch;
|
dispatch: FolderEditDispatch;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -37,6 +42,9 @@ type StateProps = {
|
|||||||
type DispatchProps = Pick<GlobalActions, 'loadMoreChats'>;
|
type DispatchProps = Pick<GlobalActions, 'loadMoreChats'>;
|
||||||
|
|
||||||
const SettingsFoldersChatFilters: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsFoldersChatFilters: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
mode,
|
mode,
|
||||||
state,
|
state,
|
||||||
dispatch,
|
dispatch,
|
||||||
@ -132,6 +140,9 @@ const SettingsFoldersChatFilters: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [mode, selectedChatIds, dispatch]);
|
}, [mode, selectedChatIds, dispatch]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect,
|
||||||
|
mode === 'included' ? SettingsScreens.FoldersIncludedChats : SettingsScreens.FoldersExcludedChats);
|
||||||
|
|
||||||
if (!displayedIds) {
|
if (!displayedIds) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
import React, {
|
import React, {
|
||||||
FC, memo, useCallback, useState, useEffect, useMemo,
|
FC, memo, useCallback, useEffect, useMemo, useState,
|
||||||
} from '../../../../lib/teact/teact';
|
} from '../../../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../../../lib/teact/teactn';
|
import { withGlobal } from '../../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../../../global/types';
|
import { GlobalActions } from '../../../../global/types';
|
||||||
|
import { SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
||||||
import { pick, findIntersectionWithSet } from '../../../../util/iteratees';
|
import { findIntersectionWithSet, pick } from '../../../../util/iteratees';
|
||||||
import { isChatPrivate } from '../../../../modules/helpers';
|
import { isChatPrivate } from '../../../../modules/helpers';
|
||||||
import getAnimationData from '../../../common/helpers/animatedAssets';
|
import getAnimationData from '../../../common/helpers/animatedAssets';
|
||||||
import {
|
import {
|
||||||
FoldersState,
|
|
||||||
FolderEditDispatch,
|
|
||||||
INCLUDED_CHAT_TYPES,
|
|
||||||
EXCLUDED_CHAT_TYPES,
|
EXCLUDED_CHAT_TYPES,
|
||||||
|
FolderEditDispatch,
|
||||||
|
FoldersState,
|
||||||
|
INCLUDED_CHAT_TYPES,
|
||||||
selectChatFilters,
|
selectChatFilters,
|
||||||
} from '../../../../hooks/reducers/useFoldersReducer';
|
} from '../../../../hooks/reducers/useFoldersReducer';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
import AnimatedSticker from '../../../common/AnimatedSticker';
|
import AnimatedSticker from '../../../common/AnimatedSticker';
|
||||||
@ -32,7 +34,10 @@ type OwnProps = {
|
|||||||
dispatch: FolderEditDispatch;
|
dispatch: FolderEditDispatch;
|
||||||
onAddIncludedChats: () => void;
|
onAddIncludedChats: () => void;
|
||||||
onAddExcludedChats: () => void;
|
onAddExcludedChats: () => void;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
|
onBack: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -54,7 +59,10 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
dispatch,
|
dispatch,
|
||||||
onAddIncludedChats,
|
onAddIncludedChats,
|
||||||
onAddExcludedChats,
|
onAddExcludedChats,
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
onReset,
|
onReset,
|
||||||
|
onBack,
|
||||||
loadedActiveChatIds,
|
loadedActiveChatIds,
|
||||||
loadedArchivedChatIds,
|
loadedArchivedChatIds,
|
||||||
editChatFolder,
|
editChatFolder,
|
||||||
@ -128,6 +136,10 @@ const SettingsFoldersEdit: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onBack, onScreenSelect, state.mode === 'edit'
|
||||||
|
? SettingsScreens.FoldersEditFolder
|
||||||
|
: SettingsScreens.FoldersCreateFolder);
|
||||||
|
|
||||||
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
const { currentTarget } = event;
|
const { currentTarget } = event;
|
||||||
dispatch({ type: 'setTitle', payload: currentTarget.value.trim() });
|
dispatch({ type: 'setTitle', payload: currentTarget.value.trim() });
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { withGlobal } from '../../../../lib/teact/teactn';
|
|||||||
|
|
||||||
import { GlobalActions } from '../../../../global/types';
|
import { GlobalActions } from '../../../../global/types';
|
||||||
import { ApiChatFolder, ApiChat, ApiUser } from '../../../../api/types';
|
import { ApiChatFolder, ApiChat, ApiUser } from '../../../../api/types';
|
||||||
import { NotifyException, NotifySettings } from '../../../../types';
|
import { NotifyException, NotifySettings, SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config';
|
||||||
import { pick } from '../../../../util/iteratees';
|
import { pick } from '../../../../util/iteratees';
|
||||||
@ -14,6 +14,7 @@ import { throttle } from '../../../../util/schedulers';
|
|||||||
import getAnimationData from '../../../common/helpers/animatedAssets';
|
import getAnimationData from '../../../common/helpers/animatedAssets';
|
||||||
import { getFolderDescriptionText } from '../../../../modules/helpers';
|
import { getFolderDescriptionText } from '../../../../modules/helpers';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
@ -23,6 +24,9 @@ import AnimatedSticker from '../../../common/AnimatedSticker';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
onCreateFolder: () => void;
|
onCreateFolder: () => void;
|
||||||
onEditFolder: (folder: ApiChatFolder) => void;
|
onEditFolder: (folder: ApiChatFolder) => void;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -44,6 +48,9 @@ const MAX_ALLOWED_FOLDERS = 10;
|
|||||||
const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
onCreateFolder,
|
onCreateFolder,
|
||||||
onEditFolder,
|
onEditFolder,
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
chatsById,
|
chatsById,
|
||||||
usersById,
|
usersById,
|
||||||
orderedFolderIds,
|
orderedFolderIds,
|
||||||
@ -90,6 +97,8 @@ const SettingsFoldersMain: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.Folders);
|
||||||
|
|
||||||
const userFolders = useMemo(() => {
|
const userFolders = useMemo(() => {
|
||||||
if (!orderedFolderIds) {
|
if (!orderedFolderIds) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -20,8 +20,11 @@ import SettingsTwoFaEmailCode from './SettingsTwoFaEmailCode';
|
|||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
state: TwoFaState;
|
state: TwoFaState;
|
||||||
currentScreen: SettingsScreens;
|
currentScreen: SettingsScreens;
|
||||||
|
shownScreen: SettingsScreens;
|
||||||
dispatch: TwoFaDispatch;
|
dispatch: TwoFaDispatch;
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = GlobalState['twoFaSettings'];
|
type StateProps = GlobalState['twoFaSettings'];
|
||||||
@ -33,13 +36,16 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
|
|
||||||
const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
currentScreen,
|
currentScreen,
|
||||||
|
shownScreen,
|
||||||
state,
|
state,
|
||||||
hint,
|
hint,
|
||||||
isLoading,
|
isLoading,
|
||||||
error,
|
error,
|
||||||
waitingEmailCodeLength,
|
waitingEmailCodeLength,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
isActive,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
updatePassword,
|
updatePassword,
|
||||||
checkPassword,
|
checkPassword,
|
||||||
clearTwoFaError,
|
clearTwoFaError,
|
||||||
@ -158,25 +164,54 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SettingsTwoFaStart
|
<SettingsTwoFaStart
|
||||||
onStart={handleStartWizard}
|
onStart={handleStartWizard}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.TwoFaNewPassword,
|
||||||
|
SettingsScreens.TwoFaNewPasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case SettingsScreens.TwoFaNewPassword:
|
case SettingsScreens.TwoFaNewPassword:
|
||||||
return (
|
return (
|
||||||
<SettingsTwoFaPassword
|
<SettingsTwoFaPassword
|
||||||
|
screen={currentScreen}
|
||||||
placeholder={lang('EnterPassword')}
|
placeholder={lang('EnterPassword')}
|
||||||
submitLabel={lang('Continue')}
|
submitLabel={lang('Continue')}
|
||||||
onSubmit={handleNewPassword}
|
onSubmit={handleNewPassword}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.TwoFaNewPasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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 || [
|
||||||
|
SettingsScreens.TwoFaNewPasswordHint,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -186,6 +221,14 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
icon="hint"
|
icon="hint"
|
||||||
placeholder={lang('PasswordHintPlaceholder')}
|
placeholder={lang('PasswordHintPlaceholder')}
|
||||||
onSubmit={handleNewPasswordHint}
|
onSubmit={handleNewPasswordHint}
|
||||||
|
screen={currentScreen}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmail,
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -200,6 +243,13 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
placeholder={lang('RecoveryEmailTitle')}
|
placeholder={lang('RecoveryEmailTitle')}
|
||||||
shouldConfirm
|
shouldConfirm
|
||||||
onSubmit={handleNewPasswordEmail}
|
onSubmit={handleNewPasswordEmail}
|
||||||
|
screen={currentScreen}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.TwoFaNewPasswordEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -210,6 +260,10 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
error={error}
|
error={error}
|
||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
onSubmit={handleEmailCode}
|
onSubmit={handleEmailCode}
|
||||||
|
screen={currentScreen}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -217,6 +271,8 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SettingsTwoFaCongratulations
|
<SettingsTwoFaCongratulations
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -224,34 +280,70 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
return (
|
return (
|
||||||
<SettingsTwoFaEnabled
|
<SettingsTwoFaEnabled
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
SettingsScreens.TwoFaChangePasswordCurrent,
|
||||||
|
SettingsScreens.TwoFaChangePasswordNew,
|
||||||
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
|
SettingsScreens.TwoFaTurnOff,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCurrentPassword,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmail,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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 || [
|
||||||
|
SettingsScreens.TwoFaChangePasswordNew,
|
||||||
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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 || [
|
||||||
|
SettingsScreens.TwoFaChangePasswordConfirm,
|
||||||
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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 || [
|
||||||
|
SettingsScreens.TwoFaChangePasswordHint,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -264,6 +356,10 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
icon="hint"
|
icon="hint"
|
||||||
placeholder={lang('PasswordHintPlaceholder')}
|
placeholder={lang('PasswordHintPlaceholder')}
|
||||||
onSubmit={handleChangePasswordHint}
|
onSubmit={handleChangePasswordHint}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || shownScreen === SettingsScreens.TwoFaCongratulations}
|
||||||
|
onReset={onReset}
|
||||||
|
screen={currentScreen}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -275,37 +371,60 @@ const SettingsTwoFa: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
clearError={clearTwoFaError}
|
clearError={clearTwoFaError}
|
||||||
hint={hint}
|
hint={hint}
|
||||||
onSubmit={handleTurnOff}
|
onSubmit={handleTurnOff}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
|
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 || [
|
||||||
|
SettingsScreens.TwoFaRecoveryEmail,
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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 || [
|
||||||
|
SettingsScreens.TwoFaRecoveryEmailCode,
|
||||||
|
SettingsScreens.TwoFaCongratulations,
|
||||||
|
].includes(shownScreen)}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
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}
|
||||||
|
onReset={onReset}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -6,25 +6,32 @@ import { SettingsScreens } from '../../../../types';
|
|||||||
|
|
||||||
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
animatedEmoji: ApiSticker;
|
animatedEmoji: ApiSticker;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsTwoFaCongratulations: FC<OwnProps & StateProps> = ({ animatedEmoji, onScreenSelect }) => {
|
const SettingsTwoFaCongratulations: FC<OwnProps & StateProps> = ({
|
||||||
|
isActive, onReset, animatedEmoji, onScreenSelect,
|
||||||
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
onScreenSelect(SettingsScreens.Privacy);
|
onScreenSelect(SettingsScreens.Privacy);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaCongratulations);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
<div className="settings-content-header">
|
<div className="settings-content-header">
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import React, {
|
|||||||
import { withGlobal } from '../../../../lib/teact/teactn';
|
import { withGlobal } from '../../../../lib/teact/teactn';
|
||||||
|
|
||||||
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 '../../../../modules/selectors';
|
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
||||||
import InputText from '../../../ui/InputText';
|
import InputText from '../../../ui/InputText';
|
||||||
@ -18,6 +20,10 @@ type OwnProps = {
|
|||||||
error?: string;
|
error?: string;
|
||||||
clearError: NoneToVoidFunction;
|
clearError: NoneToVoidFunction;
|
||||||
onSubmit: (hint: string) => void;
|
onSubmit: (hint: string) => void;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
screen: SettingsScreens;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -34,6 +40,10 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
error,
|
error,
|
||||||
clearError,
|
clearError,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
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);
|
||||||
@ -50,6 +60,8 @@ const SettingsTwoFaEmailCode: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (error && clearError) {
|
if (error && clearError) {
|
||||||
clearError();
|
clearError();
|
||||||
|
|||||||
@ -6,22 +6,29 @@ import { SettingsScreens } from '../../../../types';
|
|||||||
|
|
||||||
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../../ui/ListItem';
|
import ListItem from '../../../ui/ListItem';
|
||||||
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
||||||
import renderText from '../../../common/helpers/renderText';
|
import renderText from '../../../common/helpers/renderText';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
|
isActive?: boolean;
|
||||||
onScreenSelect: (screen: SettingsScreens) => void;
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
animatedEmoji: ApiSticker;
|
animatedEmoji: ApiSticker;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsTwoFaEnabled: FC<OwnProps & StateProps> = ({ animatedEmoji, onScreenSelect }) => {
|
const SettingsTwoFaEnabled: FC<OwnProps & StateProps> = ({
|
||||||
|
isActive, onReset, animatedEmoji, onScreenSelect,
|
||||||
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaEnabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
<div className="settings-content-header">
|
<div className="settings-content-header">
|
||||||
|
|||||||
@ -2,12 +2,16 @@ 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 PasswordMonkey from '../../../common/PasswordMonkey';
|
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;
|
||||||
@ -16,11 +20,18 @@ type OwnProps = {
|
|||||||
submitLabel?: string;
|
submitLabel?: string;
|
||||||
clearError?: NoneToVoidFunction;
|
clearError?: NoneToVoidFunction;
|
||||||
onSubmit: (password: string) => void;
|
onSubmit: (password: string) => void;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => 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,
|
||||||
|
onScreenSelect,
|
||||||
|
onReset,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
expectedPassword,
|
expectedPassword,
|
||||||
@ -50,6 +61,8 @@ const SettingsTwoFaPassword: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
<div className="settings-content-header">
|
<div className="settings-content-header">
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import React, {
|
|||||||
import { withGlobal } from '../../../../lib/teact/teactn';
|
import { withGlobal } from '../../../../lib/teact/teactn';
|
||||||
|
|
||||||
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 '../../../../modules/selectors';
|
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
||||||
import useFlag from '../../../../hooks/useFlag';
|
import useFlag from '../../../../hooks/useFlag';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import Modal from '../../../ui/Modal';
|
import Modal from '../../../ui/Modal';
|
||||||
@ -25,6 +27,10 @@ type OwnProps = {
|
|||||||
shouldConfirm?: boolean;
|
shouldConfirm?: boolean;
|
||||||
clearError?: NoneToVoidFunction;
|
clearError?: NoneToVoidFunction;
|
||||||
onSubmit: (value?: string) => void;
|
onSubmit: (value?: string) => void;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
|
screen: SettingsScreens;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -42,6 +48,10 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
shouldConfirm,
|
shouldConfirm,
|
||||||
clearError,
|
clearError,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
|
isActive,
|
||||||
|
onScreenSelect,
|
||||||
|
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);
|
||||||
@ -86,6 +96,8 @@ const SettingsTwoFaSkippableForm: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, screen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
<div className="settings-content-header">
|
<div className="settings-content-header">
|
||||||
|
|||||||
@ -2,24 +2,33 @@ import React, { FC, memo } from '../../../../lib/teact/teact';
|
|||||||
import { withGlobal } from '../../../../lib/teact/teactn';
|
import { withGlobal } from '../../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { ApiSticker } from '../../../../api/types';
|
import { ApiSticker } from '../../../../api/types';
|
||||||
|
import { SettingsScreens } from '../../../../types';
|
||||||
|
|
||||||
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
import { selectAnimatedEmoji } from '../../../../modules/selectors';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
import AnimatedEmoji from '../../../common/AnimatedEmoji';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
onStart: NoneToVoidFunction;
|
onStart: NoneToVoidFunction;
|
||||||
|
isActive?: boolean;
|
||||||
|
onScreenSelect: (screen: SettingsScreens) => void;
|
||||||
|
onReset: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
animatedEmoji: ApiSticker;
|
animatedEmoji: ApiSticker;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SettingsTwoFaStart: FC<OwnProps & StateProps> = ({ animatedEmoji, onStart }) => {
|
const SettingsTwoFaStart: FC<OwnProps & StateProps> = ({
|
||||||
|
isActive, onScreenSelect, onReset, animatedEmoji, onStart,
|
||||||
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onReset, onScreenSelect, SettingsScreens.TwoFaDisabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-content two-fa custom-scroll">
|
<div className="settings-content two-fa custom-scroll">
|
||||||
<div className="settings-content-header">
|
<div className="settings-content-header">
|
||||||
|
|||||||
@ -87,6 +87,14 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Main.history-animation-disabled & {
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
@ -104,6 +112,18 @@
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
height: calc(var(--vh, 1vh) * 100 + 1px);
|
height: calc(var(--vh, 1vh) * 100 + 1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Main.history-animation-disabled & {
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
.overlay-backdrop {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Main.history-animation-disabled .overlay-backdrop {
|
||||||
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#MiddleColumn {
|
#MiddleColumn {
|
||||||
@ -155,6 +175,14 @@
|
|||||||
transform: translate3d(-20vw, 0, 0);
|
transform: translate3d(-20vw, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Main.history-animation-disabled & {
|
||||||
|
transition: none;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.SymbolMenu {
|
.SymbolMenu {
|
||||||
|
|||||||
@ -49,6 +49,7 @@ type StateProps = {
|
|||||||
audioMessage?: ApiMessage;
|
audioMessage?: ApiMessage;
|
||||||
safeLinkModalUrl?: string;
|
safeLinkModalUrl?: string;
|
||||||
isHistoryCalendarOpen: boolean;
|
isHistoryCalendarOpen: boolean;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, (
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
@ -75,6 +76,7 @@ const Main: FC<StateProps & DispatchProps> = ({
|
|||||||
audioMessage,
|
audioMessage,
|
||||||
safeLinkModalUrl,
|
safeLinkModalUrl,
|
||||||
isHistoryCalendarOpen,
|
isHistoryCalendarOpen,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
loadAnimatedEmojis,
|
loadAnimatedEmojis,
|
||||||
loadNotificationSettings,
|
loadNotificationSettings,
|
||||||
loadNotificationExceptions,
|
loadNotificationExceptions,
|
||||||
@ -98,15 +100,17 @@ const Main: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
transitionClassNames: middleColumnTransitionClassNames,
|
transitionClassNames: middleColumnTransitionClassNames,
|
||||||
} = useShowTransition(!isLeftColumnShown, undefined, true);
|
} = useShowTransition(!isLeftColumnShown, undefined, true, undefined, shouldSkipHistoryAnimations);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
transitionClassNames: rightColumnTransitionClassNames,
|
transitionClassNames: rightColumnTransitionClassNames,
|
||||||
} = useShowTransition(isRightColumnShown, undefined, true);
|
} = useShowTransition(isRightColumnShown, undefined, true, undefined, shouldSkipHistoryAnimations);
|
||||||
|
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
middleColumnTransitionClassNames.replace(/([\w-]+)/g, 'middle-column-$1'),
|
middleColumnTransitionClassNames.replace(/([\w-]+)/g, 'middle-column-$1'),
|
||||||
rightColumnTransitionClassNames.replace(/([\w-]+)/g, 'right-column-$1'),
|
rightColumnTransitionClassNames.replace(/([\w-]+)/g, 'right-column-$1'),
|
||||||
|
shouldSkipHistoryAnimations && 'history-animation-disabled',
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -230,6 +234,7 @@ export default memo(withGlobal(
|
|||||||
audioMessage,
|
audioMessage,
|
||||||
safeLinkModalUrl: global.safeLinkModalUrl,
|
safeLinkModalUrl: global.safeLinkModalUrl,
|
||||||
isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt),
|
isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt),
|
||||||
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
|
|||||||
@ -58,6 +58,7 @@ import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'
|
|||||||
import { renderMessageText } from '../common/helpers/renderMessageText';
|
import { renderMessageText } from '../common/helpers/renderMessageText';
|
||||||
import { animateClosing, animateOpening } from './helpers/ghostAnimation';
|
import { animateClosing, animateOpening } from './helpers/ghostAnimation';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Spinner from '../ui/Spinner';
|
import Spinner from '../ui/Spinner';
|
||||||
import ShowTransition from '../ui/ShowTransition';
|
import ShowTransition from '../ui/ShowTransition';
|
||||||
@ -443,6 +444,14 @@ const MediaViewer: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isOpen, closeMediaViewer, openMediaViewer, {
|
||||||
|
chatId,
|
||||||
|
threadId,
|
||||||
|
messageId,
|
||||||
|
origin,
|
||||||
|
avatarOwnerId: avatarOwner && avatarOwner.id,
|
||||||
|
});
|
||||||
|
|
||||||
function renderSlide(isActive: boolean) {
|
function renderSlide(isActive: boolean) {
|
||||||
if (isAvatar) {
|
if (isAvatar) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import {
|
|||||||
selectIsRightColumnShown,
|
selectIsRightColumnShown,
|
||||||
selectPinnedIds,
|
selectPinnedIds,
|
||||||
selectTheme,
|
selectTheme,
|
||||||
|
selectThreadOriginChat,
|
||||||
} from '../../modules/selectors';
|
} from '../../modules/selectors';
|
||||||
import { getCanPostInChat, getMessageSendingRestrictionReason, isChatPrivate } from '../../modules/helpers';
|
import { getCanPostInChat, getMessageSendingRestrictionReason, isChatPrivate } from '../../modules/helpers';
|
||||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||||
@ -45,6 +46,7 @@ import useWindowSize from '../../hooks/useWindowSize';
|
|||||||
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
||||||
import calculateMiddleFooterTransforms from './helpers/calculateMiddleFooterTransforms';
|
import calculateMiddleFooterTransforms from './helpers/calculateMiddleFooterTransforms';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import MiddleHeader from './MiddleHeader';
|
import MiddleHeader from './MiddleHeader';
|
||||||
@ -64,6 +66,7 @@ type StateProps = {
|
|||||||
messageListType?: MessageListType;
|
messageListType?: MessageListType;
|
||||||
isPrivate?: boolean;
|
isPrivate?: boolean;
|
||||||
isPinnedMessageList?: boolean;
|
isPinnedMessageList?: boolean;
|
||||||
|
isScheduledMessageList?: boolean;
|
||||||
canPost?: boolean;
|
canPost?: boolean;
|
||||||
messageSendingRestrictionReason?: string;
|
messageSendingRestrictionReason?: string;
|
||||||
hasPinnedOrAudioMessage?: boolean;
|
hasPinnedOrAudioMessage?: boolean;
|
||||||
@ -78,9 +81,12 @@ type StateProps = {
|
|||||||
isMobileSearchActive?: boolean;
|
isMobileSearchActive?: boolean;
|
||||||
isSelectModeActive?: boolean;
|
isSelectModeActive?: boolean;
|
||||||
animationLevel?: number;
|
animationLevel?: number;
|
||||||
|
originChatId?: number;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'openChat' | 'unpinAllMessages' | 'loadUser'>;
|
type DispatchProps = Pick<GlobalActions, 'openChat' | 'unpinAllMessages' | 'loadUser' |
|
||||||
|
'closeLocalTextSearch' | 'exitMessageSelectMode'>;
|
||||||
|
|
||||||
const CLOSE_ANIMATION_DURATION = IS_SINGLE_COLUMN_LAYOUT ? 450 + ANIMATION_END_DELAY : undefined;
|
const CLOSE_ANIMATION_DURATION = IS_SINGLE_COLUMN_LAYOUT ? 450 + ANIMATION_END_DELAY : undefined;
|
||||||
|
|
||||||
@ -94,6 +100,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
messageListType,
|
messageListType,
|
||||||
isPrivate,
|
isPrivate,
|
||||||
isPinnedMessageList,
|
isPinnedMessageList,
|
||||||
|
isScheduledMessageList,
|
||||||
canPost,
|
canPost,
|
||||||
messageSendingRestrictionReason,
|
messageSendingRestrictionReason,
|
||||||
hasPinnedOrAudioMessage,
|
hasPinnedOrAudioMessage,
|
||||||
@ -108,9 +115,13 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
isMobileSearchActive,
|
isMobileSearchActive,
|
||||||
isSelectModeActive,
|
isSelectModeActive,
|
||||||
animationLevel,
|
animationLevel,
|
||||||
|
originChatId,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
openChat,
|
openChat,
|
||||||
unpinAllMessages,
|
unpinAllMessages,
|
||||||
loadUser,
|
loadUser,
|
||||||
|
closeLocalTextSearch,
|
||||||
|
exitMessageSelectMode,
|
||||||
}) => {
|
}) => {
|
||||||
const { width: windowWidth } = useWindowSize();
|
const { width: windowWidth } = useWindowSize();
|
||||||
|
|
||||||
@ -223,6 +234,31 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
renderingCanPost && isNotchShown && !isSelectModeActive && 'with-notch',
|
renderingCanPost && isNotchShown && !isSelectModeActive && 'with-notch',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const closeChat = () => {
|
||||||
|
if (renderingThreadId !== MAIN_THREAD_ID) {
|
||||||
|
openChat({ id: originChatId, threadId: MAIN_THREAD_ID }, true);
|
||||||
|
} else if (isPinnedMessageList || isScheduledMessageList) {
|
||||||
|
openChat({ id: chatId, type: 'thread' });
|
||||||
|
} else {
|
||||||
|
openChat({ id: undefined }, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useHistoryBack(renderingChatId && renderingThreadId, closeChat, openChat, {
|
||||||
|
id: chatId,
|
||||||
|
threadId: MAIN_THREAD_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
const isDiscussion = renderingChatId && renderingThreadId !== MAIN_THREAD_ID;
|
||||||
|
|
||||||
|
useHistoryBack(isDiscussion || isPinnedMessageList || isScheduledMessageList, closeChat, openChat, {
|
||||||
|
id: chatId,
|
||||||
|
threadId: renderingThreadId,
|
||||||
|
});
|
||||||
|
|
||||||
|
useHistoryBack(isMobileSearchActive, closeLocalTextSearch);
|
||||||
|
useHistoryBack(isSelectModeActive, exitMessageSelectMode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="MiddleColumn"
|
id="MiddleColumn"
|
||||||
@ -256,7 +292,7 @@ const MiddleColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
messageListType={renderingMessageListType}
|
messageListType={renderingMessageListType}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
name={animationLevel === ANIMATION_LEVEL_MAX ? 'slide' : 'fade'}
|
name={shouldSkipHistoryAnimations ? 'none' : animationLevel === ANIMATION_LEVEL_MAX ? 'slide' : 'fade'}
|
||||||
activeKey={renderingMessageListType === 'thread' && renderingThreadId === MAIN_THREAD_ID ? 1 : 2}
|
activeKey={renderingMessageListType === 'thread' && renderingThreadId === MAIN_THREAD_ID ? 1 : 2}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
>
|
>
|
||||||
@ -371,15 +407,19 @@ export default memo(withGlobal(
|
|||||||
const canPost = chat && getCanPostInChat(chat, threadId);
|
const canPost = chat && getCanPostInChat(chat, threadId);
|
||||||
const isBotNotStarted = selectIsChatBotNotStarted(global, chatId);
|
const isBotNotStarted = selectIsChatBotNotStarted(global, chatId);
|
||||||
const isPinnedMessageList = messageListType === 'pinned';
|
const isPinnedMessageList = messageListType === 'pinned';
|
||||||
|
const isScheduledMessageList = messageListType === 'scheduled';
|
||||||
|
const originChat = selectThreadOriginChat(global, chatId, threadId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
chatId,
|
chatId,
|
||||||
threadId,
|
threadId,
|
||||||
messageListType,
|
messageListType,
|
||||||
|
originChatId: originChat ? originChat.id : chatId,
|
||||||
isPrivate: isChatPrivate(chatId),
|
isPrivate: isChatPrivate(chatId),
|
||||||
canPost: !isPinnedMessageList && (!chat || canPost) && (!isBotNotStarted || IS_SINGLE_COLUMN_LAYOUT),
|
canPost: !isPinnedMessageList && (!chat || canPost) && (!isBotNotStarted || IS_SINGLE_COLUMN_LAYOUT),
|
||||||
isPinnedMessageList,
|
isPinnedMessageList,
|
||||||
|
isScheduledMessageList,
|
||||||
messageSendingRestrictionReason: chat && getMessageSendingRestrictionReason(chat),
|
messageSendingRestrictionReason: chat && getMessageSendingRestrictionReason(chat),
|
||||||
hasPinnedOrAudioMessage: (
|
hasPinnedOrAudioMessage: (
|
||||||
threadId !== MAIN_THREAD_ID
|
threadId !== MAIN_THREAD_ID
|
||||||
@ -387,9 +427,10 @@ export default memo(withGlobal(
|
|||||||
|| Boolean(audioChatId && audioMessageId)
|
|| Boolean(audioChatId && audioMessageId)
|
||||||
),
|
),
|
||||||
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
||||||
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
'openChat', 'unpinAllMessages', 'loadUser',
|
'openChat', 'unpinAllMessages', 'loadUser', 'closeLocalTextSearch', 'exitMessageSelectMode',
|
||||||
]),
|
]),
|
||||||
)(MiddleColumn));
|
)(MiddleColumn));
|
||||||
|
|||||||
@ -94,6 +94,7 @@ type StateProps = {
|
|||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
notifySettings: NotifySettings;
|
notifySettings: NotifySettings;
|
||||||
notifyExceptions?: Record<number, NotifyException>;
|
notifyExceptions?: Record<number, NotifyException>;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, (
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
@ -123,6 +124,7 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
notifySettings,
|
notifySettings,
|
||||||
notifyExceptions,
|
notifyExceptions,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
openChatWithInfo,
|
openChatWithInfo,
|
||||||
pinMessage,
|
pinMessage,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
@ -384,7 +386,10 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="MiddleHeader" ref={componentRef}>
|
<div className="MiddleHeader" ref={componentRef}>
|
||||||
<Transition name="slide-fade" activeKey={messageListType === 'thread' ? threadId : 1}>
|
<Transition
|
||||||
|
name={shouldSkipHistoryAnimations ? 'none' : 'slide-fade'}
|
||||||
|
activeKey={messageListType === 'thread' ? threadId : 1}
|
||||||
|
>
|
||||||
{renderInfo}
|
{renderInfo}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
@ -421,7 +426,7 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
export default memo(withGlobal<OwnProps>(
|
export default memo(withGlobal<OwnProps>(
|
||||||
(global, { chatId, threadId, messageListType }): StateProps => {
|
(global, { chatId, threadId, messageListType }): StateProps => {
|
||||||
const { isLeftColumnShown, lastSyncTime } = global;
|
const { isLeftColumnShown, lastSyncTime, shouldSkipHistoryAnimations } = global;
|
||||||
const { byId: chatsById } = global.chats;
|
const { byId: chatsById } = global.chats;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
|
|
||||||
@ -463,6 +468,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
notifySettings: selectNotifySettings(global),
|
notifySettings: selectNotifySettings(global),
|
||||||
notifyExceptions: selectNotifyExceptions(global),
|
notifyExceptions: selectNotifyExceptions(global),
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
};
|
};
|
||||||
|
|
||||||
const messagesById = selectChatMessages(global, chatId);
|
const messagesById = selectChatMessages(global, chatId);
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { pick } from '../../util/iteratees';
|
|||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InfiniteScroll from '../ui/InfiniteScroll';
|
import InfiniteScroll from '../ui/InfiniteScroll';
|
||||||
import GifButton from '../common/GifButton';
|
import GifButton from '../common/GifButton';
|
||||||
@ -25,6 +26,11 @@ import Loading from '../ui/Loading';
|
|||||||
|
|
||||||
import './GifSearch.scss';
|
import './GifSearch.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
query?: string;
|
query?: string;
|
||||||
results?: ApiVideo[];
|
results?: ApiVideo[];
|
||||||
@ -37,7 +43,9 @@ type DispatchProps = Pick<GlobalActions, 'searchMoreGifs' | 'sendMessage' | 'set
|
|||||||
const PRELOAD_BACKWARDS = 96; // GIF Search bot results are multiplied by 24
|
const PRELOAD_BACKWARDS = 96; // GIF Search bot results are multiplied by 24
|
||||||
const INTERSECTION_DEBOUNCE = 300;
|
const INTERSECTION_DEBOUNCE = 300;
|
||||||
|
|
||||||
const GifSearch: FC<StateProps & DispatchProps> = ({
|
const GifSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
query,
|
query,
|
||||||
results,
|
results,
|
||||||
chat,
|
chat,
|
||||||
@ -67,6 +75,8 @@ const GifSearch: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
if (query === undefined) {
|
if (query === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -6,24 +6,34 @@ import { selectChat, selectChatMessage } from '../../modules/selectors';
|
|||||||
import { buildCollectionByKey } from '../../util/iteratees';
|
import { buildCollectionByKey } from '../../util/iteratees';
|
||||||
import { getMessagePoll } from '../../modules/helpers';
|
import { getMessagePoll } from '../../modules/helpers';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PollAnswerResults from './PollAnswerResults';
|
import PollAnswerResults from './PollAnswerResults';
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
|
|
||||||
import './PollResults.scss';
|
import './PollResults.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
chat?: ApiChat;
|
chat?: ApiChat;
|
||||||
message?: ApiMessage;
|
message?: ApiMessage;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PollResults: FC<StateProps> = ({
|
const PollResults: FC<OwnProps & StateProps> = ({
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
chat,
|
chat,
|
||||||
message,
|
message,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
if (!message || !chat) {
|
if (!message || !chat) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps';
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import RightHeader from './RightHeader';
|
import RightHeader from './RightHeader';
|
||||||
import Profile from './Profile';
|
import Profile from './Profile';
|
||||||
@ -35,6 +36,7 @@ type StateProps = {
|
|||||||
threadId?: number;
|
threadId?: number;
|
||||||
currentProfileUserId?: number;
|
currentProfileUserId?: number;
|
||||||
isChatSelected: boolean;
|
isChatSelected: boolean;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, (
|
type DispatchProps = Pick<GlobalActions, (
|
||||||
@ -67,6 +69,7 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
setStickerSearchQuery,
|
setStickerSearchQuery,
|
||||||
setGifSearchQuery,
|
setGifSearchQuery,
|
||||||
closePollResults,
|
closePollResults,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
}) => {
|
}) => {
|
||||||
const { width: windowWidth } = useWindowSize();
|
const { width: windowWidth } = useWindowSize();
|
||||||
const [profileState, setProfileState] = useState<ProfileState>(ProfileState.Profile);
|
const [profileState, setProfileState] = useState<ProfileState>(ProfileState.Profile);
|
||||||
@ -88,21 +91,21 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
|
const renderingContentKey = useCurrentOrPrev(contentKey, true, !isChatSelected) ?? -1;
|
||||||
|
|
||||||
const close = useCallback(() => {
|
const close = useCallback((shouldScrollUp = true) => {
|
||||||
switch (contentKey) {
|
switch (contentKey) {
|
||||||
case RightColumnContent.ChatInfo:
|
case RightColumnContent.ChatInfo:
|
||||||
if (isScrolledDown) {
|
if (isScrolledDown && shouldScrollUp) {
|
||||||
setProfileState(ProfileState.Profile);
|
setProfileState(ProfileState.Profile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
toggleChatInfo();
|
toggleChatInfo(undefined, true);
|
||||||
break;
|
break;
|
||||||
case RightColumnContent.UserInfo:
|
case RightColumnContent.UserInfo:
|
||||||
if (isScrolledDown) {
|
if (isScrolledDown && shouldScrollUp) {
|
||||||
setProfileState(ProfileState.Profile);
|
setProfileState(ProfileState.Profile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
openUserInfo({ id: undefined });
|
openUserInfo({ id: undefined }, true);
|
||||||
break;
|
break;
|
||||||
case RightColumnContent.Management: {
|
case RightColumnContent.Management: {
|
||||||
switch (managementScreen) {
|
switch (managementScreen) {
|
||||||
@ -139,9 +142,11 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RightColumnContent.StickerSearch:
|
case RightColumnContent.StickerSearch:
|
||||||
case RightColumnContent.GifSearch: {
|
|
||||||
blurSearchInput();
|
blurSearchInput();
|
||||||
setStickerSearchQuery({ query: undefined });
|
setStickerSearchQuery({ query: undefined });
|
||||||
|
break;
|
||||||
|
case RightColumnContent.GifSearch: {
|
||||||
|
blurSearchInput();
|
||||||
setGifSearchQuery({ query: undefined });
|
setGifSearchQuery({ query: undefined });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -187,8 +192,13 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
}
|
}
|
||||||
}, [contentKey, chatId]);
|
}, [contentKey, chatId]);
|
||||||
|
|
||||||
|
|
||||||
|
useHistoryBack(isChatSelected && (contentKey === RightColumnContent.ChatInfo
|
||||||
|
|| contentKey === RightColumnContent.UserInfo || contentKey === RightColumnContent.Management),
|
||||||
|
() => close(false), toggleChatInfo);
|
||||||
|
|
||||||
// eslint-disable-next-line consistent-return
|
// eslint-disable-next-line consistent-return
|
||||||
function renderContent() {
|
function renderContent(isActive: boolean) {
|
||||||
if (renderingContentKey === -1) {
|
if (renderingContentKey === -1) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@ -206,7 +216,7 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case RightColumnContent.Search:
|
case RightColumnContent.Search:
|
||||||
return <RightSearch chatId={chatId!} threadId={threadId!} />;
|
return <RightSearch chatId={chatId!} threadId={threadId!} onClose={close} isActive={isOpen && isActive} />;
|
||||||
case RightColumnContent.Management:
|
case RightColumnContent.Management:
|
||||||
return (
|
return (
|
||||||
<Management
|
<Management
|
||||||
@ -216,14 +226,17 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
selectedChatMemberId={selectedChatMemberId}
|
selectedChatMemberId={selectedChatMemberId}
|
||||||
onScreenSelect={setManagementScreen}
|
onScreenSelect={setManagementScreen}
|
||||||
onChatMemberSelect={handleSelectChatMember}
|
onChatMemberSelect={handleSelectChatMember}
|
||||||
|
isActive={isOpen && isActive}
|
||||||
|
onClose={close}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case RightColumnContent.StickerSearch:
|
case RightColumnContent.StickerSearch:
|
||||||
return <StickerSearch />;
|
return <StickerSearch onClose={close} isActive={isOpen && isActive} />;
|
||||||
case RightColumnContent.GifSearch:
|
case RightColumnContent.GifSearch:
|
||||||
return <GifSearch />;
|
return <GifSearch onClose={close} isActive={isOpen && isActive} />;
|
||||||
case RightColumnContent.PollResults:
|
case RightColumnContent.PollResults:
|
||||||
return <PollResults />;
|
return <PollResults onClose={close} isActive={isOpen && isActive} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +261,10 @@ const RightColumn: FC<StateProps & DispatchProps> = ({
|
|||||||
profileState={profileState}
|
profileState={profileState}
|
||||||
managementScreen={managementScreen}
|
managementScreen={managementScreen}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
|
shouldSkipAnimation={shouldSkipTransition || shouldSkipHistoryAnimations}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
name={shouldSkipTransition ? 'none' : 'zoom-fade'}
|
name={(shouldSkipTransition || shouldSkipHistoryAnimations) ? 'none' : 'zoom-fade'}
|
||||||
renderCount={MAIN_SCREENS_COUNT + MANAGEMENT_SCREENS_COUNT}
|
renderCount={MAIN_SCREENS_COUNT + MANAGEMENT_SCREENS_COUNT}
|
||||||
activeKey={isManagement ? MAIN_SCREENS_COUNT + managementScreen : renderingContentKey}
|
activeKey={isManagement ? MAIN_SCREENS_COUNT + managementScreen : renderingContentKey}
|
||||||
shouldCleanup
|
shouldCleanup
|
||||||
@ -274,6 +288,7 @@ export default memo(withGlobal(
|
|||||||
threadId,
|
threadId,
|
||||||
currentProfileUserId: global.users.selectedId,
|
currentProfileUserId: global.users.selectedId,
|
||||||
isChatSelected: Boolean(chatId && areActiveChatsLoaded),
|
isChatSelected: Boolean(chatId && areActiveChatsLoaded),
|
||||||
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(setGlobal, actions): DispatchProps => pick(actions, [
|
(setGlobal, actions): DispatchProps => pick(actions, [
|
||||||
|
|||||||
@ -36,6 +36,7 @@ type OwnProps = {
|
|||||||
isStickerSearch?: boolean;
|
isStickerSearch?: boolean;
|
||||||
isGifSearch?: boolean;
|
isGifSearch?: boolean;
|
||||||
isPollResults?: boolean;
|
isPollResults?: boolean;
|
||||||
|
shouldSkipAnimation?: boolean;
|
||||||
profileState?: ProfileState;
|
profileState?: ProfileState;
|
||||||
managementScreen?: ManagementScreens;
|
managementScreen?: ManagementScreens;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -102,6 +103,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
searchTextMessagesLocal,
|
searchTextMessagesLocal,
|
||||||
toggleManagement,
|
toggleManagement,
|
||||||
openHistoryCalendar,
|
openHistoryCalendar,
|
||||||
|
shouldSkipAnimation,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const backButtonRef = useRef<HTMLDivElement>(null);
|
const backButtonRef = useRef<HTMLDivElement>(null);
|
||||||
@ -278,7 +280,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|
|
||||||
const buttonClassName = buildClassName(
|
const buttonClassName = buildClassName(
|
||||||
'animated-close-icon',
|
'animated-close-icon',
|
||||||
shouldSkipTransition && 'no-transition',
|
(shouldSkipTransition || shouldSkipAnimation) && 'no-transition',
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add class in the next AF to synchronize with animation with Transition components
|
// Add class in the next AF to synchronize with animation with Transition components
|
||||||
@ -299,7 +301,7 @@ const RightHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
<div ref={backButtonRef} className={buttonClassName} />
|
<div ref={backButtonRef} className={buttonClassName} />
|
||||||
</Button>
|
</Button>
|
||||||
<Transition
|
<Transition
|
||||||
name={shouldSkipTransition ? 'none' : 'slide-fade'}
|
name={(shouldSkipTransition || shouldSkipAnimation) ? 'none' : 'slide-fade'}
|
||||||
activeKey={renderingContentKey}
|
activeKey={renderingContentKey}
|
||||||
>
|
>
|
||||||
{renderHeaderContent}
|
{renderHeaderContent}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import useLang from '../../hooks/useLang';
|
|||||||
import { orderBy, pick } from '../../util/iteratees';
|
import { orderBy, pick } from '../../util/iteratees';
|
||||||
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||||
import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation';
|
import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InfiniteScroll from '../ui/InfiniteScroll';
|
import InfiniteScroll from '../ui/InfiniteScroll';
|
||||||
import ListItem from '../ui/ListItem';
|
import ListItem from '../ui/ListItem';
|
||||||
@ -34,6 +35,8 @@ import './RightSearch.scss';
|
|||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
threadId: number;
|
threadId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -56,6 +59,8 @@ interface Result {
|
|||||||
const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
chatId,
|
chatId,
|
||||||
threadId,
|
threadId,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
chat,
|
chat,
|
||||||
messagesById,
|
messagesById,
|
||||||
query,
|
query,
|
||||||
@ -125,6 +130,8 @@ const RightSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
// 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);
|
||||||
const handleKeyDown = useKeyboardListNavigation(containerRef, true, (index) => {
|
const handleKeyDown = useKeyboardListNavigation(containerRef, true, (index) => {
|
||||||
|
|||||||
@ -10,12 +10,18 @@ import { throttle } from '../../util/schedulers';
|
|||||||
import { selectCurrentStickerSearch } from '../../modules/selectors';
|
import { selectCurrentStickerSearch } from '../../modules/selectors';
|
||||||
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Loading from '../ui/Loading';
|
import Loading from '../ui/Loading';
|
||||||
import StickerSetResult from './StickerSetResult';
|
import StickerSetResult from './StickerSetResult';
|
||||||
|
|
||||||
import './StickerSearch.scss';
|
import './StickerSearch.scss';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
query?: string;
|
query?: string;
|
||||||
featuredIds?: string[];
|
featuredIds?: string[];
|
||||||
@ -28,7 +34,9 @@ const INTERSECTION_THROTTLE = 200;
|
|||||||
|
|
||||||
const runThrottled = throttle((cb) => cb(), 60000, true);
|
const runThrottled = throttle((cb) => cb(), 60000, true);
|
||||||
|
|
||||||
const StickerSearch: FC<StateProps & DispatchProps> = ({
|
const StickerSearch: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
query,
|
query,
|
||||||
featuredIds,
|
featuredIds,
|
||||||
resultIds,
|
resultIds,
|
||||||
@ -53,6 +61,8 @@ const StickerSearch: FC<StateProps & DispatchProps> = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
function renderContent() {
|
function renderContent() {
|
||||||
if (query === undefined) {
|
if (query === undefined) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -22,12 +22,15 @@ import Spinner from '../../ui/Spinner';
|
|||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import './Management.scss';
|
import './Management.scss';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -56,6 +59,8 @@ const ManageChannel: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
leaveChannel,
|
leaveChannel,
|
||||||
deleteChannel,
|
deleteChannel,
|
||||||
openChat,
|
openChat,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const currentTitle = chat ? (chat.title || '') : '';
|
const currentTitle = chat ? (chat.title || '') : '';
|
||||||
const currentAbout = chat && chat.fullInfo ? (chat.fullInfo.about || '') : '';
|
const currentAbout = chat && chat.fullInfo ? (chat.fullInfo.about || '') : '';
|
||||||
@ -71,6 +76,8 @@ const ManageChannel: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (progress === ManagementProgress.Complete) {
|
if (progress === ManagementProgress.Complete) {
|
||||||
setIsProfileFieldsTouched(false);
|
setIsProfileFieldsTouched(false);
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { getUserFullName, isChatChannel } from '../../../modules/helpers';
|
|||||||
|
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
@ -17,6 +18,8 @@ type OwnProps = {
|
|||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -33,9 +36,13 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
usersById,
|
usersById,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
onChatMemberSelect,
|
onChatMemberSelect,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
function handleRecentActionsClick() {
|
function handleRecentActionsClick() {
|
||||||
onScreenSelect(ManagementScreens.GroupRecentActions);
|
onScreenSelect(ManagementScreens.GroupRecentActions);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import { isChatChannel } from '../../../modules/helpers';
|
import { isChatChannel } from '../../../modules/helpers';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import SafeLink from '../../common/SafeLink';
|
import SafeLink from '../../common/SafeLink';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -26,6 +27,8 @@ type PrivacyType = 'private' | 'public';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -41,6 +44,8 @@ type DispatchProps = Pick<GlobalActions, (
|
|||||||
|
|
||||||
const ManageChatPrivacyType: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ManageChatPrivacyType: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
chat,
|
chat,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
isChannel,
|
isChannel,
|
||||||
progress,
|
progress,
|
||||||
isUsernameAvailable,
|
isUsernameAvailable,
|
||||||
@ -60,6 +65,8 @@ const ManageChatPrivacyType: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
|| (privacyType === 'private' && isPublic)
|
|| (privacyType === 'private' && isPublic)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (privacyType && !privateLink) {
|
if (privacyType && !privateLink) {
|
||||||
updatePrivateLink();
|
updatePrivateLink();
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { selectChat } from '../../../modules/selectors';
|
|||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import getAnimationData from '../../common/helpers/animatedAssets';
|
import getAnimationData from '../../common/helpers/animatedAssets';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
@ -26,6 +27,8 @@ import { isChatChannel } from '../../../modules/helpers';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -40,6 +43,8 @@ type DispatchProps = Pick<GlobalActions, 'loadGroupsForDiscussion' | 'linkDiscus
|
|||||||
|
|
||||||
const ManageDiscussion: FC<OwnProps & StateProps & DispatchProps> = ({
|
const ManageDiscussion: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||||
chat,
|
chat,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
chatId,
|
chatId,
|
||||||
chatsByIds,
|
chatsByIds,
|
||||||
linkedChat,
|
linkedChat,
|
||||||
@ -59,6 +64,8 @@ const ManageDiscussion: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const linkedChatId = linkedChat && linkedChat.id;
|
const linkedChatId = linkedChat && linkedChat.id;
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadGroupsForDiscussion();
|
loadGroupsForDiscussion();
|
||||||
}, [loadGroupsForDiscussion]);
|
}, [loadGroupsForDiscussion]);
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { selectChat } from '../../../modules/selectors';
|
|||||||
import { formatInteger } from '../../../util/textFormat';
|
import { formatInteger } from '../../../util/textFormat';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import renderText from '../../common/helpers/renderText';
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import AvatarEditable from '../../ui/AvatarEditable';
|
import AvatarEditable from '../../ui/AvatarEditable';
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
@ -30,6 +31,8 @@ import './Management.scss';
|
|||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -68,6 +71,8 @@ const ManageGroup: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
deleteChannel,
|
deleteChannel,
|
||||||
closeManagement,
|
closeManagement,
|
||||||
openChat,
|
openChat,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
||||||
const currentTitle = chat.title;
|
const currentTitle = chat.title;
|
||||||
@ -82,6 +87,8 @@ const ManageGroup: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
const currentAvatarBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (progress === ManagementProgress.Complete) {
|
if (progress === ManagementProgress.Complete) {
|
||||||
setIsProfileFieldsTouched(false);
|
setIsProfileFieldsTouched(false);
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { selectChat } from '../../../modules/selectors';
|
|||||||
import { getUserFullName, isChatBasicGroup, isChatChannel } from '../../../modules/helpers';
|
import { getUserFullName, isChatBasicGroup, isChatChannel } from '../../../modules/helpers';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -26,6 +27,8 @@ type OwnProps = {
|
|||||||
selectedChatMemberId?: number;
|
selectedChatMemberId?: number;
|
||||||
isPromotedByCurrentUser?: boolean;
|
isPromotedByCurrentUser?: boolean;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -49,6 +52,8 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
isChannel,
|
isChannel,
|
||||||
isFormFullyDisabled,
|
isFormFullyDisabled,
|
||||||
updateChatAdmin,
|
updateChatAdmin,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [permissions, setPermissions] = useState<ApiChatAdminRights>({});
|
const [permissions, setPermissions] = useState<ApiChatAdminRights>({});
|
||||||
const [isTouched, setIsTouched] = useState(false);
|
const [isTouched, setIsTouched] = useState(false);
|
||||||
@ -57,6 +62,8 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const [customTitle, setCustomTitle] = useState('');
|
const [customTitle, setCustomTitle] = useState('');
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember = useMemo(() => {
|
||||||
if (!chat.fullInfo || !chat.fullInfo.adminMembers) {
|
if (!chat.fullInfo || !chat.fullInfo.adminMembers) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import NothingFound from '../../common/NothingFound';
|
import NothingFound from '../../common/NothingFound';
|
||||||
@ -15,6 +16,8 @@ import ListItem from '../../ui/ListItem';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -31,6 +34,8 @@ const ManageGroupMembers: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
usersById,
|
usersById,
|
||||||
isChannel,
|
isChannel,
|
||||||
openUserInfo,
|
openUserInfo,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
}) => {
|
}) => {
|
||||||
const memberIds = useMemo(() => {
|
const memberIds = useMemo(() => {
|
||||||
@ -45,6 +50,8 @@ const ManageGroupMembers: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
openUserInfo({ id });
|
openUserInfo({ id });
|
||||||
}, [openUserInfo]);
|
}, [openUserInfo]);
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Management">
|
<div className="Management">
|
||||||
<div className="custom-scroll">
|
<div className="custom-scroll">
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { GlobalActions } from '../../../global/types';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
@ -21,6 +22,8 @@ type OwnProps = {
|
|||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -61,12 +64,16 @@ const ManageGroupPermissions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
chat,
|
chat,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
updateChatDefaultBannedRights,
|
updateChatDefaultBannedRights,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [permissions, setPermissions] = useState<ApiChatBannedRights>({});
|
const [permissions, setPermissions] = useState<ApiChatBannedRights>({});
|
||||||
const [havePermissionChanged, setHavePermissionChanged] = useState(false);
|
const [havePermissionChanged, setHavePermissionChanged] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const handleRemovedUsersClick = useCallback(() => {
|
const handleRemovedUsersClick = useCallback(() => {
|
||||||
onScreenSelect(ManagementScreens.GroupRemovedUsers);
|
onScreenSelect(ManagementScreens.GroupRemovedUsers);
|
||||||
}, [onScreenSelect]);
|
}, [onScreenSelect]);
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { withGlobal } from '../../../lib/teact/teactn';
|
|||||||
import { ApiChat, ApiChatMember } from '../../../api/types';
|
import { ApiChat, ApiChatMember } from '../../../api/types';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import Checkbox from '../../ui/Checkbox';
|
import Checkbox from '../../ui/Checkbox';
|
||||||
@ -13,15 +14,19 @@ import PrivateChatInfo from '../../common/PrivateChatInfo';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
chat?: ApiChat;
|
chat?: ApiChat;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat }) => {
|
const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat, onClose, isActive }) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const adminMembers = useMemo(() => {
|
const adminMembers = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.adminMembers) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.adminMembers) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@ -10,12 +10,15 @@ import { selectChat } from '../../../modules/selectors';
|
|||||||
import { getUserFullName } from '../../../modules/helpers';
|
import { getUserFullName } from '../../../modules/helpers';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { pick } from '../../../util/iteratees';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: number;
|
chatId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -29,9 +32,13 @@ const ManageGroupRemovedUsers: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
chat,
|
chat,
|
||||||
usersById,
|
usersById,
|
||||||
updateChatMemberBannedRights,
|
updateChatMemberBannedRights,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const removedMembers = useMemo(() => {
|
const removedMembers = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.kickedMembers) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.kickedMembers) {
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { pick } from '../../../util/iteratees';
|
|||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -24,6 +25,8 @@ type OwnProps = {
|
|||||||
selectedChatMemberId?: number;
|
selectedChatMemberId?: number;
|
||||||
isPromotedByCurrentUser?: boolean;
|
isPromotedByCurrentUser?: boolean;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -39,6 +42,8 @@ const ManageGroupUserPermissions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
updateChatMemberBannedRights,
|
updateChatMemberBannedRights,
|
||||||
isFormFullyDisabled,
|
isFormFullyDisabled,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [permissions, setPermissions] = useState<ApiChatBannedRights>({});
|
const [permissions, setPermissions] = useState<ApiChatBannedRights>({});
|
||||||
const [havePermissionChanged, setHavePermissionChanged] = useState(false);
|
const [havePermissionChanged, setHavePermissionChanged] = useState(false);
|
||||||
@ -46,6 +51,8 @@ const ManageGroupUserPermissions: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
const [isBanConfirmationDialogOpen, openBanConfirmationDialog, closeBanConfirmationDialog] = useFlag();
|
const [isBanConfirmationDialogOpen, openBanConfirmationDialog, closeBanConfirmationDialog] = useFlag();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.members) {
|
if (!chat || !chat.fullInfo || !chat.fullInfo.members) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { ManagementScreens } from '../../../types';
|
|||||||
|
|
||||||
import { selectChat } from '../../../modules/selectors';
|
import { selectChat } from '../../../modules/selectors';
|
||||||
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
import { sortUserIds, isChatChannel } from '../../../modules/helpers';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -17,6 +18,8 @@ type OwnProps = {
|
|||||||
chatId: number;
|
chatId: number;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
onChatMemberSelect: (memberId: number) => void;
|
onChatMemberSelect: (memberId: number) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -32,8 +35,12 @@ const ManageGroupUserPermissionsCreate: FC<OwnProps & StateProps> = ({
|
|||||||
isChannel,
|
isChannel,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
onChatMemberSelect,
|
onChatMemberSelect,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
}) => {
|
}) => {
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const memberIds = useMemo(() => {
|
const memberIds = useMemo(() => {
|
||||||
if (!members || !usersById) {
|
if (!members || !usersById) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import {
|
|||||||
import { selectIsChatMuted } from '../../../modules/helpers';
|
import { selectIsChatMuted } from '../../../modules/helpers';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import InputText from '../../ui/InputText';
|
import InputText from '../../ui/InputText';
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
@ -28,6 +29,8 @@ import './Management.scss';
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -54,12 +57,16 @@ const ManageUser: FC<OwnProps & StateProps & DispatchProps> = ({
|
|||||||
deleteHistory,
|
deleteHistory,
|
||||||
closeManagement,
|
closeManagement,
|
||||||
openChat,
|
openChat,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
const [isDeleteDialogOpen, openDeleteDialog, closeDeleteDialog] = useFlag();
|
||||||
const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false);
|
const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false);
|
||||||
const [error, setError] = useState<string | undefined>();
|
const [error, setError] = useState<string | undefined>();
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
|
useHistoryBack(isActive, onClose);
|
||||||
|
|
||||||
const currentFirstName = user ? (user.firstName || '') : '';
|
const currentFirstName = user ? (user.firstName || '') : '';
|
||||||
const currentLastName = user ? (user.lastName || '') : '';
|
const currentLastName = user ? (user.lastName || '') : '';
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,8 @@ export type OwnProps = {
|
|||||||
isPromotedByCurrentUser?: boolean;
|
isPromotedByCurrentUser?: boolean;
|
||||||
onScreenSelect: (screen: ManagementScreens) => void;
|
onScreenSelect: (screen: ManagementScreens) => void;
|
||||||
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
onChatMemberSelect: (memberId: number, isPromotedByCurrentUser?: boolean) => void;
|
||||||
|
onClose: NoneToVoidFunction;
|
||||||
|
isActive: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
@ -39,17 +41,59 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
isPromotedByCurrentUser,
|
isPromotedByCurrentUser,
|
||||||
onScreenSelect,
|
onScreenSelect,
|
||||||
onChatMemberSelect,
|
onChatMemberSelect,
|
||||||
|
onClose,
|
||||||
|
isActive,
|
||||||
managementType,
|
managementType,
|
||||||
}) => {
|
}) => {
|
||||||
switch (currentScreen) {
|
switch (currentScreen) {
|
||||||
case ManagementScreens.Initial: {
|
case ManagementScreens.Initial: {
|
||||||
switch (managementType) {
|
switch (managementType) {
|
||||||
case 'user':
|
case 'user':
|
||||||
return <ManageUser key={chatId} userId={chatId} />;
|
return (
|
||||||
|
<ManageUser
|
||||||
|
key={chatId}
|
||||||
|
userId={chatId}
|
||||||
|
onClose={onClose}
|
||||||
|
isActive={isActive}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 'group':
|
case 'group':
|
||||||
return <ManageGroup key={chatId} chatId={chatId} onScreenSelect={onScreenSelect} />;
|
return (
|
||||||
|
<ManageGroup
|
||||||
|
key={chatId}
|
||||||
|
chatId={chatId}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
onClose={onClose}
|
||||||
|
isActive={isActive || [
|
||||||
|
ManagementScreens.ChatPrivacyType,
|
||||||
|
ManagementScreens.Discussion,
|
||||||
|
ManagementScreens.GroupPermissions,
|
||||||
|
ManagementScreens.ChatAdministrators,
|
||||||
|
ManagementScreens.GroupRemovedUsers,
|
||||||
|
ManagementScreens.GroupUserPermissionsCreate,
|
||||||
|
ManagementScreens.GroupUserPermissions,
|
||||||
|
ManagementScreens.ChatAdminRights,
|
||||||
|
ManagementScreens.GroupRecentActions,
|
||||||
|
].includes(currentScreen)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
case 'channel':
|
case 'channel':
|
||||||
return <ManageChannel key={chatId} chatId={chatId} onScreenSelect={onScreenSelect} />;
|
return (
|
||||||
|
<ManageChannel
|
||||||
|
key={chatId}
|
||||||
|
chatId={chatId}
|
||||||
|
onScreenSelect={onScreenSelect}
|
||||||
|
onClose={onClose}
|
||||||
|
isActive={isActive || [
|
||||||
|
ManagementScreens.ChannelSubscribers,
|
||||||
|
ManagementScreens.ChatAdministrators,
|
||||||
|
ManagementScreens.Discussion,
|
||||||
|
ManagementScreens.ChatPrivacyType,
|
||||||
|
ManagementScreens.ChatAdminRights,
|
||||||
|
ManagementScreens.GroupRecentActions,
|
||||||
|
].includes(currentScreen)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -57,7 +101,11 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
case ManagementScreens.ChatPrivacyType:
|
case ManagementScreens.ChatPrivacyType:
|
||||||
return (
|
return (
|
||||||
<ManageChatPrivacyType chatId={chatId} />
|
<ManageChatPrivacyType
|
||||||
|
chatId={chatId}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case ManagementScreens.Discussion:
|
case ManagementScreens.Discussion:
|
||||||
@ -65,6 +113,8 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
<ManageDiscussion
|
<ManageDiscussion
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -74,12 +124,22 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
onChatMemberSelect={onChatMemberSelect}
|
onChatMemberSelect={onChatMemberSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
ManagementScreens.GroupRemovedUsers,
|
||||||
|
ManagementScreens.GroupUserPermissionsCreate,
|
||||||
|
ManagementScreens.GroupUserPermissions,
|
||||||
|
].includes(currentScreen)}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case ManagementScreens.GroupRemovedUsers:
|
case ManagementScreens.GroupRemovedUsers:
|
||||||
return (
|
return (
|
||||||
<ManageGroupRemovedUsers chatId={chatId} />
|
<ManageGroupRemovedUsers
|
||||||
|
chatId={chatId}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case ManagementScreens.GroupUserPermissionsCreate:
|
case ManagementScreens.GroupUserPermissionsCreate:
|
||||||
@ -88,6 +148,10 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
onChatMemberSelect={onChatMemberSelect}
|
onChatMemberSelect={onChatMemberSelect}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
ManagementScreens.GroupUserPermissions,
|
||||||
|
].includes(currentScreen)}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -98,6 +162,8 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
selectedChatMemberId={selectedChatMemberId}
|
selectedChatMemberId={selectedChatMemberId}
|
||||||
isPromotedByCurrentUser={isPromotedByCurrentUser}
|
isPromotedByCurrentUser={isPromotedByCurrentUser}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -107,6 +173,11 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
onChatMemberSelect={onChatMemberSelect}
|
onChatMemberSelect={onChatMemberSelect}
|
||||||
|
isActive={isActive || [
|
||||||
|
ManagementScreens.ChatAdminRights,
|
||||||
|
ManagementScreens.GroupRecentActions,
|
||||||
|
].includes(currentScreen)}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -114,6 +185,8 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
return (
|
return (
|
||||||
<ManageGroupRecentActions
|
<ManageGroupRecentActions
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -124,13 +197,19 @@ const Management: FC<OwnProps & StateProps> = ({
|
|||||||
selectedChatMemberId={selectedChatMemberId}
|
selectedChatMemberId={selectedChatMemberId}
|
||||||
isPromotedByCurrentUser={isPromotedByCurrentUser}
|
isPromotedByCurrentUser={isPromotedByCurrentUser}
|
||||||
onScreenSelect={onScreenSelect}
|
onScreenSelect={onScreenSelect}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
case ManagementScreens.ChannelSubscribers:
|
case ManagementScreens.ChannelSubscribers:
|
||||||
case ManagementScreens.GroupMembers:
|
case ManagementScreens.GroupMembers:
|
||||||
return (
|
return (
|
||||||
<ManageGroupMembers chatId={chatId} />
|
<ManageGroupMembers
|
||||||
|
chatId={chatId}
|
||||||
|
isActive={isActive}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,9 @@ type OwnProps = {
|
|||||||
positionX?: 'left' | 'right';
|
positionX?: 'left' | 'right';
|
||||||
positionY?: 'top' | 'bottom';
|
positionY?: 'top' | 'bottom';
|
||||||
footer?: string;
|
footer?: string;
|
||||||
|
forceOpen?: boolean;
|
||||||
|
onOpen?: NoneToVoidFunction;
|
||||||
|
onClose?: NoneToVoidFunction;
|
||||||
children: any;
|
children: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,6 +23,9 @@ const DropdownMenu: FC<OwnProps> = ({
|
|||||||
positionX = 'left',
|
positionX = 'left',
|
||||||
positionY = 'top',
|
positionY = 'top',
|
||||||
footer,
|
footer,
|
||||||
|
forceOpen,
|
||||||
|
onOpen,
|
||||||
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
@ -29,6 +35,9 @@ const DropdownMenu: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const toggleIsOpen = () => {
|
const toggleIsOpen = () => {
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
|
if (isOpen) {
|
||||||
|
if (onClose) onClose();
|
||||||
|
} else if (onOpen) onOpen();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<any>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<any>) => {
|
||||||
@ -48,6 +57,7 @@ const DropdownMenu: FC<OwnProps> = ({
|
|||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
if (onClose) onClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -61,13 +71,14 @@ const DropdownMenu: FC<OwnProps> = ({
|
|||||||
<Menu
|
<Menu
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
containerRef={dropdownRef}
|
containerRef={dropdownRef}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen || !!forceOpen}
|
||||||
className={className || ''}
|
className={className || ''}
|
||||||
positionX={positionX}
|
positionX={positionX}
|
||||||
positionY={positionY}
|
positionY={positionY}
|
||||||
footer={footer}
|
footer={footer}
|
||||||
autoClose
|
autoClose
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
|
shouldSkipTransition={forceOpen}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
|||||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import './Menu.scss';
|
import './Menu.scss';
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ type OwnProps = {
|
|||||||
positionX?: 'left' | 'right';
|
positionX?: 'left' | 'right';
|
||||||
positionY?: 'top' | 'bottom';
|
positionY?: 'top' | 'bottom';
|
||||||
autoClose?: boolean;
|
autoClose?: boolean;
|
||||||
|
shouldSkipTransition?: boolean;
|
||||||
footer?: string;
|
footer?: string;
|
||||||
noCloseOnBackdrop?: boolean;
|
noCloseOnBackdrop?: boolean;
|
||||||
onKeyDown?: (e: React.KeyboardEvent<any>) => void;
|
onKeyDown?: (e: React.KeyboardEvent<any>) => void;
|
||||||
@ -48,6 +50,7 @@ const Menu: FC<OwnProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
|
shouldSkipTransition,
|
||||||
}) => {
|
}) => {
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
let menuRef = useRef<HTMLDivElement>(null);
|
let menuRef = useRef<HTMLDivElement>(null);
|
||||||
@ -56,9 +59,22 @@ const Menu: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
const backdropContainerRef = containerRef || menuRef;
|
const backdropContainerRef = containerRef || menuRef;
|
||||||
|
|
||||||
const { transitionClassNames } = useShowTransition(isOpen, onCloseAnimationEnd);
|
const {
|
||||||
|
transitionClassNames,
|
||||||
|
} = useShowTransition(
|
||||||
|
isOpen,
|
||||||
|
onCloseAnimationEnd,
|
||||||
|
shouldSkipTransition,
|
||||||
|
undefined,
|
||||||
|
shouldSkipTransition,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => (isOpen && onClose ? captureEscKeyListener(onClose) : undefined), [isOpen, onClose]);
|
useEffect(
|
||||||
|
() => (isOpen && onClose ? captureEscKeyListener(onClose) : undefined),
|
||||||
|
[isOpen, onClose],
|
||||||
|
);
|
||||||
|
|
||||||
|
useHistoryBack(isOpen, onClose, undefined, undefined, autoClose);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsOpen]) => {
|
useEffectWithPrevDeps(([prevIsOpen]) => {
|
||||||
if (prevIsOpen !== undefined) {
|
if (prevIsOpen !== undefined) {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import React, { FC, useEffect, useRef } from '../../lib/teact/teact';
|
import React, {
|
||||||
|
FC, useEffect, useRef,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
|
||||||
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
||||||
import trapFocus from '../../util/trapFocus';
|
import trapFocus from '../../util/trapFocus';
|
||||||
@ -7,6 +9,7 @@ import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'
|
|||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
|
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Portal from './Portal';
|
import Portal from './Portal';
|
||||||
@ -28,20 +31,29 @@ type OwnProps = {
|
|||||||
onEnter?: () => void;
|
onEnter?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Modal: FC<OwnProps> = (props) => {
|
type StateProps = {
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Modal: FC<OwnProps & StateProps> = ({
|
||||||
|
title,
|
||||||
|
className,
|
||||||
|
isOpen,
|
||||||
|
header,
|
||||||
|
hasCloseButton,
|
||||||
|
noBackdrop,
|
||||||
|
children,
|
||||||
|
onClose,
|
||||||
|
onCloseAnimationEnd,
|
||||||
|
onEnter,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
|
}) => {
|
||||||
const {
|
const {
|
||||||
title,
|
shouldRender,
|
||||||
className,
|
transitionClassNames,
|
||||||
isOpen,
|
} = useShowTransition(
|
||||||
header,
|
isOpen, onCloseAnimationEnd, shouldSkipHistoryAnimations, undefined, shouldSkipHistoryAnimations,
|
||||||
hasCloseButton,
|
);
|
||||||
noBackdrop,
|
|
||||||
children,
|
|
||||||
onClose,
|
|
||||||
onCloseAnimationEnd,
|
|
||||||
onEnter,
|
|
||||||
} = props;
|
|
||||||
const { shouldRender, transitionClassNames } = useShowTransition(isOpen, onCloseAnimationEnd);
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -50,6 +62,8 @@ const Modal: FC<OwnProps> = (props) => {
|
|||||||
: 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]);
|
||||||
|
|
||||||
|
useHistoryBack(isOpen, onClose);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsOpen]) => {
|
useEffectWithPrevDeps(([prevIsOpen]) => {
|
||||||
document.body.classList.toggle('has-open-dialog', isOpen);
|
document.body.classList.toggle('has-open-dialog', isOpen);
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.skip-slide-transition {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* scroll-slide
|
* scroll-slide
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'
|
|||||||
|
|
||||||
import './Transition.scss';
|
import './Transition.scss';
|
||||||
|
|
||||||
type ChildrenFn = (isActive: boolean, isFrom: boolean) => any;
|
type ChildrenFn = (isActive: boolean, isFrom: boolean, currentKey: number) => any;
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
ref?: RefObject<HTMLDivElement>;
|
ref?: RefObject<HTMLDivElement>;
|
||||||
activeKey: number;
|
activeKey: number;
|
||||||
@ -239,7 +239,9 @@ const Transition: FC<OwnProps> = ({
|
|||||||
const render = renders[key];
|
const render = renders[key];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
typeof render === 'function' ? <div key={key}>{render(key === activeKey, key === prevActiveKey)}</div> : undefined
|
typeof render === 'function'
|
||||||
|
? <div key={key}>{render(key === activeKey, key === prevActiveKey, activeKey)}</div>
|
||||||
|
: undefined
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,10 @@ import { addReducer } from '../lib/teact/teactn';
|
|||||||
import { INITIAL_STATE } from './initial';
|
import { INITIAL_STATE } from './initial';
|
||||||
import { initCache, loadCache } from './cache';
|
import { initCache, loadCache } from './cache';
|
||||||
import { cloneDeep } from '../util/iteratees';
|
import { cloneDeep } from '../util/iteratees';
|
||||||
import { selectCurrentMessageList } from '../modules/selectors';
|
|
||||||
|
|
||||||
initCache();
|
initCache();
|
||||||
|
|
||||||
addReducer('init', () => {
|
addReducer('init', () => {
|
||||||
const initial = cloneDeep(INITIAL_STATE);
|
const initial = cloneDeep(INITIAL_STATE);
|
||||||
const newGlobal = loadCache(initial) || initial;
|
return loadCache(initial) || initial;
|
||||||
|
|
||||||
const currentMessageList = selectCurrentMessageList(newGlobal) || {};
|
|
||||||
window.history.replaceState(currentMessageList, '');
|
|
||||||
|
|
||||||
return newGlobal;
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -65,6 +65,7 @@ export type GlobalState = {
|
|||||||
isLeftColumnShown: boolean;
|
isLeftColumnShown: boolean;
|
||||||
isPollModalOpen?: boolean;
|
isPollModalOpen?: boolean;
|
||||||
uiReadyState: 0 | 1 | 2;
|
uiReadyState: 0 | 1 | 2;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
connectionState?: ApiUpdateConnectionStateType;
|
connectionState?: ApiUpdateConnectionStateType;
|
||||||
currentUserId?: number;
|
currentUserId?: number;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
@ -403,7 +404,8 @@ export type ActionTypes = (
|
|||||||
'showNotification' | 'dismissNotification' | 'showDialog' | 'dismissDialog' |
|
'showNotification' | 'dismissNotification' | 'showDialog' | 'dismissDialog' |
|
||||||
// ui
|
// ui
|
||||||
'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'addRecentSticker' | 'toggleLeftColumn' |
|
'toggleChatInfo' | 'setIsUiReady' | 'addRecentEmoji' | 'addRecentSticker' | 'toggleLeftColumn' |
|
||||||
'toggleSafeLinkModal' | 'openHistoryCalendar' | 'closeHistoryCalendar' | 'disableContextMenuHint' |
|
'toggleSafeLinkModal' | 'disableHistoryAnimations' | 'openHistoryCalendar' | 'closeHistoryCalendar' |
|
||||||
|
'disableContextMenuHint' |
|
||||||
// auth
|
// auth
|
||||||
'setAuthPhoneNumber' | 'setAuthCode' | 'setAuthPassword' | 'signUp' | 'returnToAuthPhoneNumber' | 'signOut' |
|
'setAuthPhoneNumber' | 'setAuthCode' | 'setAuthPassword' | 'signUp' | 'returnToAuthPhoneNumber' | 'signOut' |
|
||||||
'setAuthRememberMe' | 'clearAuthError' | 'uploadProfilePhoto' | 'goToAuthQrCode' | 'clearCache' |
|
'setAuthRememberMe' | 'clearAuthError' | 'uploadProfilePhoto' | 'goToAuthQrCode' | 'clearCache' |
|
||||||
|
|||||||
@ -1,15 +1,137 @@
|
|||||||
// This is unsafe and can be not chained as `popstate` event is asynchronous
|
import { useEffect, useRef } from '../lib/teact/teact';
|
||||||
|
|
||||||
export default function useHistoryBack(handler: NoneToVoidFunction) {
|
import { IS_IOS } from '../util/environment';
|
||||||
function handlePopState() {
|
import usePrevious from './usePrevious';
|
||||||
handler();
|
import { getDispatch } from '../lib/teact/teactn';
|
||||||
|
|
||||||
|
// Carefully selected by swiping and observing visual changes
|
||||||
|
// 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_DURATION = 350;
|
||||||
|
|
||||||
|
let isEdge = false;
|
||||||
|
|
||||||
|
const handleTouchStart = (event: TouchEvent) => {
|
||||||
|
const x = event.touches[0].pageX;
|
||||||
|
|
||||||
|
if (x <= SAFARI_EDGE_BACK_GESTURE_LIMIT || x >= window.innerWidth - SAFARI_EDGE_BACK_GESTURE_LIMIT) {
|
||||||
|
isEdge = true;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener('popstate', handlePopState);
|
const handleTouchEnd = () => {
|
||||||
window.history.pushState({}, '');
|
if (isEdge) {
|
||||||
|
setTimeout(() => {
|
||||||
|
isEdge = false;
|
||||||
|
}, SAFARI_EDGE_BACK_GESTURE_DURATION);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return () => {
|
if (IS_IOS) {
|
||||||
window.removeEventListener('popstate', handlePopState);
|
window.addEventListener('touchstart', handleTouchStart);
|
||||||
window.history.back();
|
window.addEventListener('touchend', handleTouchEnd);
|
||||||
};
|
window.addEventListener('popstate', handleTouchEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentIndex = 0;
|
||||||
|
let nextStateIndexToReplace = -1;
|
||||||
|
let isHistoryAltered = false;
|
||||||
|
const currentIndexes: number[] = [];
|
||||||
|
|
||||||
|
window.history.replaceState({ index: currentIndex }, '');
|
||||||
|
|
||||||
|
export default function useHistoryBack(
|
||||||
|
isActive: boolean | undefined,
|
||||||
|
onBack: ((noDisableAnimation: boolean) => void) | undefined,
|
||||||
|
onForward?: (state: any) => void,
|
||||||
|
currentState?: any,
|
||||||
|
shouldReplaceNext = false,
|
||||||
|
) {
|
||||||
|
const indexRef = useRef(-1);
|
||||||
|
const isForward = useRef(false);
|
||||||
|
const prevIsActive = usePrevious(isActive);
|
||||||
|
const isClosed = useRef(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handlePopState = (event: PopStateEvent) => {
|
||||||
|
if (isHistoryAltered) {
|
||||||
|
setTimeout(() => {
|
||||||
|
isHistoryAltered = false;
|
||||||
|
}, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { index: i } = event.state;
|
||||||
|
const index = i || 0;
|
||||||
|
|
||||||
|
const prev = currentIndexes[currentIndexes.indexOf(indexRef.current) - 1];
|
||||||
|
|
||||||
|
if (!isClosed.current && (index === 0 || index === prev)) {
|
||||||
|
currentIndexes.splice(currentIndexes.indexOf(indexRef.current), 1);
|
||||||
|
|
||||||
|
if (onBack) {
|
||||||
|
if (isEdge) {
|
||||||
|
getDispatch().disableHistoryAnimations();
|
||||||
|
}
|
||||||
|
onBack(!isEdge);
|
||||||
|
isClosed.current = true;
|
||||||
|
}
|
||||||
|
} else if (index === indexRef.current && isClosed.current && onForward) {
|
||||||
|
isForward.current = true;
|
||||||
|
if (isEdge) {
|
||||||
|
getDispatch().disableHistoryAnimations();
|
||||||
|
}
|
||||||
|
onForward(event.state.state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prevIsActive !== isActive) {
|
||||||
|
if (isActive) {
|
||||||
|
isClosed.current = false;
|
||||||
|
|
||||||
|
if (isForward.current) {
|
||||||
|
isForward.current = false;
|
||||||
|
currentIndexes.push(indexRef.current);
|
||||||
|
} else {
|
||||||
|
setTimeout(() => {
|
||||||
|
const index = ++currentIndex;
|
||||||
|
|
||||||
|
currentIndexes.push(index);
|
||||||
|
|
||||||
|
window.history[
|
||||||
|
(currentIndexes.includes(nextStateIndexToReplace - 1)
|
||||||
|
&& window.history.state.index !== 0
|
||||||
|
&& nextStateIndexToReplace === index
|
||||||
|
&& !shouldReplaceNext)
|
||||||
|
? 'replaceState'
|
||||||
|
: 'pushState'
|
||||||
|
]({
|
||||||
|
index,
|
||||||
|
state: currentState,
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
indexRef.current = index;
|
||||||
|
|
||||||
|
if (shouldReplaceNext) {
|
||||||
|
nextStateIndexToReplace = currentIndex + 1;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
} else if (!isClosed.current) {
|
||||||
|
if (indexRef.current === currentIndex || !shouldReplaceNext) {
|
||||||
|
isHistoryAltered = true;
|
||||||
|
window.history.back();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
nextStateIndexToReplace = -1;
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
currentIndexes.splice(currentIndexes.indexOf(indexRef.current), 1);
|
||||||
|
|
||||||
|
isClosed.current = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('popstate', handlePopState);
|
||||||
|
return () => window.removeEventListener('popstate', handlePopState);
|
||||||
|
}, [currentState, isActive, onBack, onForward, prevIsActive, shouldReplaceNext]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export default (
|
|||||||
// СSS class should be added in a separate tick to turn on CSS transition.
|
// СSS class should be added in a separate tick to turn on CSS transition.
|
||||||
const [hasOpenClassName, setHasOpenClassName] = useState(isOpen && noOpenTransition);
|
const [hasOpenClassName, setHasOpenClassName] = useState(isOpen && noOpenTransition);
|
||||||
|
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setIsClosed(false);
|
setIsClosed(false);
|
||||||
setHasOpenClassName(true);
|
setHasOpenClassName(true);
|
||||||
@ -39,12 +40,14 @@ export default (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `noCloseTransition`, when set to true, should remove the open class immediately
|
||||||
|
const shouldHaveOpenClassName = hasOpenClassName && !(noCloseTransition && !isOpen);
|
||||||
const isClosing = Boolean(closeTimeoutRef.current);
|
const isClosing = Boolean(closeTimeoutRef.current);
|
||||||
const shouldRender = isOpen || isClosing;
|
const shouldRender = isOpen || isClosing;
|
||||||
const transitionClassNames = buildClassName(
|
const transitionClassNames = buildClassName(
|
||||||
className && 'opacity-transition',
|
className && 'opacity-transition',
|
||||||
className,
|
className,
|
||||||
hasOpenClassName && 'open',
|
shouldHaveOpenClassName && 'open',
|
||||||
shouldRender && 'shown',
|
shouldRender && 'shown',
|
||||||
isClosing && 'closing',
|
isClosing && 'closing',
|
||||||
);
|
);
|
||||||
|
|||||||
@ -8,26 +8,28 @@ const delegationRegistry: Record<string, Map<HTMLElement, Handler>> = {};
|
|||||||
const delegatedEventsByElement = new Map<HTMLElement, Set<string>>();
|
const delegatedEventsByElement = new Map<HTMLElement, Set<string>>();
|
||||||
const documentEventCounters: Record<string, number> = {};
|
const documentEventCounters: Record<string, number> = {};
|
||||||
|
|
||||||
export function addEventListener(element: HTMLElement, propName: string, handler: Handler) {
|
export function addEventListener(element: HTMLElement, propName: string, handler: Handler, asCapture = false) {
|
||||||
const eventName = resolveEventName(propName, element);
|
const eventName = resolveEventName(propName, element);
|
||||||
if (canUseEventDelegation(eventName, element)) {
|
if (canUseEventDelegation(eventName, element, asCapture)) {
|
||||||
addDelegatedListener(eventName, element, handler);
|
addDelegatedListener(eventName, element, handler);
|
||||||
} else {
|
} else {
|
||||||
element.addEventListener(eventName, handler);
|
element.addEventListener(eventName, handler, asCapture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeEventListener(element: HTMLElement, propName: string, handler: Handler) {
|
export function removeEventListener(element: HTMLElement, propName: string, handler: Handler, asCapture = false) {
|
||||||
const eventName = resolveEventName(propName, element);
|
const eventName = resolveEventName(propName, element);
|
||||||
if (canUseEventDelegation(eventName, element)) {
|
if (canUseEventDelegation(eventName, element, asCapture)) {
|
||||||
removeDelegatedListener(eventName, element);
|
removeDelegatedListener(eventName, element);
|
||||||
} else {
|
} else {
|
||||||
element.removeEventListener(eventName, handler);
|
element.removeEventListener(eventName, handler, asCapture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveEventName(propName: string, element: HTMLElement) {
|
function resolveEventName(propName: string, element: HTMLElement) {
|
||||||
const eventName = propName.replace(/^on/, '').toLowerCase();
|
const eventName = propName
|
||||||
|
.replace(/^on/, '')
|
||||||
|
.replace(/Capture$/, '').toLowerCase();
|
||||||
|
|
||||||
if (eventName === 'change' && element.tagName !== 'SELECT') {
|
if (eventName === 'change' && element.tagName !== 'SELECT') {
|
||||||
// React behavior repeated here.
|
// React behavior repeated here.
|
||||||
@ -51,9 +53,10 @@ function resolveEventName(propName: string, element: HTMLElement) {
|
|||||||
return eventName;
|
return eventName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function canUseEventDelegation(realEventName: string, element: HTMLElement) {
|
function canUseEventDelegation(realEventName: string, element: HTMLElement, asCapture: boolean) {
|
||||||
return (
|
return (
|
||||||
!NON_BUBBLEABLE_EVENTS.has(realEventName)
|
!asCapture
|
||||||
|
&& !NON_BUBBLEABLE_EVENTS.has(realEventName)
|
||||||
&& element.tagName !== 'VIDEO'
|
&& element.tagName !== 'VIDEO'
|
||||||
&& element.tagName !== 'IFRAME'
|
&& element.tagName !== 'IFRAME'
|
||||||
);
|
);
|
||||||
|
|||||||
@ -428,7 +428,7 @@ function addAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
element.style.cssText = value;
|
element.style.cssText = value;
|
||||||
} else if (key.startsWith('on')) {
|
} else if (key.startsWith('on')) {
|
||||||
addEventListener(element, key, value);
|
addEventListener(element, key, value, key.endsWith('Capture'));
|
||||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||||
element.setAttribute(key, value);
|
element.setAttribute(key, value);
|
||||||
} else if (!FILTERED_ATTRIBUTES.has(key)) {
|
} else if (!FILTERED_ATTRIBUTES.has(key)) {
|
||||||
@ -444,7 +444,7 @@ function removeAttribute(element: HTMLElement, key: string, value: any) {
|
|||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
element.style.cssText = '';
|
element.style.cssText = '';
|
||||||
} else if (key.startsWith('on')) {
|
} else if (key.startsWith('on')) {
|
||||||
removeEventListener(element, key, value);
|
removeEventListener(element, key, value, key.endsWith('Capture'));
|
||||||
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
} else if (key.startsWith('data-') || HTML_ATTRIBUTES.has(key)) {
|
||||||
element.removeAttribute(key);
|
element.removeAttribute(key);
|
||||||
} else if (!FILTERED_ATTRIBUTES.has(key)) {
|
} else if (!FILTERED_ATTRIBUTES.has(key)) {
|
||||||
|
|||||||
@ -46,10 +46,15 @@ function runCallbacks() {
|
|||||||
|
|
||||||
const runCallbacksThrottled = throttleWithRaf(runCallbacks);
|
const runCallbacksThrottled = throttleWithRaf(runCallbacks);
|
||||||
|
|
||||||
export function setGlobal(newGlobal?: GlobalState) {
|
// `noThrottle = true` is used as a workaround for iOS gesture history navigation
|
||||||
|
export function setGlobal(newGlobal?: GlobalState, noThrottle = false) {
|
||||||
if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) {
|
if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) {
|
||||||
currentGlobal = newGlobal;
|
currentGlobal = newGlobal;
|
||||||
runCallbacksThrottled();
|
if (!noThrottle) {
|
||||||
|
runCallbacksThrottled();
|
||||||
|
} else {
|
||||||
|
runCallbacks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,12 +66,12 @@ export function getDispatch() {
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDispatch(name: string, payload?: ActionPayload) {
|
function onDispatch(name: string, payload?: ActionPayload, noThrottle?: boolean) {
|
||||||
if (reducers[name]) {
|
if (reducers[name]) {
|
||||||
reducers[name].forEach((reducer) => {
|
reducers[name].forEach((reducer) => {
|
||||||
const newGlobal = reducer(currentGlobal, actions, payload);
|
const newGlobal = reducer(currentGlobal, actions, payload);
|
||||||
if (newGlobal) {
|
if (newGlobal) {
|
||||||
setGlobal(newGlobal);
|
setGlobal(newGlobal, noThrottle);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -139,8 +144,8 @@ export function addReducer(name: ActionTypes, reducer: Reducer) {
|
|||||||
if (!reducers[name]) {
|
if (!reducers[name]) {
|
||||||
reducers[name] = [];
|
reducers[name] = [];
|
||||||
|
|
||||||
actions[name] = (payload?: ActionPayload) => {
|
actions[name] = (payload?: ActionPayload, noThrottle = false) => {
|
||||||
onDispatch(name, payload);
|
onDispatch(name, payload, noThrottle);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,14 @@
|
|||||||
import { addReducer, getDispatch, setGlobal } from '../../../lib/teact/teactn';
|
import { addReducer, setGlobal } from '../../../lib/teact/teactn';
|
||||||
import {
|
import {
|
||||||
exitMessageSelectMode,
|
exitMessageSelectMode,
|
||||||
updateCurrentMessageList,
|
updateCurrentMessageList,
|
||||||
} from '../../reducers';
|
} from '../../reducers';
|
||||||
import { selectCurrentMessageList } from '../../selectors';
|
import { selectCurrentMessageList } from '../../selectors';
|
||||||
|
import { closeLocalTextSearch } from './localSearch';
|
||||||
window.addEventListener('popstate', (e) => {
|
|
||||||
if (!e.state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { chatId: id, threadId, messageListType: type } = e.state;
|
|
||||||
|
|
||||||
getDispatch().openChat({
|
|
||||||
id, threadId, type, noPushState: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
addReducer('openChat', (global, actions, payload) => {
|
addReducer('openChat', (global, actions, payload) => {
|
||||||
const {
|
const {
|
||||||
id, threadId = -1, type = 'thread', noPushState,
|
id, threadId = -1, type = 'thread',
|
||||||
} = payload!;
|
} = payload!;
|
||||||
|
|
||||||
const currentMessageList = selectCurrentMessageList(global);
|
const currentMessageList = selectCurrentMessageList(global);
|
||||||
@ -31,6 +20,7 @@ addReducer('openChat', (global, actions, payload) => {
|
|||||||
|| currentMessageList.type !== type
|
|| currentMessageList.type !== type
|
||||||
)) {
|
)) {
|
||||||
global = exitMessageSelectMode(global);
|
global = exitMessageSelectMode(global);
|
||||||
|
global = closeLocalTextSearch(global);
|
||||||
|
|
||||||
global = {
|
global = {
|
||||||
...global,
|
...global,
|
||||||
@ -44,10 +34,6 @@ addReducer('openChat', (global, actions, payload) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
|
|
||||||
if (!noPushState) {
|
|
||||||
window.history.pushState({ chatId: id, threadId, messageListType: type }, '');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateCurrentMessageList(global, id, threadId, type);
|
return updateCurrentMessageList(global, id, threadId, type);
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import { setLanguage } from '../../../util/langProvider';
|
|||||||
import switchTheme from '../../../util/switchTheme';
|
import switchTheme from '../../../util/switchTheme';
|
||||||
import { selectTheme } from '../../selectors';
|
import { selectTheme } from '../../selectors';
|
||||||
|
|
||||||
|
const HISTORY_ANIMATION_DURATION = 450;
|
||||||
|
|
||||||
subscribeToSystemThemeChange();
|
subscribeToSystemThemeChange();
|
||||||
|
|
||||||
addReducer('init', (global) => {
|
addReducer('init', (global) => {
|
||||||
@ -68,6 +70,21 @@ addReducer('clearAuthError', (global) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addReducer('disableHistoryAnimations', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setGlobal({
|
||||||
|
...getGlobal(),
|
||||||
|
shouldSkipHistoryAnimations: false,
|
||||||
|
});
|
||||||
|
document.body.classList.remove('no-animate');
|
||||||
|
}, HISTORY_ANIMATION_DURATION);
|
||||||
|
|
||||||
|
setGlobal({
|
||||||
|
...getGlobal(),
|
||||||
|
shouldSkipHistoryAnimations: true,
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
function subscribeToSystemThemeChange() {
|
function subscribeToSystemThemeChange() {
|
||||||
function handleSystemThemeChange() {
|
function handleSystemThemeChange() {
|
||||||
const currentThemeMatch = document.documentElement.className.match(/theme-(\w+)/);
|
const currentThemeMatch = document.documentElement.className.match(/theme-(\w+)/);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||||
import { selectCurrentMessageList } from '../../selectors';
|
import { selectCurrentMessageList } from '../../selectors';
|
||||||
import { buildChatThreadKey } from '../../helpers';
|
import { buildChatThreadKey } from '../../helpers';
|
||||||
|
import { GlobalState } from '../../../global/types';
|
||||||
|
|
||||||
addReducer('openLocalTextSearch', (global) => {
|
addReducer('openLocalTextSearch', (global) => {
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
||||||
@ -18,16 +19,7 @@ addReducer('openLocalTextSearch', (global) => {
|
|||||||
return updateLocalTextSearch(global, chatId, threadId, true);
|
return updateLocalTextSearch(global, chatId, threadId, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('closeLocalTextSearch', (global) => {
|
addReducer('closeLocalTextSearch', closeLocalTextSearch);
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
|
||||||
if (!chatId || !threadId) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
global = updateLocalTextSearch(global, chatId, threadId, false);
|
|
||||||
global = replaceLocalTextSearchResults(global, chatId, threadId, undefined);
|
|
||||||
return global;
|
|
||||||
});
|
|
||||||
|
|
||||||
addReducer('setLocalTextSearchQuery', (global, actions, payload) => {
|
addReducer('setLocalTextSearchQuery', (global, actions, payload) => {
|
||||||
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
||||||
@ -57,3 +49,14 @@ addReducer('setLocalMediaSearchType', (global, actions, payload) => {
|
|||||||
const { mediaType } = payload!;
|
const { mediaType } = payload!;
|
||||||
return updateLocalMediaSearchType(global, chatId, mediaType);
|
return updateLocalMediaSearchType(global, chatId, mediaType);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function closeLocalTextSearch(global: GlobalState): GlobalState {
|
||||||
|
const { chatId, threadId } = selectCurrentMessageList(global) || {};
|
||||||
|
if (!chatId || !threadId) {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateLocalTextSearch(global, chatId, threadId, false);
|
||||||
|
global = replaceLocalTextSearchResults(global, chatId, threadId, undefined);
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@
|
|||||||
|
|
||||||
// Used by ChatList and ContactList components
|
// Used by ChatList and ContactList components
|
||||||
.chat-list {
|
.chat-list {
|
||||||
|
background: var(--color-background);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: .5rem .125rem .5rem .4375rem;
|
padding: .5rem .125rem .5rem .4375rem;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user