Skip to content

Commit efdb15e

Browse files
Merge pull request #607 from H7GhosT/feature/botforum-managed-topics
Botforums in case topics cannot be managed
2 parents 51e34df + 86ee1ef commit efdb15e

File tree

19 files changed

+167
-25
lines changed

19 files changed

+167
-25
lines changed

‎src/components/appSelectPeers.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ export default class AppSelectPeers {
196196
excludeMonoforums?: boolean,
197197
excludeBotforums?: boolean,
198198
placeholderSizes?: ConstructorParameters<typeof DialogsPlaceholder>[0],
199-
getPeerIdFromKey?: AppSelectPeers['getPeerIdFromKey']
199+
getPeerIdFromKey?: AppSelectPeers['getPeerIdFromKey'],
200+
topSectionContentElements?: HTMLElement[]
200201
}) {
201202
safeAssign(this, options);
202203

@@ -388,6 +389,10 @@ export default class AppSelectPeers {
388389
section.content = section.generateContentElement();
389390
}
390391

392+
if(options.topSectionContentElements) {
393+
section.content.append(...options.topSectionContentElements);
394+
}
395+
391396
// it can't have full height then
392397
if(!this.sectionCaption) {
393398
section.content.classList.add('selector-list-section-content');

‎src/components/chat/bubbleGroups.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,7 @@ export default class BubbleGroups {
510510

511511
if(item.message._ !== 'message') return;
512512
if(!bubbleAddons.continueLastTopicReplyMarkup) return;
513+
if(!getMessageThreadId(item.message, {isBotforum: this.chat.isBotforum})) return;
513514

514515
bubbleAddons.continueLastTopicReplyMarkup.feedProps<false>({
515516
visible: visible && !(item.message.reply_markup as ReplyMarkup.replyInlineMarkup)?.rows

‎src/components/chat/bubbleParts/continuouslyTypingMessage.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export function wrapContinuouslyTypingMessage({root, bubble, scrollable, isEnd =
4242
if(ended) return;
4343
ended = true;
4444

45-
if(!isEnd) appendDots(lastTextNode);
45+
if(!isEnd && lastTextNode) appendDots(lastTextNode);
4646
}
4747

4848
const result = {

‎src/components/chat/bubbles.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9928,7 +9928,7 @@ export default class ChatBubbles {
99289928
return this.renderBotforumPlaceholder();
99299929
}
99309930

9931-
if(side === 'top' && value && this.chat.isBot) {
9931+
if(side === 'top' && value && this.chat.isBot && !this.chat.threadId) {
99329932
return this.renderBotPlaceholder();
99339933
}
99349934

@@ -10148,6 +10148,7 @@ export default class ChatBubbles {
1014810148
this.scrollable.loadedAll.bottom &&
1014910149
this.emptyPlaceholderBubble === undefined &&
1015010150
!shouldShowUnknownUserPlaceholder(this.peerSettings) &&
10151+
(!this.chat.isBotforum || this.chat.canManageBotforumTopics) &&
1015110152
(
1015210153
this.chat.isRestricted ||
1015310154
this.chat.type === ChatType.Logs ||
@@ -10500,7 +10501,7 @@ export default class ChatBubbles {
1050010501
}
1050110502

1050210503
private shouldShowBotforumNewTopic() {
10503-
return this.chat.isBotforum && !this.chat.threadId;
10504+
return this.chat.isBotforum && !this.chat.threadId && this.chat.canManageBotforumTopics;
1050410505
}
1050510506

1050610507
private finalizeTypingMessage(message: MyMessage, tempId: number) {

‎src/components/chat/chat.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export default class Chat extends EventListenerBase<{
167167
public isMonoforum: boolean;
168168
public isBotforum: boolean;
169169
public canManageDirectMessages: boolean;
170+
public canManageBotforumTopics: boolean;
170171
public isTemporaryThread: boolean;
171172
public noInput: boolean;
172173

@@ -912,6 +913,7 @@ export default class Chat extends EventListenerBase<{
912913

913914
const isForum = apiManagerProxy.isForum(peerId);
914915
const isBotforum = apiManagerProxy.isBotforum(peerId);
916+
const canManageBotforumTopics = apiManagerProxy.canManageBotforumTopics(peerId);
915917

916918
if(threadId && !isForum && !isBotforum) {
917919
options.type = options.peerId === rootScope.myId ? ChatType.Saved : ChatType.Discussion;
@@ -976,6 +978,7 @@ export default class Chat extends EventListenerBase<{
976978
this.isMonoforum = !!(chat?._ === 'channel' && chat?.pFlags?.monoforum);
977979
this.isBotforum = isBotforum;
978980
this.canManageDirectMessages = canManageDirectMessages;
981+
this.canManageBotforumTopics = canManageBotforumTopics;
979982

980983
if(starsAmount.cached) {
981984
this.starsAmount = await starsAmount.result;
@@ -1334,9 +1337,10 @@ export default class Chat extends EventListenerBase<{
13341337
this.managers.appPeersManager.isBot(this.peerId),
13351338
this.managers.appMessagesManager.getDialogOnly(this.peerId),
13361339
this.getHistoryStorage(true),
1337-
this.peerId.isUser() ? this.managers.appProfileManager.isCachedUserBlocked(this.peerId.toUserId()) : undefined
1338-
]).then(([isBot, dialog, historyStorage, isUserBlocked]) => {
1339-
if(!isBot || isVerificationBot(this.peerId)) {
1340+
this.peerId.isUser() ? this.managers.appProfileManager.isCachedUserBlocked(this.peerId.toUserId()) : undefined,
1341+
this.managers.appPeersManager.isBotforum(this.peerId)
1342+
]).then(([isBot, dialog, historyStorage, isUserBlocked, isBotforum]) => {
1343+
if(!isBot || isVerificationBot(this.peerId) || isBotforum) {
13401344
return false;
13411345
}
13421346

‎src/components/chat/input.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,8 @@ export default class ChatInput {
22712271
});
22722272
}
22732273

2274+
haveSomethingInControl ||= this.chat.isBotforum && this.chat.canManageBotforumTopics;
2275+
22742276
this.botStartBtn.classList.toggle('hide', haveSomethingInControl);
22752277

22762278
if(this.messageInput) {
@@ -2411,6 +2413,8 @@ export default class ChatInput {
24112413
this.chat.monoforumThreadId || this.directMessagesHandler.store.isReplying ?
24122414
'Message' :
24132415
'ChannelDirectMessages.ChooseMessage';
2416+
} else if(this.chat.isBotforum && !this.chat.canManageBotforumTopics && !this.chat.threadId) {
2417+
key = 'OffThreadMessage'
24142418
} else if(
24152419
(this.sendAsPeerId !== undefined && this.sendAsPeerId !== rootScope.myId) ||
24162420
await this.managers.appMessagesManager.isAnonymousSending(peerId)

‎src/components/dialogsContextMenu.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default class DialogsContextMenu {
6161
this.monoforumParentPeerId = +li.dataset.monoforumParentPeerId || undefined;
6262

6363
if(li.dataset.isAllChats) {
64-
throw {};
64+
throw 'All chats dialog';
6565
}
6666

6767
this.dialog = this.monoforumParentPeerId ?

‎src/components/forumTab/botforumTab.ts‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ export class BotforumTab extends ForumTab {
8181
}
8282
});
8383

84+
this.listenerSetter.add(rootScope)('dialog_drop', (dialog) => {
85+
if(this.peerId !== dialog.peerId) return;
86+
this.updateDialogsCount();
87+
});
88+
8489
this.listenerSetter.add(rootScope)('dialog_draft', ({dialog}) => {
8590
if(!isDialog(dialog) || dialog.peerId !== this.peerId) return;
8691
this.updateAllChatsDialog(dialog);
@@ -123,13 +128,21 @@ export class BotforumTab extends ForumTab {
123128
}));
124129

125130
this.title.append(peerTitle);
126-
this.subtitle.append(this.dialogsCountI18nEl = i18n('TopicsCount', [dialogs ? dialogs.count + '' : '~']))
131+
this.subtitle.append(this.dialogsCountI18nEl = this.getTopicsCountI18n(dialogs ? dialogs.count : null));
127132
} catch{}
128133
}
129134

135+
private getTopicsCountI18n(count: number | null) {
136+
if(count === 0) {
137+
return i18n('NoTopics');
138+
}
139+
140+
return i18n('TopicsCount', [count ? count + '' : '~']);
141+
}
142+
130143
private updateDialogsCount = asyncThrottle(async() => {
131144
if(!this.dialogsCountI18nEl) return;
132145
const {count} = await this.managers.dialogsStorage.getDialogs({filterId: this.peerId, limit: 1});
133-
this.dialogsCountI18nEl.replaceWith(this.dialogsCountI18nEl = i18n('TopicsCount', [count + '']));
146+
this.dialogsCountI18nEl.replaceWith(this.dialogsCountI18nEl = this.getTopicsCountI18n(count));
134147
}, 0);
135148
}

‎src/components/peerTitle.ts‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,32 @@ export default class PeerTitle {
195195
getTopicIconPromise
196196
]);
197197

198-
if(icons?.elements?.length || icons?.botVerification || topicIcon) {
198+
const createInnerTitleSpan = () => {
199199
const inner = document.createElement('span');
200200
inner.classList.add('peer-title-inner');
201201
hasInner = true;
202202
setInnerHTML(inner, title);
203203

204+
return inner;
205+
};
206+
207+
const setElementContent = (nodes: (Node | string)[]) => {
204208
const fragment = document.createDocumentFragment();
205-
fragment.append(...[icons.botVerification, topicIcon, inner, ...(icons.elements ?? [])].filter(Boolean));
209+
fragment.append(...nodes);
206210

207211
setInnerHTML(this.element, fragment);
212+
}
213+
214+
if(topicIcon) {
215+
setElementContent(
216+
[topicIcon, createInnerTitleSpan()].filter(Boolean)
217+
);
218+
} else if(icons?.elements?.length || icons?.botVerification) {
219+
setElementContent(
220+
[
221+
icons.botVerification, topicIcon, createInnerTitleSpan(), ...(icons.elements ?? [])
222+
].filter(Boolean)
223+
);
208224
} else {
209225
setInnerHTML(this.element, title);
210226
}

‎src/components/popups/pickUser.ts‎

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {AutonomousMonoforumThreadList} from '@components/autonomousDialogList/mo
3030
import Scrollable from '@components/scrollable';
3131
import SortedDialogList from '@components/sortedDialogList';
3232
import rootScope from '@lib/rootScope';
33+
import apiManagerProxy from '@lib/apiManagerProxy';
3334

3435
type PopupPickUserOptions = Modify<ConstructorParameters<typeof AppSelectPeers>[0], {
3536
multiSelect?: never,
@@ -77,6 +78,29 @@ async function wrapTopicRow({
7778
return row.container;
7879
}
7980

81+
function wrapAllMessagesRow({
82+
onClick
83+
}: {
84+
onClick: () => void
85+
}) {
86+
const row = new Row({
87+
title: i18n('AllMessages'),
88+
clickable: onClick
89+
});
90+
row.container.classList.add('selector-forum-topic');
91+
const media = row.createMedia('abitbigger');
92+
media.append(wrapEmojiText('💬'));
93+
return row.container;
94+
}
95+
96+
type OnSelectOptions = {
97+
threadId?: number;
98+
monoforumThreadId?: PeerId;
99+
done?: boolean;
100+
};
101+
102+
type LocalOnSelectCallback = (peerId: PeerId, options?: OnSelectOptions) => void;
103+
80104
export default class PopupPickUser extends PopupElement {
81105
public selector: AppSelectPeers;
82106
public forumSelector: AppSelectPeers;
@@ -110,13 +134,15 @@ export default class PopupPickUser extends PopupElement {
110134
const headerSearch = options.headerSearch ?? isMultiSelect;
111135

112136
let ignoreOnSelect: boolean;
113-
const onSelect = async(peerId: PeerId | PeerId[], threadId?: number, monoforumThreadId?: PeerId) => {
137+
138+
const onSelect = async(peerId: PeerId | PeerId[], {threadId, monoforumThreadId, done}: OnSelectOptions = {}) => {
114139
if(ignoreOnSelect) {
115140
return;
116141
}
117142

118143
if(
119144
options.useTopics &&
145+
!done &&
120146
!Array.isArray(peerId) &&
121147
!threadId && !monoforumThreadId &&
122148
(await this.managers.appPeersManager.isForum(peerId) || await this.managers.appPeersManager.isBotforum(peerId))
@@ -134,6 +160,7 @@ export default class PopupPickUser extends PopupElement {
134160

135161
if(
136162
!Array.isArray(peerId) &&
163+
!done &&
137164
!threadId && !monoforumThreadId &&
138165
await this.managers.appPeersManager.canManageDirectMessages(peerId) &&
139166
await this.managers.appPeersManager.isMonoforum(peerId)
@@ -263,7 +290,7 @@ export default class PopupPickUser extends PopupElement {
263290
tabsContainer: HTMLElement,
264291
peerId: PeerId,
265292
placeholder: LangPackKey,
266-
onSelect: (peerId: PeerId, threadId: number) => any
293+
onSelect: LocalOnSelectCallback
267294
}) {
268295
const middlewareHelper = this.middlewareHelper.get().create();
269296
const middleware = middlewareHelper.get();
@@ -313,7 +340,7 @@ export default class PopupPickUser extends PopupElement {
313340
forumSelector.list[!append ? 'append' : 'prepend'](...elements);
314341
},
315342
onSelect: (topicId) => {
316-
onSelect(peerId, topicId);
343+
onSelect(peerId, {threadId: topicId});
317344
},
318345
placeholderSizes: {
319346
avatarSize: 32,
@@ -329,7 +356,14 @@ export default class PopupPickUser extends PopupElement {
329356
},
330357
onFirstRender: () => {
331358
deferred.resolve();
332-
}
359+
},
360+
topSectionContentElements: apiManagerProxy.isBotforum(peerId) && !apiManagerProxy.canManageBotforumTopics(peerId) ? [
361+
wrapAllMessagesRow({
362+
onClick: () => {
363+
onSelect(peerId, {done: true});
364+
}
365+
})
366+
] : undefined
333367
});
334368

335369
forumSelector.container.classList.add('tabs-tab');
@@ -363,7 +397,7 @@ export default class PopupPickUser extends PopupElement {
363397
peerId: PeerId,
364398
tabsContainer: HTMLElement,
365399
placeholder: LangPackKey,
366-
onSelect: PopupPickUserOptions['onSelect']
400+
onSelect: LocalOnSelectCallback
367401
}) {
368402
const middlewareHelper = this.middlewareHelper.get().create();
369403
const middleware = middlewareHelper.get();
@@ -404,7 +438,7 @@ export default class PopupPickUser extends PopupElement {
404438
const peerId = target.dataset.peerId?.toPeerId?.();
405439
if(!peerId) return;
406440

407-
onSelect?.(parentPeerId, undefined, peerId);
441+
onSelect?.(parentPeerId, {monoforumThreadId: peerId});
408442
});
409443

410444
const container = document.createElement('div');

0 commit comments

Comments
 (0)