diff --git a/src/components/appSelectPeers.ts b/src/components/appSelectPeers.ts index 9a8737d3..7b1a7efa 100644 --- a/src/components/appSelectPeers.ts +++ b/src/components/appSelectPeers.ts @@ -69,17 +69,10 @@ export default class AppSelectPeers { rippleEnabled?: boolean, avatarSize?: AppSelectPeers['avatarSize'], }) { - for(let i in options) { - // @ts-ignore - this[i] = options[i]; - } + Object.assign(this, options); this.container.classList.add('selector'); - this.peerType.forEach(type => { - this.tempIds[type] = 0; - }); - let needSwitchList = false; const f = (this.renderResultsFunc || this.renderResults).bind(this); this.renderResultsFunc = (peerIds: number[]) => { @@ -220,6 +213,14 @@ export default class AppSelectPeers { } } + private getTempId(type: keyof AppSelectPeers['tempIds']) { + if(this.tempIds[type] === undefined) { + this.tempIds[type] = 0; + } + + return ++this.tempIds[type]; + } + private async getMoreDialogs(): Promise { if(this.promise) return this.promise; @@ -230,8 +231,7 @@ export default class AppSelectPeers { // в десктопе - сначала без группы, потом архивные, потом контакты без сообщений const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; - const tempId = ++this.tempIds.dialogs; - + const tempId = this.getTempId('dialogs'); this.promise = appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, this.folderId); const value = await this.promise; this.promise = null; @@ -292,7 +292,7 @@ export default class AppSelectPeers { this.promise = Promise.all(promises); this.cachedContacts = (await this.promise)[0].slice(); */ - const tempId = ++this.tempIds.contacts; + const tempId = this.getTempId('contacts'); this.promise = appUsersManager.getContacts(this.query); this.cachedContacts = (await this.promise).slice(); if(this.tempIds.contacts !== tempId) { @@ -328,7 +328,7 @@ export default class AppSelectPeers { const pageCount = 50; // same as in group permissions to use cache - const tempId = ++this.tempIds.channelParticipants; + const tempId = this.getTempId('channelParticipants'); const promise = appProfileManager.getChannelParticipants(-this.peerId, {_: 'channelParticipantsSearch', q: this.query}, pageCount, this.list.childElementCount); const participants = await promise; if(this.tempIds.channelParticipants !== tempId) { diff --git a/src/components/editPeer.ts b/src/components/editPeer.ts index bad36f9e..9d008f4c 100644 --- a/src/components/editPeer.ts +++ b/src/components/editPeer.ts @@ -23,10 +23,7 @@ export default class EditPeer { listenerSetter: ListenerSetter, doNotEditAvatar?: boolean, }) { - for(let i in options) { - // @ts-ignore - this[i] = options[i]; - } + Object.assign(this, options); this.nextBtn = Button('btn-circle btn-corner tgico-check'); diff --git a/src/components/preloader.ts b/src/components/preloader.ts index 24593ffb..f7a0a01e 100644 --- a/src/components/preloader.ts +++ b/src/components/preloader.ts @@ -34,10 +34,7 @@ export default class ProgressivePreloader { attachMethod: ProgressivePreloader['attachMethod'] }>) { if(options) { - for(let i in options) { - // @ts-ignore - this[i] = options[i]; - } + Object.assign(this, options); } } diff --git a/src/components/row.ts b/src/components/row.ts index 2e2ef663..dfaf055e 100644 --- a/src/components/row.ts +++ b/src/components/row.ts @@ -3,7 +3,6 @@ import RadioField from "./radioField"; import { ripple } from "./ripple"; import { SliderSuperTab } from "./slider"; import RadioForm from "./radioForm"; -import { attachClickEvent, cancelEvent } from "../helpers/dom"; export default class Row { public container: HTMLElement; @@ -26,7 +25,7 @@ export default class Row { clickable: boolean | ((e: Event) => void), navigationTab: SliderSuperTab }> = {}) { - this.container = document.createElement('div'); + this.container = document.createElement(options.radioField || options.checkboxField ? 'label' : 'div'); this.container.classList.add('row'); this.subtitle = document.createElement('div'); @@ -56,18 +55,6 @@ export default class Row { const i = options.radioField || options.checkboxField; i.label.classList.add('disable-hover'); - - if(options.radioField) { - attachClickEvent(this.container, (e) => { - cancelEvent(e); - i.checked = true; - }); - } else { - attachClickEvent(this.container, (e) => { - cancelEvent(e); - i.checked = !i.checked; - }); - } } else { if(options.title) { let c: HTMLElement; diff --git a/src/components/sidebarLeft/tabs/activeSessions.ts b/src/components/sidebarLeft/tabs/activeSessions.ts index 808af56b..96ea240c 100644 --- a/src/components/sidebarLeft/tabs/activeSessions.ts +++ b/src/components/sidebarLeft/tabs/activeSessions.ts @@ -59,15 +59,14 @@ export default class AppActiveSessionsTab extends SliderSuperTab { text: 'TERMINATE', isDanger: true, callback: () => { - const b = [btnTerminate]; - toggleDisability(b, true); + const toggle = toggleDisability([btnTerminate], true); apiManager.invokeApi('auth.resetAuthorizations').then(value => { //toggleDisability([btnTerminate], false); btnTerminate.remove(); otherSection.container.remove(); this.privacyTab.updateActiveSessions(); }, onError).finally(() => { - toggleDisability(b, false); + toggle(); }); } }], { diff --git a/src/components/sidebarRight/tabs/editChannel.ts b/src/components/sidebarRight/tabs/editChannel.ts index 79bc2548..d092c945 100644 --- a/src/components/sidebarRight/tabs/editChannel.ts +++ b/src/components/sidebarRight/tabs/editChannel.ts @@ -162,12 +162,12 @@ export default class AppEditChannelTab extends SliderSuperTab { buttons: addCancelButton([{ text: 'DELETE', callback: () => { - toggleDisability([btnDelete], true); + const toggle = toggleDisability([btnDelete], true); appChatsManager.deleteChannel(-this.peerId).then(() => { this.close(); }, () => { - toggleDisability([btnDelete], false); + toggle(); }); }, isDanger: true diff --git a/src/components/sidebarRight/tabs/editContact.ts b/src/components/sidebarRight/tabs/editContact.ts index acd17e3f..66636ee6 100644 --- a/src/components/sidebarRight/tabs/editContact.ts +++ b/src/components/sidebarRight/tabs/editContact.ts @@ -135,12 +135,12 @@ export default class AppEditContactTab extends SliderSuperTab { buttons: addCancelButton([{ text: 'DELETE', callback: () => { - toggleDisability([btnDelete], true); + const toggle = toggleDisability([btnDelete], true); appUsersManager.deleteContacts([this.peerId]).then(() => { this.close(); }, () => { - toggleDisability([btnDelete], false); + toggle(); }); }, isDanger: true diff --git a/src/components/sidebarRight/tabs/editGroup.ts b/src/components/sidebarRight/tabs/editGroup.ts index d9adca14..86c98456 100644 --- a/src/components/sidebarRight/tabs/editGroup.ts +++ b/src/components/sidebarRight/tabs/editGroup.ts @@ -207,12 +207,12 @@ export default class AppEditGroupTab extends SliderSuperTab { buttons: addCancelButton([{ text: 'DELETE', callback: () => { - toggleDisability([btnDelete], true); + const toggle = toggleDisability([btnDelete], true); appChatsManager.deleteChannel(this.chatId).then(() => { this.close(); }, () => { - toggleDisability([btnDelete], false); + toggle(); }); }, isDanger: true diff --git a/src/components/sidebarRight/tabs/groupPermissions.ts b/src/components/sidebarRight/tabs/groupPermissions.ts index d8c22245..b32a2770 100644 --- a/src/components/sidebarRight/tabs/groupPermissions.ts +++ b/src/components/sidebarRight/tabs/groupPermissions.ts @@ -1,11 +1,10 @@ -import { attachClickEvent, cancelEvent, findUpTag } from "../../../helpers/dom"; +import { attachClickEvent, findUpTag } from "../../../helpers/dom"; import ListenerSetter from "../../../helpers/listenerSetter"; import ScrollableLoader from "../../../helpers/listLoader"; import { ChannelParticipant, Chat, ChatBannedRights, Update } from "../../../layer"; import appChatsManager, { ChatRights } from "../../../lib/appManagers/appChatsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; import appProfileManager from "../../../lib/appManagers/appProfileManager"; -import appUsersManager from "../../../lib/appManagers/appUsersManager"; import rootScope from "../../../lib/rootScope"; import CheckboxField from "../../checkboxField"; import PopupPickUser from "../../popups/pickUser"; @@ -115,7 +114,7 @@ export class ChatPermissions { export default class AppGroupPermissionsTab extends SliderSuperTabEventable { public chatId: number; - protected init() { + protected async init() { this.container.classList.add('edit-peer-container', 'group-permissions-container'); this.title.innerHTML = 'Permissions'; @@ -165,10 +164,6 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { let participant: AppUserPermissionsTab['participant']; try { participant = await appProfileManager.getChannelParticipant(this.chatId, peerId) as any; - - if(participant._ !== 'channelParticipantBanned') { - participant = undefined; - } } catch(err) { toast('User is no longer participant'); return; @@ -255,7 +250,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { this.listenerSetter.add(rootScope, 'apiUpdate', (update: Update) => { if(update._ === 'updateChannelParticipant') { - const needAdd = update.new_participant?._ === 'channelParticipantBanned'; + const needAdd = update.new_participant?._ === 'channelParticipantBanned' && !update.new_participant.banned_rights.pFlags.view_messages; const li = list.querySelector(`[data-peer-id="${update.user_id}"]`); if(needAdd) { if(!li) { @@ -304,6 +299,8 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable { }); this.scrollable.append(section.container); + + await loader.load(); } } diff --git a/src/components/sidebarRight/tabs/groupType.ts b/src/components/sidebarRight/tabs/groupType.ts index 80ca2a84..7b17989d 100644 --- a/src/components/sidebarRight/tabs/groupType.ts +++ b/src/components/sidebarRight/tabs/groupType.ts @@ -75,10 +75,10 @@ export default class AppGroupTypeTab extends SliderSuperTabEventable { new PopupConfirmAction('revoke-link', [{ text: 'OK', callback: () => { - toggleDisability([btnRevoke], true); + const toggle = toggleDisability([btnRevoke], true); appProfileManager.getChatInviteLink(-this.peerId, true).then(link => { - toggleDisability([btnRevoke], false); + toggle(); linkRow.title.innerHTML = link; //revoked = true; //onChange(); diff --git a/src/components/sidebarRight/tabs/userPermissions.ts b/src/components/sidebarRight/tabs/userPermissions.ts index e19debbe..6da5a93d 100644 --- a/src/components/sidebarRight/tabs/userPermissions.ts +++ b/src/components/sidebarRight/tabs/userPermissions.ts @@ -1,17 +1,19 @@ -import { attachClickEvent } from "../../../helpers/dom"; +import { attachClickEvent, toggleDisability } from "../../../helpers/dom"; import { deepEqual } from "../../../helpers/object"; import { ChannelParticipant } from "../../../layer"; import appChatsManager from "../../../lib/appManagers/appChatsManager"; import appDialogsManager from "../../../lib/appManagers/appDialogsManager"; -import appProfileManager from "../../../lib/appManagers/appProfileManager"; +import appPeersManager from "../../../lib/appManagers/appPeersManager"; import appUsersManager from "../../../lib/appManagers/appUsersManager"; import Button from "../../button"; +import { addCancelButton } from "../../popups"; +import PopupPeer from "../../popups/peer"; import { SettingSection } from "../../sidebarLeft"; import { SliderSuperTabEventable } from "../../sliderTab"; import { ChatPermissions } from "./groupPermissions"; export default class AppUserPermissionsTab extends SliderSuperTabEventable { - public participant: ChannelParticipant.channelParticipantBanned; + public participant: ChannelParticipant; public chatId: number; public userId: number; @@ -19,6 +21,8 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { this.container.classList.add('edit-peer-container', 'user-permissions-container'); this.title.innerHTML = 'User Permissions'; + let destroyListener: () => void; + { const section = new SettingSection({ name: 'What can this user do?', @@ -45,18 +49,20 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { chatId: this.chatId, listenerSetter: this.listenerSetter, appendTo: section.content, - participant: this.participant + participant: this.participant._ === 'channelParticipantBanned' ? this.participant : undefined }); - this.eventListener.addEventListener('destroy', () => { + destroyListener = () => { //appChatsManager.editChatDefaultBannedRights(this.chatId, p.takeOut()); const rights = p.takeOut(); - if(deepEqual(this.participant.banned_rights.pFlags, rights.pFlags)) { + if(this.participant._ === 'channelParticipantBanned' && deepEqual(this.participant.banned_rights.pFlags, rights.pFlags)) { return; } appChatsManager.editBanned(this.chatId, this.participant, rights); - }); + }; + + this.eventListener.addEventListener('destroy', destroyListener); this.scrollable.append(section.container); } @@ -64,27 +70,44 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable { { const section = new SettingSection({}); + if(this.participant._ === 'channelParticipantBanned') { + const btnDeleteException = Button('btn-primary btn-transparent danger', {icon: 'delete', text: 'Delete Exception'}); + + attachClickEvent(btnDeleteException, () => { + const toggle = toggleDisability([btnDeleteException], true); + appChatsManager.clearChannelParticipantBannedRights(this.chatId, this.participant).then(() => { + this.eventListener.removeEventListener('destroy', destroyListener); + this.close(); + }, () => { + toggle(); + }); + }, {listenerSetter: this.listenerSetter}); + + section.content.append(btnDeleteException); + } + const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'deleteuser', text: 'Ban and Remove From Group'}); attachClickEvent(btnDelete, () => { - /* new PopupPeer('popup-delete-group', { + new PopupPeer('popup-group-kick-user', { peerId: -this.chatId, - title: 'Delete Group?', - description: `Are you sure you want to delete this group? All members will be removed, and all messages will be lost.`, + title: 'Ban User?', + description: `Are you sure you want to ban ${appPeersManager.getPeerTitle(this.userId)}`, buttons: addCancelButton([{ - text: 'DELETE', + text: 'BAN', callback: () => { - toggleDisability([btnDelete], true); + const toggle = toggleDisability([btnDelete], true); - appChatsManager.deleteChannel(this.chatId).then(() => { + appChatsManager.kickFromChannel(this.chatId, this.participant).then(() => { + this.eventListener.removeEventListener('destroy', destroyListener); this.close(); }, () => { - toggleDisability([btnDelete], false); + toggle(); }); }, isDanger: true }]) - }).show(); */ + }).show(); }, {listenerSetter: this.listenerSetter}); section.content.append(btnDelete); diff --git a/src/components/slider.ts b/src/components/slider.ts index 5aab9b67..31855a69 100644 --- a/src/components/slider.ts +++ b/src/components/slider.ts @@ -24,10 +24,7 @@ export default class SidebarSlider { canHideFirst?: SidebarSlider['canHideFirst'], navigationType: SidebarSlider['navigationType'] }) { - for(const i in options) { - // @ts-ignore - this[i] = options[i]; - } + Object.assign(this, options); if(!this.tabs) { this.tabs = new Map(); diff --git a/src/helpers/dom.ts b/src/helpers/dom.ts index 64154227..4421aa48 100644 --- a/src/helpers/dom.ts +++ b/src/helpers/dom.ts @@ -791,6 +791,8 @@ export function toggleDisability(elements: HTMLElement[], disable: boolean) { } else { elements.forEach(el => el.removeAttribute('disabled')); } + + return () => toggleDisability(elements, !disable); } export function canFocus(isFirstInput: boolean) { diff --git a/src/helpers/listLoader.ts b/src/helpers/listLoader.ts index 139aab0f..b938ea32 100644 --- a/src/helpers/listLoader.ts +++ b/src/helpers/listLoader.ts @@ -1,28 +1,46 @@ import Scrollable from "../components/scrollable"; export default class ScrollableLoader { + public loading = false; + private scrollable: Scrollable; + private getPromise: () => Promise; + private promise: Promise; + private loaded = false; + constructor(options: { - scrollable: Scrollable, - getPromise: () => Promise + scrollable: ScrollableLoader['scrollable'], + getPromise: ScrollableLoader['getPromise'] }) { - let loading = false; + Object.assign(this, options); + options.scrollable.onScrolledBottom = () => { - if(loading) { - return; - } + this.load(); + }; + } + + public load() { + if(this.loaded) { + return Promise.resolve(); + } + + if(this.loading) { + return this.promise; + } - loading = true; - options.getPromise().then(done => { - loading = false; + this.loading = true; + this.promise = this.getPromise().then(done => { + this.loading = false; + this.promise = undefined; - if(done) { - options.scrollable.onScrolledBottom = null; - } else { - options.scrollable.checkForTriggers(); - } - }, () => { - loading = false; - }); - }; + if(done) { + this.loaded = true; + this.scrollable.onScrolledBottom = null; + } else { + this.scrollable.checkForTriggers(); + } + }, () => { + this.promise = undefined; + this.loading = false; + }); } } diff --git a/src/helpers/object.ts b/src/helpers/object.ts index e7f565fa..f8396428 100644 --- a/src/helpers/object.ts +++ b/src/helpers/object.ts @@ -119,4 +119,4 @@ export function validateInitObject(initObject: any, currentObject: any) { validateInitObject(initObject[i], currentObject[i]); } } -} \ No newline at end of file +} diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 4837ea42..9dc242d3 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -698,10 +698,21 @@ export class AppChatsManager { }); } - public kickFromChannel(id: number, userId: number) { - return this.editBanned(id, userId, { + public clearChannelParticipantBannedRights(id: number, participant: number | ChannelParticipant) { + return this.editBanned(id, participant, { _: 'chatBannedRights', - until_date: 0 + until_date: 0, + pFlags: {} + }); + } + + public kickFromChannel(id: number, participant: number | ChannelParticipant) { + return this.editBanned(id, participant, { + _: 'chatBannedRights', + until_date: 0, + pFlags: { + view_messages: true + } }); } } diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 3ca0c2f2..d0417056 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -817,6 +817,10 @@ export class AppDialogsManager { this.sliceTimeout = window.setTimeout(() => { this.sliceTimeout = undefined; + if(!this.chatList.childElementCount) { + return; + } + /* const observer = new IntersectionObserver((entries) => { const }); @@ -828,9 +832,10 @@ export class AppDialogsManager { //const scrollTopWas = this.scroll.scrollTop; const rect = this.scroll.container.getBoundingClientRect(); + const rectX = this.chatList.firstElementChild.getBoundingClientRect(); const children = Array.from(this.scroll.splitUp.children) as HTMLElement[]; - const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rect.x), Math.ceil(rect.y + 1)), 'LI') as HTMLElement; - const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rect.x), Math.floor(rect.y + rect.height - 1)), 'LI') as HTMLElement; + const firstElement = findUpTag(document.elementFromPoint(Math.ceil(rectX.x), Math.ceil(rect.y + 1)), 'LI') as HTMLElement; + const lastElement = findUpTag(document.elementFromPoint(Math.ceil(rectX.x), Math.floor(rect.y + rect.height - 1)), 'LI') as HTMLElement; //alert('got element:' + rect.y); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 7c78b602..56c89976 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -2644,7 +2644,7 @@ export class AppMessagesManager { case 'messageActionChatAddUser': { const users: number[] = (action as MessageAction.messageActionChatAddUser).users || [(action as MessageAction.messageActionChatDeleteUser).user_id]; - l = langPack[_].replace('{}', users.map((userId: number) => getNameDivHTML(userId)).join(', ')); + l = langPack[_].replace('{}', users.map((userId: number) => getNameDivHTML(userId).trim()).join(', ')); break; } diff --git a/src/lib/idb.ts b/src/lib/idb.ts index 878290b1..c73d5c2a 100644 --- a/src/lib/idb.ts +++ b/src/lib/idb.ts @@ -36,10 +36,7 @@ export default class IDBStorage { public storeName: string; constructor(options: IDBOptions) { - for(let i in options) { - // @ts-ignore - this[i] = options[i]; - } + Object.assign(this, options); this.openDatabase(true); } diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index 403016bf..8ffa0eb2 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -775,13 +775,7 @@ } .sidebar-left-section { - &:first-child { - padding-top: 0; - } - - &:not(:last-child) { - padding-bottom: 0; - } + padding: 0 0 .5rem; } // * supernew and correct layout