Voice messages privacy
This commit is contained in:
parent
31ac41c354
commit
34572dbf3a
@ -565,6 +565,19 @@ export default class ChatBubbles {
|
|||||||
this.safeRenderMessage(message, true, bubble);
|
this.safeRenderMessage(message, true, bubble);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.listenerSetter.add(rootScope)('message_error', async({storageKey, tempId}) => {
|
||||||
|
if(storageKey !== this.chat.messagesStorageKey) return;
|
||||||
|
|
||||||
|
const bubble = this.bubbles[tempId];
|
||||||
|
if(!bubble) return;
|
||||||
|
|
||||||
|
await getHeavyAnimationPromise();
|
||||||
|
if(this.bubbles[tempId] !== bubble) return;
|
||||||
|
|
||||||
|
bubble.classList.remove('is-outgoing');
|
||||||
|
bubble.classList.add('is-error');
|
||||||
|
});
|
||||||
|
|
||||||
this.listenerSetter.add(rootScope)('album_edit', ({peerId, messages, deletedMids}) => {
|
this.listenerSetter.add(rootScope)('album_edit', ({peerId, messages, deletedMids}) => {
|
||||||
if(peerId !== this.peerId) return;
|
if(peerId !== this.peerId) return;
|
||||||
|
|
||||||
@ -2094,7 +2107,7 @@ export default class ChatBubbles {
|
|||||||
if(bubble) {
|
if(bubble) {
|
||||||
this.unreadOut.delete(msgId);
|
this.unreadOut.delete(msgId);
|
||||||
|
|
||||||
if(bubble.classList.contains('is-outgoing')) {
|
if(bubble.classList.contains('is-outgoing') || bubble.classList.contains('is-error')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3764,12 +3777,13 @@ export default class ChatBubbles {
|
|||||||
if(our) {
|
if(our) {
|
||||||
if(message.pFlags.unread || isOutgoing) this.unreadOut.add(message.mid);
|
if(message.pFlags.unread || isOutgoing) this.unreadOut.add(message.mid);
|
||||||
let status = '';
|
let status = '';
|
||||||
if(isOutgoing) status = 'is-sending';
|
if(message.error) status = 'is-error';
|
||||||
|
else if(isOutgoing) status = 'is-sending';
|
||||||
else status = message.pFlags.unread || (message as Message.message).pFlags.is_scheduled ? 'is-sent' : 'is-read';
|
else status = message.pFlags.unread || (message as Message.message).pFlags.is_scheduled ? 'is-sent' : 'is-read';
|
||||||
bubble.classList.add(status);
|
bubble.classList.add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isOutgoing) {
|
if(isOutgoing && !message.error) {
|
||||||
bubble.classList.add('is-outgoing');
|
bubble.classList.add('is-outgoing');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +17,9 @@ import emoticonsDropdown from '../emoticonsDropdown';
|
|||||||
import PopupCreatePoll from '../popups/createPoll';
|
import PopupCreatePoll from '../popups/createPoll';
|
||||||
import PopupForward from '../popups/forward';
|
import PopupForward from '../popups/forward';
|
||||||
import PopupNewMedia from '../popups/newMedia';
|
import PopupNewMedia from '../popups/newMedia';
|
||||||
import {toast} from '../toast';
|
import {toast, toastNew} from '../toast';
|
||||||
import {wrapReply} from '../wrappers';
|
import {wrapReply} from '../wrappers';
|
||||||
import InputField from '../inputField';
|
import {MessageEntity, DraftMessage, WebPage, Message, UserFull} from '../../layer';
|
||||||
import {MessageEntity, DraftMessage, WebPage, Message, ChatFull, UserFull} from '../../layer';
|
|
||||||
import StickersHelper from './stickersHelper';
|
import StickersHelper from './stickersHelper';
|
||||||
import ButtonIcon from '../buttonIcon';
|
import ButtonIcon from '../buttonIcon';
|
||||||
import ButtonMenuToggle from '../buttonMenuToggle';
|
import ButtonMenuToggle from '../buttonMenuToggle';
|
||||||
@ -96,6 +95,7 @@ import filterAsync from '../../helpers/array/filterAsync';
|
|||||||
import InputFieldAnimated from '../inputFieldAnimated';
|
import InputFieldAnimated from '../inputFieldAnimated';
|
||||||
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
|
import getStickerEffectThumb from '../../lib/appManagers/utils/stickers/getStickerEffectThumb';
|
||||||
import PopupStickers from '../popups/stickers';
|
import PopupStickers from '../popups/stickers';
|
||||||
|
import wrapPeerTitle from '../wrappers/peerTitle';
|
||||||
|
|
||||||
const RECORD_MIN_TIME = 500;
|
const RECORD_MIN_TIME = 500;
|
||||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
||||||
@ -2086,7 +2086,8 @@ export default class ChatInput {
|
|||||||
this.sendMessage();
|
this.sendMessage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(this.chat.peerId.isAnyChat() && !(await this.chat.canSend('send_media'))) {
|
const isAnyChat = this.chat.peerId.isAnyChat();
|
||||||
|
if(isAnyChat && !(await this.chat.canSend('send_media'))) {
|
||||||
toast(POSTING_MEDIA_NOT_ALLOWED);
|
toast(POSTING_MEDIA_NOT_ALLOWED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2094,6 +2095,23 @@ export default class ChatInput {
|
|||||||
this.chatInput.classList.add('is-locked');
|
this.chatInput.classList.add('is-locked');
|
||||||
blurActiveElement();
|
blurActiveElement();
|
||||||
|
|
||||||
|
let restricted = false;
|
||||||
|
if(!isAnyChat) {
|
||||||
|
const userFull = await this.managers.appProfileManager.getProfile(this.chat.peerId.toUserId());
|
||||||
|
if(userFull?.pFlags.voice_messages_forbidden) {
|
||||||
|
toastNew({
|
||||||
|
langPackKey: 'Chat.SendVoice.PrivacyError',
|
||||||
|
langPackArguments: [await wrapPeerTitle({peerId: this.chat.peerId})]
|
||||||
|
});
|
||||||
|
restricted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(restricted) {
|
||||||
|
this.chatInput.classList.remove('is-locked');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.recorder.start().then(() => {
|
this.recorder.start().then(() => {
|
||||||
this.releaseMediaPlayback = appMediaPlaybackController.setSingleMedia();
|
this.releaseMediaPlayback = appMediaPlaybackController.setSingleMedia();
|
||||||
this.recordCanceled = false;
|
this.recordCanceled = false;
|
||||||
@ -2427,6 +2445,13 @@ export default class ChatInput {
|
|||||||
...sendingParams,
|
...sendingParams,
|
||||||
dropAuthor: this.forwardElements && this.forwardElements.hideSender.checkboxField.checked,
|
dropAuthor: this.forwardElements && this.forwardElements.hideSender.checkboxField.checked,
|
||||||
dropCaptions: this.isDroppingCaptions()
|
dropCaptions: this.isDroppingCaptions()
|
||||||
|
}).catch(async(err: ApiError) => {
|
||||||
|
if(err.type === 'VOICE_MESSAGES_FORBIDDEN') {
|
||||||
|
toastNew({
|
||||||
|
langPackKey: 'Chat.SendVoice.PrivacyError',
|
||||||
|
langPackArguments: [await wrapPeerTitle({peerId})]
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,6 +853,7 @@ export default class ChatSelection extends AppSelection {
|
|||||||
public canSelectBubble(bubble: HTMLElement) {
|
public canSelectBubble(bubble: HTMLElement) {
|
||||||
return !bubble.classList.contains('service') &&
|
return !bubble.classList.contains('service') &&
|
||||||
!bubble.classList.contains('is-outgoing') &&
|
!bubble.classList.contains('is-outgoing') &&
|
||||||
|
!bubble.classList.contains('is-error') &&
|
||||||
!bubble.classList.contains('bubble-first') &&
|
!bubble.classList.contains('bubble-first') &&
|
||||||
!bubble.classList.contains('avoid-selection');
|
!bubble.classList.contains('avoid-selection');
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ export default class PrivacySection {
|
|||||||
]>);
|
]>);
|
||||||
for(const [k, chatKey, usersKey] of a) {
|
for(const [k, chatKey, usersKey] of a) {
|
||||||
if(this.exceptions.get(k).row.container.classList.contains('hide')) {
|
if(this.exceptions.get(k).row.container.classList.contains('hide')) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _peerIds = this.peerIds[k];
|
const _peerIds = this.peerIds[k];
|
||||||
|
@ -30,9 +30,11 @@ export function setSendingStatus(
|
|||||||
message?: Message.message | Message.messageService,
|
message?: Message.message | Message.messageService,
|
||||||
disableAnimationIfRippleFound?: boolean
|
disableAnimationIfRippleFound?: boolean
|
||||||
) {
|
) {
|
||||||
let className: 'check' | 'checks' | 'sending';
|
let className: 'check' | 'checks' | 'sending' | 'sendingerror';
|
||||||
if(message?.pFlags.out) {
|
if(message?.pFlags.out) {
|
||||||
if(message.pFlags.is_outgoing) {
|
if(message.error) {
|
||||||
|
className = 'sendingerror';
|
||||||
|
} else if(message.pFlags.is_outgoing) {
|
||||||
className = 'sending';
|
className = 'sending';
|
||||||
} else if(message.pFlags.unread) {
|
} else if(message.pFlags.unread) {
|
||||||
className = 'check';
|
className = 'check';
|
||||||
|
28
src/components/sidebarLeft/tabs/privacy/voices.ts
Normal file
28
src/components/sidebarLeft/tabs/privacy/voices.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* https://github.com/morethanwords/tweb
|
||||||
|
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||||||
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {SliderSuperTabEventable} from '../../../sliderTab';
|
||||||
|
import PrivacySection from '../../../privacySection';
|
||||||
|
import {LangPackKey} from '../../../../lib/langPack';
|
||||||
|
|
||||||
|
export default class AppPrivacyVoicesTab extends SliderSuperTabEventable {
|
||||||
|
protected init() {
|
||||||
|
this.header.classList.add('with-border');
|
||||||
|
this.container.classList.add('privacy-tab', 'privacy-voices');
|
||||||
|
this.setTitle('PrivacyVoiceMessages');
|
||||||
|
|
||||||
|
const caption: LangPackKey = 'PrivacyVoiceMessagesInfo';
|
||||||
|
new PrivacySection({
|
||||||
|
tab: this,
|
||||||
|
title: 'PrivacyVoiceMessagesTitle',
|
||||||
|
inputKey: 'inputPrivacyKeyVoiceMessages',
|
||||||
|
captions: [caption, caption, caption],
|
||||||
|
exceptionTexts: ['PrivacySettingsController.NeverAllow', 'PrivacySettingsController.AlwaysAllow'],
|
||||||
|
appendTo: this.scrollable,
|
||||||
|
managers: this.managers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -32,6 +32,7 @@ import PrivacyType from '../../../lib/appManagers/utils/privacy/privacyType';
|
|||||||
import confirmationPopup, {PopupConfirmationOptions} from '../../confirmationPopup';
|
import confirmationPopup, {PopupConfirmationOptions} from '../../confirmationPopup';
|
||||||
import noop from '../../../helpers/noop';
|
import noop from '../../../helpers/noop';
|
||||||
import {toastNew} from '../../toast';
|
import {toastNew} from '../../toast';
|
||||||
|
import AppPrivacyVoicesTab from './privacy/voices';
|
||||||
|
|
||||||
export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
|
export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
|
||||||
private activeSessionsRow: Row;
|
private activeSessionsRow: Row;
|
||||||
@ -209,6 +210,19 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
|
|||||||
listenerSetter: this.listenerSetter
|
listenerSetter: this.listenerSetter
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const voicesRow = rowsByKeys['inputPrivacyKeyVoiceMessages'] = new Row({
|
||||||
|
titleLangKey: 'PrivacyVoiceMessagesTitle',
|
||||||
|
subtitleLangKey: SUBTITLE,
|
||||||
|
clickable: () => {
|
||||||
|
if(!rootScope.premium) {
|
||||||
|
toastNew({langPackKey: 'PrivacyVoiceMessagesPremiumOnly'});
|
||||||
|
} else {
|
||||||
|
this.slider.createTab(AppPrivacyVoicesTab).open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
listenerSetter: this.listenerSetter
|
||||||
|
});
|
||||||
|
|
||||||
const updatePrivacyRow = (key: InputPrivacyKey['_']) => {
|
const updatePrivacyRow = (key: InputPrivacyKey['_']) => {
|
||||||
const row = rowsByKeys[key];
|
const row = rowsByKeys[key];
|
||||||
if(!row) {
|
if(!row) {
|
||||||
@ -236,7 +250,8 @@ export default class AppPrivacyAndSecurityTab extends SliderSuperTabEventable {
|
|||||||
photoVisibilityRow.container,
|
photoVisibilityRow.container,
|
||||||
callRow.container,
|
callRow.container,
|
||||||
linkAccountRow.container,
|
linkAccountRow.container,
|
||||||
groupChatsAddRow.container
|
groupChatsAddRow.container,
|
||||||
|
voicesRow.container
|
||||||
);
|
);
|
||||||
this.scrollable.append(section.container);
|
this.scrollable.append(section.container);
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ const App = {
|
|||||||
version: process.env.VERSION,
|
version: process.env.VERSION,
|
||||||
versionFull: process.env.VERSION_FULL,
|
versionFull: process.env.VERSION_FULL,
|
||||||
build: +process.env.BUILD,
|
build: +process.env.BUILD,
|
||||||
langPackVersion: '0.4.5',
|
langPackVersion: '0.4.6',
|
||||||
langPack: 'macos',
|
langPack: 'macos',
|
||||||
langPackCode: 'en',
|
langPackCode: 'en',
|
||||||
domains: [MAIN_DOMAIN] as string[],
|
domains: [MAIN_DOMAIN] as string[],
|
||||||
|
3
src/global.d.ts
vendored
3
src/global.d.ts
vendored
@ -42,7 +42,8 @@ declare global {
|
|||||||
|
|
||||||
type ServerErrorType = 'FILE_REFERENCE_EXPIRED' | 'SESSION_REVOKED' | 'AUTH_KEY_DUPLICATED' |
|
type ServerErrorType = 'FILE_REFERENCE_EXPIRED' | 'SESSION_REVOKED' | 'AUTH_KEY_DUPLICATED' |
|
||||||
'SESSION_PASSWORD_NEEDED' | 'CONNECTION_NOT_INITED' | 'ERROR_EMPTY' | 'MTPROTO_CLUSTER_INVALID' |
|
'SESSION_PASSWORD_NEEDED' | 'CONNECTION_NOT_INITED' | 'ERROR_EMPTY' | 'MTPROTO_CLUSTER_INVALID' |
|
||||||
'BOT_PRECHECKOUT_TIMEOUT' | 'TMP_PASSWORD_INVALID' | 'PASSWORD_HASH_INVALID' | 'CHANNEL_PRIVATE';
|
'BOT_PRECHECKOUT_TIMEOUT' | 'TMP_PASSWORD_INVALID' | 'PASSWORD_HASH_INVALID' | 'CHANNEL_PRIVATE' |
|
||||||
|
'VOICE_MESSAGES_FORBIDDEN' | 'PHOTO_INVALID_DIMENSIONS' | 'PHOTO_SAVE_FILE_INVALID';
|
||||||
|
|
||||||
type ErrorType = LocalErrorType | ServerErrorType;
|
type ErrorType = LocalErrorType | ServerErrorType;
|
||||||
|
|
||||||
|
@ -6,13 +6,19 @@
|
|||||||
|
|
||||||
import type {MyDocument} from '../lib/appManagers/appDocsManager';
|
import type {MyDocument} from '../lib/appManagers/appDocsManager';
|
||||||
import type {MyPhoto} from '../lib/appManagers/appPhotosManager';
|
import type {MyPhoto} from '../lib/appManagers/appPhotosManager';
|
||||||
|
import {THUMB_TYPE_FULL} from '../lib/mtproto/mtproto_config';
|
||||||
import type {ThumbCache} from '../lib/storages/thumbs';
|
import type {ThumbCache} from '../lib/storages/thumbs';
|
||||||
import getImageFromStrippedThumb from './getImageFromStrippedThumb';
|
import getImageFromStrippedThumb from './getImageFromStrippedThumb';
|
||||||
|
|
||||||
export default function getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, cacheContext: ThumbCache, useBlur: boolean, ignoreCache = false) {
|
export default function getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, cacheContext: ThumbCache, useBlur: boolean, ignoreCache = false) {
|
||||||
const isVideo = (['video', 'gif'] as MyDocument['type'][]).includes((photo as MyDocument).type);
|
const isVideo = (['video', 'gif'] as MyDocument['type'][]).includes((photo as MyDocument).type);
|
||||||
if(!cacheContext.downloaded || isVideo || ignoreCache) {
|
if(!cacheContext.downloaded || isVideo || ignoreCache) {
|
||||||
if(photo._ === 'document' && cacheContext.downloaded && !ignoreCache && !isVideo) {
|
if(
|
||||||
|
photo._ === 'document' &&
|
||||||
|
cacheContext.downloaded &&
|
||||||
|
!ignoreCache &&
|
||||||
|
(!isVideo || cacheContext.type !== THUMB_TYPE_FULL)
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,11 @@ export default function copy<T>(obj: T): T {
|
|||||||
return clonedArr;
|
return clonedArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(ArrayBuffer.isView(obj)) {
|
||||||
|
// @ts-ignore
|
||||||
|
return obj.slice();
|
||||||
|
}
|
||||||
|
|
||||||
// lastly, handle objects
|
// lastly, handle objects
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const clonedObj = new obj.constructor();
|
const clonedObj = new obj.constructor();
|
||||||
|
@ -759,6 +759,10 @@ const lang = {
|
|||||||
'NewChatsFromNonContacts': 'New chats from unknown users',
|
'NewChatsFromNonContacts': 'New chats from unknown users',
|
||||||
'ArchiveAndMute': 'Archive and Mute',
|
'ArchiveAndMute': 'Archive and Mute',
|
||||||
'ArchiveAndMuteInfo': 'Automatically archive and mute new chats, groups and channels from non-contacts.',
|
'ArchiveAndMuteInfo': 'Automatically archive and mute new chats, groups and channels from non-contacts.',
|
||||||
|
'PrivacyVoiceMessages': 'Voice Messages',
|
||||||
|
'PrivacyVoiceMessagesTitle': 'Who can send me voice or video messages?',
|
||||||
|
'PrivacyVoiceMessagesInfo': 'You can restrict who can send you voice or video messages with granular precision.',
|
||||||
|
'PrivacyVoiceMessagesPremiumOnly': 'Only subscribers of *Telegram Premium* can restrict receiving voice messages.',
|
||||||
|
|
||||||
// * macos
|
// * macos
|
||||||
'AccountSettings.Filters': 'Chat Folders',
|
'AccountSettings.Filters': 'Chat Folders',
|
||||||
@ -835,6 +839,7 @@ const lang = {
|
|||||||
'Chat.DropQuickDesc': 'in a quick way',
|
'Chat.DropQuickDesc': 'in a quick way',
|
||||||
'Chat.DropAsFilesDesc': 'without compression',
|
'Chat.DropAsFilesDesc': 'without compression',
|
||||||
'Chat.Edit.Cancel.Text': 'Are you sure you want to discard all changes?',
|
'Chat.Edit.Cancel.Text': 'Are you sure you want to discard all changes?',
|
||||||
|
'Chat.SendVoice.PrivacyError': '%@ doesn\'t accept voice and video messages',
|
||||||
'Chat.Service.Call.Cancelled': 'Cancelled',
|
'Chat.Service.Call.Cancelled': 'Cancelled',
|
||||||
'Chat.Service.Call.Missed': 'Missed',
|
'Chat.Service.Call.Missed': 'Missed',
|
||||||
'Chat.Service.PeerJoinedTelegram': '%@ joined Telegram',
|
'Chat.Service.PeerJoinedTelegram': '%@ joined Telegram',
|
||||||
|
10
src/layer.d.ts
vendored
10
src/layer.d.ts
vendored
@ -881,14 +881,15 @@ export namespace Message {
|
|||||||
viaBotId?: PeerId,
|
viaBotId?: PeerId,
|
||||||
clear_history?: boolean,
|
clear_history?: boolean,
|
||||||
pending?: boolean,
|
pending?: boolean,
|
||||||
error?: any,
|
error?: ApiError,
|
||||||
send?: () => Promise<any>,
|
send?: () => Promise<any>,
|
||||||
totalEntities?: MessageEntity[],
|
totalEntities?: MessageEntity[],
|
||||||
reply_to_mid?: number,
|
reply_to_mid?: number,
|
||||||
savedFrom?: string,
|
savedFrom?: string,
|
||||||
sponsoredMessage?: SponsoredMessage.sponsoredMessage,
|
sponsoredMessage?: SponsoredMessage.sponsoredMessage,
|
||||||
promise?: CancellablePromise<void>,
|
promise?: CancellablePromise<void>,
|
||||||
uploadingFileName?: string
|
uploadingFileName?: string,
|
||||||
|
storageKey?: MessagesStorageKey
|
||||||
};
|
};
|
||||||
|
|
||||||
export type messageService = {
|
export type messageService = {
|
||||||
@ -920,11 +921,12 @@ export namespace Message {
|
|||||||
rReply?: string,
|
rReply?: string,
|
||||||
viaBotId?: PeerId,
|
viaBotId?: PeerId,
|
||||||
pending?: boolean,
|
pending?: boolean,
|
||||||
error?: any,
|
error?: ApiError,
|
||||||
send?: () => Promise<any>,
|
send?: () => Promise<any>,
|
||||||
random_id?: string,
|
random_id?: string,
|
||||||
reply_to_mid?: number,
|
reply_to_mid?: number,
|
||||||
clear_history?: boolean
|
clear_history?: boolean,
|
||||||
|
storageKey?: MessagesStorageKey
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,17 +530,13 @@ export class AppMessagesManager extends AppManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleError = (on: boolean) => {
|
const toggleError = (error?: ApiError) => {
|
||||||
if(on) {
|
this.onMessagesSendError([message], error);
|
||||||
message.error = true;
|
|
||||||
} else {
|
|
||||||
delete message.error;
|
|
||||||
}
|
|
||||||
this.rootScope.dispatchEvent('messages_pending');
|
this.rootScope.dispatchEvent('messages_pending');
|
||||||
};
|
};
|
||||||
|
|
||||||
message.send = () => {
|
message.send = () => {
|
||||||
toggleError(false);
|
toggleError();
|
||||||
const sentRequestOptions: PendingAfterMsg = {};
|
const sentRequestOptions: PendingAfterMsg = {};
|
||||||
if(this.pendingAfterMsgs[peerId]) {
|
if(this.pendingAfterMsgs[peerId]) {
|
||||||
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
|
sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;
|
||||||
@ -637,9 +633,10 @@ export class AppMessagesManager extends AppManager {
|
|||||||
// ApiUpdatesManager.processUpdateMessage(upd)
|
// ApiUpdatesManager.processUpdateMessage(upd)
|
||||||
// }, 5000)
|
// }, 5000)
|
||||||
message.promise.resolve();
|
message.promise.resolve();
|
||||||
}, (error: any) => {
|
}, (error: ApiError) => {
|
||||||
toggleError(true);
|
toggleError(error);
|
||||||
message.promise.reject(error);
|
message.promise.reject(error);
|
||||||
|
throw error;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
||||||
delete this.pendingAfterMsgs[peerId];
|
delete this.pendingAfterMsgs[peerId];
|
||||||
@ -916,13 +913,8 @@ export class AppMessagesManager extends AppManager {
|
|||||||
this.uploadFilePromises[uploadingFileName] = sentDeferred;
|
this.uploadFilePromises[uploadingFileName] = sentDeferred;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleError = (on: boolean) => {
|
const toggleError = (error?: ApiError) => {
|
||||||
if(on) {
|
this.onMessagesSendError([message], error);
|
||||||
message.error = true;
|
|
||||||
} else {
|
|
||||||
delete message.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rootScope.dispatchEvent('messages_pending');
|
this.rootScope.dispatchEvent('messages_pending');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1012,8 +1004,9 @@ export class AppMessagesManager extends AppManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sentDeferred.resolve(inputMedia);
|
sentDeferred.resolve(inputMedia);
|
||||||
}, (/* error */) => {
|
}, (error: ApiError) => {
|
||||||
toggleError(true);
|
toggleError(error);
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
return sentDeferred;
|
return sentDeferred;
|
||||||
@ -1057,7 +1050,7 @@ export class AppMessagesManager extends AppManager {
|
|||||||
send_as: options.sendAsPeerId ? this.appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
|
send_as: options.sendAsPeerId ? this.appPeersManager.getInputPeerById(options.sendAsPeerId) : undefined
|
||||||
}).then((updates) => {
|
}).then((updates) => {
|
||||||
this.apiUpdatesManager.processUpdateMessage(updates);
|
this.apiUpdatesManager.processUpdateMessage(updates);
|
||||||
}, (error) => {
|
}, (error: ApiError) => {
|
||||||
if(attachType === 'photo' &&
|
if(attachType === 'photo' &&
|
||||||
error.code === 400 &&
|
error.code === 400 &&
|
||||||
(error.type === 'PHOTO_INVALID_DIMENSIONS' ||
|
(error.type === 'PHOTO_INVALID_DIMENSIONS' ||
|
||||||
@ -1068,7 +1061,7 @@ export class AppMessagesManager extends AppManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleError(true);
|
toggleError(error);
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1173,13 +1166,8 @@ export class AppMessagesManager extends AppManager {
|
|||||||
// * test pending
|
// * test pending
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
const toggleError = (message: any, on: boolean) => {
|
const toggleError = (message: Message.message, error?: ApiError) => {
|
||||||
if(on) {
|
this.onMessagesSendError([message], error);
|
||||||
message.error = true;
|
|
||||||
} else {
|
|
||||||
delete message.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rootScope.dispatchEvent('messages_pending');
|
this.rootScope.dispatchEvent('messages_pending');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1201,8 +1189,8 @@ export class AppMessagesManager extends AppManager {
|
|||||||
}).then((updates) => {
|
}).then((updates) => {
|
||||||
this.apiUpdatesManager.processUpdateMessage(updates);
|
this.apiUpdatesManager.processUpdateMessage(updates);
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}, (error) => {
|
}, (error: ApiError) => {
|
||||||
messages.forEach((message) => toggleError(message, true));
|
messages.forEach((message) => toggleError(message, error));
|
||||||
deferred.reject(error);
|
deferred.reject(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1243,13 +1231,9 @@ export class AppMessagesManager extends AppManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return inputSingleMedia;
|
return inputSingleMedia;
|
||||||
}).catch((err: any) => {
|
}).catch((err: ApiError) => {
|
||||||
if(err.name === 'AbortError') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.error('sendAlbum upload item error:', err, message);
|
this.log.error('sendAlbum upload item error:', err, message);
|
||||||
toggleError(message, true);
|
toggleError(message, err);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1366,19 +1350,8 @@ export class AppMessagesManager extends AppManager {
|
|||||||
|
|
||||||
message.media = media;
|
message.media = media;
|
||||||
|
|
||||||
const toggleError = (on: boolean) => {
|
const toggleError = (error?: ApiError) => {
|
||||||
/* const historyMessage = this.messagesForHistory[messageId];
|
this.onMessagesSendError([message], error);
|
||||||
if (on) {
|
|
||||||
message.error = true
|
|
||||||
if (historyMessage) {
|
|
||||||
historyMessage.error = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete message.error
|
|
||||||
if (historyMessage) {
|
|
||||||
delete historyMessage.error
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
this.rootScope.dispatchEvent('messages_pending');
|
this.rootScope.dispatchEvent('messages_pending');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1428,8 +1401,9 @@ export class AppMessagesManager extends AppManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.apiUpdatesManager.processUpdateMessage(updates);
|
this.apiUpdatesManager.processUpdateMessage(updates);
|
||||||
}, (error) => {
|
}, (error: ApiError) => {
|
||||||
toggleError(true);
|
toggleError(error);
|
||||||
|
throw error;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
||||||
delete this.pendingAfterMsgs[peerId];
|
delete this.pendingAfterMsgs[peerId];
|
||||||
@ -1469,6 +1443,7 @@ export class AppMessagesManager extends AppManager {
|
|||||||
const messageId = message.id;
|
const messageId = message.id;
|
||||||
const peerId = this.getMessagePeer(message);
|
const peerId = this.getMessagePeer(message);
|
||||||
const storage = options.isScheduled ? this.getScheduledMessagesStorage(peerId) : this.getHistoryMessagesStorage(peerId);
|
const storage = options.isScheduled ? this.getScheduledMessagesStorage(peerId) : this.getHistoryMessagesStorage(peerId);
|
||||||
|
message.storageKey = storage.key;
|
||||||
const callbacks: Array<() => void> = [];
|
const callbacks: Array<() => void> = [];
|
||||||
if(options.isScheduled) {
|
if(options.isScheduled) {
|
||||||
// if(!options.isGroupedItem) {
|
// if(!options.isGroupedItem) {
|
||||||
@ -2146,6 +2121,9 @@ export class AppMessagesManager extends AppManager {
|
|||||||
}, sentRequestOptions).then((updates) => {
|
}, sentRequestOptions).then((updates) => {
|
||||||
this.log('forwardMessages updates:', updates);
|
this.log('forwardMessages updates:', updates);
|
||||||
this.apiUpdatesManager.processUpdateMessage(updates);
|
this.apiUpdatesManager.processUpdateMessage(updates);
|
||||||
|
}, (error: ApiError) => {
|
||||||
|
this.onMessagesSendError(newMessages, error);
|
||||||
|
throw error;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {
|
||||||
delete this.pendingAfterMsgs[peerId];
|
delete this.pendingAfterMsgs[peerId];
|
||||||
@ -2173,6 +2151,26 @@ export class AppMessagesManager extends AppManager {
|
|||||||
// };
|
// };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onMessagesSendError(messages: Message.message[], error?: ApiError) {
|
||||||
|
messages.forEach((message) => {
|
||||||
|
if(message.error === error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(error) {
|
||||||
|
message.error = error;
|
||||||
|
this.rootScope.dispatchEvent('message_error', {storageKey: message.storageKey, tempId: message.mid, error});
|
||||||
|
|
||||||
|
const dialog = this.getDialogOnly(message.peerId);
|
||||||
|
if(dialog) {
|
||||||
|
this.rootScope.dispatchEvent('dialog_unread', {peerId: message.peerId, dialog});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
delete message.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public getMessagesStorageByKey(key: MessagesStorageKey) {
|
public getMessagesStorageByKey(key: MessagesStorageKey) {
|
||||||
const s = key.split('_');
|
const s = key.split('_');
|
||||||
const peerId: PeerId = +s[0];
|
const peerId: PeerId = +s[0];
|
||||||
@ -3208,7 +3206,7 @@ export class AppMessagesManager extends AppManager {
|
|||||||
message.pFlags.out ||
|
message.pFlags.out ||
|
||||||
this.appChatsManager.getChat(message.peerId.toChatId())._ === 'chat' ||
|
this.appChatsManager.getChat(message.peerId.toChatId())._ === 'chat' ||
|
||||||
this.appChatsManager.hasRights(message.peerId.toChatId(), 'delete_messages')
|
this.appChatsManager.hasRights(message.peerId.toChatId(), 'delete_messages')
|
||||||
) && !message.pFlags.is_outgoing;
|
) && (!message.pFlags.is_outgoing || !!message.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getReplyKeyboard(peerId: PeerId) {
|
public getReplyKeyboard(peerId: PeerId) {
|
||||||
|
@ -1892,7 +1892,12 @@ export default class MTPNetworker {
|
|||||||
} */
|
} */
|
||||||
const pingId = message.ping_id;
|
const pingId = message.ping_id;
|
||||||
if(this.lastPingDelayDisconnectId === pingId) {
|
if(this.lastPingDelayDisconnectId === pingId) {
|
||||||
this.pingDelayDisconnectDeferred.resolve('pong');
|
const deferred = this.pingDelayDisconnectDeferred;
|
||||||
|
if(deferred) {
|
||||||
|
deferred.resolve('pong');
|
||||||
|
} else {
|
||||||
|
this.log('ping deferred deleted', pingId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -71,6 +71,7 @@ export type BroadcastEvents = {
|
|||||||
|
|
||||||
'message_edit': {storageKey: MessagesStorageKey, peerId: PeerId, mid: number, message: MyMessage},
|
'message_edit': {storageKey: MessagesStorageKey, peerId: PeerId, mid: number, message: MyMessage},
|
||||||
'message_sent': {storageKey: MessagesStorageKey, tempId: number, tempMessage: any, mid: number, message: MyMessage},
|
'message_sent': {storageKey: MessagesStorageKey, tempId: number, tempMessage: any, mid: number, message: MyMessage},
|
||||||
|
'message_error': {storageKey: MessagesStorageKey, tempId: number, error: ApiError},
|
||||||
'messages_views': {peerId: PeerId, mid: number, views: number}[],
|
'messages_views': {peerId: PeerId, mid: number, views: number}[],
|
||||||
'messages_reactions': {message: Message.message, changedResults: ReactionCount[]}[],
|
'messages_reactions': {message: Message.message, changedResults: ReactionCount[]}[],
|
||||||
'messages_pending': void,
|
'messages_pending': void,
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
{"name": "viaBotId", "type": "PeerId"},
|
{"name": "viaBotId", "type": "PeerId"},
|
||||||
{"name": "clear_history", "type": "boolean"},
|
{"name": "clear_history", "type": "boolean"},
|
||||||
{"name": "pending", "type": "boolean"},
|
{"name": "pending", "type": "boolean"},
|
||||||
{"name": "error", "type": "any"},
|
{"name": "error", "type": "ApiError"},
|
||||||
{"name": "send", "type": "() => Promise<any>"},
|
{"name": "send", "type": "() => Promise<any>"},
|
||||||
{"name": "totalEntities", "type": "MessageEntity[]"},
|
{"name": "totalEntities", "type": "MessageEntity[]"},
|
||||||
{"name": "reply_to_mid", "type": "number"},
|
{"name": "reply_to_mid", "type": "number"},
|
||||||
@ -99,7 +99,8 @@
|
|||||||
{"name": "local", "type": "true"},
|
{"name": "local", "type": "true"},
|
||||||
{"name": "sponsoredMessage", "type": "SponsoredMessage.sponsoredMessage"},
|
{"name": "sponsoredMessage", "type": "SponsoredMessage.sponsoredMessage"},
|
||||||
{"name": "promise", "type": "CancellablePromise<void>"},
|
{"name": "promise", "type": "CancellablePromise<void>"},
|
||||||
{"name": "uploadingFileName", "type": "string"}
|
{"name": "uploadingFileName", "type": "string"},
|
||||||
|
{"name": "storageKey", "type": "MessagesStorageKey"}
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
"predicate": "messageService",
|
"predicate": "messageService",
|
||||||
@ -114,12 +115,13 @@
|
|||||||
{"name": "viaBotId", "type": "PeerId"},
|
{"name": "viaBotId", "type": "PeerId"},
|
||||||
{"name": "is_single", "type": "true"},
|
{"name": "is_single", "type": "true"},
|
||||||
{"name": "pending", "type": "boolean"},
|
{"name": "pending", "type": "boolean"},
|
||||||
{"name": "error", "type": "any"},
|
{"name": "error", "type": "ApiError"},
|
||||||
{"name": "send", "type": "() => Promise<any>"},
|
{"name": "send", "type": "() => Promise<any>"},
|
||||||
{"name": "random_id", "type": "string"},
|
{"name": "random_id", "type": "string"},
|
||||||
{"name": "reply_to_mid", "type": "number"},
|
{"name": "reply_to_mid", "type": "number"},
|
||||||
{"name": "clear_history", "type": "boolean"},
|
{"name": "clear_history", "type": "boolean"},
|
||||||
{"name": "local", "type": "true"}
|
{"name": "local", "type": "true"},
|
||||||
|
{"name": "storageKey", "type": "MessagesStorageKey"}
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
"predicate": "messageEmpty",
|
"predicate": "messageEmpty",
|
||||||
|
@ -2566,6 +2566,14 @@ $bubble-beside-button-width: 38px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.is-error {
|
||||||
|
.time:after,
|
||||||
|
.time .inner:after {
|
||||||
|
content: $tgico-sendingerror;
|
||||||
|
color: var(--message-error-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* &.is-reply .name {
|
/* &.is-reply .name {
|
||||||
display: none;
|
display: none;
|
||||||
} */
|
} */
|
||||||
|
@ -268,7 +268,8 @@ ul.chatlist {
|
|||||||
.message-status,
|
.message-status,
|
||||||
.text-highlight,
|
.text-highlight,
|
||||||
.premium-icon,
|
.premium-icon,
|
||||||
.verified-icon {
|
.verified-icon,
|
||||||
|
.sending-status-icon {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +228,7 @@ $chat-input-inner-padding-handhelds: .25rem;
|
|||||||
--message-primary-color: var(--primary-color);
|
--message-primary-color: var(--primary-color);
|
||||||
--light-filled-message-primary-color: var(--light-filled-primary-color);
|
--light-filled-message-primary-color: var(--light-filled-primary-color);
|
||||||
--message-secondary-color: var(--secondary-color);
|
--message-secondary-color: var(--secondary-color);
|
||||||
|
--message-error-color: var(--danger-color);
|
||||||
|
|
||||||
$message-out-background-color: #eeffde;
|
$message-out-background-color: #eeffde;
|
||||||
@include splitColor(message-out-background-color, $message-out-background-color, true, true);
|
@include splitColor(message-out-background-color, $message-out-background-color, true, true);
|
||||||
@ -300,6 +301,7 @@ $chat-input-inner-padding-handhelds: .25rem;
|
|||||||
--message-checkbox-color: var(--primary-color);
|
--message-checkbox-color: var(--primary-color);
|
||||||
--message-checkbox-border-color: #fff;
|
--message-checkbox-border-color: #fff;
|
||||||
--message-secondary-color: var(--secondary-color);
|
--message-secondary-color: var(--secondary-color);
|
||||||
|
--message-error-color: #fff;
|
||||||
|
|
||||||
$message-out-background-color: #8774E1;
|
$message-out-background-color: #8774E1;
|
||||||
//@include splitColor(message-out-background-color, #ae582d, true, true);
|
//@include splitColor(message-out-background-color, #ae582d, true, true);
|
||||||
@ -1637,6 +1639,10 @@ hr {
|
|||||||
}
|
}
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
.tgico-sendingerror {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
&-icon {
|
&-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
line-height: 1 !important;
|
line-height: 1 !important;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user