Introduce Multi-Accounts
This commit is contained in:
parent
65e0d05ac4
commit
a6f8f232a8
@ -11,9 +11,10 @@ function compatTest() {
|
||||
var hasNumberFormat = hasIntl && typeof Intl.NumberFormat !== 'undefined';
|
||||
var hasWebLocks = typeof navigator.locks !== 'undefined';
|
||||
var hasBigInt = typeof BigInt !== 'undefined';
|
||||
var hasBroadcastChannel = typeof BroadcastChannel !== 'undefined';
|
||||
|
||||
var isCompatible = hasPromise && hasWebSockets && hasWebCrypto && hasObjectFromEntries && hasResizeObserver
|
||||
&& hasCssSupports && hasDisplayNames && hasPluralRules && hasNumberFormat && hasWebLocks && hasBigInt;
|
||||
&& hasCssSupports && hasDisplayNames && hasPluralRules && hasNumberFormat && hasWebLocks && hasBigInt && hasBroadcastChannel;
|
||||
|
||||
if (isCompatible || (window.localStorage && window.localStorage.getItem('tt-ignore-compat'))) {
|
||||
window.isCompatTestPassed = true;
|
||||
@ -33,6 +34,7 @@ function compatTest() {
|
||||
console.warn('Intl.NumberFormat', hasNumberFormat);
|
||||
console.warn('WebLocks', hasWebLocks);
|
||||
console.warn('BigInt', hasBigInt);
|
||||
console.warn('BroadcastChannel', hasBroadcastChannel);
|
||||
}
|
||||
|
||||
// Hardcoded page because server forbids iframe embedding
|
||||
|
||||
@ -159,6 +159,7 @@ export function buildAppConfig(json: GramJs.TypeJSONValue, hash: number): ApiApp
|
||||
chatlistJoined: getLimit(appConfig, 'chatlist_joined_limit', 'chatlistJoined'),
|
||||
recommendedChannels: getLimit(appConfig, 'recommended_channels_limit', 'recommendedChannels'),
|
||||
savedDialogsPinned: getLimit(appConfig, 'saved_dialogs_pinned_limit', 'savedDialogsPinned'),
|
||||
moreAccounts: DEFAULT_LIMITS.moreAccounts,
|
||||
},
|
||||
hash,
|
||||
areStoriesHidden: appConfig.stories_all_hidden,
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import BigInt from 'big-integer';
|
||||
import { Api as GramJs } from '../../lib/gramjs';
|
||||
|
||||
import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../config';
|
||||
import { DEBUG } from '../../config';
|
||||
import { DATA_BROADCAST_CHANNEL_NAME } from '../../util/multiaccount';
|
||||
import { throttle } from '../../util/schedulers';
|
||||
import { omitVirtualClassFields } from './apiBuilders/helpers';
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const IS_MULTITAB_SUPPORTED = 'BroadcastChannel' in self;
|
||||
|
||||
export type StoryRepairInfo = {
|
||||
type: 'story';
|
||||
peerId: string;
|
||||
@ -36,7 +34,7 @@ export interface LocalDb {
|
||||
channelPtsById: Record<string, number>;
|
||||
}
|
||||
|
||||
const channel = IS_MULTITAB_SUPPORTED ? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) : undefined;
|
||||
const channel = new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME);
|
||||
|
||||
let batchedUpdates: {
|
||||
name: string;
|
||||
@ -44,7 +42,7 @@ let batchedUpdates: {
|
||||
value: any;
|
||||
}[] = [];
|
||||
const throttledLocalDbUpdate = throttle(() => {
|
||||
channel!.postMessage({
|
||||
channel.postMessage({
|
||||
type: 'localDbUpdate',
|
||||
batchedUpdates,
|
||||
});
|
||||
@ -109,9 +107,7 @@ function createLocalDbInitial(initial?: LocalDb): LocalDb {
|
||||
return acc2;
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
acc[key] = IS_MULTITAB_SUPPORTED
|
||||
? createProxy(key, convertedValue)
|
||||
: convertedValue;
|
||||
acc[key] = createProxy(key, convertedValue);
|
||||
return acc;
|
||||
}, {} as LocalDb) as LocalDb;
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ export async function init(initialArgs: ApiInitialArgs) {
|
||||
const {
|
||||
userAgent, platform, sessionData, isWebmSupported, maxBufferSize, webAuthToken, dcId,
|
||||
mockScenario, shouldForceHttpTransport, shouldAllowHttpTransport,
|
||||
shouldDebugExportedSenders, langCode, isTestServerRequested,
|
||||
shouldDebugExportedSenders, langCode, isTestServerRequested, accountIds,
|
||||
} = initialArgs;
|
||||
|
||||
const session = new sessions.CallbackSession(sessionData, onSessionUpdate);
|
||||
@ -133,6 +133,7 @@ export async function init(initialArgs: ApiInitialArgs) {
|
||||
webAuthToken,
|
||||
webAuthTokenFailed: onWebAuthTokenFailed,
|
||||
mockScenario,
|
||||
accountIds,
|
||||
});
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import type { Api } from '../../../lib/gramjs';
|
||||
import type { TypedBroadcastChannel } from '../../../util/multitab';
|
||||
import type { TypedBroadcastChannel } from '../../../util/browser/multitab';
|
||||
import type { ApiInitialArgs, ApiOnProgress, OnApiUpdate } from '../../types';
|
||||
import type { LocalDb } from '../localDb';
|
||||
import type { MethodArgs, MethodResponse, Methods } from '../methods/types';
|
||||
import type { OriginPayload, ThenArg, WorkerMessageEvent } from './types';
|
||||
|
||||
import { DATA_BROADCAST_CHANNEL_NAME, DEBUG, IGNORE_UNHANDLED_ERRORS } from '../../../config';
|
||||
import { DEBUG, IGNORE_UNHANDLED_ERRORS } from '../../../config';
|
||||
import { logDebugMessage } from '../../../util/debugConsole';
|
||||
import Deferred from '../../../util/Deferred';
|
||||
import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole';
|
||||
import generateUniqueId from '../../../util/generateUniqueId';
|
||||
import { ACCOUNT_SLOT, DATA_BROADCAST_CHANNEL_NAME } from '../../../util/multiaccount';
|
||||
import { pause, throttleWithTickEnd } from '../../../util/schedulers';
|
||||
import { IS_MULTITAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
type RequestState = {
|
||||
messageId: string;
|
||||
@ -48,9 +48,7 @@ subscribeToMasterChange((isMasterTabNew) => {
|
||||
isMasterTab = isMasterTabNew;
|
||||
});
|
||||
|
||||
const channel = IS_MULTITAB_SUPPORTED
|
||||
? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadcastChannel
|
||||
: undefined;
|
||||
const channel = new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadcastChannel;
|
||||
|
||||
const postMessagesOnTickEnd = throttleWithTickEnd(() => {
|
||||
const payloads = pendingPayloads;
|
||||
@ -64,8 +62,6 @@ function postMessageOnTickEnd(payload: OriginPayload) {
|
||||
}
|
||||
|
||||
export function initApiOnMasterTab(initialArgs: ApiInitialArgs) {
|
||||
if (!channel) return;
|
||||
|
||||
channel.postMessage({
|
||||
type: 'initApi',
|
||||
token: getCurrentTabId(),
|
||||
@ -93,7 +89,14 @@ export function initApi(onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) {
|
||||
console.log('>>> START LOAD WORKER');
|
||||
}
|
||||
|
||||
worker = new Worker(new URL('./worker.ts', import.meta.url));
|
||||
const params = new URLSearchParams();
|
||||
if (ACCOUNT_SLOT) {
|
||||
params.set('account', String(ACCOUNT_SLOT));
|
||||
}
|
||||
|
||||
worker = new Worker(new URL('./worker.ts', import.meta.url), {
|
||||
name: params.toString(),
|
||||
});
|
||||
subscribeToWorker(onUpdate);
|
||||
|
||||
if (initialArgs.platform === 'iOS') {
|
||||
@ -132,8 +135,6 @@ export function updateFullLocalDb(initial: LocalDb) {
|
||||
}
|
||||
|
||||
export function callApiOnMasterTab(payload: any) {
|
||||
if (!channel) return;
|
||||
|
||||
channel.postMessage({
|
||||
type: 'callApi',
|
||||
token: getCurrentTabId(),
|
||||
@ -254,8 +255,6 @@ export function cancelApiProgress(progressCallback: ApiOnProgress) {
|
||||
if (isMasterTab) {
|
||||
cancelApiProgressMaster(messageId);
|
||||
} else {
|
||||
if (!channel) return;
|
||||
|
||||
channel.postMessage({
|
||||
type: 'cancelApiProgress',
|
||||
token: getCurrentTabId(),
|
||||
|
||||
@ -23,6 +23,7 @@ export interface ApiInitialArgs {
|
||||
shouldDebugExportedSenders?: boolean;
|
||||
langCode: string;
|
||||
isTestServerRequested?: boolean;
|
||||
accountIds?: string[];
|
||||
}
|
||||
|
||||
export interface ApiOnProgress {
|
||||
@ -102,7 +103,7 @@ export interface ApiWebSession {
|
||||
|
||||
export interface ApiSessionData {
|
||||
mainDcId: number;
|
||||
keys: Record<number, string | number[]>;
|
||||
keys: Record<number, string>;
|
||||
isTest?: true;
|
||||
}
|
||||
|
||||
@ -343,10 +344,11 @@ export type ApiLimitType =
|
||||
| 'chatlistInvites'
|
||||
| 'chatlistJoined'
|
||||
| 'recommendedChannels'
|
||||
| 'savedDialogsPinned';
|
||||
| 'savedDialogsPinned'
|
||||
| 'moreAccounts';
|
||||
|
||||
export type ApiLimitTypeWithModal = Exclude<ApiLimitType, (
|
||||
'captionLength' | 'aboutLength' | 'stickersFaved' | 'savedGifs' | 'recommendedChannels'
|
||||
'captionLength' | 'aboutLength' | 'stickersFaved' | 'savedGifs' | 'recommendedChannels' | 'moreAccounts'
|
||||
)>;
|
||||
|
||||
export type ApiLimitTypeForPromo = Exclude<ApiLimitType,
|
||||
|
||||
@ -71,6 +71,9 @@
|
||||
"PremiumPreviewUploadsDescription" = "4 GB per each document, unlimited storage for your chats and media overall.";
|
||||
"PremiumPreviewAdvancedChatManagementDescription" = "Tools to set the default folder, auto-archive and hide new chats from non-contacts.";
|
||||
"PremiumPreviewAnimatedProfilesDescription" = "Video avatars animated in chat lists and chats to allow for additional self-expression.";
|
||||
"PremiumLimitAccountsTitle" = "Limit Reached";
|
||||
"PremiumLimitAccountsNoPremium" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium.";
|
||||
"PremiumLimitAccounts" = "You have reached your current limit of connected accounts. You can free one more place by subscribing to Telegram Premium with one of these connected accounts.";
|
||||
"SendMessage" = "Send Message";
|
||||
"ConversationDefaultRestrictedMedia" = "Sending media isn't allowed in this group.";
|
||||
"AccDescrVoiceMessage" = "Record voice message";
|
||||
@ -172,6 +175,7 @@
|
||||
"LoginRegisterLastNamePlaceholder" = "Last Name";
|
||||
"Next" = "Next";
|
||||
"LoginSelectCountryTitle" = "Country";
|
||||
"MenuAddAccount" = "Add Account";
|
||||
"CountryNone" = "Country not found";
|
||||
"VoipGroupVoiceChat" = "Video Chat";
|
||||
"AccDescrMoreOptions" = "More options";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { initializeSoundsForSafari } from '../global/actions/ui/calls';
|
||||
import { IS_IOS, IS_SAFARI } from '../util/windowEnvironment';
|
||||
import { IS_IOS, IS_SAFARI } from '../util/browser/windowEnvironment';
|
||||
|
||||
export { default as GroupCall } from '../components/calls/group/GroupCall';
|
||||
export { default as ActiveCallHeader } from '../components/calls/ActiveCallHeader';
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import { getActions, getGlobal } from '../global';
|
||||
|
||||
import { DEBUG } from '../config';
|
||||
import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment';
|
||||
|
||||
export { default as Main } from '../components/main/Main';
|
||||
export { default as LockScreen } from '../components/main/LockScreen';
|
||||
@ -10,8 +7,3 @@ if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> FINISH LOAD MAIN BUNDLE');
|
||||
}
|
||||
|
||||
const { passcode: { isScreenLocked }, connectionState } = getGlobal();
|
||||
if (!connectionState && !isScreenLocked && !IS_MULTITAB_SUPPORTED) {
|
||||
getActions().initApi();
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { FC } from '../lib/teact/teact';
|
||||
import React, { useEffect, useLayoutEffect } from '../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../global';
|
||||
import { withGlobal } from '../global';
|
||||
|
||||
import type { GlobalState } from '../global/types';
|
||||
import type { ThemeKey } from '../types';
|
||||
@ -10,12 +10,12 @@ import {
|
||||
DARK_THEME_BG_COLOR, INACTIVE_MARKER, LIGHT_THEME_BG_COLOR, PAGE_TITLE,
|
||||
} from '../config';
|
||||
import { selectTabState, selectTheme } from '../global/selectors';
|
||||
import { addActiveTabChangeListener } from '../util/activeTabMonitor';
|
||||
import { IS_INSTALL_PROMPT_SUPPORTED, PLATFORM_ENV } from '../util/browser/windowEnvironment';
|
||||
import buildClassName from '../util/buildClassName';
|
||||
import { setupBeforeInstallPrompt } from '../util/installPrompt';
|
||||
import { parseInitialLocationHash } from '../util/routing';
|
||||
import { ACCOUNT_SLOT, getAccountsInfo, getAccountSlotUrl } from '../util/multiaccount';
|
||||
import { getInitialLocationHash, parseInitialLocationHash } from '../util/routing';
|
||||
import { hasStoredSession } from '../util/sessions';
|
||||
import { IS_INSTALL_PROMPT_SUPPORTED, IS_MULTITAB_SUPPORTED, PLATFORM_ENV } from '../util/windowEnvironment';
|
||||
import { updateSizes } from '../util/windowSize';
|
||||
|
||||
import useAppLayout from '../hooks/useAppLayout';
|
||||
@ -61,8 +61,6 @@ const App: FC<StateProps> = ({
|
||||
isTestServer,
|
||||
theme,
|
||||
}) => {
|
||||
const { disconnect } = getActions();
|
||||
|
||||
const [isInactive, markInactive, unmarkInactive] = useFlag(false);
|
||||
const { isMobile } = useAppLayout();
|
||||
const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android';
|
||||
@ -73,6 +71,22 @@ const App: FC<StateProps> = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const hash = getInitialLocationHash();
|
||||
// If there is no stored session on first slot, navigate to any other slot with stored session
|
||||
if (!hasStoredSession() && !ACCOUNT_SLOT && !hash) {
|
||||
const accounts = getAccountsInfo();
|
||||
Object.keys(accounts).forEach((key) => {
|
||||
const slot = Number(key);
|
||||
const account = accounts[slot];
|
||||
if (account) {
|
||||
const url = getAccountSlotUrl(slot);
|
||||
window.location.href = `${url}#${hash || 'login'}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Prevent drop on elements that do not accept it
|
||||
useEffect(() => {
|
||||
const body = document.body;
|
||||
@ -161,17 +175,6 @@ const App: FC<StateProps> = ({
|
||||
updateSizes();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (IS_MULTITAB_SUPPORTED) return;
|
||||
|
||||
addActiveTabChangeListener(() => {
|
||||
disconnect();
|
||||
document.title = INACTIVE_PAGE_TITLE;
|
||||
|
||||
markInactive();
|
||||
});
|
||||
}, [activeKey, disconnect, markInactive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isInactiveAuth) {
|
||||
document.title = INACTIVE_PAGE_TITLE;
|
||||
|
||||
@ -225,6 +225,12 @@
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.auth-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
@keyframes qr-show {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
||||
@ -6,7 +6,7 @@ import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { GlobalState } from '../../global/types';
|
||||
|
||||
import { PLATFORM_ENV } from '../../util/windowEnvironment';
|
||||
import { PLATFORM_ENV } from '../../util/browser/windowEnvironment';
|
||||
|
||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||
import useElectronDrag from '../../hooks/useElectronDrag';
|
||||
|
||||
@ -7,8 +7,8 @@ import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { GlobalState } from '../../global/types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import { pick } from '../../util/iteratees';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ChangeEvent } from 'react';
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useCallback, useEffect, useLayoutEffect, useRef, useState,
|
||||
memo, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
@ -9,18 +9,23 @@ import type { ApiCountryCode } from '../../api/types';
|
||||
import type { GlobalState } from '../../global/types';
|
||||
|
||||
import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
||||
import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import { preloadImage } from '../../util/files';
|
||||
import preloadFonts from '../../util/fonts';
|
||||
import { pick } from '../../util/iteratees';
|
||||
import { getAccountSlotUrl } from '../../util/multiaccount';
|
||||
import { oldSetLanguage } from '../../util/oldLangProvider';
|
||||
import { formatPhoneNumber, getCountryCodeByIso, getCountryFromPhoneNumber } from '../../util/phoneNumber';
|
||||
import { IS_SAFARI, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { navigateBack } from './helpers/backNavigation';
|
||||
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
||||
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLangString from '../../hooks/useLangString';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useMultiaccountInfo from '../../hooks/useMultiaccountInfo';
|
||||
|
||||
import Icon from '../common/icons/Icon';
|
||||
import Button from '../ui/Button';
|
||||
import Checkbox from '../ui/Checkbox';
|
||||
import InputText from '../ui/InputText';
|
||||
@ -62,7 +67,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
loadCountryList,
|
||||
clearAuthErrorKey,
|
||||
goToAuthQrCode,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -78,6 +83,16 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
const [lastSelection, setLastSelection] = useState<[number, number] | undefined>();
|
||||
const [isLoading, markIsLoading, unmarkIsLoading] = useFlag();
|
||||
|
||||
const accountsInfo = useMultiaccountInfo();
|
||||
const hasActiveAccount = Object.values(accountsInfo).length > 0;
|
||||
const phoneNumberSlots = useMemo(() => (
|
||||
Object.entries(accountsInfo)
|
||||
.reduce((acc, [key, { phone }]) => {
|
||||
if (phone) acc[phone] = Number(key);
|
||||
return acc;
|
||||
}, {} as Record<string, number>)
|
||||
), [accountsInfo]);
|
||||
|
||||
const fullNumber = country ? `+${country.countryCode} ${phoneNumber || ''}` : phoneNumber;
|
||||
const canSubmit = fullNumber && fullNumber.replace(/[^\d]+/g, '').length >= MIN_NUMBER_LENGTH;
|
||||
|
||||
@ -105,7 +120,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
}
|
||||
}, [country, authNearestCountry, isTouched, phoneCodeList]);
|
||||
|
||||
const parseFullNumber = useCallback((newFullNumber: string) => {
|
||||
const parseFullNumber = useLastCallback((newFullNumber: string) => {
|
||||
if (!newFullNumber.length) {
|
||||
setPhoneNumber('');
|
||||
}
|
||||
@ -123,17 +138,17 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
setCountry(selectedCountry);
|
||||
}
|
||||
setPhoneNumber(formatPhoneNumber(newFullNumber, selectedCountry));
|
||||
}, [phoneCodeList, country]);
|
||||
});
|
||||
|
||||
const handleLangChange = useCallback(() => {
|
||||
const handleLangChange = useLastCallback(() => {
|
||||
markIsLoading();
|
||||
|
||||
void oldSetLanguage(suggestedLanguage, () => {
|
||||
unmarkIsLoading();
|
||||
|
||||
setSettingOption({ language: suggestedLanguage });
|
||||
setSharedSettingOption({ language: suggestedLanguage });
|
||||
});
|
||||
}, [markIsLoading, setSettingOption, suggestedLanguage, unmarkIsLoading]);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (phoneNumber === undefined && authPhoneNumber) {
|
||||
@ -148,19 +163,23 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
}, [lastSelection]);
|
||||
|
||||
const isJustPastedRef = useRef(false);
|
||||
const handlePaste = useCallback(() => {
|
||||
const handlePaste = useLastCallback(() => {
|
||||
isJustPastedRef.current = true;
|
||||
requestMeasure(() => {
|
||||
isJustPastedRef.current = false;
|
||||
});
|
||||
}, []);
|
||||
});
|
||||
|
||||
const handleCountryChange = useCallback((value: ApiCountryCode) => {
|
||||
const handleBackNavigation = useLastCallback(() => {
|
||||
navigateBack();
|
||||
});
|
||||
|
||||
const handleCountryChange = useLastCallback((value: ApiCountryCode) => {
|
||||
setCountry(value);
|
||||
setPhoneNumber('');
|
||||
}, []);
|
||||
});
|
||||
|
||||
const handlePhoneNumberChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const handlePhoneNumberChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (authErrorKey) {
|
||||
clearAuthErrorKey();
|
||||
}
|
||||
@ -186,11 +205,11 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
&& value.length - fullNumber.length > 1 && !isJustPastedRef.current
|
||||
);
|
||||
parseFullNumber(shouldFixSafariAutoComplete ? `${country!.countryCode} ${value}` : value);
|
||||
}, [authErrorKey, country, fullNumber, parseFullNumber]);
|
||||
});
|
||||
|
||||
const handleKeepSessionChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const handleKeepSessionChange = useLastCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setAuthRememberMe(e.target.checked);
|
||||
}, [setAuthRememberMe]);
|
||||
});
|
||||
|
||||
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
@ -199,19 +218,30 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const adaptedPhoneNumber = fullNumber?.replace(/[^\d]/g, '');
|
||||
if (adaptedPhoneNumber && phoneNumberSlots[adaptedPhoneNumber]) {
|
||||
window.location.replace(getAccountSlotUrl(phoneNumberSlots[adaptedPhoneNumber]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (canSubmit) {
|
||||
setAuthPhoneNumber({ phoneNumber: fullNumber });
|
||||
}
|
||||
}
|
||||
|
||||
const handleGoToAuthQrCode = useCallback(() => {
|
||||
const handleGoToAuthQrCode = useLastCallback(() => {
|
||||
goToAuthQrCode();
|
||||
}, [goToAuthQrCode]);
|
||||
});
|
||||
|
||||
const isAuthReady = authState === 'authorizationStateWaitPhoneNumber';
|
||||
|
||||
return (
|
||||
<div id="auth-phone-number-form" className="custom-scroll">
|
||||
{hasActiveAccount && (
|
||||
<Button size="smaller" round color="translucent" className="auth-close" onClick={handleBackNavigation}>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
)}
|
||||
<div className="auth-form">
|
||||
<div id="logo" />
|
||||
<h1>{lang('AuthTitle')}</h1>
|
||||
@ -263,7 +293,7 @@ const AuthPhoneNumber: FC<StateProps> = ({
|
||||
export default memo(withGlobal(
|
||||
(global): StateProps => {
|
||||
const {
|
||||
settings: { byKey: { language } },
|
||||
sharedState: { settings: { language } },
|
||||
countryList: { phoneCodes: phoneCodeList },
|
||||
} = global;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, {
|
||||
memo, useCallback, useLayoutEffect, useRef,
|
||||
memo, useLayoutEffect, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
@ -7,18 +7,23 @@ import type { GlobalState } from '../../global/types';
|
||||
|
||||
import { STRICTERDOM_ENABLED } from '../../config';
|
||||
import { disableStrict, enableStrict } from '../../lib/fasterdom/stricterdom';
|
||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { oldSetLanguage } from '../../util/oldLangProvider';
|
||||
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
|
||||
import { navigateBack } from './helpers/backNavigation';
|
||||
import { getSuggestedLanguage } from './helpers/getSuggestedLanguage';
|
||||
|
||||
import useAsync from '../../hooks/useAsync';
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useLang from '../../hooks/useLang';
|
||||
import useLangString from '../../hooks/useLangString';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useMediaTransitionDeprecated from '../../hooks/useMediaTransitionDeprecated';
|
||||
import useMultiaccountInfo from '../../hooks/useMultiaccountInfo';
|
||||
|
||||
import AnimatedIcon from '../common/AnimatedIcon';
|
||||
import Icon from '../common/icons/Icon';
|
||||
import Button from '../ui/Button';
|
||||
import Loading from '../ui/Loading';
|
||||
|
||||
@ -26,7 +31,9 @@ import blankUrl from '../../assets/blank.png';
|
||||
|
||||
type StateProps =
|
||||
Pick<GlobalState, 'connectionState' | 'authState' | 'authQrCode'>
|
||||
& { language?: string };
|
||||
& {
|
||||
language?: string;
|
||||
};
|
||||
|
||||
const DATA_PREFIX = 'tg://login?token=';
|
||||
const QR_SIZE = 280;
|
||||
@ -50,7 +57,7 @@ const AuthCode = ({
|
||||
}: StateProps) => {
|
||||
const {
|
||||
returnToAuthPhoneNumber,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
} = getActions();
|
||||
|
||||
const suggestedLanguage = getSuggestedLanguage();
|
||||
@ -63,6 +70,9 @@ const AuthCode = ({
|
||||
const [isLoading, markIsLoading, unmarkIsLoading] = useFlag();
|
||||
const [isQrMounted, markQrMounted, unmarkQrMounted] = useFlag();
|
||||
|
||||
const accountsInfo = useMultiaccountInfo();
|
||||
const hasActiveAccount = Object.values(accountsInfo).length > 0;
|
||||
|
||||
const { result: qrCode } = useAsync(async () => {
|
||||
const QrCodeStyling = (await ensureQrCodeStyling()).default;
|
||||
return new QrCodeStyling({
|
||||
@ -125,24 +135,33 @@ const AuthCode = ({
|
||||
return undefined;
|
||||
}, [isConnected, authQrCode, isQrMounted, markQrMounted, unmarkQrMounted, qrCode]);
|
||||
|
||||
const handleLangChange = useCallback(() => {
|
||||
const handleBackNavigation = useLastCallback(() => {
|
||||
navigateBack();
|
||||
});
|
||||
|
||||
const handleLangChange = useLastCallback(() => {
|
||||
markIsLoading();
|
||||
|
||||
void oldSetLanguage(suggestedLanguage, () => {
|
||||
unmarkIsLoading();
|
||||
|
||||
setSettingOption({ language: suggestedLanguage });
|
||||
setSharedSettingOption({ language: suggestedLanguage });
|
||||
});
|
||||
}, [markIsLoading, setSettingOption, suggestedLanguage, unmarkIsLoading]);
|
||||
});
|
||||
|
||||
const habdleReturnToAuthPhoneNumber = useCallback(() => {
|
||||
const handleReturnToAuthPhoneNumber = useLastCallback(() => {
|
||||
returnToAuthPhoneNumber();
|
||||
}, [returnToAuthPhoneNumber]);
|
||||
});
|
||||
|
||||
const isAuthReady = authState === 'authorizationStateWaitQrCode';
|
||||
|
||||
return (
|
||||
<div id="auth-qr-form" className="custom-scroll">
|
||||
{hasActiveAccount && (
|
||||
<Button size="smaller" round color="translucent" className="auth-close" onClick={handleBackNavigation}>
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
)}
|
||||
<div className="auth-form qr">
|
||||
<div className="qr-outer">
|
||||
<div
|
||||
@ -172,7 +191,7 @@ const AuthCode = ({
|
||||
<li><span>{lang('LoginQRHelp3')}</span></li>
|
||||
</ol>
|
||||
{isAuthReady && (
|
||||
<Button size="smaller" isText onClick={habdleReturnToAuthPhoneNumber}>{lang('LoginQRCancel')}</Button>
|
||||
<Button size="smaller" isText onClick={handleReturnToAuthPhoneNumber}>{lang('LoginQRCancel')}</Button>
|
||||
)}
|
||||
{suggestedLanguage && suggestedLanguage !== language && continueText && (
|
||||
<Button size="smaller" isText isLoading={isLoading} onClick={handleLangChange}>{continueText}</Button>
|
||||
@ -185,9 +204,11 @@ const AuthCode = ({
|
||||
export default memo(withGlobal(
|
||||
(global): StateProps => {
|
||||
const {
|
||||
connectionState, authState, authQrCode, settings: { byKey: { language } },
|
||||
connectionState, authState, authQrCode,
|
||||
} = global;
|
||||
|
||||
const { language } = selectSharedSettings(global);
|
||||
|
||||
return {
|
||||
connectionState,
|
||||
authState,
|
||||
|
||||
@ -7,10 +7,10 @@ import { withGlobal } from '../../global';
|
||||
import type { ApiCountryCode } from '../../api/types';
|
||||
|
||||
import { ANIMATION_END_DELAY } from '../../config';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { isoToEmoji } from '../../util/emoji/emoji';
|
||||
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useLang from '../../hooks/useLang';
|
||||
|
||||
13
src/components/auth/helpers/backNavigation.ts
Normal file
13
src/components/auth/helpers/backNavigation.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { getAccountSlotUrl } from '../../../util/multiaccount';
|
||||
|
||||
export function navigateBack() {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const referrer = document.referrer ? new URL(document.referrer) : undefined;
|
||||
if (referrer?.origin === currentUrl.origin && referrer.pathname === currentUrl.pathname) {
|
||||
window.history.back(); // Return to previous account with it's state
|
||||
return;
|
||||
}
|
||||
|
||||
const url = getAccountSlotUrl(1);
|
||||
window.location.href = url;
|
||||
}
|
||||
@ -14,9 +14,9 @@ import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getUserStreams, THRESHOLD } from '../../../lib/secret-sauce';
|
||||
import { selectChat, selectUser } from '../../../global/selectors';
|
||||
import { animate } from '../../../util/animation';
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { fastRaf } from '../../../util/schedulers';
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import formatGroupCallVolume from './helpers/formatGroupCallVolume';
|
||||
|
||||
import useInterval from '../../../hooks/schedulers/useInterval';
|
||||
|
||||
@ -13,13 +13,13 @@ import {
|
||||
} from '../../../lib/secret-sauce';
|
||||
import { selectTabState } from '../../../global/selectors';
|
||||
import { selectPhoneCallUser } from '../../../global/selectors/calls';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatMediaDuration } from '../../../util/dates/dateFormat';
|
||||
import {
|
||||
IS_ANDROID,
|
||||
IS_IOS,
|
||||
IS_REQUEST_FULLSCREEN_SUPPORTED,
|
||||
} from '../../../util/windowEnvironment';
|
||||
} from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatMediaDuration } from '../../../util/dates/dateFormat';
|
||||
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
|
||||
@ -13,11 +13,11 @@ import type RLottieInstance from '../../lib/rlottie/RLottie';
|
||||
|
||||
import { requestMeasure } from '../../lib/fasterdom/fasterdom';
|
||||
import { ensureRLottie, getRLottie } from '../../lib/rlottie/RLottie.async';
|
||||
import { IS_ELECTRON } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import buildStyle from '../../util/buildStyle';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
import { hexToRgb } from '../../util/switchTheme';
|
||||
import { IS_ELECTRON } from '../../util/windowEnvironment';
|
||||
|
||||
import useColorFilter from '../../hooks/stickers/useColorFilter';
|
||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||
|
||||
@ -9,7 +9,7 @@ import type {
|
||||
} from '../../api/types';
|
||||
import type { BufferedRange } from '../../hooks/useBuffering';
|
||||
import type { OldLangFn } from '../../hooks/useOldLang';
|
||||
import type { ISettings } from '../../types';
|
||||
import type { ThemeKey } from '../../types';
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
import { AudioOrigin } from '../../types';
|
||||
|
||||
@ -51,7 +51,7 @@ import Icon from './icons/Icon';
|
||||
import './Audio.scss';
|
||||
|
||||
type OwnProps = {
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
message: ApiMessage;
|
||||
senderTitle?: string;
|
||||
uploadProgress?: number;
|
||||
@ -628,7 +628,7 @@ function renderVoice(
|
||||
}
|
||||
|
||||
function useWaveformCanvas(
|
||||
theme: ISettings['theme'],
|
||||
theme: ThemeKey,
|
||||
media?: ApiVoice | ApiVideo,
|
||||
playProgress = 0,
|
||||
isOwn = false,
|
||||
|
||||
@ -69,6 +69,7 @@ type OwnProps = {
|
||||
peer?: ApiPeer | CustomPeer;
|
||||
photo?: ApiPhoto;
|
||||
webPhoto?: ApiWebDocument;
|
||||
previewUrl?: string;
|
||||
text?: string;
|
||||
isSavedMessages?: boolean;
|
||||
isSavedDialog?: boolean;
|
||||
@ -93,6 +94,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
peer,
|
||||
photo,
|
||||
webPhoto,
|
||||
previewUrl,
|
||||
text,
|
||||
isSavedMessages,
|
||||
isSavedDialog,
|
||||
@ -169,7 +171,8 @@ const Avatar: FC<OwnProps> = ({
|
||||
|
||||
const imgBlobUrl = useMedia(imageHash, false, ApiMediaFormat.BlobUrl);
|
||||
const videoBlobUrl = useMedia(videoHash, !shouldLoadVideo, ApiMediaFormat.BlobUrl);
|
||||
const hasBlobUrl = Boolean(imgBlobUrl || videoBlobUrl);
|
||||
const imgUrl = imgBlobUrl || previewUrl;
|
||||
const hasBlobUrl = Boolean(imgUrl || videoBlobUrl);
|
||||
// `videoBlobUrl` can be taken from memory cache, so we need to check `shouldLoadVideo` again
|
||||
const shouldPlayVideo = Boolean(videoBlobUrl && shouldLoadVideo);
|
||||
|
||||
@ -205,7 +208,7 @@ const Avatar: FC<OwnProps> = ({
|
||||
content = (
|
||||
<>
|
||||
<img
|
||||
src={imgBlobUrl}
|
||||
src={imgUrl}
|
||||
className={buildClassName(cn.media, 'avatar-media', transitionClassNames, videoBlobUrl && 'poster')}
|
||||
alt={author}
|
||||
decoding="async"
|
||||
@ -262,10 +265,10 @@ const Avatar: FC<OwnProps> = ({
|
||||
withStorySolid && forceFriendStorySolid && 'close-friend',
|
||||
withStorySolid && (realPeer?.hasUnreadStories || forceUnreadStorySolid) && 'has-unread-story',
|
||||
onClick && 'interactive',
|
||||
(!isSavedMessages && !imgBlobUrl) && 'no-photo',
|
||||
(!isSavedMessages && !imgUrl) && 'no-photo',
|
||||
);
|
||||
|
||||
const hasMedia = Boolean(isSavedMessages || imgBlobUrl);
|
||||
const hasMedia = Boolean(isSavedMessages || imgUrl);
|
||||
|
||||
const { handleClick, handleMouseDown } = useFastClick((e: ReactMouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
if (withStory && storyViewerMode !== 'disabled' && realPeer?.hasStories) {
|
||||
|
||||
@ -35,9 +35,9 @@ import type {
|
||||
import type {
|
||||
IAnchorPosition,
|
||||
InlineBotSettings,
|
||||
ISettings,
|
||||
MessageList,
|
||||
MessageListType,
|
||||
ThemeKey,
|
||||
ThreadId,
|
||||
} from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
@ -103,6 +103,8 @@ import {
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import { selectCurrentLimit } from '../../global/selectors/limits';
|
||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||
import { IS_IOS, IS_VOICE_RECORDING_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { formatMediaDuration, formatVoiceRecordDuration } from '../../util/dates/dateFormat';
|
||||
import { processDeepLink } from '../../util/deeplink';
|
||||
@ -115,7 +117,6 @@ import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||
import parseHtmlAsFormattedText from '../../util/parseHtmlAsFormattedText';
|
||||
import { insertHtmlInSelection } from '../../util/selection';
|
||||
import { getServerTime } from '../../util/serverTime';
|
||||
import { IS_IOS, IS_VOICE_RECORDING_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import windowSize from '../../util/windowSize';
|
||||
import applyIosAutoCapitalizationFix from '../middle/composer/helpers/applyIosAutoCapitalizationFix';
|
||||
import buildAttachment, { prepareAttachmentsToSend } from '../middle/composer/helpers/buildAttachment';
|
||||
@ -256,7 +257,7 @@ type StateProps =
|
||||
requestedDraftFiles?: File[];
|
||||
attachBots: GlobalState['attachMenu']['bots'];
|
||||
attachMenuPeerType?: ApiAttachMenuPeerType;
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
fileSizeLimit: number;
|
||||
captionLimit: number;
|
||||
isCurrentUserPremium?: boolean;
|
||||
@ -2300,9 +2301,9 @@ export default memo(withGlobal<OwnProps>(
|
||||
const messageWithActualBotKeyboard = (isChatWithBot || !isChatWithUser)
|
||||
&& selectNewestMessageWithBotKeyboardButtons(global, chatId, threadId);
|
||||
const {
|
||||
language, shouldSuggestStickers, shouldSuggestCustomEmoji, shouldUpdateStickerSetOrder,
|
||||
shouldPaidMessageAutoApprove,
|
||||
shouldSuggestStickers, shouldSuggestCustomEmoji, shouldUpdateStickerSetOrder, shouldPaidMessageAutoApprove,
|
||||
} = global.settings.byKey;
|
||||
const { language, shouldCollectDebugLogs } = selectSharedSettings(global);
|
||||
const {
|
||||
forwardMessages: { messageIds: forwardMessageIds },
|
||||
} = selectTabState(global);
|
||||
@ -2434,7 +2435,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
canBuyPremium: !isCurrentUserPremium && !selectIsPremiumPurchaseBlocked(global),
|
||||
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
|
||||
canSendOneTimeMedia: !isChatWithSelf && isChatWithUser && !isChatWithBot && !isInScheduledList,
|
||||
shouldCollectDebugLogs: global.settings.byKey.shouldCollectDebugLogs,
|
||||
shouldCollectDebugLogs,
|
||||
sentStoryReaction,
|
||||
stealthMode: global.stories.stealthMode,
|
||||
replyToTopic,
|
||||
|
||||
@ -30,9 +30,9 @@ import {
|
||||
selectIsCurrentUserPremium,
|
||||
} from '../../global/selectors';
|
||||
import animateHorizontalScroll from '../../util/animateHorizontalScroll';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { pickTruthy, unique, uniqueByField } from '../../util/iteratees';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { REM } from './helpers/mediaDimensions';
|
||||
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
@ -74,7 +74,7 @@ const Document = ({
|
||||
onMediaClick,
|
||||
onDateClick,
|
||||
}: OwnProps) => {
|
||||
const { cancelMediaDownload, downloadMedia, setSettingOption } = getActions();
|
||||
const { cancelMediaDownload, downloadMedia, setSharedSettingOption } = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@ -158,7 +158,7 @@ const Document = ({
|
||||
});
|
||||
|
||||
const handleSvgConfirm = useLastCallback(() => {
|
||||
setSettingOption({ shouldWarnAboutSvg: !shouldNotWarnAboutSvg });
|
||||
setSharedSettingOption({ shouldWarnAboutSvg: !shouldNotWarnAboutSvg });
|
||||
closeSvgDialog();
|
||||
handleDownload();
|
||||
});
|
||||
|
||||
@ -5,9 +5,9 @@ import React, {
|
||||
|
||||
import type { IconName } from '../../types/icons';
|
||||
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { formatMediaDateTime, formatPastTimeShort } from '../../util/dates/dateFormat';
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import { getColorFromExtension, getFileSizeString } from './helpers/documentInfo';
|
||||
import { getDocumentThumbnailDimensions } from './helpers/mediaDimensions';
|
||||
import renderText from './helpers/renderText';
|
||||
|
||||
@ -63,9 +63,9 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
noLoopLimit,
|
||||
canCopyTitle,
|
||||
iconElement,
|
||||
statusSparklesColor,
|
||||
onEmojiStatusClick,
|
||||
observeIntersection,
|
||||
statusSparklesColor,
|
||||
}) => {
|
||||
const lang = useOldLang();
|
||||
const { showNotification } = getActions();
|
||||
@ -73,9 +73,10 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
const customPeer = 'isCustomPeer' in peer ? peer : undefined;
|
||||
const isUser = realPeer && isApiPeerUser(realPeer);
|
||||
const title = realPeer && (isUser ? getUserFullName(realPeer) : getChatTitle(lang, realPeer));
|
||||
const isPremium = isUser && realPeer.isPremium;
|
||||
const canShowEmojiStatus = withEmojiStatus && !isSavedMessages && realPeer;
|
||||
const emojiStatus = realPeer?.emojiStatus;
|
||||
const isPremium = (isUser && realPeer.isPremium) || customPeer?.isPremium;
|
||||
const canShowEmojiStatus = withEmojiStatus && !isSavedMessages;
|
||||
const emojiStatus = realPeer?.emojiStatus
|
||||
|| (customPeer?.emojiStatusId ? { type: 'regular', documentId: customPeer.emojiStatusId } : undefined);
|
||||
|
||||
const handleTitleClick = useLastCallback((e) => {
|
||||
if (!title || !canCopyTitle) {
|
||||
@ -89,7 +90,7 @@ const FullNameTitle: FC<OwnProps> = ({
|
||||
|
||||
const specialTitle = useMemo(() => {
|
||||
if (customPeer) {
|
||||
return customPeer.title || lang(customPeer.titleKey!);
|
||||
return renderText(customPeer.title || lang(customPeer.titleKey!));
|
||||
}
|
||||
|
||||
if (isSavedMessages) {
|
||||
|
||||
@ -7,8 +7,8 @@ import type { ApiVideo } from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
|
||||
import { getVideoMediaHash, getVideoPreviewMediaHash } from '../../global/helpers';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur';
|
||||
|
||||
import useBuffering from '../../hooks/useBuffering';
|
||||
|
||||
@ -6,9 +6,9 @@ import React, {
|
||||
|
||||
import { MIN_PASSWORD_LENGTH } from '../../config';
|
||||
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import stopEvent from '../../util/stopEvent';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import useTimeout from '../../hooks/schedulers/useTimeout';
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
@ -20,10 +20,10 @@ import {
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { captureEvents, SwipeDirection } from '../../util/captureEvents';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import renderText from './helpers/renderText';
|
||||
|
||||
import useIntervalForceUpdate from '../../hooks/schedulers/useIntervalForceUpdate';
|
||||
|
||||
@ -17,9 +17,9 @@ import {
|
||||
isDeletedUser,
|
||||
isUserId,
|
||||
} from '../../global/helpers';
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { getFirstLetters } from '../../util/textFormat';
|
||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../../util/windowEnvironment';
|
||||
import { getPeerColorClass } from './helpers/peerColor';
|
||||
import renderText from './helpers/renderText';
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../lib/teact/teact';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import Button from '../ui/Button';
|
||||
import Icon from './icons/Icon';
|
||||
|
||||
@ -7,9 +7,9 @@ import { getActions } from '../../global';
|
||||
import type { ApiBotInlineMediaResult, ApiSticker } from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { getServerTime } from '../../util/serverTime';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMessageInputBlur';
|
||||
|
||||
import useDynamicColorListener from '../../hooks/stickers/useDynamicColorListener';
|
||||
|
||||
@ -7,9 +7,9 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
|
||||
import { getStickerMediaHash } from '../../global/helpers';
|
||||
import { selectIsAlwaysHighPriorityEmoji } from '../../global/selectors';
|
||||
import { IS_ANDROID, IS_IOS, IS_WEBM_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import * as mediaLoader from '../../util/mediaLoader';
|
||||
import { IS_ANDROID, IS_IOS, IS_WEBM_SUPPORTED } from '../../util/windowEnvironment';
|
||||
|
||||
import useColorFilter from '../../hooks/stickers/useColorFilter';
|
||||
import useCoordsInSharedCanvas from '../../hooks/useCoordsInSharedCanvas';
|
||||
|
||||
@ -4,7 +4,7 @@ import type {
|
||||
|
||||
import { STICKER_SIZE_INLINE_DESKTOP_FACTOR, STICKER_SIZE_INLINE_MOBILE_FACTOR } from '../../../config';
|
||||
import { getPhotoInlineDimensions, getVideoDimensions } from '../../../global/helpers';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import windowSize from '../../../util/windowSize';
|
||||
|
||||
export const MEDIA_VIEWER_MEDIA_QUERY = '(max-height: 640px)';
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
BASE_URL, IS_PACKAGED_ELECTRON, RE_LINK_TEMPLATE, RE_MENTION_TEMPLATE,
|
||||
} from '../../../config';
|
||||
import EMOJI_REGEX from '../../../lib/twemojiRegex';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { isDeepLink } from '../../../util/deepLinkParser';
|
||||
import {
|
||||
@ -16,7 +17,6 @@ import {
|
||||
} from '../../../util/emoji/emoji';
|
||||
import fixNonStandardEmoji from '../../../util/emoji/fixNonStandardEmoji';
|
||||
import { compact } from '../../../util/iteratees';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
import MentionLink from '../../middle/message/MentionLink';
|
||||
import SafeLink from '../SafeLink';
|
||||
|
||||
@ -3,9 +3,9 @@ import { getActions } from '../../../global';
|
||||
|
||||
import type { ActiveEmojiInteraction } from '../../../types';
|
||||
|
||||
import { IS_ELECTRON } from '../../../util/browser/windowEnvironment';
|
||||
import buildStyle from '../../../util/buildStyle';
|
||||
import safePlay from '../../../util/safePlay';
|
||||
import { IS_ELECTRON } from '../../../util/windowEnvironment';
|
||||
import { REM } from '../helpers/mediaDimensions';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { type TeactNode } from '../../../lib/teact/teact';
|
||||
|
||||
import { IS_IOS } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { IS_IOS } from '../../../util/windowEnvironment';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
@ -5,12 +5,12 @@ import React, {
|
||||
import type { ApiBusinessWorkHours } from '../../../api/types';
|
||||
|
||||
import { requestMeasure, requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatTime, formatWeekday } from '../../../util/dates/dateFormat';
|
||||
import {
|
||||
getUtcOffset, getWeekStart, shiftTimeRanges, splitDays,
|
||||
} from '../../../util/dates/workHours';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useSelectorSignal from '../../../hooks/data/useSelectorSignal';
|
||||
import useInterval from '../../../hooks/schedulers/useInterval';
|
||||
|
||||
@ -10,11 +10,11 @@ import {
|
||||
import { requestMeasure } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getStickerMediaHash } from '../../../global/helpers';
|
||||
import { selectIsPremiumPurchaseBlocked } from '../../../global/selectors';
|
||||
import { IS_OFFSET_PATH_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatDateToString } from '../../../util/dates/dateFormat';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import * as mediaLoader from '../../../util/mediaLoader';
|
||||
import { IS_OFFSET_PATH_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import renderText from '../helpers/renderText';
|
||||
|
||||
import useTimeout from '../../../hooks/schedulers/useTimeout';
|
||||
|
||||
@ -4,9 +4,9 @@ import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
import type { ApiEmojiStatusType, ApiReactionCustomEmoji } from '../../../api/types';
|
||||
|
||||
import { getStickerHashById } from '../../../global/helpers';
|
||||
import { IS_OFFSET_PATH_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import buildStyle from '../../../util/buildStyle';
|
||||
import { IS_OFFSET_PATH_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
|
||||
|
||||
@ -8,8 +8,8 @@ import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import { isSameReaction } from '../../../global/helpers';
|
||||
import { selectPerformanceSettingsValue, selectTabState } from '../../../global/selectors';
|
||||
import { IS_ANDROID, IS_IOS } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { IS_ANDROID, IS_IOS } from '../../../util/windowEnvironment';
|
||||
import { LOCAL_TGS_URLS } from '../helpers/animatedAssets';
|
||||
import { REM } from '../helpers/mediaDimensions';
|
||||
|
||||
|
||||
@ -10,11 +10,11 @@ import type { ReducerAction } from '../../hooks/useReducer';
|
||||
import { LeftColumnContent, SettingsScreens } from '../../types';
|
||||
|
||||
import { selectCurrentChat, selectIsForumPanelOpen, selectTabState } from '../../global/selectors';
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
import { captureControlledSwipe } from '../../util/swipeController';
|
||||
import {
|
||||
IS_APP, IS_FIREFOX, IS_MAC_OS, IS_TOUCH_ENV, LAYERS_ANIMATION_NAME,
|
||||
} from '../../util/windowEnvironment';
|
||||
} from '../../util/browser/windowEnvironment';
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
import { captureControlledSwipe } from '../../util/swipeController';
|
||||
|
||||
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
|
||||
import { useHotkeys } from '../../hooks/useHotkeys';
|
||||
|
||||
117
src/components/left/main/AccountMenuItems.tsx
Normal file
117
src/components/left/main/AccountMenuItems.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import type { ApiUser } from '../../../api/types';
|
||||
import type { CustomPeer } from '../../../types';
|
||||
|
||||
import { getCurrentMaxAccountCount, getCurrentProdAccountCount } from '../../../global/helpers';
|
||||
import { getAccountSlotUrl } from '../../../util/multiaccount';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useMultiaccountInfo from '../../../hooks/useMultiaccountInfo';
|
||||
|
||||
import Avatar from '../../common/Avatar';
|
||||
import FullNameTitle from '../../common/FullNameTitle';
|
||||
import MenuItem from '../../ui/MenuItem';
|
||||
import MenuSeparator from '../../ui/MenuSeparator';
|
||||
|
||||
type OwnProps = {
|
||||
currentUser: ApiUser;
|
||||
totalLimit: number;
|
||||
onSelectCurrent?: VoidFunction;
|
||||
};
|
||||
|
||||
const NOTIFICATION_DURATION = 7000;
|
||||
|
||||
const AccountMenuItems = ({
|
||||
currentUser,
|
||||
totalLimit,
|
||||
onSelectCurrent,
|
||||
}: OwnProps) => {
|
||||
const { showNotification } = getActions();
|
||||
const lang = useLang();
|
||||
const accounts = useMultiaccountInfo(currentUser);
|
||||
|
||||
const currentCount = getCurrentProdAccountCount();
|
||||
const maxCount = getCurrentMaxAccountCount();
|
||||
|
||||
const shouldShowLimit = currentCount >= maxCount;
|
||||
|
||||
const handleLimitClick = useLastCallback(() => {
|
||||
showNotification({
|
||||
title: lang('PremiumLimitAccountsTitle'),
|
||||
message: currentUser.isPremium ? lang('PremiumLimitAccounts') : lang('PremiumLimitAccountsNoPremium'),
|
||||
duration: NOTIFICATION_DURATION,
|
||||
});
|
||||
});
|
||||
|
||||
const newAccountUrl = useMemo(() => {
|
||||
if (!Object.values(accounts).length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (currentCount === totalLimit) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let freeIndex = 1;
|
||||
while (accounts[freeIndex]) {
|
||||
freeIndex += 1;
|
||||
}
|
||||
|
||||
return getAccountSlotUrl(freeIndex, true);
|
||||
}, [accounts, currentCount, totalLimit]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.entries(accounts || {})
|
||||
.sort(([, account]) => (account.userId === currentUser.id ? -1 : 1))
|
||||
.map(([slot, account], index, arr) => {
|
||||
const mockUser: CustomPeer = {
|
||||
title: [account.firstName, account.lastName].filter(Boolean).join(' '),
|
||||
isCustomPeer: true,
|
||||
peerColorId: account.color,
|
||||
emojiStatusId: account.emojiStatusId,
|
||||
isPremium: account.isPremium,
|
||||
};
|
||||
|
||||
const hasSeparator = account.userId === currentUser.id && (newAccountUrl || arr.length > 1);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
className="account-menu-item"
|
||||
customIcon={(
|
||||
<Avatar
|
||||
size="mini"
|
||||
className="account-avatar"
|
||||
peer={mockUser}
|
||||
previewUrl={account.avatarUri}
|
||||
/>
|
||||
)}
|
||||
onClick={account.userId === currentUser.id ? onSelectCurrent : undefined}
|
||||
href={account.userId !== currentUser.id ? getAccountSlotUrl(Number(slot)) : undefined}
|
||||
>
|
||||
<FullNameTitle peer={mockUser} withEmojiStatus emojiStatusSize={REM} />
|
||||
</MenuItem>
|
||||
{hasSeparator && <MenuSeparator />}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
{newAccountUrl && (
|
||||
<MenuItem
|
||||
icon="add"
|
||||
rel="noopener" // Allow referrer to be passed
|
||||
href={!shouldShowLimit ? newAccountUrl : undefined}
|
||||
onClick={shouldShowLimit ? handleLimitClick : undefined}
|
||||
>
|
||||
{lang('MenuAddAccount')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(AccountMenuItems);
|
||||
@ -48,9 +48,9 @@ import {
|
||||
selectUser,
|
||||
selectUserStatus,
|
||||
} from '../../../global/selectors';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { createLocationHash } from '../../../util/routing';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
import useSelectorSignal from '../../../hooks/data/useSelectorSignal';
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
|
||||
@ -14,11 +14,11 @@ import type { TabWithProperties } from '../../ui/TabList';
|
||||
import { ALL_FOLDER_ID } from '../../../config';
|
||||
import { selectCanShareFolder, selectTabState } from '../../../global/selectors';
|
||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import { captureEvents, SwipeDirection } from '../../../util/captureEvents';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||
|
||||
import useDerivedState from '../../../hooks/useDerivedState';
|
||||
|
||||
@ -19,10 +19,10 @@ import {
|
||||
FRESH_AUTH_PERIOD,
|
||||
SAVED_FOLDER_ID,
|
||||
} from '../../../config';
|
||||
import { IS_APP, IS_MAC_OS } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getOrderKey, getPinnedChatsCount } from '../../../util/folderManager';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { IS_APP, IS_MAC_OS } from '../../../util/windowEnvironment';
|
||||
|
||||
import usePeerStoriesPolling from '../../../hooks/polling/usePeerStoriesPolling';
|
||||
import useTopOverscroll from '../../../hooks/scroll/useTopOverscroll';
|
||||
|
||||
@ -22,11 +22,11 @@ import {
|
||||
selectTabState,
|
||||
selectTopicsInfo,
|
||||
} from '../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import { captureEvents, SwipeDirection } from '../../../util/captureEvents';
|
||||
import { waitForTransitionEnd } from '../../../util/cssAnimationEndListeners';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
|
||||
@ -9,8 +9,8 @@ import type { SettingsScreens } from '../../../types';
|
||||
import { LeftColumnContent } from '../../../types';
|
||||
|
||||
import { PRODUCTION_URL } from '../../../config';
|
||||
import { IS_ELECTRON, IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { IS_ELECTRON, IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useForumPanelRender from '../../../hooks/useForumPanelRender';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
@ -111,20 +111,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-status-effect {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
.StatusButton {
|
||||
.emoji-status-effect {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.emoji-status {
|
||||
overflow: visible;
|
||||
--custom-emoji-size: 1.5rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.emoji-status {
|
||||
overflow: visible;
|
||||
--custom-emoji-size: 1.5rem;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.StarIcon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
.StarIcon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
// @optimization
|
||||
@ -153,4 +155,19 @@
|
||||
right: -0.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.account-menu-item {
|
||||
--custom-emoji-size: 1rem;
|
||||
|
||||
.account-avatar {
|
||||
margin-inline: 0.375rem 1.125rem;
|
||||
}
|
||||
|
||||
.fullName {
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
line-height: 1;
|
||||
padding-top: 0.1875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { ThemeKey } from '../../../types';
|
||||
import { LeftColumnContent, SettingsScreens } from '../../../types';
|
||||
|
||||
import {
|
||||
@ -20,10 +20,11 @@ import {
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
} from '../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { IS_APP, IS_ELECTRON, IS_MAC_OS } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import { formatDateToString } from '../../../util/dates/dateFormat';
|
||||
import { IS_APP, IS_ELECTRON, IS_MAC_OS } from '../../../util/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useConnectionStatus from '../../../hooks/useConnectionStatus';
|
||||
@ -68,10 +69,10 @@ type StateProps =
|
||||
isLoading: boolean;
|
||||
globalSearchChatId?: string;
|
||||
searchDate?: number;
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
isMessageListOpen: boolean;
|
||||
isCurrentUserPremium?: boolean;
|
||||
isConnectionStatusMinimized: ISettings['isConnectionStatusMinimized'];
|
||||
isConnectionStatusMinimized?: boolean;
|
||||
areChatsLoaded?: boolean;
|
||||
hasPasscode?: boolean;
|
||||
canSetPasscode?: boolean;
|
||||
@ -109,7 +110,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
}) => {
|
||||
const {
|
||||
setGlobalSearchDate,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
setGlobalSearchChatId,
|
||||
lockScreen,
|
||||
requestNextSettingsScreen,
|
||||
@ -185,7 +186,7 @@ const LeftMainHeader: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const toggleConnectionStatus = useLastCallback(() => {
|
||||
setSettingOption({ isConnectionStatusMinimized: !isConnectionStatusMinimized });
|
||||
setSharedSettingOption({ isConnectionStatusMinimized: !isConnectionStatusMinimized });
|
||||
});
|
||||
|
||||
const handleLockScreen = useLastCallback(() => {
|
||||
@ -340,7 +341,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
connectionState, isSyncing, isFetchingDifference,
|
||||
} = global;
|
||||
const { isConnectionStatusMinimized } = global.settings.byKey;
|
||||
const { isConnectionStatusMinimized } = selectSharedSettings(global);
|
||||
|
||||
return {
|
||||
searchQuery,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiUser } from '../../../api/types';
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { AnimationLevel, ThemeKey } from '../../../types';
|
||||
|
||||
@ -20,10 +21,13 @@ import {
|
||||
INITIAL_PERFORMANCE_STATE_MID,
|
||||
INITIAL_PERFORMANCE_STATE_MIN,
|
||||
} from '../../../global/initialState';
|
||||
import { selectTabState, selectTheme } from '../../../global/selectors';
|
||||
import { selectTabState, selectTheme, selectUser } from '../../../global/selectors';
|
||||
import { selectPremiumLimit } from '../../../global/selectors/limits';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { IS_MULTIACCOUNT_SUPPORTED } from '../../../util/browser/globalEnvironment';
|
||||
import { IS_ELECTRON } from '../../../util/browser/windowEnvironment';
|
||||
import { getPromptInstall } from '../../../util/installPrompt';
|
||||
import { switchPermanentWebVersion } from '../../../util/permanentWebVersion';
|
||||
import { IS_ELECTRON } from '../../../util/windowEnvironment';
|
||||
|
||||
import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManager';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -32,8 +36,10 @@ import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import AttachBotItem from '../../middle/composer/AttachBotItem';
|
||||
import MenuItem from '../../ui/MenuItem';
|
||||
import MenuSeparator from '../../ui/MenuSeparator';
|
||||
import Switcher from '../../ui/Switcher';
|
||||
import Toggle from '../../ui/Toggle';
|
||||
import AccountMenuItems from './AccountMenuItems';
|
||||
|
||||
type OwnProps = {
|
||||
onSelectSettings: NoneToVoidFunction;
|
||||
@ -45,9 +51,11 @@ type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
animationLevel: AnimationLevel;
|
||||
currentUser?: ApiUser;
|
||||
theme: ThemeKey;
|
||||
canInstall?: boolean;
|
||||
attachBots: GlobalState['attachMenu']['bots'];
|
||||
accountsTotalLimit: number;
|
||||
} & Pick<GlobalState, 'currentUserId' | 'archiveSettings'>;
|
||||
|
||||
const LeftSideMenuItems = ({
|
||||
@ -57,6 +65,8 @@ const LeftSideMenuItems = ({
|
||||
theme,
|
||||
canInstall,
|
||||
attachBots,
|
||||
currentUser,
|
||||
accountsTotalLimit,
|
||||
onSelectArchived,
|
||||
onSelectContacts,
|
||||
onSelectSettings,
|
||||
@ -65,7 +75,7 @@ const LeftSideMenuItems = ({
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
openChat,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
updatePerformanceSettings,
|
||||
openChatByUsername,
|
||||
openUrl,
|
||||
@ -91,8 +101,8 @@ const LeftSideMenuItems = ({
|
||||
e.stopPropagation();
|
||||
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||||
|
||||
setSettingOption({ theme: newTheme });
|
||||
setSettingOption({ shouldUseSystemTheme: false });
|
||||
setSharedSettingOption({ theme: newTheme });
|
||||
setSharedSettingOption({ shouldUseSystemTheme: false });
|
||||
});
|
||||
|
||||
const handleAnimationLevelChange = useLastCallback((e: React.SyntheticEvent<HTMLElement>) => {
|
||||
@ -106,7 +116,7 @@ const LeftSideMenuItems = ({
|
||||
? INITIAL_PERFORMANCE_STATE_MIN
|
||||
: (newLevel === ANIMATION_LEVEL_MAX ? INITIAL_PERFORMANCE_STATE_MAX : INITIAL_PERFORMANCE_STATE_MID);
|
||||
|
||||
setSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
setSharedSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
updatePerformanceSettings(performanceSettings);
|
||||
});
|
||||
|
||||
@ -132,6 +142,16 @@ const LeftSideMenuItems = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
{IS_MULTIACCOUNT_SUPPORTED && currentUser && (
|
||||
<>
|
||||
<AccountMenuItems
|
||||
currentUser={currentUser}
|
||||
totalLimit={accountsTotalLimit}
|
||||
onSelectCurrent={onSelectSettings}
|
||||
/>
|
||||
<MenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<MenuItem
|
||||
icon="saved-messages"
|
||||
onClick={handleSelectSaved}
|
||||
@ -244,16 +264,18 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
currentUserId, archiveSettings,
|
||||
} = global;
|
||||
const { animationLevel } = global.settings.byKey;
|
||||
const { animationLevel } = selectSharedSettings(global);
|
||||
const attachBots = global.attachMenu.bots;
|
||||
|
||||
return {
|
||||
currentUserId,
|
||||
currentUser: selectUser(global, currentUserId!),
|
||||
theme: selectTheme(global),
|
||||
animationLevel,
|
||||
canInstall: Boolean(tabState.canInstall),
|
||||
archiveSettings,
|
||||
attachBots,
|
||||
accountsTotalLimit: selectPremiumLimit(global, 'moreAccounts'),
|
||||
};
|
||||
},
|
||||
)(LeftSideMenuItems));
|
||||
|
||||
@ -64,7 +64,7 @@ const StatusButton: FC<StateProps> = ({ emojiStatus, collectibleStatuses }) => {
|
||||
}, [openStatusPicker]);
|
||||
|
||||
return (
|
||||
<div className="extra-spacing">
|
||||
<div className="StatusButton extra-spacing">
|
||||
{Boolean(isEffectShown && emojiStatus) && (
|
||||
<CustomEmojiEffect
|
||||
reaction={emojiStatus!}
|
||||
|
||||
@ -27,9 +27,9 @@ import {
|
||||
selectThreadParam,
|
||||
selectTopics,
|
||||
} from '../../../global/selectors';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { createLocationHash } from '../../../util/routing';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
|
||||
@ -5,8 +5,8 @@ import type { ApiChat, ApiTopic } from '../../../../api/types';
|
||||
import type { MenuItemContextAction } from '../../../ui/ListItem';
|
||||
|
||||
import { getCanManageTopic, getHasAdminRight } from '../../../../global/helpers';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../../util/browser/windowEnvironment';
|
||||
import { compact } from '../../../../util/iteratees';
|
||||
import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../../util/windowEnvironment';
|
||||
|
||||
import useOldLang from '../../../../hooks/useOldLang';
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { memo, useCallback, useState } from '../../../lib/teact/teact';
|
||||
|
||||
import { LeftColumnContent } from '../../../types';
|
||||
|
||||
import { LAYERS_ANIMATION_NAME } from '../../../util/windowEnvironment';
|
||||
import { LAYERS_ANIMATION_NAME } from '../../../util/browser/windowEnvironment';
|
||||
|
||||
import Transition from '../../ui/Transition';
|
||||
import NewChatStep1 from './NewChatStep1';
|
||||
|
||||
@ -2,13 +2,14 @@ import type {
|
||||
ApiChat, ApiGlobalMessageSearchType, ApiMessage, ApiUser,
|
||||
} from '../../../../api/types';
|
||||
import type { GlobalState, TabState } from '../../../../global/types';
|
||||
import type { ISettings } from '../../../../types';
|
||||
import type { ThemeKey } from '../../../../types';
|
||||
import type { SearchResultKey } from '../../../../util/keys/searchResultKey';
|
||||
|
||||
import { selectChat, selectTabState, selectTheme } from '../../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../../global/selectors/sharedState';
|
||||
|
||||
export type StateProps = {
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
isLoading?: boolean;
|
||||
chatsById: Record<string, ApiChat>;
|
||||
usersById: Record<string, ApiUser>;
|
||||
@ -29,6 +30,8 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
|
||||
fetchingStatus, resultsByType, chatId,
|
||||
} = tabState.globalSearch;
|
||||
|
||||
const { shouldWarnAboutSvg } = selectSharedSettings(global);
|
||||
|
||||
// One component is used for two different types of results.
|
||||
// The differences between them are only in the isVoice property.
|
||||
// The rest of the search results use their own personal components.
|
||||
@ -50,7 +53,7 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) {
|
||||
searchChatId: chatId,
|
||||
activeDownloads,
|
||||
isChatProtected: chatId ? selectChat(global, chatId)?.isProtected : undefined,
|
||||
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
|
||||
shouldWarnAboutSvg,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import type { FolderEditDispatch, FoldersState } from '../../../hooks/reducers/u
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { selectTabState } from '../../../global/selectors';
|
||||
import { LAYERS_ANIMATION_NAME } from '../../../util/windowEnvironment';
|
||||
import { LAYERS_ANIMATION_NAME } from '../../../util/browser/windowEnvironment';
|
||||
|
||||
import useTwoFaReducer from '../../../hooks/reducers/useTwoFaReducer';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
@ -5,7 +5,7 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiSticker, ApiStickerSet } from '../../../api/types';
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { AccountSettings } from '../../../types';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
@ -23,7 +23,7 @@ type OwnProps = {
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
type StateProps = Pick<ISettings, (
|
||||
type StateProps = Pick<AccountSettings, (
|
||||
'shouldSuggestCustomEmoji'
|
||||
)> & {
|
||||
customEmojiSetIds?: string[];
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { AccountSettings } from '../../../types';
|
||||
|
||||
import { AUTODOWNLOAD_FILESIZE_MB_LIMITS } from '../../../config';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
@ -18,7 +18,7 @@ type OwnProps = {
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
type StateProps = Pick<ISettings, (
|
||||
type StateProps = Pick<AccountSettings, (
|
||||
'canAutoLoadPhotoFromContacts' |
|
||||
'canAutoLoadPhotoInPrivateChats' |
|
||||
'canAutoLoadPhotoInGroups' |
|
||||
|
||||
@ -4,7 +4,7 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { AccountSettings } from '../../../types';
|
||||
|
||||
import { SUPPORTED_TRANSLATION_LANGUAGES } from '../../../config';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
@ -50,7 +50,7 @@ type OwnProps = {
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
type StateProps = Pick<ISettings, 'doNotTranslate'>;
|
||||
type StateProps = Pick<AccountSettings, 'doNotTranslate'>;
|
||||
|
||||
const SettingsDoNotTranslate: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
|
||||
@ -5,9 +5,14 @@ import React, {
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import { DEBUG_LOG_FILENAME } from '../../../config';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import {
|
||||
IS_ELECTRON,
|
||||
IS_SNAP_EFFECT_SUPPORTED,
|
||||
IS_WAVE_TRANSFORM_SUPPORTED,
|
||||
} from '../../../util/browser/windowEnvironment';
|
||||
import { getDebugLogs } from '../../../util/debugConsole';
|
||||
import download from '../../../util/download';
|
||||
import { IS_ELECTRON, IS_SNAP_EFFECT_SUPPORTED, IS_WAVE_TRANSFORM_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
@ -39,7 +44,7 @@ const SettingsExperimental: FC<OwnProps & StateProps> = ({
|
||||
shouldCollectDebugLogs,
|
||||
shouldDebugExportedSenders,
|
||||
}) => {
|
||||
const { requestConfetti, setSettingOption, requestWave } = getActions();
|
||||
const { requestConfetti, setSharedSettingOption, requestWave } = getActions();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const snapButtonRef = useRef<HTMLDivElement>(null);
|
||||
@ -128,7 +133,7 @@ const SettingsExperimental: FC<OwnProps & StateProps> = ({
|
||||
label="Allow HTTP Transport"
|
||||
checked={Boolean(shouldAllowHttpTransport)}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={() => setSettingOption({ shouldAllowHttpTransport: !shouldAllowHttpTransport })}
|
||||
onCheck={() => setSharedSettingOption({ shouldAllowHttpTransport: !shouldAllowHttpTransport })}
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
@ -136,21 +141,21 @@ const SettingsExperimental: FC<OwnProps & StateProps> = ({
|
||||
disabled={!shouldAllowHttpTransport}
|
||||
checked={Boolean(shouldForceHttpTransport)}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={() => setSettingOption({ shouldForceHttpTransport: !shouldForceHttpTransport })}
|
||||
onCheck={() => setSharedSettingOption({ shouldForceHttpTransport: !shouldForceHttpTransport })}
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
label={lang('DebugMenuEnableLogs')}
|
||||
checked={Boolean(shouldCollectDebugLogs)}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={() => setSettingOption({ shouldCollectDebugLogs: !shouldCollectDebugLogs })}
|
||||
onCheck={() => setSharedSettingOption({ shouldCollectDebugLogs: !shouldCollectDebugLogs })}
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
label="Enable exported senders debug"
|
||||
checked={Boolean(shouldDebugExportedSenders)}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onCheck={() => setSettingOption({ shouldDebugExportedSenders: !shouldDebugExportedSenders })}
|
||||
onCheck={() => setSharedSettingOption({ shouldDebugExportedSenders: !shouldDebugExportedSenders })}
|
||||
/>
|
||||
|
||||
{IS_ELECTRON && (
|
||||
@ -174,11 +179,18 @@ const SettingsExperimental: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal(
|
||||
(global): StateProps => {
|
||||
const {
|
||||
shouldForceHttpTransport,
|
||||
shouldAllowHttpTransport,
|
||||
shouldCollectDebugLogs,
|
||||
shouldDebugExportedSenders,
|
||||
} = selectSharedSettings(global);
|
||||
|
||||
return {
|
||||
shouldForceHttpTransport: global.settings.byKey.shouldForceHttpTransport,
|
||||
shouldAllowHttpTransport: global.settings.byKey.shouldAllowHttpTransport,
|
||||
shouldCollectDebugLogs: global.settings.byKey.shouldCollectDebugLogs,
|
||||
shouldDebugExportedSenders: global.settings.byKey.shouldDebugExportedSenders,
|
||||
shouldForceHttpTransport,
|
||||
shouldAllowHttpTransport,
|
||||
shouldCollectDebugLogs,
|
||||
shouldDebugExportedSenders,
|
||||
};
|
||||
},
|
||||
)(SettingsExperimental));
|
||||
|
||||
@ -4,16 +4,16 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ISettings, TimeFormat } from '../../../types';
|
||||
import type { SharedSettings, ThemeKey, TimeFormat } from '../../../types';
|
||||
import type { IRadioOption } from '../../ui/RadioGroup';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { pick } from '../../../util/iteratees';
|
||||
import { setTimeFormat } from '../../../util/oldLangProvider';
|
||||
import { getSystemTheme } from '../../../util/systemTheme';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import {
|
||||
IS_ANDROID, IS_ELECTRON, IS_IOS, IS_MAC_OS, IS_WINDOWS,
|
||||
} from '../../../util/windowEnvironment';
|
||||
} from '../../../util/browser/windowEnvironment';
|
||||
import { setTimeFormat } from '../../../util/oldLangProvider';
|
||||
import { getSystemTheme } from '../../../util/systemTheme';
|
||||
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
@ -31,28 +31,26 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps =
|
||||
Pick<ISettings, (
|
||||
Pick<SharedSettings, (
|
||||
'messageTextSize' |
|
||||
'animationLevel' |
|
||||
'messageSendKeyCombo' |
|
||||
'timeFormat'
|
||||
)> & {
|
||||
theme: ISettings['theme'];
|
||||
shouldUseSystemTheme: boolean;
|
||||
};
|
||||
'timeFormat' |
|
||||
'theme' |
|
||||
'shouldUseSystemTheme'
|
||||
)>;
|
||||
|
||||
const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
onScreenSelect,
|
||||
onReset,
|
||||
messageTextSize,
|
||||
messageSendKeyCombo,
|
||||
timeFormat,
|
||||
theme,
|
||||
shouldUseSystemTheme,
|
||||
onScreenSelect,
|
||||
onReset,
|
||||
}) => {
|
||||
const {
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -96,26 +94,26 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
document.documentElement.style.setProperty('--message-text-size', `${newSize}px`);
|
||||
document.documentElement.setAttribute('data-message-text-size', newSize.toString());
|
||||
|
||||
setSettingOption({ messageTextSize: newSize });
|
||||
}, [setSettingOption]);
|
||||
setSharedSettingOption({ messageTextSize: newSize });
|
||||
}, []);
|
||||
|
||||
const handleAppearanceThemeChange = useCallback((value: string) => {
|
||||
const newTheme = value === 'auto' ? getSystemTheme() : value as ISettings['theme'];
|
||||
const newTheme = value === 'auto' ? getSystemTheme() : value as ThemeKey;
|
||||
|
||||
setSettingOption({ theme: newTheme });
|
||||
setSettingOption({ shouldUseSystemTheme: value === 'auto' });
|
||||
}, [setSettingOption]);
|
||||
setSharedSettingOption({ theme: newTheme });
|
||||
setSharedSettingOption({ shouldUseSystemTheme: value === 'auto' });
|
||||
}, []);
|
||||
|
||||
const handleTimeFormatChange = useCallback((newTimeFormat: string) => {
|
||||
setSettingOption({ timeFormat: newTimeFormat as TimeFormat });
|
||||
setSettingOption({ wasTimeFormatSetManually: true });
|
||||
setSharedSettingOption({ timeFormat: newTimeFormat as TimeFormat });
|
||||
setSharedSettingOption({ wasTimeFormatSetManually: true });
|
||||
|
||||
setTimeFormat(newTimeFormat as TimeFormat);
|
||||
}, [setSettingOption]);
|
||||
}, []);
|
||||
|
||||
const handleMessageSendComboChange = useCallback((newCombo: string) => {
|
||||
setSettingOption({ messageSendKeyCombo: newCombo as ISettings['messageSendKeyCombo'] });
|
||||
}, [setSettingOption]);
|
||||
setSharedSettingOption({ messageSendKeyCombo: newCombo as SharedSettings['messageSendKeyCombo'] });
|
||||
}, []);
|
||||
|
||||
const [isTrayIconEnabled, setIsTrayIconEnabled] = useState(false);
|
||||
useEffect(() => {
|
||||
@ -204,17 +202,18 @@ const SettingsGeneral: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { theme, shouldUseSystemTheme } = global.settings.byKey;
|
||||
const {
|
||||
theme,
|
||||
shouldUseSystemTheme,
|
||||
messageSendKeyCombo,
|
||||
messageTextSize,
|
||||
timeFormat,
|
||||
} = selectSharedSettings(global);
|
||||
|
||||
return {
|
||||
...pick(global.settings.byKey, [
|
||||
'messageTextSize',
|
||||
'animationLevel',
|
||||
'messageSendKeyCombo',
|
||||
'isSensitiveEnabled',
|
||||
'canChangeSensitive',
|
||||
'timeFormat',
|
||||
]),
|
||||
messageSendKeyCombo,
|
||||
messageTextSize,
|
||||
timeFormat,
|
||||
theme,
|
||||
shouldUseSystemTheme,
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import type { ThemeKey } from '../../../types';
|
||||
import { SettingsScreens, UPLOADING_WALLPAPER_SLUG } from '../../../types';
|
||||
|
||||
import { DARK_THEME_PATTERN_COLOR, DEFAULT_PATTERN_COLOR } from '../../../config';
|
||||
import { selectTheme } from '../../../global/selectors';
|
||||
import { selectTheme, selectThemeValues } from '../../../global/selectors';
|
||||
import { getAverageColor, getPatternColor, rgb2hex } from '../../../util/colors';
|
||||
import { validateFiles } from '../../../util/files';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
@ -173,7 +173,7 @@ const SettingsGeneralBackground: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const theme = selectTheme(global);
|
||||
const { background, isBlurred } = global.settings.themes[theme] || {};
|
||||
const { background, isBlurred } = selectThemeValues(global, theme) || {};
|
||||
const { loadedWallpapers } = global.settings;
|
||||
|
||||
return {
|
||||
|
||||
@ -8,7 +8,7 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type { ThemeKey } from '../../../types';
|
||||
import type { RealTouchEvent } from '../../../util/captureEvents';
|
||||
|
||||
import { selectTheme } from '../../../global/selectors';
|
||||
import { selectTheme, selectThemeValues } from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { captureEvents } from '../../../util/captureEvents';
|
||||
import {
|
||||
@ -351,7 +351,7 @@ function drawHue(canvas: HTMLCanvasElement) {
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const theme = selectTheme(global);
|
||||
const { backgroundColor } = global.settings.themes[theme] || {};
|
||||
const { backgroundColor } = selectThemeValues(global, theme) || {};
|
||||
return {
|
||||
backgroundColor,
|
||||
theme,
|
||||
|
||||
@ -4,13 +4,13 @@ import React, {
|
||||
} from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiLanguage } from '../../../api/types';
|
||||
import type { ISettings, LangCode } from '../../../types';
|
||||
import type { AccountSettings, LangCode, SharedSettings } from '../../../types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { selectIsCurrentUserPremium } from '../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { IS_TRANSLATION_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import { oldSetLanguage } from '../../../util/oldLangProvider';
|
||||
import { IS_TRANSLATION_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
@ -30,8 +30,8 @@ type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
isCurrentUserPremium: boolean;
|
||||
languages?: ApiLanguage[];
|
||||
} & Pick<ISettings, | 'language' | 'canTranslate' | 'canTranslateChats' | 'doNotTranslate'>;
|
||||
} & Pick<AccountSettings, 'canTranslate' | 'canTranslateChats' | 'doNotTranslate'>
|
||||
& Pick<SharedSettings, 'language' | 'languages'>;
|
||||
|
||||
const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
@ -47,6 +47,7 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
const {
|
||||
loadLanguages,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
openPremiumModal,
|
||||
} = getActions();
|
||||
|
||||
@ -70,7 +71,7 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
void oldSetLanguage(langCode as LangCode, () => {
|
||||
unmarkIsLoading();
|
||||
|
||||
setSettingOption({ language: langCode as LangCode });
|
||||
setSharedSettingOption({ language: langCode as LangCode });
|
||||
});
|
||||
});
|
||||
|
||||
@ -182,9 +183,9 @@ const SettingsLanguage: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const {
|
||||
language, canTranslate, canTranslateChats, doNotTranslate,
|
||||
canTranslate, canTranslateChats, doNotTranslate,
|
||||
} = global.settings.byKey;
|
||||
const languages = global.settings.languages;
|
||||
const { language, languages } = selectSharedSettings(global);
|
||||
|
||||
const isCurrentUserPremium = selectIsCurrentUserPremium(global);
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
} from '../../../global/initialState';
|
||||
import { selectPerformanceSettings } from '../../../global/selectors';
|
||||
import { areDeepEqual } from '../../../util/areDeepEqual';
|
||||
import { IS_BACKDROP_BLUR_SUPPORTED, IS_SNAP_EFFECT_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
import { IS_BACKDROP_BLUR_SUPPORTED, IS_SNAP_EFFECT_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -81,7 +81,7 @@ function SettingsPerformance({
|
||||
onReset,
|
||||
}: OwnProps & StateProps) {
|
||||
const {
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
updatePerformanceSettings,
|
||||
} = getActions();
|
||||
|
||||
@ -138,9 +138,9 @@ function SettingsPerformance({
|
||||
? INITIAL_PERFORMANCE_STATE_MIN
|
||||
: (newLevel === ANIMATION_LEVEL_MED ? INITIAL_PERFORMANCE_STATE_MID : INITIAL_PERFORMANCE_STATE_MAX);
|
||||
|
||||
setSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
setSharedSettingOption({ animationLevel: newLevel as AnimationLevel });
|
||||
updatePerformanceSettings(performance);
|
||||
}, [setSettingOption]);
|
||||
}, []);
|
||||
|
||||
const handlePropertyGroupChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, checked } = e.target;
|
||||
|
||||
@ -7,6 +7,7 @@ import type { GlobalState } from '../../../global/types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { selectCanSetPasscode, selectIsCurrentUserPremium } from '../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -66,7 +67,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
loadGlobalPrivacySettings,
|
||||
updateGlobalPrivacySettings,
|
||||
loadWebAuthorizations,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
} = getActions();
|
||||
|
||||
useEffect(() => {
|
||||
@ -97,7 +98,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
}, [updateGlobalPrivacySettings]);
|
||||
|
||||
const handleChatInTitleChange = useCallback((isChecked: boolean) => {
|
||||
setSettingOption({
|
||||
setSharedSettingOption({
|
||||
canDisplayChatInTitle: isChecked,
|
||||
});
|
||||
}, []);
|
||||
@ -405,7 +406,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
settings: {
|
||||
byKey: {
|
||||
hasPassword, isSensitiveEnabled, canChangeSensitive, shouldArchiveAndMuteNewNonContact,
|
||||
canDisplayChatInTitle, shouldNewNonContactPeersRequirePremium, nonContactPeersPaidStars,
|
||||
shouldNewNonContactPeersRequirePremium, nonContactPeersPaidStars,
|
||||
},
|
||||
privacy,
|
||||
},
|
||||
@ -416,6 +417,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
appConfig,
|
||||
} = global;
|
||||
|
||||
const { canDisplayChatInTitle } = selectSharedSettings(global);
|
||||
const shouldChargeForMessages = Boolean(nonContactPeersPaidStars);
|
||||
|
||||
return {
|
||||
|
||||
@ -10,7 +10,7 @@ import type {
|
||||
ApiSticker,
|
||||
ApiStickerSet,
|
||||
} from '../../../api/types';
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { AccountSettings } from '../../../types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
@ -36,7 +36,7 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps =
|
||||
Pick<ISettings, (
|
||||
Pick<AccountSettings, (
|
||||
'shouldSuggestStickers' | 'shouldUpdateStickerSetOrder'
|
||||
)> & {
|
||||
addedSetIds?: string[];
|
||||
|
||||
@ -7,7 +7,7 @@ import { withGlobal } from '../../../../global';
|
||||
import type { ApiSticker } from '../../../../api/types';
|
||||
|
||||
import { selectAnimatedEmoji, selectTabState } from '../../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/windowEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/browser/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||
import useHistoryBack from '../../../../hooks/useHistoryBack';
|
||||
|
||||
@ -7,7 +7,7 @@ import { withGlobal } from '../../../../global';
|
||||
import type { ApiSticker } from '../../../../api/types';
|
||||
|
||||
import { selectAnimatedEmoji } from '../../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/windowEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/browser/windowEnvironment';
|
||||
import renderText from '../../../common/helpers/renderText';
|
||||
|
||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||
|
||||
@ -6,10 +6,10 @@ import type { TabState } from '../../global/types';
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
|
||||
import { selectTabState } from '../../global/selectors';
|
||||
import { IS_OPFS_SUPPORTED, IS_SERVICE_WORKER_SUPPORTED, MAX_BUFFER_SIZE } from '../../util/browser/windowEnvironment';
|
||||
import download from '../../util/download';
|
||||
import generateUniqueId from '../../util/generateUniqueId';
|
||||
import * as mediaLoader from '../../util/mediaLoader';
|
||||
import { IS_OPFS_SUPPORTED, IS_SERVICE_WORKER_SUPPORTED, MAX_BUFFER_SIZE } from '../../util/windowEnvironment';
|
||||
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
import useRunDebounced from '../../hooks/useRunDebounced';
|
||||
|
||||
@ -30,13 +30,14 @@ import {
|
||||
selectTabState,
|
||||
selectUser,
|
||||
} from '../../global/selectors';
|
||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||
import { IS_ANDROID, IS_ELECTRON, IS_WAVE_TRANSFORM_SUPPORTED } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { waitForTransitionEnd } from '../../util/cssAnimationEndListeners';
|
||||
import { processDeepLink } from '../../util/deeplink';
|
||||
import { Bundles, loadBundle } from '../../util/moduleLoader';
|
||||
import { parseInitialLocationHash, parseLocationHash } from '../../util/routing';
|
||||
import updateIcon from '../../util/updateIcon';
|
||||
import { IS_ANDROID, IS_ELECTRON, IS_WAVE_TRANSFORM_SUPPORTED } from '../../util/windowEnvironment';
|
||||
|
||||
import useInterval from '../../hooks/schedulers/useInterval';
|
||||
import useTimeout from '../../hooks/schedulers/useTimeout';
|
||||
@ -598,11 +599,6 @@ const Main = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { isMobile }): StateProps => {
|
||||
const {
|
||||
settings: {
|
||||
byKey: {
|
||||
wasTimeFormatSetManually,
|
||||
},
|
||||
},
|
||||
currentUserId,
|
||||
} = global;
|
||||
|
||||
@ -631,6 +627,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
deleteFolderDialogModal,
|
||||
} = selectTabState(global);
|
||||
|
||||
const { wasTimeFormatSetManually } = selectSharedSettings(global);
|
||||
|
||||
const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId);
|
||||
const gameTitle = gameMessage?.content.game?.title;
|
||||
const { chatId } = selectCurrentMessageList(global) || {};
|
||||
|
||||
@ -8,8 +8,8 @@ import type { ApiCountryCode, ApiUser, ApiUserStatus } from '../../api/types';
|
||||
|
||||
import { getUserStatus } from '../../global/helpers';
|
||||
import { selectUser, selectUserStatus } from '../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import { formatPhoneNumberWithCode } from '../../util/phoneNumber';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
|
||||
import useCurrentOrPrev from '../../hooks/useCurrentOrPrev';
|
||||
|
||||
@ -88,6 +88,7 @@ const LIMITS_TITLES: Record<ApiLimitTypeForPromo, string> = {
|
||||
dialogFilters: 'FoldersLimitTitle',
|
||||
dialogFiltersChats: 'ChatPerFolderLimitTitle',
|
||||
recommendedChannels: 'SimilarChannelsLimitTitle',
|
||||
moreAccounts: 'ConnectedAccountsLimitTitle',
|
||||
};
|
||||
|
||||
const LIMITS_DESCRIPTIONS: Record<ApiLimitTypeForPromo, string> = {
|
||||
@ -101,6 +102,7 @@ const LIMITS_DESCRIPTIONS: Record<ApiLimitTypeForPromo, string> = {
|
||||
dialogFilters: 'FoldersLimitSubtitle',
|
||||
dialogFiltersChats: 'ChatPerFolderLimitSubtitle',
|
||||
recommendedChannels: 'SimilarChannelsLimitSubtitle',
|
||||
moreAccounts: 'ConnectedAccountsLimitSubtitle',
|
||||
};
|
||||
|
||||
const BORDER_THRESHOLD = 20;
|
||||
@ -216,13 +218,16 @@ const PremiumFeatureModal: FC<OwnProps> = ({
|
||||
stopScrolling();
|
||||
});
|
||||
|
||||
const currentSection = filteredSections[currentSlideIndex];
|
||||
const hasHeaderBackdrop = currentSection !== 'double_limits' && currentSection !== 'stories';
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Button
|
||||
round
|
||||
size="smaller"
|
||||
className={buildClassName(styles.backButton, currentSlideIndex !== 0 && styles.whiteBackButton)}
|
||||
color={currentSlideIndex === 0 ? 'translucent' : 'translucent-white'}
|
||||
className={buildClassName(styles.backButton, hasHeaderBackdrop && styles.whiteBackButton)}
|
||||
color={hasHeaderBackdrop ? 'translucent-white' : 'translucent'}
|
||||
onClick={onBack}
|
||||
ariaLabel={oldLang('Back')}
|
||||
>
|
||||
|
||||
@ -11,9 +11,10 @@ import { MEDIA_TIMESTAMP_SAVE_MINIMUM_DURATION } from '../../config';
|
||||
import {
|
||||
selectIsMessageProtected, selectMessageTimestampableDuration, selectTabState,
|
||||
} from '../../global/selectors';
|
||||
import { ARE_WEBCODECS_SUPPORTED } from '../../util/browser/globalEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import stopEvent from '../../util/stopEvent';
|
||||
import { ARE_WEBCODECS_SUPPORTED, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { calculateMediaViewerDimensions } from '../common/helpers/mediaDimensions';
|
||||
import { renderMessageText } from '../common/helpers/renderMessageText';
|
||||
import getViewableMedia from './helpers/getViewableMedia';
|
||||
|
||||
@ -3,9 +3,9 @@ import React, { useEffect, useState } from '../../lib/teact/teact';
|
||||
|
||||
import type { TextPart } from '../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { throttle } from '../../util/schedulers';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
import { REM } from '../common/helpers/mediaDimensions';
|
||||
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
@ -8,6 +8,7 @@ import type { RealTouchEvent } from '../../util/captureEvents';
|
||||
import type { MediaViewerItem } from './helpers/getViewableMedia';
|
||||
|
||||
import { animateNumber, timingFunctions } from '../../util/animation';
|
||||
import { IS_IOS, IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import {
|
||||
captureEvents,
|
||||
@ -17,7 +18,6 @@ import {
|
||||
} from '../../util/captureEvents';
|
||||
import { clamp, isBetween, round } from '../../util/math';
|
||||
import { debounce } from '../../util/schedulers';
|
||||
import { IS_IOS, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import useTimeout from '../../hooks/schedulers/useTimeout';
|
||||
import useDebouncedCallback from '../../hooks/useDebouncedCallback';
|
||||
|
||||
@ -9,11 +9,11 @@ import type { BufferedRange } from '../../hooks/useBuffering';
|
||||
|
||||
import { createVideoPreviews, getPreviewDimensions, renderVideoPreview } from '../../lib/video-preview/VideoPreview';
|
||||
import { animateNumber } from '../../util/animation';
|
||||
import { IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { captureEvents } from '../../util/captureEvents';
|
||||
import { formatMediaDuration } from '../../util/dates/dateFormat';
|
||||
import { clamp, round } from '../../util/math';
|
||||
import { IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import { useThrottledSignal } from '../../hooks/useAsyncResolvers';
|
||||
import useCurrentTimeSignal from '../../hooks/useCurrentTimeSignal';
|
||||
|
||||
@ -6,10 +6,10 @@ import { getActions } from '../../global';
|
||||
|
||||
import type { ApiDimensions } from '../../api/types';
|
||||
|
||||
import { IS_IOS, IS_TOUCH_ENV, IS_YA_BROWSER } from '../../util/browser/windowEnvironment';
|
||||
import { clamp } from '../../util/math';
|
||||
import safePlay from '../../util/safePlay';
|
||||
import stopEvent from '../../util/stopEvent';
|
||||
import { IS_IOS, IS_TOUCH_ENV, IS_YA_BROWSER } from '../../util/windowEnvironment';
|
||||
|
||||
import useUnsupportedMedia from '../../hooks/media/useUnsupportedMedia';
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
|
||||
@ -9,10 +9,10 @@ import type { ApiDimensions } from '../../api/types';
|
||||
import type { BufferedRange } from '../../hooks/useBuffering';
|
||||
import type { IconName } from '../../types/icons';
|
||||
|
||||
import { IS_IOS, IS_TOUCH_ENV } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { formatMediaDuration } from '../../util/dates/dateFormat';
|
||||
import { formatFileSize } from '../../util/textFormat';
|
||||
import { IS_IOS, IS_TOUCH_ENV } from '../../util/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
import useCurrentTimeSignal from '../../hooks/useCurrentTimeSignal';
|
||||
|
||||
@ -5,10 +5,10 @@ import { ANIMATION_END_DELAY, MESSAGE_CONTENT_SELECTOR } from '../../../config';
|
||||
import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getMessageHtmlId } from '../../../global/helpers';
|
||||
import { applyStyles } from '../../../util/animation';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import stopEvent from '../../../util/stopEvent';
|
||||
import getOffsetToContainer from '../../../util/visibility/getOffsetToContainer';
|
||||
import { isElementInViewport } from '../../../util/visibility/isElementInViewport';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import windowSize from '../../../util/windowSize';
|
||||
import {
|
||||
calculateDimensions,
|
||||
|
||||
@ -10,8 +10,8 @@ import type { ActiveEmojiInteraction } from '../../types';
|
||||
import {
|
||||
selectAnimatedEmojiEffect,
|
||||
} from '../../global/selectors';
|
||||
import { IS_ANDROID } from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { IS_ANDROID } from '../../util/windowEnvironment';
|
||||
|
||||
import useFlag from '../../hooks/useFlag';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
|
||||
@ -31,7 +31,7 @@ import {
|
||||
selectTranslationLanguage,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import { ARE_CALLS_SUPPORTED, IS_APP } from '../../util/windowEnvironment';
|
||||
import { ARE_CALLS_SUPPORTED, IS_APP } from '../../util/browser/windowEnvironment';
|
||||
|
||||
import { useHotkeys } from '../../hooks/useHotkeys';
|
||||
import useLastCallback from '../../hooks/useLastCallback';
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
selectSelectedMessagesCount,
|
||||
selectTabState,
|
||||
} from '../../global/selectors';
|
||||
import { selectSharedSettings } from '../../global/selectors/sharedState';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import captureKeyboardListeners from '../../util/captureKeyboardListeners';
|
||||
|
||||
@ -75,7 +76,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
||||
showNotification,
|
||||
reportMessages,
|
||||
openDeleteMessageModal,
|
||||
setSettingOption,
|
||||
setSharedSettingOption,
|
||||
} = getActions();
|
||||
const lang = useOldLang();
|
||||
|
||||
@ -133,7 +134,7 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
||||
});
|
||||
|
||||
const handleSvgConfirm = useLastCallback(() => {
|
||||
setSettingOption({ shouldWarnAboutSvg: false });
|
||||
setSharedSettingOption({ shouldWarnAboutSvg: false });
|
||||
closeSvgDialog();
|
||||
handleDownload();
|
||||
});
|
||||
@ -238,7 +239,9 @@ const MessageSelectToolbar: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const tabState = selectTabState(global);
|
||||
const { shouldWarnAboutSvg } = selectSharedSettings(global);
|
||||
const chat = selectCurrentChat(global);
|
||||
|
||||
const { type: messageListType, chatId } = selectCurrentMessageList(global) || {};
|
||||
const isSchedule = messageListType === 'scheduled';
|
||||
const { canDelete } = selectCanDeleteSelectedMessages(global);
|
||||
@ -263,7 +266,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
selectedMessageIds,
|
||||
hasProtectedMessage,
|
||||
isAnyModalOpen,
|
||||
shouldWarnAboutSvg: global.settings.byKey.shouldWarnAboutSvg,
|
||||
shouldWarnAboutSvg,
|
||||
};
|
||||
},
|
||||
)(MessageSelectToolbar));
|
||||
|
||||
@ -54,17 +54,18 @@ import {
|
||||
selectPinnedIds,
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
selectThemeValues,
|
||||
selectThreadInfo,
|
||||
selectTopic,
|
||||
selectTopics,
|
||||
selectUserFullInfo,
|
||||
} from '../../global/selectors';
|
||||
import {
|
||||
IS_ANDROID, IS_ELECTRON, IS_IOS, IS_SAFARI, IS_TRANSLATION_SUPPORTED, MASK_IMAGE_DISABLED,
|
||||
} from '../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import buildStyle from '../../util/buildStyle';
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
import {
|
||||
IS_ANDROID, IS_ELECTRON, IS_IOS, IS_SAFARI, IS_TRANSLATION_SUPPORTED, MASK_IMAGE_DISABLED,
|
||||
} from '../../util/windowEnvironment';
|
||||
import calculateMiddleFooterTransforms from './helpers/calculateMiddleFooterTransforms';
|
||||
|
||||
import useAppLayout from '../../hooks/useAppLayout';
|
||||
@ -726,7 +727,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const theme = selectTheme(global);
|
||||
const {
|
||||
isBlurred: isBackgroundBlurred, background: customBackground, backgroundColor, patternColor,
|
||||
} = global.settings.themes[theme] || {};
|
||||
} = selectThemeValues(global, theme) || {};
|
||||
|
||||
const {
|
||||
messageLists, isLeftColumnShown, activeEmojiInteractions,
|
||||
|
||||
@ -2,7 +2,7 @@ import React, { memo } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import { getUserFirstOrLastName } from '../../global/helpers';
|
||||
import { selectTheme, selectUser } from '../../global/selectors';
|
||||
import { selectTheme, selectThemeValues, selectUser } from '../../global/selectors';
|
||||
import { formatStarsAsIcon } from '../../util/localization/format';
|
||||
import { LOCAL_TGS_URLS } from '../common/helpers/animatedAssets';
|
||||
import renderText from '../common/helpers/renderText';
|
||||
@ -95,7 +95,7 @@ function RequirementToContactMessage({ patternColor, userName, paidMessagesStars
|
||||
export default memo(
|
||||
withGlobal<OwnProps>((global, { userId }): StateProps => {
|
||||
const theme = selectTheme(global);
|
||||
const { patternColor } = global.settings.themes[theme] || {};
|
||||
const { patternColor } = selectThemeValues(global, theme) || {};
|
||||
const user = selectUser(global, userId);
|
||||
|
||||
return {
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
|
||||
import type { ApiDocument } from '../../../api/types';
|
||||
import type { ISettings } from '../../../types';
|
||||
import type { ThemeKey } from '../../../types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
|
||||
import { getDocumentMediaHash } from '../../../global/helpers';
|
||||
@ -15,7 +15,7 @@ import styles from './AttachBotIcon.module.scss';
|
||||
|
||||
type OwnProps = {
|
||||
icon: ApiDocument;
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
};
|
||||
|
||||
const ADDITIONAL_STROKE_WIDTH = '0.5px';
|
||||
|
||||
@ -5,7 +5,7 @@ import React, {
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import type { ApiAttachBot } from '../../../api/types';
|
||||
import type { IAnchorPosition, ISettings, ThreadId } from '../../../types';
|
||||
import type { IAnchorPosition, ThemeKey, ThreadId } from '../../../types';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
@ -17,7 +17,7 @@ import AttachBotIcon from './AttachBotIcon';
|
||||
|
||||
type OwnProps = {
|
||||
bot: ApiAttachBot;
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
isInSideMenu?: true;
|
||||
chatId?: string;
|
||||
threadId?: ThreadId;
|
||||
|
||||
@ -6,7 +6,7 @@ import React, {
|
||||
|
||||
import type { ApiAttachMenuPeerType, ApiMessage } from '../../../api/types';
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import type { ISettings, MessageListType, ThreadId } from '../../../types';
|
||||
import type { MessageListType, ThemeKey, ThreadId } from '../../../types';
|
||||
|
||||
import {
|
||||
CONTENT_TYPES_WITH_PREVIEW, DEBUG_LOG_FILENAME, SUPPORTED_AUDIO_CONTENT_TYPES,
|
||||
@ -20,11 +20,11 @@ import {
|
||||
getMessageWebPagePhoto,
|
||||
getMessageWebPageVideo,
|
||||
} from '../../../global/helpers';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getDebugLogs } from '../../../util/debugConsole';
|
||||
import { validateFiles } from '../../../util/files';
|
||||
import { openSystemFilesDialog } from '../../../util/systemFilesDialog';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
@ -54,7 +54,7 @@ export type OwnProps = {
|
||||
attachBots?: GlobalState['attachMenu']['bots'];
|
||||
peerType?: ApiAttachMenuPeerType;
|
||||
shouldCollectDebugLogs?: boolean;
|
||||
theme: ISettings['theme'];
|
||||
theme: ThemeKey;
|
||||
onFileSelect: (files: File[], shouldSuggestCompression?: boolean) => void;
|
||||
onPollCreate: NoneToVoidFunction;
|
||||
onMenuOpen: NoneToVoidFunction;
|
||||
|
||||
@ -22,6 +22,7 @@ import { requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { getAttachmentMediaType, isUserId } from '../../../global/helpers';
|
||||
import { selectChatFullInfo, selectIsChatWithSelf } from '../../../global/selectors';
|
||||
import { selectCurrentLimit } from '../../../global/selectors/limits';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../../util/captureEscKeyListener';
|
||||
import { validateFiles } from '../../../util/files';
|
||||
@ -750,7 +751,8 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
const chatFullInfo = !isUserId(chatId) ? selectChatFullInfo(global, chatId) : undefined;
|
||||
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
||||
const { language, shouldSuggestCustomEmoji } = global.settings.byKey;
|
||||
const { shouldSuggestCustomEmoji } = global.settings.byKey;
|
||||
const { language } = selectSharedSettings(global);
|
||||
const baseEmojiKeywords = global.emojiKeywords[BASE_EMOJI_KEYWORD_LANG];
|
||||
const emojiKeywords = language !== BASE_EMOJI_KEYWORD_LANG ? global.emojiKeywords[language] : undefined;
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { getActions } from '../../../global';
|
||||
|
||||
import type { ApiBotCommand } from '../../../api/types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
@ -5,7 +5,7 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type { ApiMessage } from '../../../api/types';
|
||||
|
||||
import { selectChatMessage, selectCurrentMessageList } from '../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import renderKeyboardButtonText from './helpers/renderKeyboardButtonText';
|
||||
|
||||
import useMouseInside from '../../../hooks/useMouseInside';
|
||||
|
||||
@ -10,8 +10,8 @@ import type {
|
||||
} from '../../../api/types';
|
||||
import type { IAnchorPosition } from '../../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useEffectWithPrevDeps from '../../../hooks/useEffectWithPrevDeps';
|
||||
import useFlag from '../../../hooks/useFlag';
|
||||
|
||||
@ -2,9 +2,9 @@ import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo } from '../../../lib/teact/teact';
|
||||
|
||||
import { BASE_URL, IS_PACKAGED_ELECTRON } from '../../../config';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { handleEmojiLoad, LOADED_EMOJIS } from '../../../util/emoji/emoji';
|
||||
import { IS_EMOJI_SUPPORTED } from '../../../util/windowEnvironment';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
|
||||
@ -16,11 +16,11 @@ import type {
|
||||
import { MENU_TRANSITION_DURATION, RECENT_SYMBOL_SET_ID } from '../../../config';
|
||||
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
|
||||
import animateScroll from '../../../util/animateScroll';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { uncompressEmoji } from '../../../util/emoji/emoji';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import { REM } from '../../common/helpers/mediaDimensions';
|
||||
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
|
||||
@ -8,8 +8,8 @@ import type { ApiVideo } from '../../../api/types';
|
||||
|
||||
import { SLIDE_TRANSITION_DURATION } from '../../../config';
|
||||
import { selectCurrentMessageList, selectIsChatWithSelf } from '../../../global/selectors';
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
@ -7,11 +7,11 @@ import type {
|
||||
} from '../../../api/types';
|
||||
import { LoadMoreDirection } from '../../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { throttle } from '../../../util/schedulers';
|
||||
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
@ -9,22 +9,23 @@ import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiInputMessageReplyInfo } from '../../../api/types';
|
||||
import type {
|
||||
IAnchorPosition, ISettings, MessageListType, ThreadId,
|
||||
IAnchorPosition, MessageListType, SharedSettings, ThreadId,
|
||||
} from '../../../types';
|
||||
import type { Signal } from '../../../util/signals';
|
||||
|
||||
import { EDITABLE_INPUT_ID } from '../../../config';
|
||||
import { requestForcedReflow, requestMutation } from '../../../lib/fasterdom/fasterdom';
|
||||
import { selectCanPlayAnimatedEmojis, selectDraft, selectIsInSelectMode } from '../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import {
|
||||
IS_ANDROID, IS_EMOJI_SUPPORTED, IS_IOS, IS_TOUCH_ENV,
|
||||
} from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import captureKeyboardListeners from '../../../util/captureKeyboardListeners';
|
||||
import { getIsDirectTextInputDisabled } from '../../../util/directInputManager';
|
||||
import parseEmojiOnlyString from '../../../util/emoji/parseEmojiOnlyString';
|
||||
import focusEditableElement from '../../../util/focusEditableElement';
|
||||
import { debounce } from '../../../util/schedulers';
|
||||
import {
|
||||
IS_ANDROID, IS_EMOJI_SUPPORTED, IS_IOS, IS_TOUCH_ENV,
|
||||
} from '../../../util/windowEnvironment';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { isSelectionInsideInput } from './helpers/selection';
|
||||
|
||||
@ -83,7 +84,7 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
replyInfo?: ApiInputMessageReplyInfo;
|
||||
isSelectModeActive?: boolean;
|
||||
messageSendKeyCombo?: ISettings['messageSendKeyCombo'];
|
||||
messageSendKeyCombo?: SharedSettings['messageSendKeyCombo'];
|
||||
canPlayAnimatedEmojis: boolean;
|
||||
};
|
||||
|
||||
@ -649,7 +650,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId, threadId }: OwnProps): StateProps => {
|
||||
const { messageSendKeyCombo } = global.settings.byKey;
|
||||
const { messageSendKeyCombo } = selectSharedSettings(global);
|
||||
|
||||
return {
|
||||
messageSendKeyCombo,
|
||||
|
||||
@ -4,9 +4,9 @@ import { getActions, getGlobal } from '../../../global';
|
||||
|
||||
import type { ApiSendAsPeerId } from '../../../api/types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/browser/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import useMouseInside from '../../../hooks/useMouseInside';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user