more fixes

This commit is contained in:
Eduard Kuzmenko 2022-11-18 13:43:52 +04:00
parent 3135aa2b5d
commit f746a661e7
81 changed files with 611 additions and 405 deletions

View File

@ -33,7 +33,7 @@ export default class AutocompletePeerHelper extends AutocompleteHelper {
this.container.classList.add(AutocompletePeerHelper.BASE_CLASS, className);
}
protected init() {
public init() {
this.list = document.createElement('div');
this.list.classList.add(AutocompletePeerHelper.BASE_CLASS + '-list', this.className + '-list');

View File

@ -32,7 +32,7 @@ export default class EmojiHelper extends AutocompleteHelper {
this.container.classList.add('emoji-helper');
}
protected init() {
public init() {
this.list = document.createElement('div');
this.list.classList.add('emoji-helper-emojis', 'super-emojis');

View File

@ -281,7 +281,7 @@ export default class InlineHelper extends AutocompleteHelper {
return {user: peer, renderPromise};
};
protected init() {
public init() {
this.list = document.createElement('div');
this.list.classList.add('inline-helper-results');

View File

@ -1512,10 +1512,17 @@ export default class ChatInput {
});
if(IS_TOUCH_SUPPORTED) {
attachClickEvent(this.messageInput, () => {
attachClickEvent(this.messageInput, (e) => {
if(emoticonsDropdown.isActive()) {
emoticonsDropdown.toggle(false);
blurActiveElement();
cancelEvent(e);
// this.messageInput.focus();
return;
}
this.appImManager.selectTab(1); // * set chat tab for album orientation
// this.saveScroll();
emoticonsDropdown.toggle(false);
}, {listenerSetter: this.listenerSetter});
/* this.listenerSetter.add(window)('resize', () => {

View File

@ -61,7 +61,7 @@ export default class ReplyKeyboard extends DropdownHover {
});
}
protected init() {
public init() {
this.appendTo.append(this.element);
this.listenerSetter.add(this)('open', async() => {

View File

@ -125,7 +125,7 @@ export default class StickersHelper extends AutocompleteHelper {
});
}
protected init() {
public init() {
this.list = document.createElement('div');
this.list.classList.add('stickers-helper-stickers', 'super-stickers');

View File

@ -147,7 +147,7 @@ export class EmoticonsDropdown extends DropdownHover {
return this.tabs[this.tabId];
}
protected init() {
public init() {
this.managers = rootScope.managers;
this.emojiTab = new EmojiTab(this.managers);
this.stickersTab = new StickersTab(this.managers);

View File

@ -10,7 +10,7 @@ import throttle from '../helpers/schedulers/throttle';
import {logger, LogTypes} from '../lib/logger';
const PARALLEL_LIMIT = 8;
const IGNORE_ERRORS: Set<ErrorType> = new Set(['NO_ENTRY_FOUND', 'STORAGE_OFFLINE', 'MIDDLEWARE']);
const IGNORE_ERRORS: Set<ErrorType> = new Set(['NO_ENTRY_FOUND', 'STORAGE_OFFLINE', 'MIDDLEWARE', 'NO_AUTO_DOWNLOAD']);
export type LazyLoadElementBase = {
load: () => Promise<any>,

View File

@ -27,7 +27,7 @@ export default class AppTwoStepVerificationEmailTab extends SliderSuperTab {
public hint: string;
public isFirst = false;
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-email');
this.setTitle('RecoveryEmailTitle');

View File

@ -26,7 +26,7 @@ export default class AppTwoStepVerificationEmailConfirmationTab extends SliderSu
public length: number;
public isFirst = false;
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-email-confirmation');
this.setTitle('TwoStepAuth.RecoveryTitle');

View File

@ -27,7 +27,7 @@ export default class AppTwoStepVerificationEnterPasswordTab extends SliderSuperT
public plainPassword: string;
public isFirst = true;
protected init() {
public init() {
const isNew = !this.state.pFlags.has_password || this.plainPassword;
this.container.classList.add('two-step-verification', 'two-step-verification-enter-password');
this.setTitle(isNew ? 'PleaseEnterFirstPassword' : 'PleaseEnterCurrentPassword');

View File

@ -22,7 +22,7 @@ export default class AppTwoStepVerificationHintTab extends SliderSuperTab {
public plainPassword: string;
public newPassword: string;
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-hint');
this.setTitle('TwoStepAuth.SetupHintTitle');

View File

@ -20,7 +20,7 @@ export default class AppTwoStepVerificationTab extends SliderSuperTab {
public state: AccountPassword;
public plainPassword: string;
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-main');
this.setTitle('TwoStepVerificationTitle');

View File

@ -12,7 +12,7 @@ import wrapStickerEmoji from '../../../wrappers/stickerEmoji';
import AppSettingsTab from '../settings';
export default class AppTwoStepVerificationSetTab extends SliderSuperTab {
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-set');
this.setTitle('TwoStepVerificationPasswordSet');

View File

@ -21,7 +21,7 @@ export default class AppTwoStepVerificationReEnterPasswordTab extends SliderSupe
public plainPassword: string;
public newPassword: string;
protected init() {
public init() {
this.container.classList.add('two-step-verification', 'two-step-verification-enter-password', 'two-step-verification-re-enter-password');
this.setTitle('PleaseReEnterPassword');

View File

@ -26,7 +26,7 @@ export default class AppActiveSessionsTab extends SliderSuperTabEventable {
public authorizations: Authorization.authorization[];
private menuElement: HTMLElement;
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('active-sessions-container');
this.setTitle('SessionsTitle');

View File

@ -17,7 +17,14 @@ export default class AppAddMembersTab extends SliderSuperTab {
private takeOut: (peerIds: PeerId[]) => Promise<any> | false | void;
private skippable: boolean;
protected init() {
public init(options: {
title: LangPackKey,
placeholder: LangPackKey,
type: AppAddMembersTab['peerType'],
takeOut?: AppAddMembersTab['takeOut'],
skippable: boolean,
selectedPeerIds?: PeerId[]
}) {
this.container.classList.add('add-members-container');
this.nextBtn = ButtonCorner({icon: 'arrow_next'});
this.content.append(this.nextBtn);
@ -39,28 +46,8 @@ export default class AppAddMembersTab extends SliderSuperTab {
}
}
});
}
public attachToPromise(promise: Promise<any>) {
const removeLoader = setButtonLoader(this.nextBtn, 'arrow_next');
promise.then(() => {
this.close();
}, () => {
removeLoader();
});
}
public open(options: {
title: LangPackKey,
placeholder: LangPackKey,
type: AppAddMembersTab['peerType'],
takeOut?: AppAddMembersTab['takeOut'],
skippable: boolean,
selectedPeerIds?: PeerId[]
}) {
const ret = super.open();
//
this.setTitle(options.title);
this.peerType = options.type;
this.takeOut = options.takeOut;
@ -87,7 +74,15 @@ export default class AppAddMembersTab extends SliderSuperTab {
this.nextBtn.innerHTML = '';
this.nextBtn.disabled = false;
this.nextBtn.classList.toggle('is-visible', this.skippable);
return ret;
}
}
public attachToPromise(promise: Promise<any>) {
const removeLoader = setButtonLoader(this.nextBtn, 'arrow_next');
promise.then(() => {
this.close();
}, () => {
removeLoader();
});
}
}

View File

@ -12,7 +12,7 @@ export default class AppArchivedTab extends SliderSuperTab {
private static filterId: REAL_FOLDER_ID = FOLDER_ID_ARCHIVE;
private wasFilterId: number;
protected init() {
public init() {
this.wasFilterId = appDialogsManager.filterId;
this.container.id = 'chats-archived-container';

View File

@ -13,7 +13,7 @@ import {RangeSettingSelector} from '../generalSettings';
import {autoDownloadPeerTypeSection} from './photo';
export default class AppAutoDownloadFileTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadFiles');

View File

@ -51,7 +51,7 @@ export function autoDownloadPeerTypeSection(type: 'photo' | 'video' | 'file', ti
}
export default class AppAutoDownloadPhotoTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadPhotos');

View File

@ -8,7 +8,7 @@ import {SliderSuperTabEventable} from '../../../sliderTab';
import {autoDownloadPeerTypeSection} from './photo';
export default class AppAutoDownloadVideoTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.setTitle('AutoDownloadVideos');

View File

@ -23,7 +23,7 @@ export default class AppBlockedUsersTab extends SliderSuperTab {
public peerIds: PeerId[];
private menuElement: HTMLElement;
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('blocked-users-container');
this.setTitle('BlockedUsers');

View File

@ -34,6 +34,10 @@ export default class AppChatFoldersTab extends SliderSuperTab {
private filtersRendered: {[filterId: number]: Row} = {};
private loadAnimationPromise: ReturnType<LottieLoader['waitForFirstFrame']>;
public static getInitArgs() {
return lottieLoader.loadAnimationFromURLManually('Folders_1');
}
private async renderFolder(
dialogFilter: DialogFilterSuggested | MyDialogFilter,
container?: HTMLElement,
@ -129,7 +133,7 @@ export default class AppChatFoldersTab extends SliderSuperTab {
return row;
}
protected async init() {
public init(p: ReturnType<typeof AppChatFoldersTab['getInitArgs']> = AppChatFoldersTab.getInitArgs()) {
this.container.classList.add('chat-folders-container');
this.setTitle('ChatList.Filter.List.Title');
@ -227,19 +231,21 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.toggleAllChats();
});
this.loadAnimationPromise = lottieLoader.loadAnimationAsAsset({
container: this.stickerContainer,
loop: false,
autoplay: false,
width: 86,
height: 86
}, 'Folders_1').then((player) => {
this.loadAnimationPromise = p.then(async(cb) => {
const player = await cb({
container: this.stickerContainer,
loop: false,
autoplay: false,
width: 86,
height: 86
});
this.animation = player;
return lottieLoader.waitForFirstFrame(player);
});
this.getSuggestedFilters()
this.getSuggestedFilters();
/* return Promise.all([
this.loadAnimationPromise
@ -252,6 +258,8 @@ export default class AppChatFoldersTab extends SliderSuperTab {
this.animation.autoplay = true;
this.animation.play();
});
return super.onOpenAfterTimeout();
}
private toggleAllChats() {

View File

@ -26,7 +26,7 @@ export default class AppContactsTab extends SliderSuperTab {
private middleware: ReturnType<typeof getMiddleware>;
private sortedUserList: SortedUserList;
protected init() {
public init() {
this.container.id = 'contacts-container';
// this.list = appDialogsManager.createChatList(/* {avatarSize: 48, handheldsSize: 66} */);
@ -53,6 +53,8 @@ export default class AppContactsTab extends SliderSuperTab {
this.middleware = getMiddleware();
this.openContacts();
// preload contacts
// appUsersManager.getContacts();
}
@ -83,11 +85,6 @@ export default class AppContactsTab extends SliderSuperTab {
}
public openContacts(query?: string) {
if(this.init) {
this.init();
this.init = null;
}
this.middleware.clean();
const middleware = this.middleware.get();
this.scrollable.onScrolledBottom = null;
@ -126,9 +123,4 @@ export default class AppContactsTab extends SliderSuperTab {
replaceContent(this.scrollable.container, sortedUserList.list);
});
}
public open() {
this.openContacts();
return super.open();
}
}

View File

@ -32,7 +32,7 @@ const AUTO_DOWNLOAD_FOR_KEYS: {[k in keyof AutoDownloadPeerTypeSettings]: LangPa
};
export default class AppDataAndStorageTab extends SliderSuperTabEventable {
protected async init() {
public async init() {
this.header.classList.add('with-border');
this.setTitle('DataSettings');

View File

@ -46,7 +46,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
private type: 'edit' | 'create';
private loadAnimationPromise: ReturnType<LottieLoader['waitForFirstFrame']>;
protected init() {
public init() {
this.container.classList.add('edit-folder-container');
this.caption = document.createElement('div');
this.caption.classList.add('caption');
@ -390,6 +390,7 @@ export default class AppEditFolderTab extends SliderSuperTab {
this.type = 'edit';
}
// @ts-ignore
return super.open().then(() => {
if(this.type === 'edit') {
this.setFilter(this.originalFilter, true);

View File

@ -26,18 +26,27 @@ export default class AppEditProfileTab extends SliderSuperTab {
private editPeer: EditPeer;
protected async init() {
public static getInitArgs() {
return {
bioMaxLength: rootScope.managers.apiManager.getLimit('bio'),
user: rootScope.managers.appUsersManager.getSelf(),
userFull: rootScope.managers.appProfileManager.getProfile(rootScope.myId.toUserId())
};
}
public async init(p: ReturnType<typeof AppEditProfileTab['getInitArgs']>) {
this.container.classList.add('edit-profile-container');
this.setTitle('EditAccount.Title');
const inputFields: InputField[] = [];
const [bioMaxLength, user, userFull] = await Promise.all([p.bioMaxLength, p.user, p.userFull]);
{
const section = generateSection(this.scrollable, undefined, 'Bio.Description');
const inputWrapper = document.createElement('div');
inputWrapper.classList.add('input-wrapper');
const bioMaxLength = await this.managers.apiManager.getLimit('bio');
this.firstNameInputField = new InputField({
label: 'EditProfile.FirstNameLabel',
name: 'first-name',
@ -124,7 +133,12 @@ export default class AppEditProfileTab extends SliderSuperTab {
const promises: Promise<any>[] = [];
promises.push(this.managers.appProfileManager.updateProfile(this.firstNameInputField.value, this.lastNameInputField.value, this.bioInputField.value).then(() => {
const profilePromise = this.managers.appProfileManager.updateProfile(
this.firstNameInputField.value,
this.lastNameInputField.value,
this.bioInputField.value
);
promises.push(profilePromise.then(() => {
this.close();
}, (err) => {
console.error('updateProfile error:', err);
@ -145,10 +159,6 @@ export default class AppEditProfileTab extends SliderSuperTab {
});
}, {listenerSetter: this.listenerSetter});
const user = await this.managers.appUsersManager.getSelf();
const userFull = await this.managers.appProfileManager.getProfile(user.id, true);
this.firstNameInputField.setOriginalValue(user.first_name, true);
this.lastNameInputField.setOriginalValue(user.last_name, true);
this.bioInputField.setOriginalValue(userFull.about, true);

View File

@ -36,7 +36,16 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
private dialogsByFilters: Map<DialogFilter, Set<PeerId>>;
private limit: number;
protected init() {
public init(
filter: DialogFilter,
type: 'included' | 'excluded',
editFolderTab: AppIncludedChatsTab['editFolderTab']
) {
this.originalFilter = filter;
this.filter = copy(this.originalFilter);
this.type = type;
this.editFolderTab = editFolderTab;
this.content.remove();
this.container.classList.add('included-chatlist-container');
this.confirmBtn = ButtonIcon('check btn-confirm blue', {noRipple: true});
@ -178,11 +187,6 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
};
onOpen() {
if(this.init) {
this.init();
this.init = null;
}
this.confirmBtn.style.display = this.type === 'excluded' ? '' : 'none';
this.setTitle(this.type === 'included' ? 'FilterAlwaysShow' : 'FilterNeverShow');
@ -287,16 +291,4 @@ export default class AppIncludedChatsTab extends SliderSuperTab {
return super.onCloseAfterTimeout();
}
/**
* Do not ignore arguments!
*/
public open(filter?: DialogFilter, type?: 'included' | 'excluded', editFolderTab?: AppIncludedChatsTab['editFolderTab']) {
this.originalFilter = filter;
this.filter = copy(this.originalFilter);
this.type = type;
this.editFolderTab = editFolderTab;
return super.open();
}
}

View File

@ -7,12 +7,24 @@
import {SettingSection} from '..';
import {randomLong} from '../../../helpers/random';
import I18n from '../../../lib/langPack';
import rootScope from '../../../lib/rootScope';
import RadioField from '../../radioField';
import Row, {RadioFormFromRows} from '../../row';
import {SliderSuperTab} from '../../slider'
export default class AppLanguageTab extends SliderSuperTab {
protected async init() {
public static getInitArgs() {
return {
languages1: rootScope.managers.apiManager.invokeApiCacheable('langpack.getLanguages', {
lang_pack: 'web'
}),
languages2: rootScope.managers.apiManager.invokeApiCacheable('langpack.getLanguages', {
lang_pack: 'macos'
})
};
}
public init(p: ReturnType<typeof AppLanguageTab['getInitArgs']>) {
this.header.classList.add('with-border');
this.container.classList.add('language-container');
this.setTitle('Telegram.LanguageViewController');
@ -22,12 +34,8 @@ export default class AppLanguageTab extends SliderSuperTab {
const radioRows: Map<string, Row> = new Map();
const promise = Promise.all([
this.managers.apiManager.invokeApiCacheable('langpack.getLanguages', {
lang_pack: 'web'
}),
this.managers.apiManager.invokeApiCacheable('langpack.getLanguages', {
lang_pack: 'macos'
}),
p.languages1,
p.languages2
]).then(([languages1, languages2]) => {
const rendered: Set<string> = new Set();
const webLangCodes = languages1.map((language) => language.lang_code);

View File

@ -23,7 +23,7 @@ export default class AppNewChannelTab extends SliderSuperTab {
private nextBtn: HTMLButtonElement;
private avatarEdit: AvatarEdit;
protected init() {
public init() {
this.container.classList.add('new-channel-container');
this.setTitle('NewChannel');

View File

@ -40,7 +40,13 @@ export default class AppNewGroupTab extends SliderSuperTab {
private userLocationCoords: {lat: number, long: number};
private userLocationAddress: string;
protected init() {
public init(
peerIds: PeerId[],
isGeoChat: boolean = false
) {
this.isGeoChat = isGeoChat;
this.peerIds = peerIds;
this.container.classList.add('new-group-container');
this.setTitle('NewGroup');
@ -146,6 +152,26 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.content.append(this.nextBtn);
this.scrollable.append(section.container, chatsSection.container);
if(isGeoChat) {
this.setTitle('NearbyCreateGroup');
this.groupLocationInputField.container.classList.remove('hide');
this.groupLocationInputField.setValueSilently(I18n.format('Loading', true));
this.startLocating();
} else {
this.groupLocationInputField.container.classList.add('hide');
}
return Promise.all(this.peerIds.map(async(userId) => {
const {dom} = appDialogsManager.addDialogNew({
peerId: userId,
container: this.list,
rippleEnabled: false,
avatarSize: 48
});
dom.lastMessageSpan.append(getUserStatusString(await this.managers.appUsersManager.getUser(userId)));
}));
}
public onCloseAfterTimeout() {
@ -156,35 +182,6 @@ export default class AppNewGroupTab extends SliderSuperTab {
this.nextBtn.disabled = false;
}
public open(peerIds: PeerId[], isGeoChat: boolean = false) {
this.isGeoChat = isGeoChat;
this.peerIds = peerIds;
const result = super.open();
result.then(() => {
if(isGeoChat) {
this.setTitle('NearbyCreateGroup');
this.groupLocationInputField.container.classList.remove('hide');
this.groupLocationInputField.setValueSilently(I18n.format('Loading', true));
this.startLocating();
} else {
this.groupLocationInputField.container.classList.add('hide');
}
return Promise.all(this.peerIds.map(async(userId) => {
const {dom} = appDialogsManager.addDialogNew({
peerId: userId,
container: this.list,
rippleEnabled: false,
avatarSize: 48
});
dom.lastMessageSpan.append(getUserStatusString(await this.managers.appUsersManager.getUser(userId)));
}));
});
return result;
}
private startLocating() {
navigator.geolocation.getCurrentPosition((location) => {
this.userLocationCoords = {

View File

@ -19,7 +19,7 @@ import apiManagerProxy from '../../../lib/mtproto/mtprotoworker';
type InputNotifyKey = Exclude<InputNotifyPeer['_'], 'inputNotifyPeer'>;
export default class AppNotificationsTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('notifications-container', 'with-border');
this.setTitle('Telegram.NotificationSettingsViewController');

View File

@ -35,7 +35,7 @@ export default class AppPeopleNearbyTab extends SliderSuperTab {
protected locatedPeers: Map<PeerId, PeerLocated.peerLocated>;
// protected async init() {
// public async init() {
// this.container.classList.add('people-nearby-container');
// this.setTitle('PeopleNearby');
@ -180,53 +180,53 @@ export default class AppPeopleNearbyTab extends SliderSuperTab {
}
}
public open() {
const result = super.open();
result.then(() => {
this.retryBtn.classList.remove('is-visible');
navigator.geolocation.getCurrentPosition((location) => {
this.latestLocationSaved = {
latitude: location.coords.latitude,
longitude: location.coords.longitude,
accuracy: location.coords.accuracy
};
// public open() {
// const result = super.open();
// result.then(() => {
// this.retryBtn.classList.remove('is-visible');
// navigator.geolocation.getCurrentPosition((location) => {
// this.latestLocationSaved = {
// latitude: location.coords.latitude,
// longitude: location.coords.longitude,
// accuracy: location.coords.accuracy
// };
console.log(this.latestLocationSaved);
// console.log(this.latestLocationSaved);
this.managers.appUsersManager.getLocated(
location.coords.latitude,
location.coords.longitude,
location.coords.accuracy
).then((response) => {
const update = (response as Updates.updates).updates[0] as Update.updatePeerLocated;
const peers = update.peers as PeerLocated.peerLocated[];
const orderedPeers = peers.sort((a, b) => a.distance - b.distance);
const groupsCounter = peers.filter((e) => e.peer._ == 'peerChannel').length;
const usersCounter = peers.filter((e) => e.peer._ != 'peerChannel').length;
orderedPeers?.forEach((peer) => {
const peerId = getPeerId(peer.peer);
const section = peerId.isUser() ? this.peopleSection : this.chatsSection;
this.locatedPeers.set(peerId, peer);
section.sortedList.add(peerId);
});
// this.managers.appUsersManager.getLocated(
// location.coords.latitude,
// location.coords.longitude,
// location.coords.accuracy
// ).then((response) => {
// const update = (response as Updates.updates).updates[0] as Update.updatePeerLocated;
// const peers = update.peers as PeerLocated.peerLocated[];
// const orderedPeers = peers.sort((a, b) => a.distance - b.distance);
// const groupsCounter = peers.filter((e) => e.peer._ == 'peerChannel').length;
// const usersCounter = peers.filter((e) => e.peer._ != 'peerChannel').length;
// orderedPeers?.forEach((peer) => {
// const peerId = getPeerId(peer.peer);
// const section = peerId.isUser() ? this.peopleSection : this.chatsSection;
// this.locatedPeers.set(peerId, peer);
// section.sortedList.add(peerId);
// });
this.errorCategory.classList.toggle('hide', !!(usersCounter || groupsCounter));
this.errorCategory.innerHTML = 'No groups or channels found around you.';
});
}, (error) => {
this.errorCategory.classList.remove('hide');
this.retryBtn.classList.add('is-visible');
this.retryBtn.addEventListener('click', this.open);
if(error instanceof GeolocationPositionError) {
this.errorCategory.innerHTML = 'Location permission denied. Click below to retry.';
} else {
this.errorCategory.innerHTML = 'An error has occurred. Please retry later clicking the button below.';
}
});
});
// this.errorCategory.classList.toggle('hide', !!(usersCounter || groupsCounter));
// this.errorCategory.innerHTML = 'No groups or channels found around you.';
// });
// }, (error) => {
// this.errorCategory.classList.remove('hide');
// this.retryBtn.classList.add('is-visible');
// this.retryBtn.addEventListener('click', this.open);
// if(error instanceof GeolocationPositionError) {
// this.errorCategory.innerHTML = 'Location permission denied. Click below to retry.';
// } else {
// this.errorCategory.innerHTML = 'An error has occurred. Please retry later clicking the button below.';
// }
// });
// });
return result;
}
// return result;
// }
private startWatching() {
if(!this.latestLocationSaved || this.isLocationWatched) return;

View File

@ -10,7 +10,7 @@ import {LangPackKey} from '../../../../lib/langPack';
import PrivacyType from '../../../../lib/appManagers/utils/privacy/privacyType';
export default class AppPrivacyAddToGroupsTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-add-to-groups');
this.setTitle('PrivacySettings.Groups');

View File

@ -9,7 +9,7 @@ import PrivacySection from '../../../privacySection';
import {LangPackKey} from '../../../../lib/langPack';
export default class AppPrivacyCallsTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-calls');
this.setTitle('PrivacySettings.VoiceCalls');

View File

@ -9,7 +9,7 @@ import PrivacySection from '../../../privacySection';
import {LangPackKey} from '../../../../lib/langPack';
export default class AppPrivacyForwardMessagesTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-forward-messages');
this.setTitle('PrivacySettings.Forwards');

View File

@ -9,7 +9,7 @@ import PrivacySection from '../../../privacySection';
import {LangPackKey} from '../../../../lib/langPack';
export default class AppPrivacyLastSeenTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-last-seen');
this.setTitle('PrivacyLastSeen');

View File

@ -11,7 +11,7 @@ import anchorCopy from '../../../../helpers/dom/anchorCopy';
import PrivacyType from '../../../../lib/appManagers/utils/privacy/privacyType';
export default class AppPrivacyPhoneNumberTab extends SliderSuperTabEventable {
protected async init() {
public async init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-phone-number');
this.setTitle('PrivacyPhone');

View File

@ -10,7 +10,7 @@ import {LangPackKey} from '../../../../lib/langPack';
import PrivacyType from '../../../../lib/appManagers/utils/privacy/privacyType';
export default class AppPrivacyProfilePhotoTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-profile-photo');
this.setTitle('PrivacyProfilePhoto');

View File

@ -9,7 +9,7 @@ import PrivacySection from '../../../privacySection';
import {LangPackKey} from '../../../../lib/langPack';
export default class AppPrivacyVoicesTab extends SliderSuperTabEventable {
protected init() {
public init() {
this.header.classList.add('with-border');
this.container.classList.add('privacy-tab', 'privacy-voices');
this.setTitle('PrivacyVoiceMessages');

View File

@ -4,7 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import {SliderSuperTabEventable} from '../../sliderTab';
import SliderSuperTab, {SliderSuperTabEventable} from '../../sliderTab';
import {SettingSection} from '..';
import Row from '../../row';
import {AccountPassword, Authorization, InputPrivacyKey, Updates} from '../../../layer';
@ -38,7 +38,15 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
private activeSessionsRow: Row;
private authorizations: Authorization.authorization[];
protected init() {
public static getInitArgs(fromTab: SliderSuperTab) {
return {
appConfig: fromTab.managers.apiManager.getAppConfig(),
globalPrivacy: fromTab.managers.appPrivacyManager.getGlobalPrivacySettings(),
contentSettings: fromTab.managers.apiManager.invokeApi('account.getContentSettings')
};
}
public init(p: ReturnType<typeof AppPrivacyAndSecurityTab['getInitArgs']>) {
this.header.classList.add('with-border');
this.container.classList.add('dont-u-dare-block-me');
this.setTitle('PrivacySettings');
@ -287,8 +295,8 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
}, {once: true});
const promise = Promise.all([
this.managers.apiManager.getAppConfig(),
this.managers.appPrivacyManager.getGlobalPrivacySettings()
p.appConfig,
p.globalPrivacy
]).then(([appConfig, settings]) => {
if(destroyed) {
return;
@ -336,7 +344,7 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
});
}, {once: true});
const promise = this.managers.apiManager.invokeApi('account.getContentSettings').then((settings) => {
const promise = p.contentSettings.then((settings) => {
if(!settings.pFlags.sensitive_can_change) {
return;
}

View File

@ -12,7 +12,7 @@ import SliderSuperTab from '../../sliderTab';
import wrapStickerToRow from '../../wrappers/stickerToRow';
export default class AppQuickReactionTab extends SliderSuperTab {
protected init() {
public init() {
this.header.classList.add('with-border');
this.setTitle('DoubleTapSetting');
this.container.classList.add('quick-reaction-container');

View File

@ -47,7 +47,7 @@ export default class AppSettingsTab extends SliderSuperTab {
private authorizations: Authorization.authorization[];
private getAuthorizationsPromise: Promise<AccountAuthorizations.accountAuthorizations>;
protected async init() {
public async init() {
this.container.classList.add('settings-container');
this.setTitle('Settings');
@ -146,26 +146,40 @@ export default class AppSettingsTab extends SliderSuperTab {
const buttonsDiv = document.createElement('div');
buttonsDiv.classList.add('profile-buttons');
const b: [string, LangPackKey, SliderSuperTabConstructable][] = [
['unmute', 'AccountSettings.Notifications', AppNotificationsTab],
['data', 'DataSettings', AppDataAndStorageTab],
['lock', 'AccountSettings.PrivacyAndSecurity', AppPrivacyAndSecurityTab],
['settings', 'Telegram.GeneralSettingsViewController', AppGeneralSettingsTab],
['folder', 'AccountSettings.Filters', AppChatFoldersTab]
type ConstructorP<T> = T extends {
new (...args: any[]): infer U;
} ? U : never;
const m = <T extends SliderSuperTabConstructable>(
icon: string,
text: LangPackKey,
c: T,
args?: Parameters<ConstructorP<T>['init']>
): [string, LangPackKey, T, any[]?] => {
return [icon, text, c, args] as any;
};
const b = [
m('unmute', 'AccountSettings.Notifications', AppNotificationsTab),
m('data', 'DataSettings', AppDataAndStorageTab),
m('lock', 'AccountSettings.PrivacyAndSecurity', AppPrivacyAndSecurityTab, [AppPrivacyAndSecurityTab.getInitArgs(this)]),
m('settings', 'Telegram.GeneralSettingsViewController', AppGeneralSettingsTab),
m('folder', 'AccountSettings.Filters', AppChatFoldersTab, [AppChatFoldersTab.getInitArgs()])
];
const rows = b.map(([icon, langPackKey, tabConstructor]) => {
const rows = b.map(([icon, langPackKey, tabConstructor, args]) => {
return new Row({
titleLangKey: langPackKey,
icon,
clickable: () => {
this.slider.createTab(tabConstructor).open();
this.slider.createTab(tabConstructor as any).open(...(args || []));
// new tabConstructor(this.slider, true).open();
},
listenerSetter: this.listenerSetter
});
});
const languageArgs = AppLanguageTab.getInitArgs();
rows.push(
this.devicesRow = new Row({
titleLangKey: 'Devices',
@ -192,7 +206,7 @@ export default class AppSettingsTab extends SliderSuperTab {
titleRightSecondary: i18n('LanguageName'),
icon: 'language',
clickable: () => {
this.slider.createTab(AppLanguageTab).open();
this.slider.createTab(AppLanguageTab).open(languageArgs);
},
listenerSetter: this.listenerSetter
})
@ -208,10 +222,25 @@ export default class AppSettingsTab extends SliderSuperTab {
this.scrollable.append(this.profile.element/* profileSection.container */, buttonsSection.container);
const getEditProfileArgs = () => {
editProfileArgs = AppEditProfileTab.getInitArgs();
};
let editProfileArgs: ReturnType<typeof AppEditProfileTab['getInitArgs']>;
attachClickEvent(this.buttons.edit, () => {
const tab = this.slider.createTab(AppEditProfileTab);
tab.open();
tab.open(editProfileArgs);
}, {listenerSetter: this.listenerSetter});
getEditProfileArgs();
// this.listenerSetter.add(rootScope)('user_full_update', (userId) => {
// if(rootScope.myId.toUserId() === userId) {
// getEditProfileArgs();
// }
// });
this.listenerSetter.add(rootScope)('user_update', (userId) => {
if(rootScope.myId.toUserId() === userId) {
getEditProfileArgs();
}
});
lottieLoader.loadLottieWorkers();

View File

@ -17,7 +17,7 @@ import wrapStickerToRow from '../../wrappers/stickerToRow';
export default class AppChatReactionsTab extends SliderSuperTabEventable {
public chatId: ChatId;
protected async init() {
public async init() {
this.setTitle('Reactions');
const availableReactions = await this.managers.appReactionsManager.getActiveAvailableReactions();

View File

@ -27,7 +27,7 @@ export default class AppChatTypeTab extends SliderSuperTabEventable {
public chatId: ChatId;
public chatFull: ChatFull;
protected async init() {
public async init() {
this.container.classList.add('edit-peer-container', 'group-type-container');
const isBroadcast = await this.managers.appChatsManager.isBroadcast(this.chatId);

View File

@ -366,7 +366,7 @@ export default class AppEditChatTab extends SliderSuperTab {
}
}
protected init() {
public init() {
return this._init();
}
}

View File

@ -27,7 +27,7 @@ export default class AppEditContactTab extends SliderSuperTab {
private editPeer: EditPeer;
public peerId: PeerId;
protected async init() {
public async init() {
this.container.classList.add('edit-peer-container', 'edit-contact-container');
const isNew = !(await this.managers.appUsersManager.isContact(this.peerId.toUserId()));
this.setTitle(isNew ? 'AddContactTitle' : 'Edit');

View File

@ -4,6 +4,7 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import type {MyDocument} from '../../../lib/appManagers/appDocsManager';
import {SliderSuperTab} from '../../slider';
import InputSearch from '../../inputSearch';
import animationIntersector, {AnimationItemGroup} from '../../animationIntersector';
@ -11,7 +12,6 @@ import appSidebarRight from '..';
import {AppInlineBotsManager} from '../../../lib/appManagers/appInlineBotsManager';
import GifsMasonry from '../../gifsMasonry';
import appImManager from '../../../lib/appManagers/appImManager';
import type {MyDocument} from '../../../lib/appManagers/appDocsManager';
import mediaSizes from '../../../helpers/mediaSizes';
import findUpClassName from '../../../helpers/dom/findUpClassName';
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
@ -31,7 +31,7 @@ export default class AppGifsTab extends SliderSuperTab {
private searchPromise: ReturnType<AppInlineBotsManager['getInlineResults']>;
protected init() {
public init() {
this.container.id = 'search-gifs-container';
this.inputSearch = new InputSearch('SearchGifsTitle', (value) => {
@ -49,6 +49,14 @@ export default class AppGifsTab extends SliderSuperTab {
this.masonry = new GifsMasonry(this.gifsDiv, ANIMATIONGROUP, this.scrollable);
// this.backBtn.parentElement.append(this.inputSearch.container);
appSidebarRight.toggleSidebar(true).then(() => {
this.search('', true);
this.scrollable.onScrolledBottom = () => {
this.search(this.inputSearch.value, false);
};
});
}
private onGifsClick = async(e: MouseEvent | TouchEvent) => {
@ -84,18 +92,6 @@ export default class AppGifsTab extends SliderSuperTab {
this.masonry.clear();
}
public open() {
const ret = super.open();
appSidebarRight.toggleSidebar(true).then(() => {
this.search('', true);
this.scrollable.onScrolledBottom = () => {
this.search(this.inputSearch.value, false);
};
});
return ret;
}
public async search(query: string, newSearch = true) {
if(this.searchPromise || this.loadedAll) return;

View File

@ -142,7 +142,7 @@ export class ChatPermissions {
export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
public chatId: ChatId;
protected async init() {
public async init() {
this.container.classList.add('edit-peer-container', 'group-permissions-container');
this.setTitle('ChannelPermissions');

View File

@ -16,17 +16,14 @@ import wrapEmojiText from '../../../lib/richTextProcessor/wrapEmojiText';
export default class AppPollResultsTab extends SliderSuperTab {
private resultsDiv: HTMLElement;
protected init() {
public async init(message: any) {
this.container.id = 'poll-results-container';
this.container.classList.add('chatlist-container');
this.resultsDiv = document.createElement('div');
this.resultsDiv.classList.add('poll-results');
this.scrollable.append(this.resultsDiv);
}
public async open(message: any) {
const ret = super.open();
const poll = await this.managers.appPollsManager.getPoll(message.media.poll.id);
this.setTitle(poll.poll.pFlags.quiz ? 'PollResults.Title.Quiz' : 'PollResults.Title.Poll');
@ -124,7 +121,5 @@ export default class AppPollResultsTab extends SliderSuperTab {
console.log('gOt VotEs', votes);
}); */
});
return ret;
}
}

View File

@ -27,7 +27,12 @@ export default class AppPrivateSearchTab extends SliderSuperTab {
this.appSearch.beginSearch(this.peerId, this.threadId, this.query);
}
protected init() {
public init(
peerId: PeerId,
threadId?: number,
onDatePick?: AppPrivateSearchTab['onDatePick'],
query?: string
) {
this.container.id = 'search-private-container';
this.container.classList.add('chatlist-container');
this.inputSearch = new InputSearch('Search');
@ -42,10 +47,6 @@ export default class AppPrivateSearchTab extends SliderSuperTab {
this.appSearch = new AppSearch(c, this.inputSearch, {
messages: new SearchGroup('Chat.Search.PrivateSearch', 'messages')
});
}
open(peerId: PeerId, threadId?: number, onDatePick?: AppPrivateSearchTab['onDatePick'], query?: string) {
const ret = super.open();
if(!this.peerId) {
this.query = query;
@ -66,7 +67,5 @@ export default class AppPrivateSearchTab extends SliderSuperTab {
} else {
this.appSearch.beginSearch(this.peerId, this.threadId, query);
}
return ret;
}
}

View File

@ -26,7 +26,7 @@ export default class AppStickersTab extends SliderSuperTab {
private setsDiv: HTMLDivElement;
private lazyLoadQueue: LazyLoadQueue;
protected init() {
public init() {
this.container.id = 'stickers-container';
this.container.classList.add('chatlist-container');
@ -83,6 +83,10 @@ export default class AppStickersTab extends SliderSuperTab {
});
}
}, {listenerSetter: this.listenerSetter});
appSidebarRight.toggleSidebar(true).then(() => {
this.renderFeatured();
});
}
public onCloseAfterTimeout() {
@ -194,15 +198,6 @@ export default class AppStickersTab extends SliderSuperTab {
this.setsDiv.append(div);
}
public open() {
const ret = super.open();
appSidebarRight.toggleSidebar(true).then(() => {
this.renderFeatured();
});
return ret;
}
public renderFeatured() {
return this.managers.appStickersManager.getFeaturedStickers().then((coveredSets) => {
if(this.inputSearch.value) {

View File

@ -20,7 +20,7 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
public chatId: ChatId;
public userId: UserId;
protected async init() {
public async init() {
this.container.classList.add('edit-peer-container', 'user-permissions-container');
this.setTitle('UserRestrictions');

View File

@ -7,15 +7,13 @@
import {horizontalMenu} from './horizontalMenu';
import {TransitionSlider} from './transition';
import appNavigationController, {NavigationItem} from './appNavigationController';
import SliderSuperTab, {SliderSuperTabConstructable, SliderTab} from './sliderTab';
import {attachClickEvent} from '../helpers/dom/clickEvent';
import SliderSuperTab, {SliderSuperTabConstructable} from './sliderTab';
import indexOfAndSplice from '../helpers/array/indexOfAndSplice';
import safeAssign from '../helpers/object/safeAssign';
import {AppManagers} from '../lib/appManagers/managers';
const TRANSITION_TIME = 250;
export type {SliderTab};
export {SliderSuperTab};
export default class SidebarSlider {
@ -23,7 +21,7 @@ export default class SidebarSlider {
public historyTabIds: (number | SliderSuperTab)[] = []; // * key is any, since right sidebar is ugly nowz
public tabsContainer: HTMLElement;
public sidebarEl: HTMLElement;
public tabs: Map<any, SliderTab>; // * key is any, since right sidebar is ugly now
public tabs: Map<any, SliderSuperTab>; // * key is any, since right sidebar is ugly now
private canHideFirst = false;
private navigationType: NavigationItem['type'];
protected managers: AppManagers;
@ -46,9 +44,9 @@ export default class SidebarSlider {
this._selectTab(0);
}
Array.from(this.sidebarEl.querySelectorAll('.sidebar-close-button') as any as HTMLElement[]).forEach((el) => {
attachClickEvent(el, this.onCloseBtnClick);
});
// Array.from(this.sidebarEl.querySelectorAll('.sidebar-close-button') as any as HTMLElement[]).forEach((el) => {
// attachClickEvent(el, this.onCloseBtnClick);
// });
}
public onCloseBtnClick = () => {
@ -84,14 +82,15 @@ export default class SidebarSlider {
return false;
}
const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
const tab: SliderSuperTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
if(tab) {
if(tab.onOpen) {
tab.onOpen();
}
// @ts-ignore
tab.onOpen?.();
// @ts-ignore
if(tab.onOpenAfterTimeout) {
setTimeout(() => {
// @ts-ignore
tab.onOpenAfterTimeout();
}, TRANSITION_TIME);
}
@ -143,14 +142,15 @@ export default class SidebarSlider {
appNavigationController.removeByType(this.navigationType, true);
}
const tab: SliderTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
const tab: SliderSuperTab = id instanceof SliderSuperTab ? id : this.tabs.get(id);
if(tab) {
if(tab.onClose) {
tab.onClose();
}
// @ts-ignore
tab.onClose?.();
// @ts-ignore
if(tab.onCloseAfterTimeout) {
setTimeout(() => {
// @ts-ignore
tab.onCloseAfterTimeout();
}, TRANSITION_TIME + 30);
}

View File

@ -12,13 +12,6 @@ import ButtonIcon from './buttonIcon';
import Scrollable from './scrollable';
import SidebarSlider from './slider';
export interface SliderTab {
onOpen?: () => void,
onOpenAfterTimeout?: () => void,
onClose?: () => void,
onCloseAfterTimeout?: () => void
}
export interface SliderSuperTabConstructable<T extends SliderSuperTab = any> {
new(slider: SidebarSlider, destroyable: boolean): T;
}
@ -27,7 +20,9 @@ export interface SliderSuperTabEventableConstructable {
new(slider: SidebarSlider, destroyable: boolean): SliderSuperTabEventable;
}
export default class SliderSuperTab implements SliderTab {
export default class SliderSuperTab {
public static getInitArgs?(fromTab: SliderSuperTab): any;
public container: HTMLElement;
public header: HTMLElement;
@ -82,10 +77,10 @@ export default class SliderSuperTab implements SliderTab {
return this.slider.closeTab(this);
}
public async open(...args: any[]) {
public async open(...args: Parameters<typeof this['init']>) {
if(this.init) {
try {
const result = this.init();
const result = this.init(...args);
this.init = null;
if(result instanceof Promise) {
@ -99,11 +94,15 @@ export default class SliderSuperTab implements SliderTab {
this.slider.selectTab(this);
}
protected init(): Promise<any> | any {
public init(...args: any[]): Promise<any> | any {
}
public onCloseAfterTimeout() {
protected onOpen() {}
protected onOpenAfterTimeout() {}
protected onClose() {}
protected onCloseAfterTimeout() {
if(this.destroyable) { // ! WARNING, пока что это будет работать только с самой последней внутренней вкладкой !
this.slider.tabs.delete(this);
this.container.remove();
@ -113,8 +112,7 @@ export default class SliderSuperTab implements SliderTab {
}
protected setTitle(key: LangPackKey) {
this.title.innerHTML = '';
this.title.append(i18n(key));
this.title.replaceChildren(i18n(key));
}
}

View File

@ -250,6 +250,11 @@ const Transition = (
}
if(to) {
const transitionTimeout = to.dataset.transitionTimeout;
if(transitionTimeout) {
clearTimeout(+transitionTimeout);
}
onTransitionEndCallbacks.set(to, () => {
to.classList.remove('to');
onTransitionEndCallbacks.delete(to);
@ -281,6 +286,8 @@ const Transition = (
});
}
_from.dataset.transitionTimeout = '' + timeout;
if(isHeavy) {
if(!animationDeferred) {
animationDeferred = deferredPromise<void>();

View File

@ -14,6 +14,7 @@ import createVideo from '../../helpers/dom/createVideo';
import isInDOM from '../../helpers/dom/isInDOM';
import renderImageFromUrl from '../../helpers/dom/renderImageFromUrl';
import getStrippedThumbIfNeeded from '../../helpers/getStrippedThumbIfNeeded';
import makeError from '../../helpers/makeError';
import mediaSizes, {ScreenSize} from '../../helpers/mediaSizes';
import {Middleware} from '../../helpers/middleware';
import noop from '../../helpers/noop';
@ -502,7 +503,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
}
} else if(doc.supportsStreaming) {
if(noAutoDownload) {
loadPromise = Promise.reject();
loadPromise = Promise.reject(makeError('NO_AUTO_DOWNLOAD'));
} else if(!cacheContext.downloaded && preloader) { // * check for uploading video
preloader.attach(container, false, null);
video.addEventListener(IS_SAFARI ? 'timeupdate' : 'canplay', () => {
@ -541,12 +542,15 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
}
renderDeferred.resolve();
}, (err) => {
console.error('video load error', err);
renderDeferred.reject(err);
});
renderImageFromUrl(video, cacheContext.url);
}, noop);
return {download: loadPromise, render: renderDeferred};
return {download: loadPromise, render: Promise.all([loadPromise, renderDeferred])};
};
if(preloader && !uploadFileName) {

3
src/global.d.ts vendored
View File

@ -51,7 +51,8 @@ declare global {
type FiltersError = 'PINNED_DIALOGS_TOO_MUCH';
type LocalFileError = ApiFileManagerError | ReferenceError | StorageError;
type LocalErrorType = LocalFileError | NetworkerError | FiltersError | 'UNKNOWN' | 'NO_DOC' | 'MIDDLEWARE' | 'PORT_DISCONNECTED';
type LocalErrorType = LocalFileError | NetworkerError | FiltersError |
'UNKNOWN' | 'NO_DOC' | 'MIDDLEWARE' | 'PORT_DISCONNECTED' | 'NO_AUTO_DOWNLOAD';
type ServerErrorType = 'FILE_REFERENCE_EXPIRED' | 'SESSION_REVOKED' | 'AUTH_KEY_DUPLICATED' |
'SESSION_PASSWORD_NEEDED' | 'CONNECTION_NOT_INITED' | 'ERROR_EMPTY' | 'MTPROTO_CLUSTER_INVALID' |

View File

@ -0,0 +1,39 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import cancelEvent from './dom/cancelEvent';
import parseUriParams from './string/parseUriParams';
export default function addAnchorListener<Params extends {pathnameParams?: any, uriParams?: any}>(options: {
name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers' | 'im' |
'resolve' | 'privatepost' | 'addstickers' | 'voicechat' | 'joinchat' | 'join' | 'invoice' |
'addemoji',
protocol?: 'tg',
callback: (params: Params, element?: HTMLAnchorElement) => boolean | any,
noPathnameParams?: boolean,
noUriParams?: boolean
}) {
(window as any)[(options.protocol ? options.protocol + '_' : '') + options.name] = (element?: HTMLAnchorElement/* , e: Event */) => {
cancelEvent(null);
let href = element.href;
let pathnameParams: any[];
let uriParams: any;
const u = new URL(href);
const match = u.host.match(/(.+?)\.t(?:elegram)?\.me/);
if(match) {
u.pathname = match[1] + (u.pathname === '/' ? '' : u.pathname);
href = u.toString();
}
if(!options.noPathnameParams) pathnameParams = new URL(href).pathname.split('/').slice(1);
if(!options.noUriParams) uriParams = parseUriParams(href);
const res = options.callback({pathnameParams, uriParams} as Params, element);
return res === undefined ? res : false;
};
}

View File

@ -125,7 +125,7 @@ export default class DropdownHover extends EventListenerBase<{
}, TOGGLE_TIMEOUT);
};
protected init() {
public init() {
if(!IS_TOUCH_SUPPORTED) {
this.element.onmouseout = this.onMouseOut;
this.element.onmouseover = (e) => {

View File

@ -1,12 +1,23 @@
import {IS_APPLE_MOBILE} from '../environment/userAgent';
export default function onMediaLoad(media: HTMLMediaElement, readyState = media.HAVE_METADATA, useCanplayOnIos?: boolean) {
return new Promise<void>((resolve) => {
return new Promise<void>((resolve, reject) => {
if(media.readyState >= readyState) {
resolve();
return;
}
media.addEventListener(IS_APPLE_MOBILE && !useCanplayOnIos ? 'loadeddata' : 'canplay', () => resolve(), {once: true});
const loadEventName = IS_APPLE_MOBILE && !useCanplayOnIos ? 'loadeddata' : 'canplay';
const errorEventName = 'error';
const onLoad = () => {
media.removeEventListener(errorEventName, onError);
resolve();
};
const onError = (e: ErrorEvent) => {
media.removeEventListener(loadEventName, onLoad);
reject(e);
};
media.addEventListener(loadEventName, onLoad, {once: true});
media.addEventListener(errorEventName, onError, {once: true});
});
}

View File

@ -0,0 +1,15 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
export default function parseUriParams(uri: string, splitted = uri.split('?')) {
const params: any = {};
if(!splitted[1]) return params;
splitted[1].split('&').forEach((item) => {
params[item.split('=')[0]] = decodeURIComponent(item.split('=')[1]);
});
return params;
}

View File

@ -73,6 +73,9 @@
<div class="auth-image"></div>
</div>
</div>
<div class="tabs-tab page-signImport">
<div class="container center-align"></div>
</div>
<div class="tabs-tab page-authCode">
<div class="container center-align">
<div class="auth-image"></div>

View File

@ -26,6 +26,10 @@ import getProxiedManagers from './lib/appManagers/getProxiedManagers';
import themeController from './helpers/themeController';
import overlayCounter from './helpers/overlayCounter';
import singleInstance from './lib/mtproto/singleInstance';
import parseUriParams from './helpers/string/parseUriParams';
import Modes from './config/modes';
import {AuthState} from './types';
// import appNavigationController from './components/appNavigationController';
document.addEventListener('DOMContentLoaded', async() => {
toggleAttributePolyfill();
@ -242,7 +246,37 @@ document.addEventListener('DOMContentLoaded', async() => {
console.log('got state, time:', performance.now() - perf);
const authState = stateResult.state.authState;
let authState = stateResult.state.authState;
const hash = location.hash;
const splitted = hash.split('?');
const params = parseUriParams(hash, splitted);
if(params.tgWebAuthToken && authState._ !== 'authStateSignedIn') {
const data: AuthState.signImport['data'] = {
token: params.tgWebAuthToken,
dcId: +params.tgWebAuthDcId,
userId: params.tgWebAuthUserId.toUserId(),
isTest: params.tgWebAuthTest !== undefined && !!+params.tgWebAuthTest,
tgAddr: params.tgaddr
};
if(data.isTest !== Modes.test) {
const urlSearchParams = new URLSearchParams(location.search);
if(+params.tgWebAuthTest) {
urlSearchParams.set('test', '1');
} else {
urlSearchParams.delete('test');
}
location.search = urlSearchParams.toString();
return;
}
rootScope.managers.appStateManager.pushToState('authState', authState = {_: 'authStateSignImport', data});
// appNavigationController.overrideHash('?tgaddr=' + encodeURIComponent(params.tgaddr));
}
if(authState._ !== 'authStateSignedIn'/* || 1 === 1 */) {
console.log('Will mount auth page:', authState._, Date.now() / 1000);
@ -294,6 +328,9 @@ document.addEventListener('DOMContentLoaded', async() => {
case 'authStateSignUp':
pagePromise = (await import('./pages/pageSignUp')).default.mount(authState.authCode);
break;
case 'authStateSignImport':
pagePromise = (await import('./pages/pageSignImport')).default.mount(authState.data);
break;
}
// });

8
src/layer.d.ts vendored
View File

@ -6850,6 +6850,7 @@ export namespace ChannelAdminLogEventsFilter {
group_call?: true,
invites?: true,
send?: true,
forums?: true,
}>
};
}
@ -13950,6 +13951,12 @@ export type MessagesGetExtendedMedia = {
id: Array<number>
};
export type AuthImportWebTokenAuthorization = {
api_id: number,
api_hash: string,
web_auth_token: string
};
export interface MethodDeclMap {
'invokeAfterMsg': {req: InvokeAfterMsg, res: any},
'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any},
@ -14407,5 +14414,6 @@ export interface MethodDeclMap {
'messages.getRecentReactions': {req: MessagesGetRecentReactions, res: MessagesReactions},
'messages.clearRecentReactions': {req: MessagesClearRecentReactions, res: boolean},
'messages.getExtendedMedia': {req: MessagesGetExtendedMedia, res: Updates},
'auth.importWebTokenAuthorization': {req: AuthImportWebTokenAuthorization, res: AuthAuthorization},
}

View File

@ -674,9 +674,9 @@ class ApiUpdatesManager {
state.date = 1; */
// state.pts -= 100;
/* state.date = 1628623682;
state.pts = 2007500;
state.seq = 503; */
// state.date = 1628623682;
// state.pts = 2007500;
// state.seq = 503;
Object.assign(this.updatesState, state);

View File

@ -627,10 +627,12 @@ export class AppDialogsManager {
const containerToAppend = this.folders.menu as HTMLElement;
const r = await Promise.all(order.map(async(filterId) => {
return {
indexKey: await this.managers.dialogsStorage.getDialogIndexKeyByFilterId(filterId),
filter: await this.managers.filtersStorage.getFilter(filterId)
};
const [indexKey, filter] = await Promise.all([
this.managers.dialogsStorage.getDialogIndexKeyByFilterId(filterId),
this.managers.filtersStorage.getFilter(filterId)
]);
return {indexKey, filter};
}));
order.forEach((filterId, idx) => {

View File

@ -95,6 +95,8 @@ import wrapPeerTitle from '../../components/wrappers/peerTitle';
import NBSP from '../../helpers/string/nbsp';
import {makeMediaSize, MediaSize} from '../../helpers/mediaSize';
import {MiddleEllipsisElement} from '../../components/middleEllipsis';
import addAnchorListener from '../../helpers/addAnchorListener';
import parseUriParams from '../../helpers/string/parseUriParams';
export type ChatSavedPosition = {
mids: number[],
@ -494,7 +496,7 @@ export class AppImManager extends EventListenerBase<{
setInterval(setAuthorized, ONE_DAY);
setAuthorized();
this.addAnchorListener<{}>({
addAnchorListener<{}>({
name: 'showMaskedAlert',
callback: (params, element) => {
const href = element.href;
@ -518,7 +520,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{uriParams: {command: string, bot: string}}>({
addAnchorListener<{uriParams: {command: string, bot: string}}>({
name: 'execBotCommand',
callback: ({uriParams}) => {
const {command, bot} = uriParams;
@ -534,7 +536,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{uriParams: {hashtag: string}}>({
addAnchorListener<{uriParams: {hashtag: string}}>({
name: 'searchByHashtag',
callback: ({uriParams}) => {
const {hashtag} = uriParams;
@ -553,7 +555,7 @@ export class AppImManager extends EventListenerBase<{
'addstickers' | 'addemoji',
INTERNAL_LINK_TYPE.STICKER_SET | INTERNAL_LINK_TYPE.EMOJI_SET
][]).forEach(([name, type]) => {
this.addAnchorListener<{pathnameParams: [typeof name, string]}>({
addAnchorListener<{pathnameParams: [typeof name, string]}>({
name,
callback: ({pathnameParams}) => {
if(!pathnameParams[1]) {
@ -569,7 +571,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{
addAnchorListener<{
uriParams: {
set: string
}
@ -585,7 +587,7 @@ export class AppImManager extends EventListenerBase<{
// * t.me/invoice/asdasdad
// * t.me/$asdasdad
this.addAnchorListener<{pathnameParams: ['invoice', string] | string}>({
addAnchorListener<{pathnameParams: ['invoice', string] | string}>({
name: 'invoice',
callback: ({pathnameParams}) => {
const link: InternalLink = {
@ -598,7 +600,7 @@ export class AppImManager extends EventListenerBase<{
});
// Support old t.me/joinchat/asd and new t.me/+asd
this.addAnchorListener<{pathnameParams: ['joinchat', string]}>({
addAnchorListener<{pathnameParams: ['joinchat', string]}>({
name: 'joinchat',
callback: ({pathnameParams}) => {
const link: InternalLink = {
@ -611,7 +613,7 @@ export class AppImManager extends EventListenerBase<{
});
if(IS_GROUP_CALL_SUPPORTED) {
this.addAnchorListener<{
addAnchorListener<{
uriParams: Omit<InternalLink.InternalLinkVoiceChat, '_'>
}>({
name: 'voicechat',
@ -623,7 +625,7 @@ export class AppImManager extends EventListenerBase<{
});
}
this.addAnchorListener<{
addAnchorListener<{
// pathnameParams: ['c', string, string],
// uriParams: {thread?: number}
// } | {
@ -662,7 +664,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{
addAnchorListener<{
uriParams: {
domain: string,
@ -701,7 +703,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{
addAnchorListener<{
uriParams: {
channel: string,
post: string,
@ -717,7 +719,7 @@ export class AppImManager extends EventListenerBase<{
}
});
this.addAnchorListener<{
addAnchorListener<{
uriParams: {
slug: string
}
@ -731,7 +733,7 @@ export class AppImManager extends EventListenerBase<{
});
['joinchat' as const, 'join' as const].forEach((name) => {
this.addAnchorListener<{
addAnchorListener<{
uriParams: {
invite: string
}
@ -988,53 +990,15 @@ export class AppImManager extends EventListenerBase<{
public openUrl(url: string) {
const {url: wrappedUrl, onclick} = wrapUrl(url);
if(!onclick) {
return;
}
const a = document.createElement('a');
a.href = wrappedUrl;
(window as any)[onclick](a);
}
private addAnchorListener<Params extends {pathnameParams?: any, uriParams?: any}>(options: {
name: 'showMaskedAlert' | 'execBotCommand' | 'searchByHashtag' | 'addstickers' | 'im' |
'resolve' | 'privatepost' | 'addstickers' | 'voicechat' | 'joinchat' | 'join' | 'invoice' |
'addemoji',
protocol?: 'tg',
callback: (params: Params, element?: HTMLAnchorElement) => boolean | any,
noPathnameParams?: boolean,
noUriParams?: boolean
}) {
(window as any)[(options.protocol ? options.protocol + '_' : '') + options.name] = (element?: HTMLAnchorElement/* , e: Event */) => {
cancelEvent(null);
let href = element.href;
let pathnameParams: any[];
let uriParams: any;
const u = new URL(href);
const match = u.host.match(/(.+?)\.t(?:elegram)?\.me/);
if(match) {
u.pathname = match[1] + (u.pathname === '/' ? '' : u.pathname);
href = u.toString();
}
if(!options.noPathnameParams) pathnameParams = new URL(href).pathname.split('/').slice(1);
if(!options.noUriParams) uriParams = this.parseUriParams(href);
const res = options.callback({pathnameParams, uriParams} as Params, element);
return res === undefined ? res : false;
};
}
private parseUriParams(uri: string, splitted = uri.split('?')) {
const params: any = {};
if(!splitted[1]) return params;
splitted[1].split('&').forEach((item) => {
params[item.split('=')[0]] = decodeURIComponent(item.split('=')[1]);
});
return params;
}
private onHashChange = (saveState?: boolean) => {
const hash = location.hash;
if(!saveState) {
@ -1042,19 +1006,14 @@ export class AppImManager extends EventListenerBase<{
}
const splitted = hash.split('?');
const params = this.parseUriParams(hash, splitted);
const params = parseUriParams(hash, splitted);
this.log('hashchange', hash, splitted[0], params);
if(!hash) {
return;
}
if(params.tgaddr) {
const {onclick} = wrapUrl(params.tgaddr);
if(onclick) {
const a = document.createElement('a');
a.href = params.tgaddr;
(window as any)[onclick](a);
}
this.openUrl(params.tgaddr);
return;
}

View File

@ -47,7 +47,7 @@ export class AppPhotosManager extends AppManager {
if(photo.sizes?.length) {
// * sometimes photoStrippedSize can be the last item
photo.sizes.sort((a, b) => {
return ((a as PhotoSize.photoSize).size || 0) - ((b as PhotoSize.photoSize).size || 0);
return ((a as PhotoSize.photoSize).size || ((a as PhotoSize.photoSizeProgressive).sizes ? Infinity : 0)) - ((b as PhotoSize.photoSize).size || ((b as PhotoSize.photoSizeProgressive).sizes ? Infinity : 0));
});
const size = photo.sizes[photo.sizes.length - 1];

View File

@ -74,7 +74,7 @@ export class AppPrivacyManager extends AppManager {
}
public getGlobalPrivacySettings() {
return this.apiManager.invokeApi('account.getGlobalPrivacySettings')
return this.apiManager.invokeApi('account.getGlobalPrivacySettings');
}
public setGlobalPrivacySettings(settings: GlobalPrivacySettings) {

View File

@ -108,6 +108,12 @@ export class ApiManager extends ApiManagerMethods {
}
});
this.rootScope.addEventListener('user_auth', () => {
if(this.config) { // refresh configs if had a config during authorization
this.apiUpdatesManager.processLocalUpdate({_: 'updateConfig'});
}
});
this.rootScope.addEventListener('premium_toggle', (isPremium) => {
this.iterateNetworkers(({networker, connectionType, dcId, transportType}) => {
if(connectionType === 'client' || transportType !== 'websocket') {

View File

@ -46,8 +46,8 @@ export default abstract class ApiManagerMethods extends AppManager {
}
} = {};
private config: Config;
private appConfig: MTAppConfig;
protected config: Config;
protected appConfig: MTAppConfig;
constructor() {
super();

File diff suppressed because one or more lines are too long

View File

@ -87,38 +87,56 @@ export class LottieLoader {
});
}
public loadAnimationAsAsset(params: Omit<RLottieOptions, 'animationData' | 'name'>, name: LottieAssetName) {
(params as RLottieOptions).name = name;
return this.loadAnimationFromURL(params, 'assets/tgs/' + name + '.json');
private makeAssetUrl(name: LottieAssetName) {
return 'assets/tgs/' + name + '.json';
}
public loadAnimationFromURL(params: Omit<RLottieOptions, 'animationData'>, url: string): Promise<RLottiePlayer> {
public loadAnimationAsAsset(params: Omit<RLottieOptions, 'animationData' | 'name'>, name: LottieAssetName) {
// (params as RLottieOptions).name = name;
return this.loadAnimationFromURL(params, this.makeAssetUrl(name));
}
public loadAnimationDataFromURL(url: string): Promise<Blob> {
if(!IS_WEB_ASSEMBLY_SUPPORTED) {
return this.loadPromise as any;
}
if(!this.loaded) {
this.loadLottieWorkers();
}
this.loadLottieWorkers();
return fetch(url)
.then((res) => {
if(!res.headers || res.headers.get('content-type') === 'application/octet-stream') {
return res.arrayBuffer().then((data) => apiManagerProxy.invokeCrypto('gzipUncompress', data)).then((arr) => blobConstruct(arr as Uint8Array, ''))
return res.arrayBuffer()
.then((data) => apiManagerProxy.invokeCrypto('gzipUncompress', data))
.then((arr) => blobConstruct(arr as Uint8Array, ''));
} else {
return res.blob();
}
})
});
/* .then((str) => {
return new Promise<string>((resolve) => setTimeout(() => resolve(str), 2e3));
}) */
.then((blob) => {
const newParams = Object.assign(params, {animationData: blob, needUpscale: true});
if(!newParams.name) newParams.name = url;
return this.loadAnimationWorker(newParams);
}
public loadAnimationFromURLManually(name: LottieAssetName) {
const url = this.makeAssetUrl(name);
return this.loadAnimationDataFromURL(url).then((blob) => {
return (params: Omit<RLottieOptions, 'animationData'>) => this.loadAnimationFromURLNext(blob, params, url);
});
}
public loadAnimationFromURL(params: Omit<RLottieOptions, 'animationData'>, url: string) {
return this.loadAnimationDataFromURL(url).then((blob) => {
return this.loadAnimationFromURLNext(blob, params, url);
});
}
public loadAnimationFromURLNext(blob: Blob, params: Omit<RLottieOptions, 'animationData'>, url: string) {
const newParams = Object.assign(params, {animationData: blob, needUpscale: true});
newParams.name ||= url;
return this.loadAnimationWorker(newParams);
}
public waitForFirstFrame(player: RLottiePlayer) {
return Promise.race([
/* new Promise<void>((resolve) => {

View File

@ -0,0 +1,56 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import {putPreloader} from '../components/putPreloader';
import App from '../config/app';
import rootScope from '../lib/rootScope';
import {AuthState} from '../types';
import Page from './page';
let data: AuthState.signImport['data'];
const onFirstMount = async() => {
putPreloader(page.pageEl.firstElementChild, true);
const {dcId, token, tgAddr} = data;
let mountPageAfter: Promise<Page>;
try {
rootScope.managers.apiManager.setBaseDcId(dcId);
const authorization = await rootScope.managers.apiManager.invokeApi('auth.importWebTokenAuthorization', {
api_id: App.id,
api_hash: App.hash,
web_auth_token: token
}, {dcId, ignoreErrors: true});
if(authorization._ === 'auth.authorization') {
rootScope.managers.apiManager.setUser(authorization.user);
mountPageAfter = import('./pageIm').then((m) => m.default);
// return;
}
} catch(err) {
switch((err as ApiError).type) {
case 'SESSION_PASSWORD_NEEDED':
(err as ApiError).handled = true;
mountPageAfter = import('./pagePassword').then((m) => m.default);
break;
default:
console.error('authorization import error:', err);
break;
}
}
location.hash = tgAddr?.trim() ? '#?tgaddr=' + encodeURIComponent(tgAddr) : '';
if(mountPageAfter) {
mountPageAfter.then((page) => page.mount());
}
};
const page = new Page('page-signImport', true, onFirstMount, (_data: typeof data) => {
data = _data;
rootScope.managers.appStateManager.pushToState('authState', {_: 'authStateSignImport', data});
});
export default page;

View File

@ -201,7 +201,6 @@ const onFirstMount = async() => {
} catch(err) {
switch((err as ApiError).type) {
case 'SESSION_PASSWORD_NEEDED':
console.warn('pageSignQR: SESSION_PASSWORD_NEEDED');
(err as ApiError).handled = true;
import('./pagePassword').then((m) => m.default.mount());
stop = true;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1837,8 +1837,12 @@ $bubble-border-radius-big: 12px;
.reaction-block {
--chosen-background-color: var(--primary-color);
&:hover:after {
content: none;
&:not(.is-chosen) {
@include hover() {
&:after {
background-color: #fff;
}
}
}
}
}
@ -2759,12 +2763,12 @@ $bubble-border-radius-big: 12px;
}
}
&-time,
&-time,
&-subtitle {
color: var(--message-out-status-color);
}
&-toggle,
&-toggle,
&-download,
&.corner-download .preloader-container {
background-color: var(--message-out-primary-color);
@ -3009,38 +3013,10 @@ $bubble-border-radius-big: 12px;
text-decoration: none !important;
}
&:after {
content: " ";
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: inherit;
opacity: 0;
z-index: -1;
background-color: #fff;
}
@include hover() {
&:after {
opacity: var(--hover-alpha);
}
}
& + & {
margin-left: $bubble-margin;
}
&:first-child {
border-bottom-left-radius: inherit;
}
&:last-child {
border-bottom-right-radius: inherit;
}
&.tgico:before,
.forward-icon {
position: absolute;
@ -3064,6 +3040,30 @@ $bubble-border-radius-big: 12px;
}
}
.reply-markup-button,
.bubble.just-media .reply,
.bubble-beside-button {
&:after {
content: " ";
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: inherit;
opacity: 0;
z-index: -1;
background-color: #fff;
}
@include hover() {
&:after {
opacity: var(--hover-alpha);
}
}
}
.bubble-primary-color {
color: var(--message-primary-color);
font-weight: var(--font-weight-bold);

View File

@ -148,14 +148,14 @@
// visibility: hidden;
border-radius: inherit;
// transform: scale3d(.2, .2, .2);
transform: scale3d(0, 0, 0);
transform: scale(0, 0);
opacity: 0;
}
&.is-chosen {
&:not(.backwards) {
&:before {
transform: scale3d(1, 1, 1);
transform: scale(1, 1);
opacity: 1;
// visibility: visible;
}

13
src/types.d.ts vendored
View File

@ -88,7 +88,7 @@ export type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn;
export type AuthState = AuthState.signIn | AuthState.signQr | AuthState.authCode | AuthState.password | AuthState.signUp | AuthState.signedIn | AuthState.signImport;
export namespace AuthState {
export type signIn = {
_: 'authStateSignIn'
@ -118,6 +118,17 @@ export namespace AuthState {
export type signedIn = {
_: 'authStateSignedIn'
};
export type signImport = {
_: 'authStateSignImport',
data: {
token: string,
userId: UserId,
dcId: DcId,
isTest: boolean,
tgAddr: string
}
};
}
export type SendMessageEmojiInteractionData = {