Settings Privacy: Implement account self destruction (#5939)
Co-authored-by: zubiden <19638254+zubiden@users.noreply.github.com>
This commit is contained in:
parent
f2d14ca78f
commit
f1a538ed8e
@ -99,3 +99,29 @@ export function toggleSponsoredMessages({
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildApiAccountDays(ttl: GramJs.AccountDaysTTL): { days: number } {
|
||||
return {
|
||||
days: ttl.days,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiAccountDaysTTL(days: number): GramJs.AccountDaysTTL {
|
||||
return new GramJs.AccountDaysTTL({
|
||||
days,
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchAccountTTL() {
|
||||
const result = await invokeRequest(new GramJs.account.GetAccountTTL());
|
||||
if (!result) return undefined;
|
||||
return buildApiAccountDays(result);
|
||||
}
|
||||
|
||||
export function setAccountTTL({ days }: { days: number }) {
|
||||
return invokeRequest(new GramJs.account.SetAccountTTL({
|
||||
ttl: buildApiAccountDaysTTL(days),
|
||||
}), {
|
||||
shouldReturnTrue: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1552,6 +1552,10 @@
|
||||
"CloseMiniApps" = "Close Mini Apps";
|
||||
"DoNotAskAgain" = "Don't ask again";
|
||||
"PaymentInfoDone" = "Proceed to checkout";
|
||||
"DeleteMyAccount" = "Delete my account";
|
||||
"DeleteAccountIfAwayFor" = "If away for";
|
||||
"SelfDestructTitle" = "Account self-destruction";
|
||||
"SelfDestructSessionsDescription" = "If you don't come online from a specific session at least once within this period, it will be terminated.";
|
||||
"EmojiStatusAccessText" = "**{name}** requests access to set your **emoji status**. You will be able to revoke this access in the profile page of **{name}**.";
|
||||
"VideoConversionTitle" = "Improving Video...";
|
||||
"VideoConversionText" = "The video will be published after it's optimized for the best viewing experience.";
|
||||
|
||||
@ -23,6 +23,7 @@ export { default as StatusPickerMenu } from '../components/left/main/StatusPicke
|
||||
export { default as SuggestedStatusModal } from '../components/modals/suggestedStatus/SuggestedStatusModal';
|
||||
export { default as BoostModal } from '../components/modals/boost/BoostModal';
|
||||
export { default as GiftCodeModal } from '../components/modals/giftcode/GiftCodeModal';
|
||||
export { default as DeleteAccountModal } from '../components/modals/deleteAccount/DeleteAccountModal';
|
||||
export { default as ChatlistModal } from '../components/modals/chatlist/ChatlistModal';
|
||||
export { default as ChatInviteModal } from '../components/modals/chatInvite/ChatInviteModal';
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import { useMemo } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
@ -6,14 +7,17 @@ import type { ApiPrivacySettings } from '../../../api/types';
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
import { SettingsScreens } from '../../../types';
|
||||
|
||||
import { ACCOUNT_TTL_OPTIONS } from '../../../config.ts';
|
||||
import {
|
||||
selectCanSetPasscode, selectIsCurrentUserFrozen,
|
||||
selectIsCurrentUserPremium,
|
||||
} from '../../../global/selectors';
|
||||
import { selectSharedSettings } from '../../../global/selectors/sharedState';
|
||||
import { getClosestEntry } from '../../../util/getClosestEntry.ts';
|
||||
|
||||
import useHistoryBack from '../../../hooks/useHistoryBack';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback.ts';
|
||||
import useOldLang from '../../../hooks/useOldLang';
|
||||
|
||||
import StarIcon from '../../common/icons/StarIcon';
|
||||
@ -41,8 +45,11 @@ type StateProps = {
|
||||
canDisplayChatInTitle?: boolean;
|
||||
isCurrentUserFrozen?: boolean;
|
||||
privacy: GlobalState['settings']['privacy'];
|
||||
accountDaysTtl?: number;
|
||||
};
|
||||
|
||||
const DAYS_PER_MONTH = 30;
|
||||
|
||||
const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
isCurrentUserPremium,
|
||||
@ -61,8 +68,10 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
privacy,
|
||||
onReset,
|
||||
isCurrentUserFrozen,
|
||||
accountDaysTtl,
|
||||
}) => {
|
||||
const {
|
||||
openDeleteAccountModal,
|
||||
loadPrivacySettings,
|
||||
loadBlockedUsers,
|
||||
loadContentSettings,
|
||||
@ -72,6 +81,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
loadWebAuthorizations,
|
||||
setSharedSettingOption,
|
||||
openSettingsScreen,
|
||||
loadAccountDaysTtl,
|
||||
} = getActions();
|
||||
|
||||
useEffect(() => {
|
||||
@ -86,6 +96,7 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
useEffect(() => {
|
||||
if (isActive && !isCurrentUserFrozen) {
|
||||
loadGlobalPrivacySettings();
|
||||
loadAccountDaysTtl();
|
||||
}
|
||||
}, [isActive, isCurrentUserFrozen, loadGlobalPrivacySettings]);
|
||||
|
||||
@ -113,6 +124,16 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
updateContentSettings({ isSensitiveEnabled: isChecked });
|
||||
}, [updateContentSettings]);
|
||||
|
||||
const handleOpenDeleteAccountModal = useLastCallback(() => {
|
||||
if (!accountDaysTtl) return;
|
||||
openDeleteAccountModal({ days: accountDaysTtl });
|
||||
});
|
||||
|
||||
const dayOption = useMemo(() => {
|
||||
if (!accountDaysTtl) return undefined;
|
||||
return getClosestEntry(ACCOUNT_TTL_OPTIONS, accountDaysTtl / DAYS_PER_MONTH).toString();
|
||||
}, [accountDaysTtl]);
|
||||
|
||||
function getVisibilityValue(setting?: ApiPrivacySettings) {
|
||||
if (!setting) return oldLang('Loading');
|
||||
|
||||
@ -402,6 +423,21 @@ const SettingsPrivacy: FC<OwnProps & StateProps> = ({
|
||||
onCheck={handleChatInTitleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="settings-item">
|
||||
<h4 className="settings-item-header" dir={oldLang.isRtl ? 'rtl' : undefined}>
|
||||
{lang('DeleteMyAccount')}
|
||||
</h4>
|
||||
<ListItem
|
||||
narrow
|
||||
onClick={handleOpenDeleteAccountModal}
|
||||
>
|
||||
{lang('DeleteAccountIfAwayFor')}
|
||||
<span className="settings-item__current-value">
|
||||
{lang('Months', { count: dayOption }, { pluralValue: 1 })}
|
||||
</span>
|
||||
</ListItem>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -415,6 +451,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
shouldNewNonContactPeersRequirePremium, nonContactPeersPaidStars,
|
||||
},
|
||||
privacy,
|
||||
accountDaysTtl,
|
||||
},
|
||||
blocked,
|
||||
passcode: {
|
||||
@ -443,6 +480,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
canDisplayChatInTitle,
|
||||
canSetPasscode: selectCanSetPasscode(global),
|
||||
isCurrentUserFrozen,
|
||||
accountDaysTtl,
|
||||
};
|
||||
},
|
||||
)(SettingsPrivacy));
|
||||
|
||||
@ -14,6 +14,7 @@ import BoostModal from './boost/BoostModal.async';
|
||||
import ChatInviteModal from './chatInvite/ChatInviteModal.async';
|
||||
import ChatlistModal from './chatlist/ChatlistModal.async';
|
||||
import CollectibleInfoModal from './collectible/CollectibleInfoModal.async';
|
||||
import DeleteAccountModal from './deleteAccount/DeleteAccountModal.async';
|
||||
import EmojiStatusAccessModal from './emojiStatusAccess/EmojiStatusAccessModal.async';
|
||||
import FrozenAccountModal from './frozenAccount/FrozenAccountModal.async';
|
||||
import PremiumGiftModal from './gift/GiftModal.async';
|
||||
@ -82,7 +83,8 @@ type ModalKey = keyof Pick<TabState,
|
||||
'giftStatusInfoModal' |
|
||||
'giftTransferModal' |
|
||||
'chatRefundModal' |
|
||||
'isFrozenAccountModalOpen'
|
||||
'isFrozenAccountModalOpen' |
|
||||
'deleteAccountModal'
|
||||
>;
|
||||
|
||||
type StateProps = {
|
||||
@ -135,6 +137,7 @@ const MODALS: ModalRegistry = {
|
||||
giftTransferModal: GiftTransferModal,
|
||||
chatRefundModal: ChatRefundModal,
|
||||
isFrozenAccountModalOpen: FrozenAccountModal,
|
||||
deleteAccountModal: DeleteAccountModal,
|
||||
};
|
||||
const MODAL_KEYS = Object.keys(MODALS) as ModalKey[];
|
||||
const MODAL_ENTRIES = Object.entries(MODALS) as Entries<ModalRegistry>;
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React from '../../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './DeleteAccountModal';
|
||||
|
||||
import { Bundles } from '../../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||
|
||||
const DeleteAccountModalAsync: FC<OwnProps> = (props) => {
|
||||
const { modal } = props;
|
||||
const DeleteAccountModal = useModuleLoader(Bundles.Extra, 'DeleteAccountModal', !modal);
|
||||
|
||||
return DeleteAccountModal ? <DeleteAccountModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default DeleteAccountModalAsync;
|
||||
@ -0,0 +1,3 @@
|
||||
.root :global(.modal-dialog) {
|
||||
max-width: 23.25rem;
|
||||
}
|
||||
117
src/components/modals/deleteAccount/DeleteAccountModal.tsx
Normal file
117
src/components/modals/deleteAccount/DeleteAccountModal.tsx
Normal file
@ -0,0 +1,117 @@
|
||||
import React, { memo, useEffect, useMemo, useState } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { TabState } from '../../../global/types';
|
||||
|
||||
import { ACCOUNT_TTL_OPTIONS } from '../../../config.ts';
|
||||
import { getClosestEntry } from '../../../util/getClosestEntry.ts';
|
||||
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
import Button from '../../ui/Button';
|
||||
import Modal from '../../ui/Modal';
|
||||
import RadioGroup from '../../ui/RadioGroup.tsx';
|
||||
|
||||
import styles from './DeleteAccountModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
modal: TabState['deleteAccountModal'];
|
||||
};
|
||||
|
||||
export type StateProps = {
|
||||
selfDestructAccountDays?: number;
|
||||
};
|
||||
|
||||
const DAYS_PER_MONTH = 30;
|
||||
|
||||
const DeleteAccountModal = ({
|
||||
modal,
|
||||
selfDestructAccountDays,
|
||||
}: OwnProps & StateProps) => {
|
||||
const {
|
||||
closeGiftCodeModal, closeDeleteAccountModal, setAccountTTL,
|
||||
} = getActions();
|
||||
const lang = useLang();
|
||||
const isOpen = Boolean(modal);
|
||||
|
||||
const [selectedOption, setSelectedOption] = useState<string>();
|
||||
|
||||
const optionToDays = useLastCallback((value: string): number => {
|
||||
return Number(value) * DAYS_PER_MONTH;
|
||||
});
|
||||
|
||||
const initialSelectedOption = useMemo(() => {
|
||||
if (!selfDestructAccountDays) return undefined;
|
||||
return getClosestEntry(ACCOUNT_TTL_OPTIONS, selfDestructAccountDays / DAYS_PER_MONTH).toString();
|
||||
}, [selfDestructAccountDays]);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialSelectedOption) {
|
||||
setSelectedOption(initialSelectedOption);
|
||||
}
|
||||
}, [initialSelectedOption]);
|
||||
|
||||
const options: { value: string; label: string }[] = useMemo(() => {
|
||||
return ACCOUNT_TTL_OPTIONS.map((months) => ({
|
||||
value: String(months),
|
||||
label: lang('Months', { count: months }, { pluralValue: 1 }),
|
||||
}));
|
||||
}, [lang]);
|
||||
|
||||
const handleChange = useLastCallback((value: string) => {
|
||||
setSelectedOption(value);
|
||||
});
|
||||
|
||||
const confirmHandler = useLastCallback(() => {
|
||||
if (!selectedOption) return;
|
||||
setAccountTTL({ days: optionToDays(selectedOption) });
|
||||
});
|
||||
|
||||
const onCloseHandler = useLastCallback(() => {
|
||||
closeDeleteAccountModal();
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
title={lang('SelfDestructTitle')}
|
||||
onClose={closeGiftCodeModal}
|
||||
className={styles.root}
|
||||
>
|
||||
<p>{lang('SelfDestructSessionsDescription')}</p>
|
||||
<RadioGroup
|
||||
className="dialog-checkbox-group"
|
||||
name="quick-reaction-settings"
|
||||
options={options}
|
||||
selected={selectedOption}
|
||||
onChange={handleChange}
|
||||
withIcon
|
||||
/>
|
||||
<div
|
||||
className="dialog-buttons mt-2"
|
||||
>
|
||||
<Button
|
||||
className="confirm-dialog-button"
|
||||
isText
|
||||
onClick={confirmHandler}
|
||||
>
|
||||
{lang('Save')}
|
||||
</Button>
|
||||
<Button color="danger" className="confirm-dialog-button" isText onClick={onCloseHandler}>
|
||||
{lang('Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { modal }): StateProps => {
|
||||
const { selfDestructAccountDays } = modal || {};
|
||||
|
||||
return {
|
||||
selfDestructAccountDays,
|
||||
};
|
||||
},
|
||||
)(DeleteAccountModal));
|
||||
@ -471,3 +471,5 @@ export const DEFAULT_GIFT_PROFILE_FILTER_OPTIONS: GiftProfileFilterOptions = {
|
||||
export const DEFAULT_RESALE_GIFTS_FILTER_OPTIONS: ResaleGiftsFilterOptions = {
|
||||
sortType: 'byDate',
|
||||
};
|
||||
|
||||
export const ACCOUNT_TTL_OPTIONS = [1, 3, 6, 12, 18, 24];
|
||||
|
||||
@ -243,3 +243,37 @@ addActionHandler('terminateAllWebAuthorizations', async (global): Promise<void>
|
||||
};
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('loadAccountDaysTtl', async (global, actions, payload): Promise<void> => {
|
||||
const result = await callApi('fetchAccountTTL');
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
global = {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
accountDaysTtl: result.days,
|
||||
},
|
||||
};
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('setAccountTTL', async (global, actions, payload): Promise<void> => {
|
||||
const { days, tabId = getCurrentTabId() } = payload || {};
|
||||
if (!days) return;
|
||||
|
||||
const result = await callApi('setAccountTTL', { days });
|
||||
if (!result) return;
|
||||
|
||||
global = getGlobal();
|
||||
global = {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
accountDaysTtl: days,
|
||||
},
|
||||
};
|
||||
setGlobal(global);
|
||||
actions.closeDeleteAccountModal({ tabId });
|
||||
});
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import type { ActionReturnType } from '../../types';
|
||||
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { addActionHandler } from '../..';
|
||||
import { addActionHandler, setGlobal } from '../..';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import { selectTabState } from '../../selectors';
|
||||
|
||||
addActionHandler('openFrozenAccountModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
@ -19,3 +20,24 @@ addActionHandler('closeFrozenAccountModal', (global, actions, payload): ActionRe
|
||||
isFrozenAccountModalOpen: false,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('openDeleteAccountModal', (global, actions, payload): ActionReturnType => {
|
||||
const { days, tabId = getCurrentTabId() } = payload || {};
|
||||
if (!days) return;
|
||||
|
||||
global = updateTabState(global, {
|
||||
...selectTabState(global, tabId),
|
||||
deleteAccountModal: {
|
||||
selfDestructAccountDays: days,
|
||||
},
|
||||
}, tabId);
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('closeDeleteAccountModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
deleteAccountModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
@ -281,6 +281,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
cached.peers = initialState.peers;
|
||||
}
|
||||
|
||||
if (!cached.settings.accountDaysTtl) {
|
||||
cached.settings.accountDaysTtl = initialState.settings.accountDaysTtl;
|
||||
}
|
||||
|
||||
if (!cached.cacheVersion) {
|
||||
cached.cacheVersion = initialState.cacheVersion;
|
||||
// Reset because of the new action message structure
|
||||
@ -708,7 +712,7 @@ function omitLocalMedia(message: ApiMessage): ApiMessage {
|
||||
|
||||
function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] {
|
||||
const {
|
||||
byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, themes,
|
||||
byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, themes, accountDaysTtl,
|
||||
} = global.settings;
|
||||
|
||||
return {
|
||||
@ -718,6 +722,7 @@ function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings
|
||||
lastPremiumBandwithNotificationDate,
|
||||
notifyDefaults,
|
||||
themes,
|
||||
accountDaysTtl,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -309,6 +309,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
patternColor: DARK_THEME_PATTERN_COLOR,
|
||||
},
|
||||
},
|
||||
accountDaysTtl: 365,
|
||||
},
|
||||
|
||||
serviceNotifications: [],
|
||||
|
||||
@ -1040,6 +1040,14 @@ export interface ActionPayloads {
|
||||
};
|
||||
openFrozenAccountModal: WithTabId | undefined;
|
||||
closeFrozenAccountModal: WithTabId | undefined;
|
||||
openDeleteAccountModal: {
|
||||
days: number;
|
||||
} & WithTabId | undefined;
|
||||
closeDeleteAccountModal: WithTabId | undefined;
|
||||
setAccountTTL: {
|
||||
days: number;
|
||||
} & WithTabId | undefined;
|
||||
loadAccountDaysTtl: undefined;
|
||||
|
||||
// Chats
|
||||
loadPeerSettings: {
|
||||
|
||||
@ -415,6 +415,7 @@ export type GlobalState = {
|
||||
paidReactionPrivacy?: ApiPaidReactionPrivacyType;
|
||||
botVerificationShownPeerIds: string[];
|
||||
themes: Partial<Record<ThemeKey, IThemeSettings>>;
|
||||
accountDaysTtl: number;
|
||||
};
|
||||
|
||||
push?: {
|
||||
|
||||
@ -741,6 +741,10 @@ export type TabState = {
|
||||
info: ApiCheckedGiftCode;
|
||||
};
|
||||
|
||||
deleteAccountModal?: {
|
||||
selfDestructAccountDays: number;
|
||||
};
|
||||
|
||||
paidReactionModal?: {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
|
||||
96
src/lib/gramjs/tl/api.d.ts
vendored
96
src/lib/gramjs/tl/api.d.ts
vendored
@ -3212,9 +3212,10 @@ namespace Api {
|
||||
resaleStars?: long;
|
||||
canTransferAt?: int;
|
||||
canResellAt?: int;
|
||||
CONSTRUCTOR_ID: 2900347777;
|
||||
CONSTRUCTOR_ID: 775611918;
|
||||
SUBCLASS_OF_ID: 2256589094;
|
||||
className: 'MessageActionStarGiftUnique';
|
||||
|
||||
static fromReader(reader: Reader): MessageActionStarGiftUnique;
|
||||
}
|
||||
export class MessageActionPaidMessagesRefunded extends VirtualClass<{
|
||||
@ -3255,6 +3256,10 @@ namespace Api {
|
||||
callId: long;
|
||||
duration?: int;
|
||||
otherParticipants?: Api.TypePeer[];
|
||||
CONSTRUCTOR_ID: 805187450;
|
||||
SUBCLASS_OF_ID: 2256589094;
|
||||
className: 'MessageActionConferenceCall';
|
||||
|
||||
static fromReader(reader: Reader): MessageActionConferenceCall;
|
||||
}
|
||||
export class Dialog extends VirtualClass<{
|
||||
@ -5951,6 +5956,10 @@ namespace Api {
|
||||
subChainId: int;
|
||||
blocks: bytes[];
|
||||
nextOffset: int;
|
||||
CONSTRUCTOR_ID: 2759272591;
|
||||
SUBCLASS_OF_ID: 2676568142;
|
||||
className: 'UpdateGroupCallChainBlocks';
|
||||
|
||||
static fromReader(reader: Reader): UpdateGroupCallChainBlocks;
|
||||
}
|
||||
export class UpdatesTooLong extends VirtualClass<void> {
|
||||
@ -9904,6 +9913,10 @@ namespace Api {
|
||||
slug: string;
|
||||
}> {
|
||||
slug: string;
|
||||
CONSTRUCTOR_ID: 2679894519;
|
||||
SUBCLASS_OF_ID: 3634081085;
|
||||
className: 'PhoneCallDiscardReasonMigrateConferenceCall';
|
||||
|
||||
static fromReader(reader: Reader): PhoneCallDiscardReasonMigrateConferenceCall;
|
||||
}
|
||||
export class DataJSON extends VirtualClass<{
|
||||
@ -10250,8 +10263,7 @@ namespace Api {
|
||||
participantId: long;
|
||||
protocol: Api.TypePhoneCallProtocol;
|
||||
receiveDate?: int;
|
||||
conferenceCall?: Api.TypeInputGroupCall;
|
||||
CONSTRUCTOR_ID: 4006881368;
|
||||
CONSTRUCTOR_ID: 3307368215;
|
||||
SUBCLASS_OF_ID: 3296664529;
|
||||
className: 'PhoneCallWaiting';
|
||||
|
||||
@ -10277,8 +10289,7 @@ namespace Api {
|
||||
participantId: long;
|
||||
gAHash: bytes;
|
||||
protocol: Api.TypePhoneCallProtocol;
|
||||
conferenceCall?: Api.TypeInputGroupCall;
|
||||
CONSTRUCTOR_ID: 1161174115;
|
||||
CONSTRUCTOR_ID: 347139340;
|
||||
SUBCLASS_OF_ID: 3296664529;
|
||||
className: 'PhoneCallRequested';
|
||||
|
||||
@ -10304,8 +10315,7 @@ namespace Api {
|
||||
participantId: long;
|
||||
gB: bytes;
|
||||
protocol: Api.TypePhoneCallProtocol;
|
||||
conferenceCall?: Api.TypeInputGroupCall;
|
||||
CONSTRUCTOR_ID: 587035009;
|
||||
CONSTRUCTOR_ID: 912311057;
|
||||
SUBCLASS_OF_ID: 3296664529;
|
||||
className: 'PhoneCallAccepted';
|
||||
|
||||
@ -10343,8 +10353,7 @@ namespace Api {
|
||||
connections: Api.TypePhoneConnection[];
|
||||
startDate: int;
|
||||
customParameters?: Api.TypeDataJSON;
|
||||
conferenceCall?: Api.TypeInputGroupCall;
|
||||
CONSTRUCTOR_ID: 1000707084;
|
||||
CONSTRUCTOR_ID: 810769141;
|
||||
SUBCLASS_OF_ID: 3296664529;
|
||||
className: 'PhoneCall';
|
||||
|
||||
@ -10366,8 +10375,7 @@ namespace Api {
|
||||
id: long;
|
||||
reason?: Api.TypePhoneCallDiscardReason;
|
||||
duration?: int;
|
||||
conferenceCall?: Api.TypeInputGroupCall;
|
||||
CONSTRUCTOR_ID: 4191311107;
|
||||
CONSTRUCTOR_ID: 1355435489;
|
||||
SUBCLASS_OF_ID: 3296664529;
|
||||
className: 'PhoneCallDiscarded';
|
||||
|
||||
@ -11111,6 +11119,10 @@ namespace Api {
|
||||
newValue: Bool;
|
||||
}> {
|
||||
newValue: Bool;
|
||||
CONSTRUCTOR_ID: 3306682238;
|
||||
SUBCLASS_OF_ID: 2998503411;
|
||||
className: 'ChannelAdminLogEventActionToggleAutotranslation';
|
||||
|
||||
static fromReader(reader: Reader): ChannelAdminLogEventActionToggleAutotranslation;
|
||||
}
|
||||
export class ChannelAdminLogEvent extends VirtualClass<{
|
||||
@ -13281,11 +13293,11 @@ namespace Api {
|
||||
unmutedVideoCount?: int;
|
||||
unmutedVideoLimit: int;
|
||||
version: int;
|
||||
conferenceFromCall?: long;
|
||||
inviteLink?: string;
|
||||
CONSTRUCTOR_ID: 3455636451;
|
||||
CONSTRUCTOR_ID: 1429932961;
|
||||
SUBCLASS_OF_ID: 548729632;
|
||||
className: 'GroupCall';
|
||||
|
||||
static fromReader(reader: Reader): GroupCall;
|
||||
}
|
||||
export class InputGroupCall extends VirtualClass<{
|
||||
@ -13304,12 +13316,20 @@ namespace Api {
|
||||
slug: string;
|
||||
}> {
|
||||
slug: string;
|
||||
CONSTRUCTOR_ID: 4261839423;
|
||||
SUBCLASS_OF_ID: 1482758833;
|
||||
className: 'InputGroupCallSlug';
|
||||
|
||||
static fromReader(reader: Reader): InputGroupCallSlug;
|
||||
}
|
||||
export class InputGroupCallInviteMessage extends VirtualClass<{
|
||||
msgId: int;
|
||||
}> {
|
||||
msgId: int;
|
||||
CONSTRUCTOR_ID: 2349883455;
|
||||
SUBCLASS_OF_ID: 1482758833;
|
||||
className: 'InputGroupCallInviteMessage';
|
||||
|
||||
static fromReader(reader: Reader): InputGroupCallInviteMessage;
|
||||
}
|
||||
export class GroupCallParticipant extends VirtualClass<{
|
||||
@ -14052,6 +14072,10 @@ namespace Api {
|
||||
}> {
|
||||
slug: string;
|
||||
toId: Api.TypeInputPeer;
|
||||
CONSTRUCTOR_ID: 1674298252;
|
||||
SUBCLASS_OF_ID: 1919851518;
|
||||
className: 'InputInvoiceStarGiftResale';
|
||||
|
||||
static fromReader(reader: Reader): InputInvoiceStarGiftResale;
|
||||
}
|
||||
export class InputStorePaymentPremiumSubscription extends VirtualClass<{
|
||||
@ -16601,9 +16625,10 @@ namespace Api {
|
||||
upgradeStars?: long;
|
||||
resellMinStars?: long;
|
||||
title?: string;
|
||||
CONSTRUCTOR_ID: 46953416;
|
||||
CONSTRUCTOR_ID: 3324693032;
|
||||
SUBCLASS_OF_ID: 3273414923;
|
||||
className: 'StarGift';
|
||||
|
||||
static fromReader(reader: Reader): StarGift;
|
||||
}
|
||||
export class StarGiftUnique extends VirtualClass<{
|
||||
@ -16634,9 +16659,10 @@ namespace Api {
|
||||
availabilityTotal: int;
|
||||
giftAddress?: string;
|
||||
resellStars?: long;
|
||||
CONSTRUCTOR_ID: 1549979985;
|
||||
CONSTRUCTOR_ID: 1678891913;
|
||||
SUBCLASS_OF_ID: 3273414923;
|
||||
className: 'StarGiftUnique';
|
||||
|
||||
static fromReader(reader: Reader): StarGiftUnique;
|
||||
}
|
||||
export class MessageReportOption extends VirtualClass<{
|
||||
@ -16838,7 +16864,7 @@ namespace Api {
|
||||
patternColor: int;
|
||||
textColor: int;
|
||||
rarityPermille: int;
|
||||
CONSTRUCTOR_ID: 2485589858;
|
||||
CONSTRUCTOR_ID: 3644687772;
|
||||
SUBCLASS_OF_ID: 2276819400;
|
||||
className: 'StarGiftAttributeBackdrop';
|
||||
|
||||
@ -16900,9 +16926,10 @@ namespace Api {
|
||||
transferStars?: long;
|
||||
canTransferAt?: int;
|
||||
canResellAt?: int;
|
||||
CONSTRUCTOR_ID: 1616305061;
|
||||
CONSTRUCTOR_ID: 3755607193;
|
||||
SUBCLASS_OF_ID: 2385198100;
|
||||
className: 'SavedStarGift';
|
||||
|
||||
static fromReader(reader: Reader): SavedStarGift;
|
||||
}
|
||||
export class InputSavedStarGiftUser extends VirtualClass<{
|
||||
@ -16931,6 +16958,10 @@ namespace Api {
|
||||
slug: string;
|
||||
}> {
|
||||
slug: string;
|
||||
CONSTRUCTOR_ID: 545636920;
|
||||
SUBCLASS_OF_ID: 2406848942;
|
||||
className: 'InputSavedStarGiftSlug';
|
||||
|
||||
static fromReader(reader: Reader): InputSavedStarGiftSlug;
|
||||
}
|
||||
export class PaidReactionPrivacyDefault extends VirtualClass<void> {
|
||||
@ -17059,18 +17090,30 @@ namespace Api {
|
||||
documentId: long;
|
||||
}> {
|
||||
documentId: long;
|
||||
CONSTRUCTOR_ID: 1219145276;
|
||||
SUBCLASS_OF_ID: 3005295287;
|
||||
className: 'StarGiftAttributeIdModel';
|
||||
|
||||
static fromReader(reader: Reader): StarGiftAttributeIdModel;
|
||||
}
|
||||
export class StarGiftAttributeIdPattern extends VirtualClass<{
|
||||
documentId: long;
|
||||
}> {
|
||||
documentId: long;
|
||||
CONSTRUCTOR_ID: 1242965043;
|
||||
SUBCLASS_OF_ID: 3005295287;
|
||||
className: 'StarGiftAttributeIdPattern';
|
||||
|
||||
static fromReader(reader: Reader): StarGiftAttributeIdPattern;
|
||||
}
|
||||
export class StarGiftAttributeIdBackdrop extends VirtualClass<{
|
||||
backdropId: int;
|
||||
}> {
|
||||
backdropId: int;
|
||||
CONSTRUCTOR_ID: 520210263;
|
||||
SUBCLASS_OF_ID: 3005295287;
|
||||
className: 'StarGiftAttributeIdBackdrop';
|
||||
|
||||
static fromReader(reader: Reader): StarGiftAttributeIdBackdrop;
|
||||
}
|
||||
export class StarGiftAttributeCounter extends VirtualClass<{
|
||||
@ -17079,6 +17122,10 @@ namespace Api {
|
||||
}> {
|
||||
attribute: Api.TypeStarGiftAttributeId;
|
||||
count: int;
|
||||
CONSTRUCTOR_ID: 783398488;
|
||||
SUBCLASS_OF_ID: 2351477395;
|
||||
className: 'StarGiftAttributeCounter';
|
||||
|
||||
static fromReader(reader: Reader): StarGiftAttributeCounter;
|
||||
}
|
||||
export class PendingSuggestion extends VirtualClass<{
|
||||
@ -17091,6 +17138,10 @@ namespace Api {
|
||||
title: Api.TypeTextWithEntities;
|
||||
description: Api.TypeTextWithEntities;
|
||||
url: string;
|
||||
CONSTRUCTOR_ID: 3890753042;
|
||||
SUBCLASS_OF_ID: 3126949031;
|
||||
className: 'PendingSuggestion';
|
||||
|
||||
static fromReader(reader: Reader): PendingSuggestion;
|
||||
}
|
||||
export class ResPQ extends VirtualClass<{
|
||||
@ -19899,9 +19950,10 @@ namespace Api {
|
||||
customPendingSuggestion?: Api.TypePendingSuggestion;
|
||||
chats: Api.TypeChat[];
|
||||
users: Api.TypeUser[];
|
||||
CONSTRUCTOR_ID: 2352576831;
|
||||
CONSTRUCTOR_ID: 145021050;
|
||||
SUBCLASS_OF_ID: 2639877442;
|
||||
className: 'PromoData';
|
||||
|
||||
static fromReader(reader: Reader): PromoData;
|
||||
}
|
||||
export class CountryCode extends VirtualClass<{
|
||||
@ -21081,6 +21133,10 @@ namespace Api {
|
||||
chats: Api.TypeChat[];
|
||||
counters?: Api.TypeStarGiftAttributeCounter[];
|
||||
users: Api.TypeUser[];
|
||||
CONSTRUCTOR_ID: 2491028191;
|
||||
SUBCLASS_OF_ID: 3000743907;
|
||||
className: 'ResaleStarGifts';
|
||||
|
||||
static fromReader(reader: Reader): ResaleStarGifts;
|
||||
}
|
||||
}
|
||||
@ -21696,6 +21752,10 @@ namespace Api {
|
||||
countRemains: int;
|
||||
}> {
|
||||
countRemains: int;
|
||||
CONSTRUCTOR_ID: 3280453710;
|
||||
SUBCLASS_OF_ID: 3411255960;
|
||||
className: 'CanSendStoryCount';
|
||||
|
||||
static fromReader(reader: Reader): CanSendStoryCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1466,6 +1466,8 @@ account.checkUsername#2714d86c username:string = Bool;
|
||||
account.updateUsername#3e0bdd7c username:string = User;
|
||||
account.getPrivacy#dadbc950 key:InputPrivacyKey = account.PrivacyRules;
|
||||
account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> = account.PrivacyRules;
|
||||
account.getAccountTTL#8fc711d = AccountDaysTTL;
|
||||
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
|
||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||
account.resetAuthorization#df77f3bc hash:long = Bool;
|
||||
account.getPassword#548a30f5 = account.Password;
|
||||
|
||||
@ -63,6 +63,8 @@
|
||||
"account.getCollectibleEmojiStatuses",
|
||||
"account.addNoPaidMessagesException",
|
||||
"account.getPaidMessagesRevenue",
|
||||
"account.getAccountTTL",
|
||||
"account.setAccountTTL",
|
||||
"users.getUsers",
|
||||
"users.getFullUser",
|
||||
"contacts.getContacts",
|
||||
|
||||
4
src/types/language.d.ts
vendored
4
src/types/language.d.ts
vendored
@ -1272,6 +1272,10 @@ export interface LangPair {
|
||||
'CloseMiniApps': undefined;
|
||||
'DoNotAskAgain': undefined;
|
||||
'PaymentInfoDone': undefined;
|
||||
'DeleteMyAccount': undefined;
|
||||
'DeleteAccountIfAwayFor': undefined;
|
||||
'SelfDestructTitle': undefined;
|
||||
'SelfDestructSessionsDescription': undefined;
|
||||
'VideoConversionTitle': undefined;
|
||||
'VideoConversionText': undefined;
|
||||
'VideoConversionDone': undefined;
|
||||
|
||||
5
src/util/getClosestEntry.ts
Normal file
5
src/util/getClosestEntry.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export function getClosestEntry(arr: number[], value: number): number {
|
||||
return arr.reduce((prev, curr) => {
|
||||
return Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev;
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user