parent
5fdffa3b18
commit
820885dbd9
|
@ -245,7 +245,7 @@ export class AppMediaPlaybackController extends EventListenerBase<{
|
||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
|
|
||||||
const doc = getMediaFromMessage(message) as Document.document;
|
const doc = getMediaFromMessage(message, true) as Document.document;
|
||||||
storage.set(mid, media = document.createElement(doc.type === 'round' || doc.type === 'video' ? 'video' : 'audio'));
|
storage.set(mid, media = document.createElement(doc.type === 'round' || doc.type === 'video' ? 'video' : 'audio'));
|
||||||
// const source = document.createElement('source');
|
// const source = document.createElement('source');
|
||||||
// source.type = doc.type === 'voice' && !opusDecodeController.isPlaySupported() ? 'audio/wav' : doc.mime_type;
|
// source.type = doc.type === 'voice' && !opusDecodeController.isPlaySupported() ? 'audio/wav' : doc.mime_type;
|
||||||
|
@ -431,7 +431,7 @@ export class AppMediaPlaybackController extends EventListenerBase<{
|
||||||
|
|
||||||
await onMediaLoad(playingMedia, undefined, false); // have to wait for load, otherwise on macOS won't set
|
await onMediaLoad(playingMedia, undefined, false); // have to wait for load, otherwise on macOS won't set
|
||||||
|
|
||||||
const doc = getMediaFromMessage(message) as MyDocument;
|
const doc = getMediaFromMessage(message, true) as MyDocument;
|
||||||
|
|
||||||
const artwork: MediaImage[] = [];
|
const artwork: MediaImage[] = [];
|
||||||
|
|
||||||
|
@ -554,7 +554,7 @@ export class AppMediaPlaybackController extends EventListenerBase<{
|
||||||
|
|
||||||
const message = this.getMessageByMedia(playingMedia);
|
const message = this.getMessageByMedia(playingMedia);
|
||||||
return {
|
return {
|
||||||
doc: getMediaFromMessage(message) as MyDocument,
|
doc: getMediaFromMessage(message, true) as MyDocument,
|
||||||
message,
|
message,
|
||||||
media: playingMedia,
|
media: playingMedia,
|
||||||
playbackParams: this.getPlaybackParams()
|
playbackParams: this.getPlaybackParams()
|
||||||
|
@ -858,7 +858,7 @@ export class AppMediaPlaybackController extends EventListenerBase<{
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPlaybackMediaTypeFromMessage(message: Message.message) {
|
private getPlaybackMediaTypeFromMessage(message: Message.message) {
|
||||||
const doc = getMediaFromMessage(message) as MyDocument;
|
const doc = getMediaFromMessage(message, true) as MyDocument;
|
||||||
let mediaType: PlaybackMediaType = 'audio';
|
let mediaType: PlaybackMediaType = 'audio';
|
||||||
if(doc?.type) {
|
if(doc?.type) {
|
||||||
if(doc.type === 'voice' || doc.type === 'round') {
|
if(doc.type === 'voice' || doc.type === 'round') {
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||||
processItem: (item) => {
|
processItem: (item) => {
|
||||||
const isForDocument = this.searchContext.inputFilter._ === 'inputMessagesFilterDocument';
|
const isForDocument = this.searchContext.inputFilter._ === 'inputMessagesFilterDocument';
|
||||||
const {mid, peerId} = item;
|
const {mid, peerId} = item;
|
||||||
const media: MyPhoto | MyDocument = getMediaFromMessage(item);
|
const media = getMediaFromMessage(item, true);
|
||||||
|
|
||||||
if(!media) return;
|
if(!media) return;
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||||
|
|
||||||
onDownloadClick = () => {
|
onDownloadClick = () => {
|
||||||
const {message} = this.target;
|
const {message} = this.target;
|
||||||
const media = getMediaFromMessage(message);
|
const media = getMediaFromMessage(message, true);
|
||||||
if(!media) return;
|
if(!media) return;
|
||||||
appDownloadManager.downloadToDisc({media, queueId: appImManager.chat.bubbles.lazyLoadQueue.queueId});
|
appDownloadManager.downloadToDisc({media, queueId: appImManager.chat.bubbles.lazyLoadQueue.queueId});
|
||||||
};
|
};
|
||||||
|
@ -258,7 +258,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||||
|
|
||||||
const mid = message.mid;
|
const mid = message.mid;
|
||||||
const fromId = (message as Message.message).fwd_from && !message.fromId ? (message as Message.message).fwd_from.from_name : message.fromId;
|
const fromId = (message as Message.message).fwd_from && !message.fromId ? (message as Message.message).fwd_from.from_name : message.fromId;
|
||||||
const media = getMediaFromMessage(message);
|
const media = getMediaFromMessage(message, true);
|
||||||
|
|
||||||
const noForwards = await this.managers.appPeersManager.noForwards(message.peerId);
|
const noForwards = await this.managers.appPeersManager.noForwards(message.peerId);
|
||||||
const isServiceMessage = message._ === 'messageService';
|
const isServiceMessage = message._ === 'messageService';
|
||||||
|
@ -283,7 +283,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet
|
||||||
this.wholeDiv.classList.toggle('no-forwards', cantDownloadMessage);
|
this.wholeDiv.classList.toggle('no-forwards', cantDownloadMessage);
|
||||||
|
|
||||||
this.setCaption(message);
|
this.setCaption(message);
|
||||||
const promise = super._openMedia(media, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, message/* , needLoadMore */);
|
const promise = super._openMedia(media as MyPhoto | MyDocument, message.date, fromId, fromRight, target, reverse, prevTargets, nextTargets, message/* , needLoadMore */);
|
||||||
this.target.mid = mid;
|
this.target.mid = mid;
|
||||||
this.target.peerId = message.peerId;
|
this.target.peerId = message.peerId;
|
||||||
this.target.message = message;
|
this.target.message = message;
|
||||||
|
|
|
@ -42,7 +42,7 @@ import EventListenerBase from '../helpers/eventListenerBase';
|
||||||
import {MyMessage} from '../lib/appManagers/appMessagesManager';
|
import {MyMessage} from '../lib/appManagers/appMessagesManager';
|
||||||
import {NULL_PEER_ID} from '../lib/mtproto/mtproto_config';
|
import {NULL_PEER_ID} from '../lib/mtproto/mtproto_config';
|
||||||
import {isFullScreen} from '../helpers/dom/fullScreen';
|
import {isFullScreen} from '../helpers/dom/fullScreen';
|
||||||
import {attachClickEvent} from '../helpers/dom/clickEvent';
|
import {attachClickEvent, hasMouseMovedSinceDown} from '../helpers/dom/clickEvent';
|
||||||
import SearchListLoader from '../helpers/searchListLoader';
|
import SearchListLoader from '../helpers/searchListLoader';
|
||||||
import createVideo from '../helpers/dom/createVideo';
|
import createVideo from '../helpers/dom/createVideo';
|
||||||
import {AppManagers} from '../lib/appManagers/managers';
|
import {AppManagers} from '../lib/appManagers/managers';
|
||||||
|
@ -57,6 +57,7 @@ import {toastNew} from './toast';
|
||||||
import clamp from '../helpers/number/clamp';
|
import clamp from '../helpers/number/clamp';
|
||||||
import debounce from '../helpers/schedulers/debounce';
|
import debounce from '../helpers/schedulers/debounce';
|
||||||
import isBetween from '../helpers/number/isBetween';
|
import isBetween from '../helpers/number/isBetween';
|
||||||
|
import findUpAsChild from '../helpers/dom/findUpAsChild';
|
||||||
|
|
||||||
const ZOOM_STEP = 0.5;
|
const ZOOM_STEP = 0.5;
|
||||||
const ZOOM_INITIAL_VALUE = 1;
|
const ZOOM_INITIAL_VALUE = 1;
|
||||||
|
@ -137,6 +138,7 @@ export default class AppMediaViewerBase<
|
||||||
protected lastDragDelta: {x: number, y: number} = this.transform;
|
protected lastDragDelta: {x: number, y: number} = this.transform;
|
||||||
protected lastGestureTime: number;
|
protected lastGestureTime: number;
|
||||||
protected clampZoomDebounced: ReturnType<typeof debounce<() => void>>;
|
protected clampZoomDebounced: ReturnType<typeof debounce<() => void>>;
|
||||||
|
ignoreNextClick: boolean;
|
||||||
|
|
||||||
get target() {
|
get target() {
|
||||||
return this.listLoader.current;
|
return this.listLoader.current;
|
||||||
|
@ -228,13 +230,25 @@ export default class AppMediaViewerBase<
|
||||||
}, ZOOM_INITIAL_VALUE);
|
}, ZOOM_INITIAL_VALUE);
|
||||||
this.zoomElements.rangeSelector.setListeners();
|
this.zoomElements.rangeSelector.setListeners();
|
||||||
this.zoomElements.rangeSelector.setHandlers({
|
this.zoomElements.rangeSelector.setHandlers({
|
||||||
onScrub: this.setZoomValue,
|
onScrub: (value) => {
|
||||||
onMouseUp: () => this.setZoomValue()
|
const add = value - this.transform.scale;
|
||||||
|
this.addZoom(add);
|
||||||
|
this.clampZoomDebounced?.clearTimeout();
|
||||||
|
},
|
||||||
|
onMouseDown: () => {
|
||||||
|
this.moversContainer.classList.add('no-transition');
|
||||||
|
this.zoomElements.rangeSelector.container.classList.remove('with-transition');
|
||||||
|
},
|
||||||
|
onMouseUp: () => {
|
||||||
|
this.moversContainer.classList.remove('no-transition');
|
||||||
|
this.zoomElements.rangeSelector.container.classList.add('with-transition');
|
||||||
|
this.setZoomValue();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.zoomElements.container.append(this.zoomElements.btnOut, this.zoomElements.rangeSelector.container, this.zoomElements.btnIn);
|
this.zoomElements.container.append(this.zoomElements.btnOut, this.zoomElements.rangeSelector.container, this.zoomElements.btnIn);
|
||||||
|
|
||||||
if(!IS_TOUCH_SUPPORTED && false) {
|
if(!IS_TOUCH_SUPPORTED) {
|
||||||
this.wholeDiv.append(this.zoomElements.container);
|
this.wholeDiv.append(this.zoomElements.container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +363,7 @@ export default class AppMediaViewerBase<
|
||||||
this.swipeHandler = new SwipeHandler({
|
this.swipeHandler = new SwipeHandler({
|
||||||
element: this.wholeDiv,
|
element: this.wholeDiv,
|
||||||
onReset: this.onSwipeReset,
|
onReset: this.onSwipeReset,
|
||||||
onFirstSwipe: this.onSwipeFirst,
|
onFirstSwipe: this.onSwipeFirst as any,
|
||||||
onSwipe: (xDiff, yDiff, e, cancelDrag) => {
|
onSwipe: (xDiff, yDiff, e, cancelDrag) => {
|
||||||
if(isFullScreen()) {
|
if(isFullScreen()) {
|
||||||
return;
|
return;
|
||||||
|
@ -400,6 +414,7 @@ export default class AppMediaViewerBase<
|
||||||
verifyTouchTarget: (e) => {
|
verifyTouchTarget: (e) => {
|
||||||
// * Fix for seek input
|
// * Fix for seek input
|
||||||
if(isFullScreen() ||
|
if(isFullScreen() ||
|
||||||
|
findUpAsChild(e.target as HTMLElement, this.zoomElements.container) ||
|
||||||
findUpClassName(e.target, 'ckin__controls') ||
|
findUpClassName(e.target, 'ckin__controls') ||
|
||||||
findUpClassName(e.target, 'media-viewer-caption') ||
|
findUpClassName(e.target, 'media-viewer-caption') ||
|
||||||
(findUpClassName(e.target, 'media-viewer-topbar') && e.type !== 'wheel')) {
|
(findUpClassName(e.target, 'media-viewer-topbar') && e.type !== 'wheel')) {
|
||||||
|
@ -413,10 +428,13 @@ export default class AppMediaViewerBase<
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onSwipeFirst = () => {
|
protected onSwipeFirst = (e: MouseEvent | TouchEvent | WheelEvent) => {
|
||||||
this.lastDragOffset = this.lastDragDelta = {x: 0, y: 0};
|
this.lastDragOffset = this.lastDragDelta = {x: 0, y: 0};
|
||||||
this.lastTransform = {...this.transform};
|
this.lastTransform = {...this.transform};
|
||||||
this.moversContainer.classList.add('no-transition');
|
if(e.type !== 'wheel' || !this.ctrlKeyDown) { // keep transition for real mouse wheel
|
||||||
|
this.moversContainer.classList.add('no-transition');
|
||||||
|
this.zoomElements.rangeSelector.container.classList.remove('with-transition');
|
||||||
|
}
|
||||||
this.isGesturingNow = true;
|
this.isGesturingNow = true;
|
||||||
this.lastGestureTime = Date.now();
|
this.lastGestureTime = Date.now();
|
||||||
this.clampZoomDebounced.clearTimeout();
|
this.clampZoomDebounced.clearTimeout();
|
||||||
|
@ -426,11 +444,16 @@ export default class AppMediaViewerBase<
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected onSwipeReset = () => {
|
protected onSwipeReset = (e?: Event) => {
|
||||||
// move
|
// move
|
||||||
this.moversContainer.classList.remove('no-transition');
|
this.moversContainer.classList.remove('no-transition');
|
||||||
|
this.zoomElements.rangeSelector.container.classList.add('with-transition');
|
||||||
this.clampZoomDebounced.clearTimeout();
|
this.clampZoomDebounced.clearTimeout();
|
||||||
|
|
||||||
|
if(e?.type === 'mouseup' && this.draggingType === 'mousemove') {
|
||||||
|
this.ignoreNextClick = true;
|
||||||
|
}
|
||||||
|
|
||||||
const {draggingType} = this;
|
const {draggingType} = this;
|
||||||
this.isZoomingNow = false;
|
this.isZoomingNow = false;
|
||||||
this.isGesturingNow = false;
|
this.isGesturingNow = false;
|
||||||
|
@ -492,7 +515,7 @@ export default class AppMediaViewerBase<
|
||||||
this.isZoomingNow = true;
|
this.isZoomingNow = true;
|
||||||
|
|
||||||
const zoomMaxBounceValue = ZOOM_MAX_VALUE * 3;
|
const zoomMaxBounceValue = ZOOM_MAX_VALUE * 3;
|
||||||
const scale = zoomAdd ? clamp(this.lastTransform.scale + zoomAdd, ZOOM_MIN_VALUE, zoomMaxBounceValue) : (zoom ?? clamp(this.lastTransform.scale * zoomFactor, ZOOM_MIN_VALUE, zoomMaxBounceValue));
|
const scale = zoomAdd !== undefined ? clamp(this.lastTransform.scale + zoomAdd, ZOOM_MIN_VALUE, zoomMaxBounceValue) : (zoom ?? clamp(this.lastTransform.scale * zoomFactor, ZOOM_MIN_VALUE, zoomMaxBounceValue));
|
||||||
const scaleFactor = scale / this.lastTransform.scale;
|
const scaleFactor = scale / this.lastTransform.scale;
|
||||||
const offsetX = Math.abs(Math.min(this.lastTransform.x, 0));
|
const offsetX = Math.abs(Math.min(this.lastTransform.x, 0));
|
||||||
const offsetY = Math.abs(Math.min(this.lastTransform.y, 0));
|
const offsetY = Math.abs(Math.min(this.lastTransform.y, 0));
|
||||||
|
@ -661,8 +684,8 @@ export default class AppMediaViewerBase<
|
||||||
|
|
||||||
this.moversContainer.style.transform = `translate3d(${this.transform.x.toFixed(3)}px, ${this.transform.y.toFixed(3)}px, 0px) scale(${value.toFixed(3)})`;
|
this.moversContainer.style.transform = `translate3d(${this.transform.x.toFixed(3)}px, ${this.transform.y.toFixed(3)}px, 0px) scale(${value.toFixed(3)})`;
|
||||||
|
|
||||||
this.zoomElements.btnOut.classList.toggle('inactive', value === ZOOM_MIN_VALUE);
|
this.zoomElements.btnOut.classList.toggle('inactive', value <= ZOOM_MIN_VALUE);
|
||||||
this.zoomElements.btnIn.classList.toggle('inactive', value === ZOOM_MAX_VALUE);
|
this.zoomElements.btnIn.classList.toggle('inactive', value >= ZOOM_MAX_VALUE);
|
||||||
|
|
||||||
this.toggleZoom(value !== ZOOM_INITIAL_VALUE);
|
this.toggleZoom(value !== ZOOM_INITIAL_VALUE);
|
||||||
};
|
};
|
||||||
|
@ -735,6 +758,11 @@ export default class AppMediaViewerBase<
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick = (e: MouseEvent) => {
|
onClick = (e: MouseEvent) => {
|
||||||
|
if(this.ignoreNextClick) {
|
||||||
|
this.ignoreNextClick = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(this.setMoverAnimationPromise) return;
|
if(this.setMoverAnimationPromise) return;
|
||||||
|
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
|
@ -756,7 +784,11 @@ export default class AppMediaViewerBase<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isZooming = this.isZooming;
|
if(hasMouseMovedSinceDown(e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isZooming = this.isZooming && false;
|
||||||
let mover: HTMLElement = null;
|
let mover: HTMLElement = null;
|
||||||
const classNames = ['ckin__player', 'media-viewer-buttons', 'media-viewer-author', 'media-viewer-caption', 'zoom-container'];
|
const classNames = ['ckin__player', 'media-viewer-buttons', 'media-viewer-author', 'media-viewer-caption', 'zoom-container'];
|
||||||
if(isZooming) {
|
if(isZooming) {
|
||||||
|
@ -1850,8 +1882,9 @@ export default class AppMediaViewerBase<
|
||||||
const cancellablePromise = isDocument ? appDownloadManager.downloadMediaURL({media}) : appDownloadManager.downloadMediaURL({media, thumb: size});
|
const cancellablePromise = isDocument ? appDownloadManager.downloadMediaURL({media}) : appDownloadManager.downloadMediaURL({media, thumb: size});
|
||||||
|
|
||||||
const photoSizes = !isDocument && media.sizes.slice().filter((size) => (size as PhotoSize.photoSize).w) as PhotoSize.photoSize[];
|
const photoSizes = !isDocument && media.sizes.slice().filter((size) => (size as PhotoSize.photoSize).w) as PhotoSize.photoSize[];
|
||||||
photoSizes?.sort((a, b) => b.size - a.size);
|
photoSizes && photoSizes.sort((a, b) => b.size - a.size);
|
||||||
const cancellableFullPromise = !isDocument && appDownloadManager.downloadMediaURL({media, thumb: photoSizes?.[0]});
|
const fullPhotoSize = photoSizes?.[0];
|
||||||
|
const cancellableFullPromise = !isDocument && fullPhotoSize !== size && appDownloadManager.downloadMediaURL({media, thumb: fullPhotoSize});
|
||||||
|
|
||||||
onAnimationEnd.then(async() => {
|
onAnimationEnd.then(async() => {
|
||||||
if(!(await getCacheContext()).url) {
|
if(!(await getCacheContext()).url) {
|
||||||
|
@ -1866,8 +1899,6 @@ export default class AppMediaViewerBase<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////this.log('indochina', blob);
|
|
||||||
|
|
||||||
const url = (await getCacheContext()).url;
|
const url = (await getCacheContext()).url;
|
||||||
if(target instanceof SVGSVGElement) {
|
if(target instanceof SVGSVGElement) {
|
||||||
this.updateMediaSource(target, url, 'img');
|
this.updateMediaSource(target, url, 'img');
|
||||||
|
@ -1886,8 +1917,6 @@ export default class AppMediaViewerBase<
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
image.classList.add('thumbnail');
|
image.classList.add('thumbnail');
|
||||||
|
|
||||||
// this.log('will renderImageFromUrl:', image, div, target);
|
|
||||||
|
|
||||||
renderImageFromUrl(image, url, () => {
|
renderImageFromUrl(image, url, () => {
|
||||||
fastRaf(() => {
|
fastRaf(() => {
|
||||||
this.updateMediaSource(target, url, 'img');
|
this.updateMediaSource(target, url, 'img');
|
||||||
|
@ -1902,7 +1931,7 @@ export default class AppMediaViewerBase<
|
||||||
});
|
});
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
cancellableFullPromise?.then((url) => {
|
cancellableFullPromise && cancellableFullPromise.then((url) => {
|
||||||
const fullImage = new Image();
|
const fullImage = new Image();
|
||||||
fullImage.classList.add('thumbnail');
|
fullImage.classList.add('thumbnail');
|
||||||
renderImageFromUrl(fullImage, url, () => {
|
renderImageFromUrl(fullImage, url, () => {
|
||||||
|
|
|
@ -648,7 +648,7 @@ export default class AppSearchSuper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processPhotoVideoFilter({message, promises, middleware}: ProcessSearchSuperResult) {
|
private async processPhotoVideoFilter({message, promises, middleware}: ProcessSearchSuperResult) {
|
||||||
const media = getMediaFromMessage(message);
|
const media = getMediaFromMessage(message, true);
|
||||||
|
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.classList.add('grid-item');
|
div.classList.add('grid-item');
|
||||||
|
@ -711,7 +711,7 @@ export default class AppSearchSuper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async processDocumentFilter({message, inputFilter}: ProcessSearchSuperResult) {
|
private async processDocumentFilter({message, inputFilter}: ProcessSearchSuperResult) {
|
||||||
const document = getMediaFromMessage(message) as Document.document;
|
const document = getMediaFromMessage(message, true) as Document.document;
|
||||||
const showSender = this.showSender || (['voice', 'round'] as MyDocument['type'][]).includes(document.type);
|
const showSender = this.showSender || (['voice', 'round'] as MyDocument['type'][]).includes(document.type);
|
||||||
|
|
||||||
const div = await wrapDocument({
|
const div = await wrapDocument({
|
||||||
|
|
|
@ -31,7 +31,7 @@ import filterAsync from '../helpers/array/filterAsync';
|
||||||
import getParticipantPeerId from '../lib/appManagers/utils/chats/getParticipantPeerId';
|
import getParticipantPeerId from '../lib/appManagers/utils/chats/getParticipantPeerId';
|
||||||
import getChatMembersString from './wrappers/getChatMembersString';
|
import getChatMembersString from './wrappers/getChatMembersString';
|
||||||
import getUserStatusString from './wrappers/getUserStatusString';
|
import getUserStatusString from './wrappers/getUserStatusString';
|
||||||
import {Chat, User} from '../layer';
|
import {ChannelsChannelParticipants, Chat, User} from '../layer';
|
||||||
import canSendToUser from '../lib/appManagers/utils/users/canSendToUser';
|
import canSendToUser from '../lib/appManagers/utils/users/canSendToUser';
|
||||||
import hasRights from '../lib/appManagers/utils/chats/hasRights';
|
import hasRights from '../lib/appManagers/utils/chats/hasRights';
|
||||||
import getDialogIndex from '../lib/appManagers/utils/dialogs/getDialogIndex';
|
import getDialogIndex from '../lib/appManagers/utils/dialogs/getDialogIndex';
|
||||||
|
@ -75,7 +75,7 @@ export default class AppSelectPeers {
|
||||||
private onChange: (length: number) => void;
|
private onChange: (length: number) => void;
|
||||||
private peerType: SelectSearchPeerType[] = ['dialogs'];
|
private peerType: SelectSearchPeerType[] = ['dialogs'];
|
||||||
private renderResultsFunc: (peerIds: PeerId[]) => void | Promise<void>;
|
private renderResultsFunc: (peerIds: PeerId[]) => void | Promise<void>;
|
||||||
private chatRightsAction: ChatRights;
|
private chatRightsActions: ChatRights[];
|
||||||
private multiSelect = true;
|
private multiSelect = true;
|
||||||
private rippleEnabled = true;
|
private rippleEnabled = true;
|
||||||
private avatarSize: DialogElementSize = 'abitbigger';
|
private avatarSize: DialogElementSize = 'abitbigger';
|
||||||
|
@ -104,7 +104,7 @@ export default class AppSelectPeers {
|
||||||
peerId?: AppSelectPeers['peerId'],
|
peerId?: AppSelectPeers['peerId'],
|
||||||
onFirstRender?: () => void,
|
onFirstRender?: () => void,
|
||||||
renderResultsFunc?: AppSelectPeers['renderResultsFunc'],
|
renderResultsFunc?: AppSelectPeers['renderResultsFunc'],
|
||||||
chatRightsAction?: AppSelectPeers['chatRightsAction'],
|
chatRightsActions?: AppSelectPeers['chatRightsActions'],
|
||||||
multiSelect?: AppSelectPeers['multiSelect'],
|
multiSelect?: AppSelectPeers['multiSelect'],
|
||||||
rippleEnabled?: AppSelectPeers['rippleEnabled'],
|
rippleEnabled?: AppSelectPeers['rippleEnabled'],
|
||||||
avatarSize?: AppSelectPeers['avatarSize'],
|
avatarSize?: AppSelectPeers['avatarSize'],
|
||||||
|
@ -347,7 +347,7 @@ export default class AppSelectPeers {
|
||||||
dialogs = dialogs.slice();
|
dialogs = dialogs.slice();
|
||||||
findAndSplice(dialogs, d => d.peerId === rootScope.myId); // no my account
|
findAndSplice(dialogs, d => d.peerId === rootScope.myId); // no my account
|
||||||
|
|
||||||
if(this.chatRightsAction) {
|
if(this.chatRightsActions) {
|
||||||
dialogs = await filterAsync(dialogs, (d) => this.filterByRights(d.peerId));
|
dialogs = await filterAsync(dialogs, (d) => this.filterByRights(d.peerId));
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
return;
|
return;
|
||||||
|
@ -389,8 +389,8 @@ export default class AppSelectPeers {
|
||||||
private async filterByRights(peerId: PeerId) {
|
private async filterByRights(peerId: PeerId) {
|
||||||
const peer: User | Chat = await this.managers.appPeersManager.getPeer(peerId);
|
const peer: User | Chat = await this.managers.appPeersManager.getPeer(peerId);
|
||||||
if(peerId.isUser()) {
|
if(peerId.isUser()) {
|
||||||
return this.chatRightsAction !== 'send_messages' || canSendToUser(peer as User.user);
|
return this.chatRightsActions[0] !== 'send_plain' || canSendToUser(peer as User.user);
|
||||||
} else if(hasRights(peer as Chat.chat, this.chatRightsAction)) {
|
} else if(this.chatRightsActions.every((action) => hasRights(peer as Chat.chat, action))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,7 +433,7 @@ export default class AppSelectPeers {
|
||||||
// do not add global result if only dialogs needed
|
// do not add global result if only dialogs needed
|
||||||
let resultPeerIds = isGlobalSearch ? searchResult.my_results.concat(searchResult.results) : searchResult.my_results;
|
let resultPeerIds = isGlobalSearch ? searchResult.my_results.concat(searchResult.results) : searchResult.my_results;
|
||||||
|
|
||||||
if(this.chatRightsAction) {
|
if(this.chatRightsActions) {
|
||||||
resultPeerIds = await filterAsync(resultPeerIds, (peerId) => this.filterByRights(peerId));
|
resultPeerIds = await filterAsync(resultPeerIds, (peerId) => this.filterByRights(peerId));
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
return;
|
return;
|
||||||
|
@ -474,7 +474,7 @@ export default class AppSelectPeers {
|
||||||
const pageCount = 50; // same as in group permissions to use cache
|
const pageCount = 50; // same as in group permissions to use cache
|
||||||
|
|
||||||
const {middleware} = this.getTempId('channelParticipants');
|
const {middleware} = this.getTempId('channelParticipants');
|
||||||
const promise = this.managers.appProfileManager.getChannelParticipants(
|
const promise = this.managers.appProfileManager.getParticipants(
|
||||||
this.peerId.toChatId(),
|
this.peerId.toChatId(),
|
||||||
{
|
{
|
||||||
_: 'channelParticipantsSearch',
|
_: 'channelParticipantsSearch',
|
||||||
|
@ -492,18 +492,20 @@ export default class AppSelectPeers {
|
||||||
this.loadedWhat.channelParticipants = true;
|
this.loadedWhat.channelParticipants = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const participants = await promise;
|
const chatParticipants = await promise;
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const peerIds = participants.participants.map((participant) => {
|
const {participants} = chatParticipants;
|
||||||
return getParticipantPeerId(participant);
|
|
||||||
});
|
const peerIds = participants.map((participant) => getParticipantPeerId(participant));
|
||||||
indexOfAndSplice(peerIds, rootScope.myId);
|
indexOfAndSplice(peerIds, rootScope.myId);
|
||||||
this.renderResultsFunc(peerIds);
|
this.renderResultsFunc(peerIds);
|
||||||
|
|
||||||
if(this.list.childElementCount >= participants.count || participants.participants.length < pageCount) {
|
const count = (chatParticipants as ChannelsChannelParticipants.channelsChannelParticipants).count ?? participants.length;
|
||||||
|
|
||||||
|
if(this.list.childElementCount >= count || participants.length < pageCount) {
|
||||||
this.loadedWhat.channelParticipants = true;
|
this.loadedWhat.channelParticipants = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ function createWaveformBars(waveform: Uint8Array, duration: number) {
|
||||||
const bar_value = Math.max(((maxValue * maxDelta) + ((normValue + 1) / 2)) / (normValue + 1), barHeightMin);
|
const bar_value = Math.max(((maxValue * maxDelta) + ((normValue + 1) / 2)) / (normValue + 1), barHeightMin);
|
||||||
|
|
||||||
const h = `
|
const h = `
|
||||||
<rect x="${barX}" y="${barHeightMax - bar_value}" width="${barWidth}" height="${bar_value}" rx="1" ry="1"></rect>
|
<rect class="audio-waveform-bar" x="${barX}" y="${barHeightMax - bar_value}" width="${barWidth}" height="${bar_value}" rx="1" ry="1"></rect>
|
||||||
`;
|
`;
|
||||||
html += h;
|
html += h;
|
||||||
|
|
||||||
|
@ -197,6 +197,11 @@ async function wrapVoiceMessage(audioEl: AudioElement) {
|
||||||
// TODO: State to enum
|
// TODO: State to enum
|
||||||
audioEl.transcriptionState = 2;
|
audioEl.transcriptionState = 2;
|
||||||
} else {
|
} else {
|
||||||
|
const message = audioEl.message;
|
||||||
|
if(message.pFlags.is_outgoing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
audioEl.transcriptionState = 1;
|
audioEl.transcriptionState = 1;
|
||||||
!speechRecognitionLoader.parentElement && speechRecognitionDiv.append(speechRecognitionLoader);
|
!speechRecognitionLoader.parentElement && speechRecognitionDiv.append(speechRecognitionLoader);
|
||||||
doubleRaf().then(() => {
|
doubleRaf().then(() => {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import contextMenuController from '../helpers/contextMenuController';
|
import contextMenuController from '../helpers/contextMenuController';
|
||||||
import cancelEvent from '../helpers/dom/cancelEvent';
|
import cancelEvent from '../helpers/dom/cancelEvent';
|
||||||
import {AttachClickOptions, CLICK_EVENT_NAME} from '../helpers/dom/clickEvent';
|
import {AttachClickOptions, CLICK_EVENT_NAME, hasMouseMovedSinceDown} from '../helpers/dom/clickEvent';
|
||||||
import ListenerSetter from '../helpers/listenerSetter';
|
import ListenerSetter from '../helpers/listenerSetter';
|
||||||
import ButtonIcon from './buttonIcon';
|
import ButtonIcon from './buttonIcon';
|
||||||
import ButtonMenu, {ButtonMenuItemOptionsVerifiable} from './buttonMenu';
|
import ButtonMenu, {ButtonMenuItemOptionsVerifiable} from './buttonMenu';
|
||||||
|
@ -28,7 +28,7 @@ export function ButtonMenuToggleHandler({
|
||||||
const add = options?.listenerSetter ? options.listenerSetter.add(el) : el.addEventListener.bind(el);
|
const add = options?.listenerSetter ? options.listenerSetter.add(el) : el.addEventListener.bind(el);
|
||||||
|
|
||||||
add(CLICK_EVENT_NAME, (e: Event) => {
|
add(CLICK_EVENT_NAME, (e: Event) => {
|
||||||
if(!el.classList.contains('btn-menu-toggle')) return false;
|
if(!el.classList.contains('btn-menu-toggle') || hasMouseMovedSinceDown(e)) return false;
|
||||||
|
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
|
|
||||||
|
|
|
@ -656,11 +656,11 @@ export default class ChatBubbles {
|
||||||
dots?.remove();
|
dots?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!text && !pending && !transcribedText.classList.contains('has-some-text')) {
|
if(!text && !pending/* && !transcribedText.classList.contains('has-some-text') */) {
|
||||||
transcribedText.append(i18n('Chat.Voice.Transribe.Error'));
|
transcribedText.replaceChildren(i18n('Chat.Voice.Transribe.Error'));
|
||||||
transcribedText.classList.add('is-error');
|
transcribedText.classList.add('is-error');
|
||||||
} else {
|
} else if(text) {
|
||||||
transcribedText.classList.add('has-some-text');
|
// transcribedText.classList.add('has-some-text');
|
||||||
transcribedText.firstChild.textContent = text;
|
transcribedText.firstChild.textContent = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,9 +1101,14 @@ export default class ChatBubbles {
|
||||||
|
|
||||||
const chat = await this.managers.appChatsManager.getChat(chatId);
|
const chat = await this.managers.appChatsManager.getChat(chatId);
|
||||||
const hadRights = this.chatInner.classList.contains('has-rights');
|
const hadRights = this.chatInner.classList.contains('has-rights');
|
||||||
const hasRights = await this.chat.canSend();
|
const hadPlainRights = this.chat.input.canSendPlain();
|
||||||
|
const [hasRights, hasPlainRights, canEmbedLinks] = await Promise.all([
|
||||||
|
this.chat.canSend('send_messages'),
|
||||||
|
this.chat.canSend('send_plain'),
|
||||||
|
this.chat.canSend('embed_links')
|
||||||
|
]);
|
||||||
|
|
||||||
if(hadRights !== hasRights) {
|
if(hadRights !== hasRights || hadPlainRights !== hasPlainRights) {
|
||||||
const callbacks = await Promise.all([
|
const callbacks = await Promise.all([
|
||||||
this.finishPeerChange(),
|
this.finishPeerChange(),
|
||||||
this.chat.input.finishPeerChange()
|
this.chat.input.finishPeerChange()
|
||||||
|
@ -1112,6 +1117,12 @@ export default class ChatBubbles {
|
||||||
callbacks.forEach((callback) => callback());
|
callbacks.forEach((callback) => callback());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset webpage
|
||||||
|
if((canEmbedLinks && !this.chat.input.willSendWebPage) || (!canEmbedLinks && this.chat.input.willSendWebPage)) {
|
||||||
|
this.chat.input.lastUrl = '';
|
||||||
|
this.chat.input.onMessageInput();
|
||||||
|
}
|
||||||
|
|
||||||
if(!!(chat as MTChat.channel).pFlags.forum !== this.chat.isForum && this.chat.type === 'chat') {
|
if(!!(chat as MTChat.channel).pFlags.forum !== this.chat.isForum && this.chat.type === 'chat') {
|
||||||
this.chat.peerId = 0;
|
this.chat.peerId = 0;
|
||||||
this.chat.appImManager.setPeer({peerId});
|
this.chat.appImManager.setPeer({peerId});
|
||||||
|
|
|
@ -19,7 +19,7 @@ import findUpClassName from '../../helpers/dom/findUpClassName';
|
||||||
import cancelEvent from '../../helpers/dom/cancelEvent';
|
import cancelEvent from '../../helpers/dom/cancelEvent';
|
||||||
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
import {attachClickEvent, simulateClickEvent} from '../../helpers/dom/clickEvent';
|
||||||
import isSelectionEmpty from '../../helpers/dom/isSelectionEmpty';
|
import isSelectionEmpty from '../../helpers/dom/isSelectionEmpty';
|
||||||
import {Message, Poll, Chat as MTChat, MessageMedia, AvailableReaction, MessageEntity, InputStickerSet, StickerSet, Document, Reaction} from '../../layer';
|
import {Message, Poll, Chat as MTChat, MessageMedia, AvailableReaction, MessageEntity, InputStickerSet, StickerSet, Document, Reaction, Photo} from '../../layer';
|
||||||
import PopupReportMessages from '../popups/reportMessages';
|
import PopupReportMessages from '../popups/reportMessages';
|
||||||
import assumeType from '../../helpers/assumeType';
|
import assumeType from '../../helpers/assumeType';
|
||||||
import PopupSponsored from '../popups/sponsored';
|
import PopupSponsored from '../popups/sponsored';
|
||||||
|
@ -39,7 +39,7 @@ import {attachContextMenuListener} from '../../helpers/dom/attachContextMenuList
|
||||||
import filterAsync from '../../helpers/array/filterAsync';
|
import filterAsync from '../../helpers/array/filterAsync';
|
||||||
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
||||||
import {SERVICE_PEER_ID} from '../../lib/mtproto/mtproto_config';
|
import {SERVICE_PEER_ID} from '../../lib/mtproto/mtproto_config';
|
||||||
import {MessagesStorageKey} from '../../lib/appManagers/appMessagesManager';
|
import {MessagesStorageKey, MyMessage} from '../../lib/appManagers/appMessagesManager';
|
||||||
import filterUnique from '../../helpers/array/filterUnique';
|
import filterUnique from '../../helpers/array/filterUnique';
|
||||||
import replaceContent from '../../helpers/dom/replaceContent';
|
import replaceContent from '../../helpers/dom/replaceContent';
|
||||||
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
||||||
|
@ -79,6 +79,7 @@ export default class ChatContextMenu {
|
||||||
private albumMessages: Message.message[];
|
private albumMessages: Message.message[];
|
||||||
private linkToMessage: Awaited<ReturnType<ChatContextMenu['getUrlToMessage']>>;
|
private linkToMessage: Awaited<ReturnType<ChatContextMenu['getUrlToMessage']>>;
|
||||||
private selectedMessagesText: string;
|
private selectedMessagesText: string;
|
||||||
|
private selectedMessages: MyMessage[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private chat: Chat,
|
private chat: Chat,
|
||||||
|
@ -199,6 +200,7 @@ export default class ChatContextMenu {
|
||||||
this.canOpenReactedList = undefined;
|
this.canOpenReactedList = undefined;
|
||||||
this.linkToMessage = await this.getUrlToMessage();
|
this.linkToMessage = await this.getUrlToMessage();
|
||||||
this.selectedMessagesText = await this.getSelectedMessagesText();
|
this.selectedMessagesText = await this.getSelectedMessagesText();
|
||||||
|
this.selectedMessages = this.chat.selection.isSelecting ? await this.chat.selection.getSelectedMessages() : undefined;
|
||||||
|
|
||||||
const initResult = await this.init();
|
const initResult = await this.init();
|
||||||
if(!initResult) {
|
if(!initResult) {
|
||||||
|
@ -445,31 +447,9 @@ export default class ChatContextMenu {
|
||||||
icon: 'download',
|
icon: 'download',
|
||||||
text: 'MediaViewer.Context.Download',
|
text: 'MediaViewer.Context.Download',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
appDownloadManager.downloadToDisc({media: getMediaFromMessage(this.message)});
|
appDownloadManager.downloadToDisc({media: getMediaFromMessage(this.message, true)});
|
||||||
},
|
},
|
||||||
verify: () => {
|
verify: () => this.canDownload(this.message, true)
|
||||||
if(!canSaveMessageMedia(this.message) || this.noForwards) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPhoto: boolean = !!((this.message as Message.message).media as MessageMedia.messageMediaPhoto)?.photo;
|
|
||||||
let isGoodType = false
|
|
||||||
|
|
||||||
if(isPhoto) {
|
|
||||||
isGoodType = true;
|
|
||||||
} else {
|
|
||||||
const doc: MyDocument = ((this.message as Message.message).media as MessageMedia.messageMediaDocument)?.document as any;
|
|
||||||
if(!doc) return false;
|
|
||||||
// isGoodType = doc.type && (['gif', 'video', 'audio', 'voice', 'sticker'] as MyDocument['type'][]).includes(doc.type)
|
|
||||||
isGoodType = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasTarget = !!IS_TOUCH_SUPPORTED;
|
|
||||||
|
|
||||||
if(isGoodType) hasTarget ||= !!findUpClassName(this.target, 'document') || !!findUpClassName(this.target, 'audio') || !!findUpClassName(this.target, 'media-sticker-wrapper') || !!findUpClassName(this.target, 'media-photo') || !!findUpClassName(this.target, 'media-video');
|
|
||||||
|
|
||||||
return isGoodType && hasTarget;
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
icon: 'checkretract',
|
icon: 'checkretract',
|
||||||
text: 'Chat.Poll.Unvote',
|
text: 'Chat.Poll.Unvote',
|
||||||
|
@ -502,6 +482,16 @@ export default class ChatContextMenu {
|
||||||
!this.chat.selection.selectionForwardBtn.hasAttribute('disabled'),
|
!this.chat.selection.selectionForwardBtn.hasAttribute('disabled'),
|
||||||
notDirect: () => true,
|
notDirect: () => true,
|
||||||
withSelection: true
|
withSelection: true
|
||||||
|
}, {
|
||||||
|
icon: 'download',
|
||||||
|
text: 'Message.Context.Selection.Download',
|
||||||
|
onClick: () => {
|
||||||
|
this.selectedMessages.forEach((message) => {
|
||||||
|
appDownloadManager.downloadToDisc({media: getMediaFromMessage(message, true)});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
verify: () => this.selectedMessages ? this.selectedMessages.some((message) => this.canDownload(message, false)) : false,
|
||||||
|
withSelection: true
|
||||||
}, {
|
}, {
|
||||||
icon: 'flag',
|
icon: 'flag',
|
||||||
text: 'ReportChat',
|
text: 'ReportChat',
|
||||||
|
@ -574,6 +564,30 @@ export default class ChatContextMenu {
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private canDownload(message: MyMessage, withTarget?: boolean) {
|
||||||
|
if(!canSaveMessageMedia(message) || this.noForwards) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPhoto: boolean = !!((message as Message.message).media as MessageMedia.messageMediaPhoto)?.photo;
|
||||||
|
let isGoodType = false
|
||||||
|
|
||||||
|
if(isPhoto) {
|
||||||
|
isGoodType = true;
|
||||||
|
} else {
|
||||||
|
const doc: MyDocument = ((message as Message.message).media as MessageMedia.messageMediaDocument)?.document as any;
|
||||||
|
if(!doc) return false;
|
||||||
|
// isGoodType = doc.type && (['gif', 'video', 'audio', 'voice', 'sticker'] as MyDocument['type'][]).includes(doc.type)
|
||||||
|
isGoodType = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasTarget = !withTarget || !!IS_TOUCH_SUPPORTED;
|
||||||
|
|
||||||
|
if(isGoodType) hasTarget ||= !!findUpClassName(this.target, 'document') || !!findUpClassName(this.target, 'audio') || !!findUpClassName(this.target, 'media-sticker-wrapper') || !!findUpClassName(this.target, 'media-photo') || !!findUpClassName(this.target, 'media-video');
|
||||||
|
|
||||||
|
return isGoodType && hasTarget;
|
||||||
|
}
|
||||||
|
|
||||||
private getMessageWithText() {
|
private getMessageWithText() {
|
||||||
return (this.albumMessages && getAlbumText(this.albumMessages)) || this.message;
|
return (this.albumMessages && getAlbumText(this.albumMessages)) || this.message;
|
||||||
}
|
}
|
||||||
|
@ -932,7 +946,7 @@ export default class ChatContextMenu {
|
||||||
} else {
|
} else {
|
||||||
const peerId = this.peerId;
|
const peerId = this.peerId;
|
||||||
const mids = this.isTargetAGroupedItem ? [this.mid] : await this.chat.getMidsByMid(this.mid);
|
const mids = this.isTargetAGroupedItem ? [this.mid] : await this.chat.getMidsByMid(this.mid);
|
||||||
new PopupForward({
|
PopupForward.create({
|
||||||
[peerId]: mids
|
[peerId]: mids
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,8 @@ import generateQId from '../../lib/appManagers/utils/inlineBots/generateQId';
|
||||||
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
import appDownloadManager from '../../lib/appManagers/appDownloadManager';
|
||||||
import {AnimationItemGroup} from '../animationIntersector';
|
import {AnimationItemGroup} from '../animationIntersector';
|
||||||
import wrapPhoto from '../wrappers/photo';
|
import wrapPhoto from '../wrappers/photo';
|
||||||
|
import {i18n} from '../../lib/langPack';
|
||||||
|
import {POSTING_NOT_ALLOWED_MAP} from './input';
|
||||||
|
|
||||||
const ANIMATION_GROUP: AnimationItemGroup = 'INLINE-HELPER';
|
const ANIMATION_GROUP: AnimationItemGroup = 'INLINE-HELPER';
|
||||||
// const GRID_ITEMS = 5;
|
// const GRID_ITEMS = 5;
|
||||||
|
@ -39,7 +41,7 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||||
private gifsMasonry: GifsMasonry;
|
private gifsMasonry: GifsMasonry;
|
||||||
private superStickerRenderer: SuperStickerRenderer;
|
private superStickerRenderer: SuperStickerRenderer;
|
||||||
private onChangeScreen: () => void;
|
private onChangeScreen: () => void;
|
||||||
public checkQuery: (peerId: PeerId, username: string, query: string) => ReturnType<InlineHelper['_checkQuery']>;
|
public checkQuery: ReturnType<typeof debounce<InlineHelper['_checkQuery']>>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
appendTo: HTMLElement,
|
appendTo: HTMLElement,
|
||||||
|
@ -85,7 +87,7 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public _checkQuery = async(peerId: PeerId, username: string, query: string) => {
|
public _checkQuery = async(peerId: PeerId, username: string, query: string, canSendInline: boolean) => {
|
||||||
const middleware = this.controller.getMiddleware();
|
const middleware = this.controller.getMiddleware();
|
||||||
|
|
||||||
const peer = await this.managers.appUsersManager.resolveUsername(username);
|
const peer = await this.managers.appUsersManager.resolveUsername(username);
|
||||||
|
@ -97,6 +99,21 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||||
throw 'NOT_A_BOT';
|
throw 'NOT_A_BOT';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!canSendInline) {
|
||||||
|
if(!middleware()) {
|
||||||
|
throw 'PEER_CHANGED';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.init) {
|
||||||
|
this.init();
|
||||||
|
this.init = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.container.classList.add('cant-send');
|
||||||
|
this.toggle(false);
|
||||||
|
throw 'NO_INLINES';
|
||||||
|
}
|
||||||
|
|
||||||
const renderPromise = this.managers.appInlineBotsManager.getInlineResults(peerId, peer.id, query).then((botResults) => {
|
const renderPromise = this.managers.appInlineBotsManager.getInlineResults(peerId, peer.id, query).then((botResults) => {
|
||||||
if(!middleware()) {
|
if(!middleware()) {
|
||||||
throw 'PEER_CHANGED';
|
throw 'PEER_CHANGED';
|
||||||
|
@ -252,10 +269,9 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||||
parent.append(btnSwitchToPM);
|
parent.append(btnSwitchToPM);
|
||||||
}
|
}
|
||||||
parent.append(this.list = list);
|
parent.append(this.list = list);
|
||||||
|
this.container.classList.remove('cant-send');
|
||||||
|
|
||||||
if(this.gifsMasonry) {
|
this.gifsMasonry?.detach();
|
||||||
this.gifsMasonry.detach();
|
|
||||||
}
|
|
||||||
this.gifsMasonry = gifsMasonry;
|
this.gifsMasonry = gifsMasonry;
|
||||||
gifsMasonry.attach();
|
gifsMasonry.attach();
|
||||||
|
|
||||||
|
@ -290,5 +306,9 @@ export default class InlineHelper extends AutocompleteHelper {
|
||||||
this.scrollable = new Scrollable(this.container);
|
this.scrollable = new Scrollable(this.container);
|
||||||
this.lazyLoadQueue = new LazyLoadQueue();
|
this.lazyLoadQueue = new LazyLoadQueue();
|
||||||
this.superStickerRenderer = new SuperStickerRenderer(this.lazyLoadQueue, ANIMATION_GROUP, this.managers);
|
this.superStickerRenderer = new SuperStickerRenderer(this.lazyLoadQueue, ANIMATION_GROUP, this.managers);
|
||||||
|
|
||||||
|
const span = i18n(POSTING_NOT_ALLOWED_MAP['send_inline']);
|
||||||
|
span.classList.add('inline-helper-cant-send');
|
||||||
|
this.container.append(span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,9 +108,21 @@ import getAttachMenuBotIcon from '../../lib/appManagers/utils/attachMenuBots/get
|
||||||
import TelegramWebView from '../telegramWebView';
|
import TelegramWebView from '../telegramWebView';
|
||||||
import forEachReverse from '../../helpers/array/forEachReverse';
|
import forEachReverse from '../../helpers/array/forEachReverse';
|
||||||
import {MARKDOWN_ENTITIES} from '../../lib/richTextProcessor';
|
import {MARKDOWN_ENTITIES} from '../../lib/richTextProcessor';
|
||||||
|
import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport';
|
||||||
|
import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport';
|
||||||
|
import {ChatRights} from '../../lib/appManagers/appChatsManager';
|
||||||
|
|
||||||
const RECORD_MIN_TIME = 500;
|
const RECORD_MIN_TIME = 500;
|
||||||
const POSTING_MEDIA_NOT_ALLOWED = 'Posting media content isn\'t allowed in this group.';
|
|
||||||
|
export const POSTING_NOT_ALLOWED_MAP: {[action in ChatRights]?: LangPackKey} = {
|
||||||
|
send_voices: 'GlobalAttachVoiceRestricted',
|
||||||
|
send_stickers: 'GlobalAttachStickersRestricted',
|
||||||
|
send_gifs: 'GlobalAttachGifRestricted',
|
||||||
|
send_media: 'GlobalAttachMediaRestricted',
|
||||||
|
send_plain: 'GlobalSendMessageRestricted',
|
||||||
|
send_polls: 'ErrorSendRestrictedPollsAll',
|
||||||
|
send_inline: 'GlobalAttachInlineRestricted'
|
||||||
|
};
|
||||||
|
|
||||||
type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply';
|
type ChatInputHelperType = 'edit' | 'webpage' | 'forward' | 'reply';
|
||||||
|
|
||||||
|
@ -123,7 +135,7 @@ export default class ChatInput {
|
||||||
private inputMessageContainer: HTMLDivElement;
|
private inputMessageContainer: HTMLDivElement;
|
||||||
private btnSend: HTMLButtonElement;
|
private btnSend: HTMLButtonElement;
|
||||||
private btnCancelRecord: HTMLButtonElement;
|
private btnCancelRecord: HTMLButtonElement;
|
||||||
private lastUrl = '';
|
public lastUrl = '';
|
||||||
private lastTimeType = 0;
|
private lastTimeType = 0;
|
||||||
|
|
||||||
public chatInput: HTMLElement;
|
public chatInput: HTMLElement;
|
||||||
|
@ -160,7 +172,7 @@ export default class ChatInput {
|
||||||
private forwardWasDroppingAuthor: boolean;
|
private forwardWasDroppingAuthor: boolean;
|
||||||
|
|
||||||
private getWebPagePromise: Promise<void>;
|
private getWebPagePromise: Promise<void>;
|
||||||
private willSendWebPage: WebPage = null;
|
public willSendWebPage: WebPage = null;
|
||||||
private forwarding: {[fromPeerId: PeerId]: number[]};
|
private forwarding: {[fromPeerId: PeerId]: number[]};
|
||||||
public replyToMsgId: number;
|
public replyToMsgId: number;
|
||||||
public editMsgId: number;
|
public editMsgId: number;
|
||||||
|
@ -585,18 +597,37 @@ export default class ChatInput {
|
||||||
icon.classList.remove('state-back');
|
icon.classList.remove('state-back');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const getSendMediaRights = () => Promise.all([this.chat.canSend('send_photos'), this.chat.canSend('send_videos')]).then(([photos, videos]) => ({photos, videos}));
|
||||||
|
|
||||||
|
const onAttachMediaClick = (photos: boolean, videos: boolean) => {
|
||||||
|
this.fileInput.value = '';
|
||||||
|
|
||||||
|
const accept = [
|
||||||
|
...(photos ? IMAGE_MIME_TYPES_SUPPORTED : []),
|
||||||
|
...(videos ? VIDEO_MIME_TYPES_SUPPORTED : [])
|
||||||
|
].join(', ');
|
||||||
|
|
||||||
|
this.fileInput.setAttribute('accept', accept);
|
||||||
|
this.willAttachType = 'media';
|
||||||
|
this.fileInput.click();
|
||||||
|
};
|
||||||
|
|
||||||
this.attachMenuButtons = [{
|
this.attachMenuButtons = [{
|
||||||
icon: 'image',
|
icon: 'image',
|
||||||
text: 'Chat.Input.Attach.PhotoOrVideo',
|
text: 'Chat.Input.Attach.PhotoOrVideo',
|
||||||
onClick: () => {
|
onClick: () => onAttachMediaClick(true, true)
|
||||||
this.fileInput.value = '';
|
// verify: () => getSendMediaRights().then(({photos, videos}) => photos && videos)
|
||||||
const accept = [...MEDIA_MIME_TYPES_SUPPORTED].join(', ');
|
}, /* {
|
||||||
this.fileInput.setAttribute('accept', accept);
|
icon: 'image',
|
||||||
this.willAttachType = 'media';
|
text: 'AttachPhoto',
|
||||||
this.fileInput.click();
|
onClick: () => onAttachMediaClick(true, false),
|
||||||
},
|
verify: () => getSendMediaRights().then(({photos, videos}) => photos && !videos)
|
||||||
verify: () => this.chat.canSend('send_media')
|
|
||||||
}, {
|
}, {
|
||||||
|
icon: 'image',
|
||||||
|
text: 'AttachVideo',
|
||||||
|
onClick: () => onAttachMediaClick(false, true),
|
||||||
|
verify: () => getSendMediaRights().then(({photos, videos}) => !photos && videos)
|
||||||
|
}, */ {
|
||||||
icon: 'document',
|
icon: 'document',
|
||||||
text: 'Chat.Input.Attach.Document',
|
text: 'Chat.Input.Attach.Document',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
|
@ -604,17 +635,26 @@ export default class ChatInput {
|
||||||
this.fileInput.removeAttribute('accept');
|
this.fileInput.removeAttribute('accept');
|
||||||
this.willAttachType = 'document';
|
this.willAttachType = 'document';
|
||||||
this.fileInput.click();
|
this.fileInput.click();
|
||||||
},
|
}
|
||||||
verify: () => this.chat.canSend('send_media')
|
// verify: () => this.chat.canSend('send_docs')
|
||||||
}, {
|
}, {
|
||||||
icon: 'poll',
|
icon: 'poll',
|
||||||
text: 'Poll',
|
text: 'Poll',
|
||||||
onClick: () => {
|
onClick: async() => {
|
||||||
|
const action: ChatRights = 'send_polls';
|
||||||
|
if(!(await this.chat.canSend(action))) {
|
||||||
|
toastNew({langPackKey: POSTING_NOT_ALLOWED_MAP[action]});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PopupElement.createPopup(PopupCreatePoll, this.chat).show();
|
PopupElement.createPopup(PopupCreatePoll, this.chat).show();
|
||||||
},
|
},
|
||||||
verify: () => (this.chat.peerId.isAnyChat() || this.chat.isBot) && this.chat.canSend('send_polls')
|
verify: () => this.chat.peerId.isAnyChat() || this.chat.isBot
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
// preload the bots
|
||||||
|
this.managers.appAttachMenuBotsManager.getAttachMenuBots();
|
||||||
|
|
||||||
const attachMenuButtons = this.attachMenuButtons.slice();
|
const attachMenuButtons = this.attachMenuButtons.slice();
|
||||||
this.attachMenu = ButtonMenuToggle({
|
this.attachMenu = ButtonMenuToggle({
|
||||||
buttonOptions: {noRipple: true},
|
buttonOptions: {noRipple: true},
|
||||||
|
@ -1368,6 +1408,7 @@ export default class ChatInput {
|
||||||
canPinMessage,
|
canPinMessage,
|
||||||
isBot,
|
isBot,
|
||||||
canSend,
|
canSend,
|
||||||
|
canSendPlain,
|
||||||
neededFakeContainer,
|
neededFakeContainer,
|
||||||
ackedPeerFull,
|
ackedPeerFull,
|
||||||
ackedScheduledMids,
|
ackedScheduledMids,
|
||||||
|
@ -1377,7 +1418,8 @@ export default class ChatInput {
|
||||||
this.managers.appPeersManager.isBroadcast(peerId),
|
this.managers.appPeersManager.isBroadcast(peerId),
|
||||||
this.managers.appPeersManager.canPinMessage(peerId),
|
this.managers.appPeersManager.canPinMessage(peerId),
|
||||||
this.managers.appPeersManager.isBot(peerId),
|
this.managers.appPeersManager.isBot(peerId),
|
||||||
this.chat.canSend(),
|
this.chat.canSend('send_messages'),
|
||||||
|
this.chat.canSend('send_plain'),
|
||||||
this.getNeededFakeContainer(startParam),
|
this.getNeededFakeContainer(startParam),
|
||||||
modifyAckedPromise(this.managers.acknowledged.appProfileManager.getProfileByPeerId(peerId)),
|
modifyAckedPromise(this.managers.acknowledged.appProfileManager.getProfileByPeerId(peerId)),
|
||||||
btnScheduled ? modifyAckedPromise(this.managers.acknowledged.appMessagesManager.getScheduledMessages(peerId)) : undefined,
|
btnScheduled ? modifyAckedPromise(this.managers.acknowledged.appMessagesManager.getScheduledMessages(peerId)) : undefined,
|
||||||
|
@ -1385,7 +1427,7 @@ export default class ChatInput {
|
||||||
this.filterAttachMenuButtons()
|
this.filterAttachMenuButtons()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const placeholderKey = this.messageInput ? await this.getPlaceholderKey() : undefined;
|
const placeholderKey = this.messageInput ? await this.getPlaceholderKey(canSendPlain) : undefined;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// console.warn('[input] finishpeerchange start');
|
// console.warn('[input] finishpeerchange start');
|
||||||
|
@ -1394,7 +1436,6 @@ export default class ChatInput {
|
||||||
goDownBtn.classList.toggle('is-broadcast', isBroadcast);
|
goDownBtn.classList.toggle('is-broadcast', isBroadcast);
|
||||||
goDownBtn.classList.remove('hide');
|
goDownBtn.classList.remove('hide');
|
||||||
|
|
||||||
this.messageInputField?.onFakeInput();
|
|
||||||
|
|
||||||
if(this.goDownUnreadBadge) {
|
if(this.goDownUnreadBadge) {
|
||||||
this.setUnreadCount();
|
this.setUnreadCount();
|
||||||
|
@ -1439,27 +1480,18 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(previousSendAs) {
|
previousSendAs?.destroy();
|
||||||
previousSendAs.destroy();
|
setSendAsCallback?.();
|
||||||
}
|
replyKeyboard?.setPeer(peerId);
|
||||||
|
sendMenu?.setPeerId(peerId);
|
||||||
if(setSendAsCallback) {
|
|
||||||
setSendAsCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(replyKeyboard) {
|
|
||||||
replyKeyboard.setPeer(peerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sendMenu) {
|
|
||||||
sendMenu.setPeerId(peerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.messageInput) {
|
if(this.messageInput) {
|
||||||
this.updateMessageInput(canSend, placeholderKey, filteredAttachMenuButtons);
|
this.updateMessageInput(canSend, canSendPlain, placeholderKey, filteredAttachMenuButtons);
|
||||||
this.messageInput.dataset.peerId = '' + peerId;
|
this.messageInput.dataset.peerId = '' + peerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.messageInputField?.onFakeInput(undefined, true);
|
||||||
|
|
||||||
let haveSomethingInControl = false;
|
let haveSomethingInControl = false;
|
||||||
if(this.pinnedControlBtn) {
|
if(this.pinnedControlBtn) {
|
||||||
const good = this.chat.type === 'pinned';
|
const good = this.chat.type === 'pinned';
|
||||||
|
@ -1530,10 +1562,13 @@ export default class ChatInput {
|
||||||
this.updateOffset('commands', forwards, skipAnimation, useRafs);
|
this.updateOffset('commands', forwards, skipAnimation, useRafs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPlaceholderKey() {
|
private async getPlaceholderKey(canSend?: boolean) {
|
||||||
|
canSend ??= await this.chat.canSend('send_plain');
|
||||||
const {peerId, threadId, isForum} = this.chat;
|
const {peerId, threadId, isForum} = this.chat;
|
||||||
let key: LangPackKey;
|
let key: LangPackKey;
|
||||||
if(threadId && !isForum) {
|
if(!canSend) {
|
||||||
|
key = 'Channel.Persmission.MessageBlock';
|
||||||
|
} else if(threadId && !isForum) {
|
||||||
key = 'Comment';
|
key = 'Comment';
|
||||||
} else if(await this.managers.appPeersManager.isBroadcast(peerId)) {
|
} else if(await this.managers.appPeersManager.isBroadcast(peerId)) {
|
||||||
key = 'ChannelBroadcast';
|
key = 'ChannelBroadcast';
|
||||||
|
@ -1562,13 +1597,17 @@ export default class ChatInput {
|
||||||
private filterAttachMenuButtons() {
|
private filterAttachMenuButtons() {
|
||||||
if(!this.attachMenuButtons) return;
|
if(!this.attachMenuButtons) return;
|
||||||
return filterAsync(this.attachMenuButtons, (button) => {
|
return filterAsync(this.attachMenuButtons, (button) => {
|
||||||
return button.verify();
|
return button.verify ? button.verify() : true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMessageInput(canSend: boolean, placeholderKey: LangPackKey, visible: ChatInput['attachMenuButtons']) {
|
public updateMessageInput(
|
||||||
|
canSend: boolean,
|
||||||
|
canSendPlain: boolean,
|
||||||
|
placeholderKey: LangPackKey,
|
||||||
|
visible: ChatInput['attachMenuButtons']
|
||||||
|
) {
|
||||||
const {chatInput, attachMenu, messageInput} = this;
|
const {chatInput, attachMenu, messageInput} = this;
|
||||||
const {peerId, threadId} = this.chat;
|
|
||||||
const isHidden = chatInput.classList.contains('is-hidden');
|
const isHidden = chatInput.classList.contains('is-hidden');
|
||||||
const willBeHidden = !canSend;
|
const willBeHidden = !canSend;
|
||||||
if(isHidden !== willBeHidden) {
|
if(isHidden !== willBeHidden) {
|
||||||
|
@ -1580,14 +1619,18 @@ export default class ChatInput {
|
||||||
|
|
||||||
this.updateMessageInputPlaceholder(placeholderKey);
|
this.updateMessageInputPlaceholder(placeholderKey);
|
||||||
|
|
||||||
if(!canSend) {
|
if(!canSend || !canSendPlain) {
|
||||||
messageInput.contentEditable = 'inherit';
|
messageInput.contentEditable = 'false';
|
||||||
|
|
||||||
|
if(!canSendPlain) {
|
||||||
|
this.messageInputField.onFakeInput(undefined, true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
messageInput.contentEditable = 'true';
|
messageInput.contentEditable = 'true';
|
||||||
this.setDraft(undefined, false);
|
this.setDraft(undefined, false);
|
||||||
|
|
||||||
if(!messageInput.innerHTML) {
|
if(!messageInput.innerHTML) {
|
||||||
this.messageInputField.onFakeInput();
|
this.messageInputField.onFakeInput(undefined, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1651,6 +1694,14 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
attachClickEvent(this.messageInput, (e) => {
|
||||||
|
if(!this.canSendPlain()) {
|
||||||
|
toastNew({
|
||||||
|
langPackKey: POSTING_NOT_ALLOWED_MAP['send_plain']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {listenerSetter: this.listenerSetter});
|
||||||
|
|
||||||
if(IS_TOUCH_SUPPORTED) {
|
if(IS_TOUCH_SUPPORTED) {
|
||||||
attachClickEvent(this.messageInput, (e) => {
|
attachClickEvent(this.messageInput, (e) => {
|
||||||
if(emoticonsDropdown.isActive()) {
|
if(emoticonsDropdown.isActive()) {
|
||||||
|
@ -1706,6 +1757,10 @@ export default class ChatInput {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public canSendPlain() {
|
||||||
|
return !(!this.messageInput.isContentEditable && !this.chatInput.classList.contains('is-hidden'));
|
||||||
|
}
|
||||||
|
|
||||||
private prepareDocumentExecute = () => {
|
private prepareDocumentExecute = () => {
|
||||||
this.executedHistory.push(this.messageInput.innerHTML);
|
this.executedHistory.push(this.messageInput.innerHTML);
|
||||||
return () => this.canUndoFromHTML = this.messageInput.innerHTML;
|
return () => this.canUndoFromHTML = this.messageInput.innerHTML;
|
||||||
|
@ -1877,9 +1932,7 @@ export default class ChatInput {
|
||||||
|
|
||||||
// checkForSingle();
|
// checkForSingle();
|
||||||
// saveExecuted();
|
// saveExecuted();
|
||||||
if(this.appImManager.markupTooltip) {
|
this.appImManager.markupTooltip?.setActiveMarkupButton();
|
||||||
this.appImManager.markupTooltip.setActiveMarkupButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(textNode) {
|
if(textNode) {
|
||||||
(textNode.parentElement === this.messageInput ? textNode : textNode.parentElement).remove();
|
(textNode.parentElement === this.messageInput ? textNode : textNode.parentElement).remove();
|
||||||
|
@ -1957,7 +2010,7 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onMessageInput = (e?: Event) => {
|
public onMessageInput = (e?: Event) => {
|
||||||
// * validate due to manual formatting through browser's context menu
|
// * validate due to manual formatting through browser's context menu
|
||||||
/* const inputType = (e as InputEvent).inputType;
|
/* const inputType = (e as InputEvent).inputType;
|
||||||
console.log('message input event', e);
|
console.log('message input event', e);
|
||||||
|
@ -1999,17 +2052,15 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log('messageInput url:', url);
|
|
||||||
|
|
||||||
if(this.lastUrl !== url) {
|
if(this.lastUrl !== url) {
|
||||||
this.lastUrl = url;
|
this.lastUrl = url;
|
||||||
// this.willSendWebPage = null;
|
const promise = this.getWebPagePromise = Promise.all([
|
||||||
const promise = this.getWebPagePromise = this.managers.appWebPagesManager.getWebPage(url).then((webpage) => {
|
this.managers.appWebPagesManager.getWebPage(url),
|
||||||
|
this.chat.canSend('embed_links')
|
||||||
|
]).then(([webpage, canEmbedLinks]) => {
|
||||||
if(this.getWebPagePromise === promise) this.getWebPagePromise = undefined;
|
if(this.getWebPagePromise === promise) this.getWebPagePromise = undefined;
|
||||||
if(this.lastUrl !== url) return;
|
if(this.lastUrl !== url) return;
|
||||||
if(webpage._ === 'webPage') {
|
if(webpage._ === 'webPage' && canEmbedLinks) {
|
||||||
// console.log('got webpage: ', webpage);
|
|
||||||
|
|
||||||
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title || 'Webpage', webpage.description || webpage.url || '');
|
this.setTopInfo('webpage', () => {}, webpage.site_name || webpage.title || 'Webpage', webpage.description || webpage.url || '');
|
||||||
delete this.noWebPage;
|
delete this.noWebPage;
|
||||||
this.willSendWebPage = webpage;
|
this.willSendWebPage = webpage;
|
||||||
|
@ -2039,31 +2090,27 @@ export default class ChatInput {
|
||||||
this.managers.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageCancelAction'}, undefined, this.chat.threadId);
|
this.managers.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageCancelAction'}, undefined, this.chat.threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.appImManager.markupTooltip) {
|
this.appImManager.markupTooltip?.hide();
|
||||||
this.appImManager.markupTooltip.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// * Chrome has a bug - it will preserve the formatting if the input with monospace text is cleared
|
// * Chrome has a bug - it will preserve the formatting if the input with monospace text is cleared
|
||||||
// * so have to reset formatting
|
// * so have to reset formatting
|
||||||
if(document.activeElement === this.messageInput) {
|
if(document.activeElement === this.messageInput && !IS_MOBILE) {
|
||||||
// document.execCommand('styleWithCSS', false, 'true');
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if(document.activeElement === this.messageInput) {
|
if(document.activeElement === this.messageInput) {
|
||||||
this.resetCurrentFontFormatting();
|
this.messageInput.textContent = '1';
|
||||||
|
placeCaretAtEnd(this.messageInput);
|
||||||
|
this.messageInput.textContent = '';
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
// document.execCommand('styleWithCSS', false, 'false');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const time = Date.now();
|
const time = Date.now();
|
||||||
if((time - this.lastTimeType) >= 6000) {
|
if((time - this.lastTimeType) >= 6000 && e?.isTrusted) {
|
||||||
this.lastTimeType = time;
|
this.lastTimeType = time;
|
||||||
this.managers.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageTypingAction'}, undefined, this.chat.threadId);
|
this.managers.appMessagesManager.setTyping(this.chat.peerId, {_: 'sendMessageTypingAction'}, undefined, this.chat.threadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.botCommands) {
|
this.botCommands?.toggle(true);
|
||||||
this.botCommands.toggle(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.botCommands) {
|
if(this.botCommands) {
|
||||||
|
@ -2080,6 +2127,13 @@ export default class ChatInput {
|
||||||
};
|
};
|
||||||
|
|
||||||
public insertAtCaret(insertText: string, insertEntity?: MessageEntity, isHelper = true) {
|
public insertAtCaret(insertText: string, insertEntity?: MessageEntity, isHelper = true) {
|
||||||
|
if(!this.canSendPlain()) {
|
||||||
|
toastNew({
|
||||||
|
langPackKey: POSTING_NOT_ALLOWED_MAP['send_plain']
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RichInputHandler.getInstance().makeFocused(this.messageInput);
|
RichInputHandler.getInstance().makeFocused(this.messageInput);
|
||||||
|
|
||||||
const {value: fullValue, caretPos, entities} = getRichValueWithCaret(this.messageInput);
|
const {value: fullValue, caretPos, entities} = getRichValueWithCaret(this.messageInput);
|
||||||
|
@ -2192,7 +2246,12 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
public onEmojiSelected = (emoji: ReturnType<typeof getEmojiFromElement>, autocomplete: boolean) => {
|
public onEmojiSelected = (emoji: ReturnType<typeof getEmojiFromElement>, autocomplete: boolean) => {
|
||||||
const entity: MessageEntity = emoji.docId ? {_: 'messageEntityCustomEmoji', document_id: emoji.docId, length: emoji.emoji.length, offset: 0} : getEmojiEntityFromEmoji(emoji.emoji);
|
const entity: MessageEntity = emoji.docId ? {
|
||||||
|
_: 'messageEntityCustomEmoji',
|
||||||
|
document_id: emoji.docId,
|
||||||
|
length: emoji.emoji.length,
|
||||||
|
offset: 0
|
||||||
|
} : getEmojiEntityFromEmoji(emoji.emoji);
|
||||||
this.insertAtCaret(emoji.emoji, entity, autocomplete);
|
this.insertAtCaret(emoji.emoji, entity, autocomplete);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2255,12 +2314,17 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foundHelper = this.checkInlineAutocomplete(value, foundHelper);
|
let canSendInline: boolean;
|
||||||
|
if(!foundHelper) {
|
||||||
|
canSendInline = await this.chat.canSend('send_inline');
|
||||||
|
}
|
||||||
|
|
||||||
|
foundHelper = this.checkInlineAutocomplete(value, canSendInline, foundHelper);
|
||||||
|
|
||||||
this.autocompleteHelperController.hideOtherHelpers(foundHelper);
|
this.autocompleteHelperController.hideOtherHelpers(foundHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkInlineAutocomplete(value: string, foundHelper?: AutocompleteHelper): AutocompleteHelper {
|
private checkInlineAutocomplete(value: string, canSendInline: boolean, foundHelper?: AutocompleteHelper): AutocompleteHelper {
|
||||||
let needPlaceholder = false;
|
let needPlaceholder = false;
|
||||||
|
|
||||||
const setPreloaderShow = (show: boolean) => {
|
const setPreloaderShow = (show: boolean) => {
|
||||||
|
@ -2268,6 +2332,10 @@ export default class ChatInput {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(show && !canSendInline) {
|
||||||
|
show = false;
|
||||||
|
}
|
||||||
|
|
||||||
SetTransition({
|
SetTransition({
|
||||||
element: this.btnPreloader,
|
element: this.btnPreloader,
|
||||||
className: 'show',
|
className: 'show',
|
||||||
|
@ -2293,7 +2361,7 @@ export default class ChatInput {
|
||||||
setPreloaderShow(true);
|
setPreloaderShow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inlineHelper.checkQuery(this.chat.peerId, username, query).then(({user, renderPromise}) => {
|
this.inlineHelper.checkQuery(this.chat.peerId, username, query, canSendInline).then(({user, renderPromise}) => {
|
||||||
if(needPlaceholder && user.bot_inline_placeholder) {
|
if(needPlaceholder && user.bot_inline_placeholder) {
|
||||||
this.messageInput.dataset.inlinePlaceholder = user.bot_inline_placeholder;
|
this.messageInput.dataset.inlinePlaceholder = user.bot_inline_placeholder;
|
||||||
}
|
}
|
||||||
|
@ -2348,8 +2416,9 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const isAnyChat = this.chat.peerId.isAnyChat();
|
const isAnyChat = this.chat.peerId.isAnyChat();
|
||||||
if(isAnyChat && !(await this.chat.canSend('send_media'))) {
|
const flag: ChatRights = 'send_voices';
|
||||||
toast(POSTING_MEDIA_NOT_ALLOWED);
|
if(isAnyChat && !(await this.chat.canSend(flag))) {
|
||||||
|
toastNew({langPackKey: POSTING_NOT_ALLOWED_MAP[flag]});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2477,7 +2546,7 @@ export default class ChatInput {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHelperCancel = async(e?: Event, force?: boolean) => {
|
public onHelperCancel = async(e?: Event, force?: boolean) => {
|
||||||
if(e) {
|
if(e) {
|
||||||
cancelEvent(e);
|
cancelEvent(e);
|
||||||
}
|
}
|
||||||
|
@ -2770,7 +2839,7 @@ export default class ChatInput {
|
||||||
|
|
||||||
const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media');
|
const flag = document.type === 'sticker' ? 'send_stickers' : (document.type === 'gif' ? 'send_gifs' : 'send_media');
|
||||||
if(this.chat.peerId.isAnyChat() && !(await this.chat.canSend(flag))) {
|
if(this.chat.peerId.isAnyChat() && !(await this.chat.canSend(flag))) {
|
||||||
toast(POSTING_MEDIA_NOT_ALLOWED);
|
toastNew({langPackKey: POSTING_NOT_ALLOWED_MAP[flag]});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3111,31 +3180,4 @@ export default class ChatInput {
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public saveScroll() {
|
|
||||||
// this.scrollTop = this.chat.bubbles.scrollable.container.scrollTop;
|
|
||||||
// this.scrollOffsetTop = this.chatInput.offsetTop;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public restoreScroll() {
|
|
||||||
// if(this.chatInput.style.display) return;
|
|
||||||
// //console.log('input resize', offsetTop, this.chatInput.offsetTop);
|
|
||||||
// let newOffsetTop = this.chatInput.offsetTop;
|
|
||||||
// let container = this.chat.bubbles.scrollable.container;
|
|
||||||
// let scrollTop = container.scrollTop;
|
|
||||||
// let clientHeight = container.clientHeight;
|
|
||||||
// let maxScrollTop = container.scrollHeight;
|
|
||||||
|
|
||||||
// if(newOffsetTop < this.scrollOffsetTop) {
|
|
||||||
// this.scrollDiff = this.scrollOffsetTop - newOffsetTop;
|
|
||||||
// container.scrollTop += this.scrollDiff;
|
|
||||||
// } else if(scrollTop !== this.scrollTop) {
|
|
||||||
// let endDiff = maxScrollTop - (scrollTop + clientHeight);
|
|
||||||
// if(endDiff < this.scrollDiff/* && false */) {
|
|
||||||
// //container.scrollTop -= endDiff;
|
|
||||||
// } else {
|
|
||||||
// container.scrollTop -= this.scrollDiff;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import replaceContent from '../../helpers/dom/replaceContent';
|
import replaceContent from '../../helpers/dom/replaceContent';
|
||||||
import {Middleware} from '../../helpers/middleware';
|
import {Middleware} from '../../helpers/middleware';
|
||||||
import limitSymbols from '../../helpers/string/limitSymbols';
|
import limitSymbols from '../../helpers/string/limitSymbols';
|
||||||
import {Document, Message, MessageMedia, Photo, WebPage} from '../../layer';
|
import {Document, Message, MessageMedia, Photo, WebPage, VideoSize} from '../../layer';
|
||||||
import appImManager from '../../lib/appManagers/appImManager';
|
import appImManager from '../../lib/appManagers/appImManager';
|
||||||
import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize';
|
import choosePhotoSize from '../../lib/appManagers/utils/photos/choosePhotoSize';
|
||||||
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
import wrapEmojiText from '../../lib/richTextProcessor/wrapEmojiText';
|
||||||
|
@ -85,7 +85,7 @@ export async function wrapReplyDivAndCaption(options: {
|
||||||
middleware,
|
middleware,
|
||||||
loadPromises,
|
loadPromises,
|
||||||
withoutPreloader: true,
|
withoutPreloader: true,
|
||||||
videoSize: document.video_thumbs[0],
|
videoSize: document.video_thumbs[0] as Extract<VideoSize, VideoSize.videoSize>,
|
||||||
group: animationGroup
|
group: animationGroup
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,6 +37,7 @@ import {AppManagers} from '../../lib/appManagers/managers';
|
||||||
import {attachContextMenuListener} from '../../helpers/dom/attachContextMenuListener';
|
import {attachContextMenuListener} from '../../helpers/dom/attachContextMenuListener';
|
||||||
import filterUnique from '../../helpers/array/filterUnique';
|
import filterUnique from '../../helpers/array/filterUnique';
|
||||||
import appImManager from '../../lib/appManagers/appImManager';
|
import appImManager from '../../lib/appManagers/appImManager';
|
||||||
|
import {Message} from '../../layer';
|
||||||
|
|
||||||
const accumulateMapSet = (map: Map<any, Set<number>>) => {
|
const accumulateMapSet = (map: Map<any, Set<number>>) => {
|
||||||
return [...map.values()].reduce((acc, v) => acc + v.size, 0);
|
return [...map.values()].reduce((acc, v) => acc + v.size, 0);
|
||||||
|
@ -360,7 +361,7 @@ class AppSelection extends EventListenerBase<{
|
||||||
cantDelete = !size;
|
cantDelete = !size;
|
||||||
const cantSend = !size;
|
const cantSend = !size;
|
||||||
for(const [peerId, mids] of this.selectedMids) {
|
for(const [peerId, mids] of this.selectedMids) {
|
||||||
const storageKey: MessagesStorageKey = `${peerId}_${this.isScheduled ? 'scheduled' : 'history'}`;
|
const storageKey = this.getStorageKey(peerId);
|
||||||
const r = await this.managers.appMessagesManager.cantForwardDeleteMids(storageKey, Array.from(mids));
|
const r = await this.managers.appMessagesManager.cantForwardDeleteMids(storageKey, Array.from(mids));
|
||||||
cantForward = r.cantForward;
|
cantForward = r.cantForward;
|
||||||
cantDelete = r.cantDelete;
|
cantDelete = r.cantDelete;
|
||||||
|
@ -371,6 +372,20 @@ class AppSelection extends EventListenerBase<{
|
||||||
this.onUpdateContainer?.(cantForward, cantDelete, cantSend);
|
this.onUpdateContainer?.(cantForward, cantDelete, cantSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getStorageKey(peerId: PeerId): MessagesStorageKey {
|
||||||
|
return `${peerId}_${this.isScheduled ? 'scheduled' : 'history'}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSelectedMessages() {
|
||||||
|
const selectedMessagesPromises: Promise<Message.message | Message.messageService>[] = [];
|
||||||
|
this.selectedMids.forEach((mids, peerId) => {
|
||||||
|
const storageKey = this.getStorageKey(peerId);
|
||||||
|
const p = Array.from(mids).map((mid) => this.managers.appMessagesManager.getMessageFromStorage(storageKey, mid));
|
||||||
|
selectedMessagesPromises.push(...p);
|
||||||
|
});
|
||||||
|
return Promise.all(selectedMessagesPromises);
|
||||||
|
}
|
||||||
|
|
||||||
public toggleSelection(toggleCheckboxes = true, forceSelection = false) {
|
public toggleSelection(toggleCheckboxes = true, forceSelection = false) {
|
||||||
const wasSelecting = this.isSelecting;
|
const wasSelecting = this.isSelecting;
|
||||||
const size = this.selectedMids.size;
|
const size = this.selectedMids.size;
|
||||||
|
@ -988,9 +1003,10 @@ export default class ChatSelection extends AppSelection {
|
||||||
void this.selectionInputWrapper.offsetLeft; // reflow
|
void this.selectionInputWrapper.offsetLeft; // reflow
|
||||||
// background.style.opacity = '';
|
// background.style.opacity = '';
|
||||||
this.selectionInputWrapper.style.opacity = '';
|
this.selectionInputWrapper.style.opacity = '';
|
||||||
left.style.transform = '';
|
|
||||||
right.style.transform = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.selectionLeft.style.transform = '';
|
||||||
|
this.selectionRight.style.transform = '';
|
||||||
} else if(this.selectionLeft && translateButtonsX !== undefined) {
|
} else if(this.selectionLeft && translateButtonsX !== undefined) {
|
||||||
this.selectionLeft.style.transform = `translateX(-${translateButtonsX}px)`;
|
this.selectionLeft.style.transform = `translateX(-${translateButtonsX}px)`;
|
||||||
this.selectionRight.style.transform = `translateX(${translateButtonsX}px)`;
|
this.selectionRight.style.transform = `translateX(${translateButtonsX}px)`;
|
||||||
|
|
|
@ -448,7 +448,7 @@ export default class ChatTopbar {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
placeholder: 'ShareModal.Search.Placeholder',
|
placeholder: 'ShareModal.Search.Placeholder',
|
||||||
chatRightsAction: 'send_messages',
|
chatRightsActions: ['send_plain'],
|
||||||
selfPresence: 'ChatYourSelf'
|
selfPresence: 'ChatYourSelf'
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default class CheckboxField {
|
||||||
const label = this.label = document.createElement('label');
|
const label = this.label = document.createElement('label');
|
||||||
label.classList.add('checkbox-field');
|
label.classList.add('checkbox-field');
|
||||||
|
|
||||||
if(options.restriction) {
|
if(options.restriction && !options.toggle) {
|
||||||
label.classList.add('checkbox-field-restriction');
|
label.classList.add('checkbox-field-restriction');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,10 @@ export default class CheckboxField {
|
||||||
if(options.toggle) {
|
if(options.toggle) {
|
||||||
label.classList.add('checkbox-field-toggle');
|
label.classList.add('checkbox-field-toggle');
|
||||||
|
|
||||||
|
if(options.restriction) {
|
||||||
|
label.classList.add('checkbox-field-toggle-restriction');
|
||||||
|
}
|
||||||
|
|
||||||
const toggle = document.createElement('div');
|
const toggle = document.createElement('div');
|
||||||
toggle.classList.add('checkbox-toggle');
|
toggle.classList.add('checkbox-toggle');
|
||||||
label.append(toggle);
|
label.append(toggle);
|
||||||
|
|
|
@ -37,6 +37,9 @@ import BezierEasing from '../../vendor/bezier-easing';
|
||||||
import RichInputHandler from '../../helpers/dom/richInputHandler';
|
import RichInputHandler from '../../helpers/dom/richInputHandler';
|
||||||
import {getCaretPosF} from '../../helpers/dom/getCaretPosNew';
|
import {getCaretPosF} from '../../helpers/dom/getCaretPosNew';
|
||||||
import ListenerSetter from '../../helpers/listenerSetter';
|
import ListenerSetter from '../../helpers/listenerSetter';
|
||||||
|
import {ChatRights} from '../../lib/appManagers/appChatsManager';
|
||||||
|
import {toastNew} from '../toast';
|
||||||
|
import {POSTING_NOT_ALLOWED_MAP} from '../chat/input';
|
||||||
|
|
||||||
export const EMOTICONSSTICKERGROUP: AnimationItemGroup = 'emoticons-dropdown';
|
export const EMOTICONSSTICKERGROUP: AnimationItemGroup = 'emoticons-dropdown';
|
||||||
|
|
||||||
|
@ -79,12 +82,19 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||||
private savedRange: Range;
|
private savedRange: Range;
|
||||||
private managers: AppManagers;
|
private managers: AppManagers;
|
||||||
|
|
||||||
|
private rights: {[action in ChatRights]?: boolean};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
element: document.getElementById('emoji-dropdown') as HTMLDivElement,
|
element: document.getElementById('emoji-dropdown') as HTMLDivElement,
|
||||||
ignoreOutClickClassName: 'input-message-input'
|
ignoreOutClickClassName: 'input-message-input'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.rights = {
|
||||||
|
send_gifs: undefined,
|
||||||
|
send_stickers: undefined
|
||||||
|
};
|
||||||
|
|
||||||
this.addEventListener('open', async() => {
|
this.addEventListener('open', async() => {
|
||||||
if(IS_TOUCH_SUPPORTED) {
|
if(IS_TOUCH_SUPPORTED) {
|
||||||
// appImManager.chat.input.saveScroll();
|
// appImManager.chat.input.saveScroll();
|
||||||
|
@ -322,6 +332,17 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rights: {[tabId: number]: ChatRights} = {
|
||||||
|
[this.stickersTab.tabId]: 'send_stickers',
|
||||||
|
[this.gifsTab.tabId]: 'send_gifs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const action = rights[id];
|
||||||
|
if(action && !this.rights[action]) {
|
||||||
|
toastNew({langPackKey: POSTING_NOT_ALLOWED_MAP[action]});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
|
animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
|
||||||
|
|
||||||
this.tabId = id;
|
this.tabId = id;
|
||||||
|
@ -331,19 +352,19 @@ export class EmoticonsDropdown extends DropdownHover {
|
||||||
|
|
||||||
private checkRights = async() => {
|
private checkRights = async() => {
|
||||||
const {peerId, threadId} = appImManager.chat;
|
const {peerId, threadId} = appImManager.chat;
|
||||||
const children = this.tabsEl.children;
|
|
||||||
const tabsElements = Array.from(children) as HTMLElement[];
|
|
||||||
|
|
||||||
const [canSendStickers, canSendGifs] = await Promise.all([
|
const actions = Object.keys(this.rights) as ChatRights[];
|
||||||
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_stickers'),
|
|
||||||
this.managers.appMessagesManager.canSendToPeer(peerId, threadId, 'send_gifs')
|
|
||||||
]);
|
|
||||||
|
|
||||||
tabsElements[this.stickersTab.tabId + 1].toggleAttribute('disabled', !canSendStickers);
|
const rights = await Promise.all(actions.map((action) => {
|
||||||
tabsElements[this.gifsTab.tabId + 1].toggleAttribute('disabled', !canSendGifs);
|
return this.managers.appMessagesManager.canSendToPeer(peerId, threadId, action);
|
||||||
|
}));
|
||||||
|
|
||||||
|
actions.forEach((action, idx) => {
|
||||||
|
this.rights[action] = rights[idx];
|
||||||
|
});
|
||||||
|
|
||||||
const active = this.tabsEl.querySelector('.active');
|
const active = this.tabsEl.querySelector('.active');
|
||||||
if(active && whichChild(active) !== (this.emojiTab.tabId + 1) && (!canSendStickers || !canSendGifs)) {
|
if(active && whichChild(active) !== (this.emojiTab.tabId + 1) && (!this.rights['send_stickers'] || !this.rights['send_gifs'])) {
|
||||||
this.selectTab(this.emojiTab.tabId, false);
|
this.selectTab(this.emojiTab.tabId, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -177,7 +177,7 @@ let init = () => {
|
||||||
|
|
||||||
const peerId = (input.dataset.peerId || NULL_PEER_ID).toPeerId();
|
const peerId = (input.dataset.peerId || NULL_PEER_ID).toPeerId();
|
||||||
if(html.trim()) {
|
if(html.trim()) {
|
||||||
console.log(html.replace(/ (style|class|id)=".+?"/g, ''));
|
// console.log(html.replace(/ (style|class|id)=".+?"/g, ''));
|
||||||
|
|
||||||
html = html.replace(/<style([\s\S]*)<\/style>/, '');
|
html = html.replace(/<style([\s\S]*)<\/style>/, '');
|
||||||
html = html.replace(/<!--([\s\S]*)-->/, '');
|
html = html.replace(/<!--([\s\S]*)-->/, '');
|
||||||
|
@ -260,7 +260,7 @@ let init = () => {
|
||||||
mergeEntities(entities, entities2);
|
mergeEntities(entities, entities2);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('usePlainText', usePlainText);
|
// console.log('usePlainText', usePlainText);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(usePlainText) {
|
if(usePlainText) {
|
||||||
|
|
|
@ -42,20 +42,22 @@ export default class InputFieldAnimated extends InputField {
|
||||||
this.inputFake.className = this.input.className + ' input-field-input-fake';
|
this.inputFake.className = this.input.className + ' input-field-input-fake';
|
||||||
}
|
}
|
||||||
|
|
||||||
public onFakeInput(setHeight = true) {
|
public onFakeInput(setHeight = true, noAnimation?: boolean) {
|
||||||
const {scrollHeight: newHeight/* , clientHeight */} = this.inputFake;
|
const {scrollHeight: newHeight/* , clientHeight */} = this.inputFake;
|
||||||
/* if(this.wasInputFakeClientHeight && this.wasInputFakeClientHeight !== clientHeight) {
|
/* if(this.wasInputFakeClientHeight && this.wasInputFakeClientHeight !== clientHeight) {
|
||||||
this.input.classList.add('no-scrollbar'); // ! в сафари может вообще не появиться скролл после анимации, так как ему нужен полный reflow блока с overflow.
|
this.input.classList.add('no-scrollbar'); // ! в сафари может вообще не появиться скролл после анимации, так как ему нужен полный reflow блока с overflow.
|
||||||
this.showScrollDebounced();
|
this.showScrollDebounced();
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
noAnimation ??= !this.input.isContentEditable;
|
||||||
|
|
||||||
const currentHeight = +this.input.style.height.replace('px', '');
|
const currentHeight = +this.input.style.height.replace('px', '');
|
||||||
if(currentHeight === newHeight) {
|
if(currentHeight === newHeight) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TRANSITION_DURATION_FACTOR = 50;
|
const TRANSITION_DURATION_FACTOR = 50;
|
||||||
const transitionDuration = Math.round(
|
const transitionDuration = noAnimation ? 0 : Math.round(
|
||||||
TRANSITION_DURATION_FACTOR * Math.log(Math.abs(newHeight - currentHeight))
|
TRANSITION_DURATION_FACTOR * Math.log(Math.abs(newHeight - currentHeight))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,19 @@
|
||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type {ChatRights} from '../../lib/appManagers/appChatsManager';
|
||||||
|
import flatten from '../../helpers/array/flatten';
|
||||||
import appImManager from '../../lib/appManagers/appImManager';
|
import appImManager from '../../lib/appManagers/appImManager';
|
||||||
import rootScope from '../../lib/rootScope';
|
import rootScope from '../../lib/rootScope';
|
||||||
import {toastNew} from '../toast';
|
import {toastNew} from '../toast';
|
||||||
import PopupPickUser from './pickUser';
|
import PopupPickUser from './pickUser';
|
||||||
|
import getMediaFromMessage from '../../lib/appManagers/utils/messages/getMediaFromMessage';
|
||||||
|
|
||||||
export default class PopupForward extends PopupPickUser {
|
export default class PopupForward extends PopupPickUser {
|
||||||
constructor(
|
constructor(
|
||||||
peerIdMids?: {[fromPeerId: PeerId]: number[]},
|
peerIdMids?: {[fromPeerId: PeerId]: number[]},
|
||||||
onSelect?: (peerId: PeerId) => Promise<void> | void
|
onSelect?: (peerId: PeerId) => Promise<void> | void,
|
||||||
|
chatRightsAction: ChatRights[] = ['send_plain']
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
peerTypes: ['dialogs', 'contacts'],
|
peerTypes: ['dialogs', 'contacts'],
|
||||||
|
@ -43,8 +47,71 @@ export default class PopupForward extends PopupPickUser {
|
||||||
appImManager.chat.input.initMessagesForward(peerIdMids);
|
appImManager.chat.input.initMessagesForward(peerIdMids);
|
||||||
},
|
},
|
||||||
placeholder: 'ShareModal.Search.ForwardPlaceholder',
|
placeholder: 'ShareModal.Search.ForwardPlaceholder',
|
||||||
chatRightsAction: 'send_messages',
|
chatRightsActions: chatRightsAction,
|
||||||
selfPresence: 'ChatYourSelf'
|
selfPresence: 'ChatYourSelf'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async create(...args: ConstructorParameters<typeof PopupForward>) {
|
||||||
|
const [peerIdMids] = args;
|
||||||
|
const messagesPromises = Object.keys(peerIdMids).map((peerId) => {
|
||||||
|
const mids = peerIdMids[peerId as any as number];
|
||||||
|
return mids.map((mid) => {
|
||||||
|
return rootScope.managers.appMessagesManager.getMessageByPeer(peerId.toPeerId(), mid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const messages = await Promise.all(flatten(messagesPromises));
|
||||||
|
const actions: Set<ChatRights> = new Set();
|
||||||
|
messages.forEach((message) => {
|
||||||
|
if(!message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const media = getMediaFromMessage(message);
|
||||||
|
let action: ChatRights;
|
||||||
|
if(!media) {
|
||||||
|
if(message.viaBotId) {
|
||||||
|
action = 'send_inline';
|
||||||
|
} else {
|
||||||
|
action = 'send_plain';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(media._ === 'webPage') {
|
||||||
|
action = 'embed_links';
|
||||||
|
} else if(media._ === 'photo') {
|
||||||
|
action = 'send_photos';
|
||||||
|
} else if(media._ === 'game') {
|
||||||
|
action = 'send_games';
|
||||||
|
} else {
|
||||||
|
switch(media.type) {
|
||||||
|
case 'audio':
|
||||||
|
action = 'send_audios';
|
||||||
|
break;
|
||||||
|
case 'gif':
|
||||||
|
action = 'send_gifs';
|
||||||
|
break;
|
||||||
|
case 'round':
|
||||||
|
action = 'send_roundvideos';
|
||||||
|
break;
|
||||||
|
case 'sticker':
|
||||||
|
action = 'send_stickers';
|
||||||
|
break;
|
||||||
|
case 'voice':
|
||||||
|
action = 'send_voices';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
action = 'send_docs';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(action) {
|
||||||
|
actions.add(action);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new PopupForward(args[0], args[1], Array.from(actions));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
import type Chat from '../chat/chat';
|
import type Chat from '../chat/chat';
|
||||||
import type {SendFileDetails} from '../../lib/appManagers/appMessagesManager';
|
import type {SendFileDetails} from '../../lib/appManagers/appMessagesManager';
|
||||||
|
import type {ChatRights} from '../../lib/appManagers/appChatsManager';
|
||||||
import PopupElement from '.';
|
import PopupElement from '.';
|
||||||
import Scrollable from '../scrollable';
|
import Scrollable from '../scrollable';
|
||||||
import {toast} from '../toast';
|
import {toast, toastNew} from '../toast';
|
||||||
import SendContextMenu from '../chat/sendContextMenu';
|
import SendContextMenu from '../chat/sendContextMenu';
|
||||||
import {createPosterFromMedia, createPosterFromVideo} from '../../helpers/createPoster';
|
import {createPosterFromMedia, createPosterFromVideo} from '../../helpers/createPoster';
|
||||||
import {MyDocument} from '../../lib/appManagers/appDocsManager';
|
import {MyDocument} from '../../lib/appManagers/appDocsManager';
|
||||||
|
@ -41,6 +42,11 @@ import {renderImageFromUrlPromise} from '../../helpers/dom/renderImageFromUrl';
|
||||||
import ButtonMenuToggle from '../buttonMenuToggle';
|
import ButtonMenuToggle from '../buttonMenuToggle';
|
||||||
import partition from '../../helpers/array/partition';
|
import partition from '../../helpers/array/partition';
|
||||||
import InputFieldAnimated from '../inputFieldAnimated';
|
import InputFieldAnimated from '../inputFieldAnimated';
|
||||||
|
import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport';
|
||||||
|
import VIDEO_MIME_TYPES_SUPPORTED from '../../environment/videoMimeTypesSupport';
|
||||||
|
import rootScope from '../../lib/rootScope';
|
||||||
|
import shake from '../../helpers/dom/shake';
|
||||||
|
import AUDIO_MIME_TYPES_SUPPORTED from '../../environment/audioMimeTypeSupport';
|
||||||
|
|
||||||
type SendFileParams = SendFileDetails & {
|
type SendFileParams = SendFileDetails & {
|
||||||
file?: File,
|
file?: File,
|
||||||
|
@ -96,6 +102,29 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
this.construct(willAttachType);
|
this.construct(willAttachType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async canSend(peerId: PeerId, onlyVisible?: boolean) {
|
||||||
|
const actions: ChatRights[] = [
|
||||||
|
'send_photos',
|
||||||
|
'send_videos',
|
||||||
|
'send_docs',
|
||||||
|
'send_audios',
|
||||||
|
'send_gifs'
|
||||||
|
];
|
||||||
|
|
||||||
|
const actionsPromises = actions.map((action) => {
|
||||||
|
return peerId.isAnyChat() && !onlyVisible ? rootScope.managers.appChatsManager.hasRights(peerId.toChatId(), action) : true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const out: {[action in ChatRights]?: boolean} = {};
|
||||||
|
|
||||||
|
const results = await Promise.all(actionsPromises);
|
||||||
|
actions.forEach((action, idx) => {
|
||||||
|
out[action] = results[idx];
|
||||||
|
})
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
private async construct(willAttachType: PopupNewMedia['willAttach']['type']) {
|
private async construct(willAttachType: PopupNewMedia['willAttach']['type']) {
|
||||||
this.willAttach = {
|
this.willAttach = {
|
||||||
type: willAttachType,
|
type: willAttachType,
|
||||||
|
@ -106,6 +135,12 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
const captionMaxLength = await this.managers.apiManager.getLimit('caption');
|
const captionMaxLength = await this.managers.apiManager.getLimit('caption');
|
||||||
this.captionLengthMax = captionMaxLength;
|
this.captionLengthMax = captionMaxLength;
|
||||||
|
|
||||||
|
const canSend = await PopupNewMedia.canSend(this.chat.peerId, true);
|
||||||
|
|
||||||
|
const canSendPhotos = canSend.send_photos;
|
||||||
|
const canSendVideos = canSend.send_videos;
|
||||||
|
const canSendDocs = canSend.send_docs;
|
||||||
|
|
||||||
attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
|
attachClickEvent(this.btnConfirm, () => this.send(), {listenerSetter: this.listenerSetter});
|
||||||
|
|
||||||
const btnMenu = await ButtonMenuToggle({
|
const btnMenu = await ButtonMenuToggle({
|
||||||
|
@ -115,17 +150,35 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
icon: 'image',
|
icon: 'image',
|
||||||
text: 'Popup.Attach.AsMedia',
|
text: 'Popup.Attach.AsMedia',
|
||||||
onClick: () => this.changeType('media'),
|
onClick: () => this.changeType('media'),
|
||||||
verify: () => this.hasAnyMedia() && this.willAttach.type === 'document'
|
verify: () => {
|
||||||
|
if(!this.hasAnyMedia() || this.willAttach.type !== 'document') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!canSendPhotos && !canSendVideos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!canSendPhotos || !canSendVideos) {
|
||||||
|
const mimeTypes = canSendPhotos ? IMAGE_MIME_TYPES_SUPPORTED : VIDEO_MIME_TYPES_SUPPORTED;
|
||||||
|
const {media, files} = this.partition(mimeTypes);
|
||||||
|
if(files.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
icon: 'document',
|
icon: 'document',
|
||||||
text: 'SendAsFile',
|
text: 'SendAsFile',
|
||||||
onClick: () => this.changeType('document'),
|
onClick: () => this.changeType('document'),
|
||||||
verify: () => this.files.length === 1 && this.willAttach.type !== 'document'
|
verify: () => this.files.length === 1 && this.willAttach.type !== 'document' && canSendDocs
|
||||||
}, {
|
}, {
|
||||||
icon: 'document',
|
icon: 'document',
|
||||||
text: 'SendAsFiles',
|
text: 'SendAsFiles',
|
||||||
onClick: () => this.changeType('document'),
|
onClick: () => this.changeType('document'),
|
||||||
verify: () => this.files.length > 1 && this.willAttach.type !== 'document'
|
verify: () => this.files.length > 1 && this.willAttach.type !== 'document' && canSendDocs
|
||||||
}, {
|
}, {
|
||||||
icon: 'groupmedia',
|
icon: 'groupmedia',
|
||||||
text: 'Popup.Attach.GroupMedia',
|
text: 'Popup.Attach.GroupMedia',
|
||||||
|
@ -365,8 +418,8 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
this.willAttach.type = type;
|
this.willAttach.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private partition() {
|
private partition(mimeTypes = MEDIA_MIME_TYPES_SUPPORTED) {
|
||||||
const [media, files] = partition(this.willAttach.sendFileDetails, (d) => MEDIA_MIME_TYPES_SUPPORTED.has(d.file.type));
|
const [media, files] = partition(this.willAttach.sendFileDetails, (d) => mimeTypes.has(d.file.type));
|
||||||
return {
|
return {
|
||||||
media,
|
media,
|
||||||
files
|
files
|
||||||
|
@ -455,7 +508,69 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private send(force = false) {
|
private async send(force = false) {
|
||||||
|
let caption = this.messageInputField.value;
|
||||||
|
if(caption.length > this.captionLengthMax) {
|
||||||
|
toast(I18n.format('Error.PreviewSender.CaptionTooLong', true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {peerId, input} = this.chat;
|
||||||
|
|
||||||
|
const canSend = await PopupNewMedia.canSend(peerId);
|
||||||
|
const willAttach = this.willAttach;
|
||||||
|
willAttach.isMedia = willAttach.type === 'media' || undefined;
|
||||||
|
const {sendFileDetails, isMedia} = willAttach;
|
||||||
|
|
||||||
|
let foundBad = false;
|
||||||
|
this.iterate((sendFileParams) => {
|
||||||
|
if(foundBad) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBad: (LangPackKey | boolean)[] = sendFileParams.map((params) => {
|
||||||
|
const a: [Set<string> | (() => boolean), LangPackKey, ChatRights][] = [
|
||||||
|
[AUDIO_MIME_TYPES_SUPPORTED, 'GlobalAttachAudioRestricted', 'send_audios'],
|
||||||
|
[() => !MEDIA_MIME_TYPES_SUPPORTED.has(params.file.type), 'GlobalAttachDocumentsRestricted', 'send_docs']
|
||||||
|
];
|
||||||
|
|
||||||
|
if(isMedia) {
|
||||||
|
a.unshift(
|
||||||
|
[IMAGE_MIME_TYPES_SUPPORTED, 'GlobalAttachPhotoRestricted', 'send_photos'],
|
||||||
|
[() => VIDEO_MIME_TYPES_SUPPORTED.has(params.file.type as any) && params.noSound, 'GlobalAttachGifRestricted', 'send_gifs'],
|
||||||
|
[VIDEO_MIME_TYPES_SUPPORTED, 'GlobalAttachVideoRestricted', 'send_videos']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const found = a.find(([verify]) => {
|
||||||
|
return typeof(verify) === 'function' ? verify() : verify.has(params.file.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(found) {
|
||||||
|
return canSend[found[2]] ? undefined : found[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!isMedia && !canSend.send_docs && 'GlobalAttachDocumentsRestricted') || undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = isBad.find((i) => typeof(i) === 'string') as LangPackKey;
|
||||||
|
if(key) {
|
||||||
|
toastNew({
|
||||||
|
langPackKey: key
|
||||||
|
});
|
||||||
|
|
||||||
|
if(rootScope.settings.animationsEnabled) {
|
||||||
|
shake(this.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foundBad ||= !!key;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(foundBad) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(this.chat.type === 'scheduled' && !force) {
|
if(this.chat.type === 'scheduled' && !force) {
|
||||||
this.chat.input.scheduleSending(() => {
|
this.chat.input.scheduleSending(() => {
|
||||||
this.send(true);
|
this.send(true);
|
||||||
|
@ -464,18 +579,6 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let caption = this.messageInputField.value;
|
|
||||||
if(caption.length > this.captionLengthMax) {
|
|
||||||
toast(I18n.format('Error.PreviewSender.CaptionTooLong', true));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const willAttach = this.willAttach;
|
|
||||||
willAttach.isMedia = willAttach.type === 'media' || undefined;
|
|
||||||
const {sendFileDetails, isMedia} = willAttach;
|
|
||||||
|
|
||||||
const {peerId, input} = this.chat;
|
|
||||||
|
|
||||||
const {length} = sendFileDetails;
|
const {length} = sendFileDetails;
|
||||||
const sendingParams = this.chat.getMessageSendingParams();
|
const sendingParams = this.chat.getMessageSendingParams();
|
||||||
this.iterate((sendFileParams) => {
|
this.iterate((sendFileParams) => {
|
||||||
|
@ -631,7 +734,7 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
const file = params.file;
|
const file = params.file;
|
||||||
|
|
||||||
const isPhoto = file.type.startsWith('image/');
|
const isPhoto = file.type.startsWith('image/');
|
||||||
const isAudio = file.type.startsWith('audio/');
|
const isAudio = AUDIO_MIME_TYPES_SUPPORTED.has(file.type as any);
|
||||||
if(isPhoto || isAudio || file.size < 20e6) {
|
if(isPhoto || isAudio || file.size < 20e6) {
|
||||||
params.objectURL ||= await apiManagerProxy.invoke('createObjectURL', file);
|
params.objectURL ||= await apiManagerProxy.invoke('createObjectURL', file);
|
||||||
}
|
}
|
||||||
|
@ -860,3 +963,5 @@ export default class PopupNewMedia extends PopupElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(window as any).PopupNewMedia = PopupNewMedia;
|
||||||
|
|
|
@ -16,7 +16,7 @@ export default class PopupPickUser extends PopupElement {
|
||||||
peerTypes: AppSelectPeers['peerType'],
|
peerTypes: AppSelectPeers['peerType'],
|
||||||
onSelect?: (peerId: PeerId) => Promise<void> | void,
|
onSelect?: (peerId: PeerId) => Promise<void> | void,
|
||||||
placeholder: LangPackKey,
|
placeholder: LangPackKey,
|
||||||
chatRightsAction?: AppSelectPeers['chatRightsAction'],
|
chatRightsActions?: AppSelectPeers['chatRightsActions'],
|
||||||
peerId?: number,
|
peerId?: number,
|
||||||
selfPresence?: LangPackKey
|
selfPresence?: LangPackKey
|
||||||
}) {
|
}) {
|
||||||
|
@ -51,7 +51,7 @@ export default class PopupPickUser extends PopupElement {
|
||||||
this.selector.input.focus();
|
this.selector.input.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chatRightsAction: options.chatRightsAction,
|
chatRightsActions: options.chatRightsActions,
|
||||||
multiSelect: false,
|
multiSelect: false,
|
||||||
rippleEnabled: false,
|
rippleEnabled: false,
|
||||||
avatarSize: 'abitbigger',
|
avatarSize: 'abitbigger',
|
||||||
|
|
|
@ -149,13 +149,15 @@ export default function ripple(
|
||||||
// });
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
const isRippleUnneeded = (e: Event) => e.target !== elem && (
|
const isRippleUnneeded = (e: Event) => {
|
||||||
['BUTTON', 'A'].includes((e.target as HTMLElement).tagName) ||
|
return e.target !== elem && (
|
||||||
findUpClassName(e.target as HTMLElement, 'c-ripple') !== r
|
['BUTTON', 'A'].includes((e.target as HTMLElement).tagName) ||
|
||||||
) && (
|
findUpClassName(e.target as HTMLElement, 'c-ripple') !== r
|
||||||
attachListenerTo === elem ||
|
) && (
|
||||||
!findUpAsChild(e.target as HTMLElement, attachListenerTo)
|
attachListenerTo === elem ||
|
||||||
);
|
!findUpAsChild(e.target as HTMLElement, attachListenerTo)
|
||||||
|
) && !findUpClassName(e.target, 'checkbox-field');
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: rename this variable
|
// TODO: rename this variable
|
||||||
let touchStartFired = false;
|
let touchStartFired = false;
|
||||||
|
|
|
@ -181,10 +181,15 @@ export default class AppEditChatTab extends SliderSuperTab {
|
||||||
|
|
||||||
if(canChangePermissions && !isBroadcast) {
|
if(canChangePermissions && !isBroadcast) {
|
||||||
const flags = [
|
const flags = [
|
||||||
'send_messages',
|
|
||||||
'send_media',
|
|
||||||
'send_stickers',
|
'send_stickers',
|
||||||
'send_polls',
|
'send_polls',
|
||||||
|
'send_photos',
|
||||||
|
'send_videos',
|
||||||
|
'send_roundvideos',
|
||||||
|
'send_audios',
|
||||||
|
'send_voices',
|
||||||
|
'send_docs',
|
||||||
|
'send_plain',
|
||||||
'embed_links',
|
'embed_links',
|
||||||
'invite_users',
|
'invite_users',
|
||||||
'pin_messages',
|
'pin_messages',
|
||||||
|
|
|
@ -4,13 +4,15 @@
|
||||||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type {ChatRights} from '../../../lib/appManagers/appChatsManager';
|
||||||
|
import flatten from '../../../helpers/array/flatten';
|
||||||
|
import cancelEvent from '../../../helpers/dom/cancelEvent';
|
||||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||||
import findUpTag from '../../../helpers/dom/findUpTag';
|
import findUpTag from '../../../helpers/dom/findUpTag';
|
||||||
import replaceContent from '../../../helpers/dom/replaceContent';
|
import replaceContent from '../../../helpers/dom/replaceContent';
|
||||||
import ListenerSetter from '../../../helpers/listenerSetter';
|
import ListenerSetter from '../../../helpers/listenerSetter';
|
||||||
import ScrollableLoader from '../../../helpers/scrollableLoader';
|
import ScrollableLoader from '../../../helpers/scrollableLoader';
|
||||||
import {ChannelParticipant, Chat, ChatBannedRights, Update} from '../../../layer';
|
import {ChannelParticipant, Chat, ChatBannedRights} from '../../../layer';
|
||||||
import {ChatRights} from '../../../lib/appManagers/appChatsManager';
|
|
||||||
import appDialogsManager, {DialogDom, DIALOG_LIST_ELEMENT_TAG} from '../../../lib/appManagers/appDialogsManager';
|
import appDialogsManager, {DialogDom, DIALOG_LIST_ELEMENT_TAG} from '../../../lib/appManagers/appDialogsManager';
|
||||||
import {AppManagers} from '../../../lib/appManagers/managers';
|
import {AppManagers} from '../../../lib/appManagers/managers';
|
||||||
import combineParticipantBannedRights from '../../../lib/appManagers/utils/chats/combineParticipantBannedRights';
|
import combineParticipantBannedRights from '../../../lib/appManagers/utils/chats/combineParticipantBannedRights';
|
||||||
|
@ -21,20 +23,32 @@ import I18n, {i18n, join, LangPackKey} from '../../../lib/langPack';
|
||||||
import rootScope from '../../../lib/rootScope';
|
import rootScope from '../../../lib/rootScope';
|
||||||
import CheckboxField from '../../checkboxField';
|
import CheckboxField from '../../checkboxField';
|
||||||
import PopupPickUser from '../../popups/pickUser';
|
import PopupPickUser from '../../popups/pickUser';
|
||||||
import Row, {CreateRowFromCheckboxField} from '../../row';
|
import Row from '../../row';
|
||||||
import SettingSection from '../../settingSection';
|
import SettingSection from '../../settingSection';
|
||||||
import {SliderSuperTabEventable} from '../../sliderTab';
|
import {SliderSuperTabEventable} from '../../sliderTab';
|
||||||
import {toast} from '../../toast';
|
import {toast} from '../../toast';
|
||||||
import AppUserPermissionsTab from './userPermissions';
|
import AppUserPermissionsTab from './userPermissions';
|
||||||
|
import findUpAsChild from '../../../helpers/dom/findUpAsChild';
|
||||||
|
|
||||||
|
type T = {
|
||||||
|
flags: ChatRights[],
|
||||||
|
text: LangPackKey,
|
||||||
|
exceptionText: LangPackKey,
|
||||||
|
checkboxField?: CheckboxField,
|
||||||
|
nested?: T[],
|
||||||
|
nestedTo?: T,
|
||||||
|
nestedCounter?: HTMLElement,
|
||||||
|
setNestedCounter?: (count: number) => void,
|
||||||
|
toggleWith?: {checked?: ChatRights[], unchecked?: ChatRights[]}
|
||||||
|
};
|
||||||
|
|
||||||
export class ChatPermissions {
|
export class ChatPermissions {
|
||||||
public v: Array<{
|
public v: Array<T>;
|
||||||
flags: ChatRights[],
|
|
||||||
text: LangPackKey,
|
protected chat: Chat.chat | Chat.channel;
|
||||||
exceptionText: LangPackKey,
|
protected rights: ChatBannedRights.chatBannedRights;
|
||||||
checkboxField?: CheckboxField,
|
protected defaultBannedRights: ChatBannedRights.chatBannedRights;
|
||||||
}>;
|
protected restrictionText: LangPackKey;
|
||||||
private toggleWith: Partial<{[chatRight in ChatRights]: ChatRights[]}>;
|
|
||||||
|
|
||||||
constructor(private options: {
|
constructor(private options: {
|
||||||
chatId: ChatId,
|
chatId: ChatId,
|
||||||
|
@ -46,79 +60,158 @@ export class ChatPermissions {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async construct() {
|
public async construct() {
|
||||||
this.v = [
|
const mediaNested: T[] = [
|
||||||
{flags: ['send_messages'], text: 'UserRestrictionsSend', exceptionText: 'UserRestrictionsNoSend'},
|
{flags: ['send_photos'], text: 'UserRestrictionsSendPhotos', exceptionText: 'UserRestrictionsNoSendPhotos'},
|
||||||
{flags: ['send_media'], text: 'UserRestrictionsSendMedia', exceptionText: 'UserRestrictionsNoSendMedia'},
|
{flags: ['send_videos'], text: 'UserRestrictionsSendVideos', exceptionText: 'UserRestrictionsNoSendVideos'},
|
||||||
{flags: ['send_stickers', 'send_gifs'], text: 'UserRestrictionsSendStickers', exceptionText: 'UserRestrictionsNoSendStickers'},
|
{flags: ['send_stickers', 'send_gifs'], text: 'UserRestrictionsSendStickers', exceptionText: 'UserRestrictionsNoSendStickers'},
|
||||||
{flags: ['send_polls'], text: 'UserRestrictionsSendPolls', exceptionText: 'UserRestrictionsNoSendPolls'},
|
{flags: ['send_audios'], text: 'UserRestrictionsSendMusic', exceptionText: 'UserRestrictionsNoSendMusic'},
|
||||||
{flags: ['embed_links'], text: 'UserRestrictionsEmbedLinks', exceptionText: 'UserRestrictionsNoEmbedLinks'},
|
{flags: ['send_docs'], text: 'UserRestrictionsSendFiles', exceptionText: 'UserRestrictionsNoSendDocs'},
|
||||||
|
{flags: ['send_voices'], text: 'UserRestrictionsSendVoices', exceptionText: 'UserRestrictionsNoSendVoice'},
|
||||||
|
{flags: ['send_roundvideos'], text: 'UserRestrictionsSendRound', exceptionText: 'UserRestrictionsNoSendRound'},
|
||||||
|
{flags: ['embed_links'], text: 'UserRestrictionsEmbedLinks', exceptionText: 'UserRestrictionsNoEmbedLinks', toggleWith: {checked: ['send_plain']}},
|
||||||
|
{flags: ['send_polls'], text: 'UserRestrictionsSendPolls', exceptionText: 'UserRestrictionsNoSendPolls'}
|
||||||
|
];
|
||||||
|
|
||||||
|
const mediaToggleWith = flatten(mediaNested.map(({flags}) => flags));
|
||||||
|
const media: T = {flags: ['send_media'], text: 'UserRestrictionsSendMedia', exceptionText: 'UserRestrictionsNoSendMedia', nested: mediaNested, toggleWith: {unchecked: mediaToggleWith, checked: mediaToggleWith}};
|
||||||
|
|
||||||
|
this.v = [
|
||||||
|
{flags: ['send_plain'], text: 'UserRestrictionsSend', exceptionText: 'UserRestrictionsNoSend', toggleWith: {unchecked: ['embed_links']}},
|
||||||
|
media,
|
||||||
{flags: ['invite_users'], text: 'UserRestrictionsInviteUsers', exceptionText: 'UserRestrictionsNoInviteUsers'},
|
{flags: ['invite_users'], text: 'UserRestrictionsInviteUsers', exceptionText: 'UserRestrictionsNoInviteUsers'},
|
||||||
{flags: ['pin_messages'], text: 'UserRestrictionsPinMessages', exceptionText: 'UserRestrictionsNoPinMessages'},
|
{flags: ['pin_messages'], text: 'UserRestrictionsPinMessages', exceptionText: 'UserRestrictionsNoPinMessages'},
|
||||||
{flags: ['change_info'], text: 'UserRestrictionsChangeInfo', exceptionText: 'UserRestrictionsNoChangeInfo'}
|
{flags: ['change_info'], text: 'UserRestrictionsChangeInfo', exceptionText: 'UserRestrictionsNoChangeInfo'}
|
||||||
];
|
];
|
||||||
|
|
||||||
this.toggleWith = {
|
mediaNested.forEach((info) => info.nestedTo = media);
|
||||||
'send_messages': ['send_media', 'send_stickers', 'send_polls', 'embed_links']
|
|
||||||
};
|
|
||||||
|
|
||||||
const options = this.options;
|
const options = this.options;
|
||||||
const chat = await this.managers.appChatsManager.getChat(options.chatId) as Chat.chat | Chat.channel;
|
const chat = this.chat = await this.managers.appChatsManager.getChat(options.chatId) as Chat.chat | Chat.channel;
|
||||||
const defaultBannedRights = chat.default_banned_rights;
|
const defaultBannedRights = this.defaultBannedRights = chat.default_banned_rights;
|
||||||
const rights = options.participant ? combineParticipantBannedRights(chat as Chat.channel, options.participant.banned_rights) : defaultBannedRights;
|
const rights = this.rights = options.participant ? combineParticipantBannedRights(chat as Chat.channel, options.participant.banned_rights) : defaultBannedRights;
|
||||||
|
|
||||||
const restrictionText: LangPackKey = options.participant ? 'UserRestrictionsDisabled' : 'EditCantEditPermissionsPublic';
|
|
||||||
for(const info of this.v) {
|
for(const info of this.v) {
|
||||||
const mainFlag = info.flags[0];
|
const {nodes} = this.createRow(info);
|
||||||
const row = CreateRowFromCheckboxField(
|
options.appendTo.append(...nodes);
|
||||||
info.checkboxField = new CheckboxField({
|
}
|
||||||
text: info.text,
|
|
||||||
checked: hasRights(chat, mainFlag, rights),
|
|
||||||
restriction: true,
|
|
||||||
listenerSetter: options.listenerSetter
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if((
|
this.v.push(...mediaNested);
|
||||||
options.participant &&
|
}
|
||||||
defaultBannedRights.pFlags[mainFlag as keyof typeof defaultBannedRights['pFlags']]
|
|
||||||
) || (
|
|
||||||
getPeerActiveUsernames(chat as Chat.channel)[0] &&
|
|
||||||
(
|
|
||||||
info.flags.includes('pin_messages') ||
|
|
||||||
info.flags.includes('change_info')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
info.checkboxField.input.disabled = true;
|
|
||||||
|
|
||||||
/* options.listenerSetter.add(info.checkboxField.input)('change', (e) => {
|
protected createRow(info: T, isNested?: boolean) {
|
||||||
if(!e.isTrusted) {
|
const {defaultBannedRights, chat, rights, restrictionText} = this;
|
||||||
return;
|
|
||||||
|
const mainFlag = info.flags[0];
|
||||||
|
const row = new Row({
|
||||||
|
titleLangKey: isNested ? undefined : info.text,
|
||||||
|
checkboxField: info.checkboxField = new CheckboxField({
|
||||||
|
text: isNested ? info.text : undefined,
|
||||||
|
checked: info.nested ? false : hasRights(chat, mainFlag, rights),
|
||||||
|
toggle: !isNested,
|
||||||
|
listenerSetter: this.options.listenerSetter,
|
||||||
|
restriction: !isNested
|
||||||
|
}),
|
||||||
|
listenerSetter: this.options.listenerSetter,
|
||||||
|
clickable: info.nested ? (e) => {
|
||||||
|
if(findUpAsChild(e.target as HTMLElement, row.checkboxField.label)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelEvent(e);
|
||||||
|
row.container.classList.toggle('accordion-toggler-expanded');
|
||||||
|
accordion.classList.toggle('is-expanded');
|
||||||
|
} : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
if((
|
||||||
|
this.options.participant &&
|
||||||
|
defaultBannedRights.pFlags[mainFlag as keyof typeof defaultBannedRights['pFlags']]
|
||||||
|
) || (
|
||||||
|
getPeerActiveUsernames(chat as Chat.channel)[0] &&
|
||||||
|
(
|
||||||
|
info.flags.includes('pin_messages') ||
|
||||||
|
info.flags.includes('change_info')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
info.checkboxField.input.disabled = true;
|
||||||
|
|
||||||
|
attachClickEvent(info.checkboxField.label, (e) => {
|
||||||
|
toast(I18n.format(restrictionText, true));
|
||||||
|
}, {listenerSetter: this.options.listenerSetter});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(info.toggleWith || info.nestedTo) {
|
||||||
|
const processToggleWith = info.toggleWith ? (info: T) => {
|
||||||
|
const {toggleWith, nested} = info;
|
||||||
|
const value = info.checkboxField.checked;
|
||||||
|
const arr = value ? toggleWith.checked : toggleWith.unchecked;
|
||||||
|
if(!arr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const other = this.v.filter((i) => arr.includes(i.flags[0]));
|
||||||
|
other.forEach((info) => {
|
||||||
|
info.checkboxField.setValueSilently(value);
|
||||||
|
if(info.nestedTo && !nested) {
|
||||||
|
this.setNestedCounter(info.nestedTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelEvent(e);
|
if(info.toggleWith) {
|
||||||
toast('This option is disabled for all members in Group Permissions.');
|
processToggleWith(info);
|
||||||
info.checkboxField.checked = false;
|
|
||||||
}); */
|
|
||||||
|
|
||||||
attachClickEvent(info.checkboxField.label, (e) => {
|
|
||||||
toast(I18n.format(restrictionText, true));
|
|
||||||
}, {listenerSetter: options.listenerSetter});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.toggleWith[mainFlag]) {
|
|
||||||
options.listenerSetter.add(info.checkboxField.input)('change', () => {
|
|
||||||
if(!info.checkboxField.checked) {
|
|
||||||
const other = this.v.filter((i) => this.toggleWith[mainFlag].includes(i.flags[0]));
|
|
||||||
other.forEach((info) => {
|
|
||||||
info.checkboxField.checked = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
options.appendTo.append(row.container);
|
if(info.nested) {
|
||||||
|
this.setNestedCounter(info);
|
||||||
|
}
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
const processNestedTo = info.nestedTo ? () => {
|
||||||
|
const length = this.getNestedCheckedLength(info.nestedTo);
|
||||||
|
info.nestedTo.checkboxField.setValueSilently(length === info.nestedTo.nested.length);
|
||||||
|
this.setNestedCounter(info.nestedTo, length);
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
this.options.listenerSetter.add(info.checkboxField.input)('change', () => {
|
||||||
|
processToggleWith?.(info);
|
||||||
|
processNestedTo?.();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nodes: HTMLElement[] = [row.container];
|
||||||
|
let accordion: HTMLElement, nestedCounter: HTMLElement;
|
||||||
|
if(info.nested) {
|
||||||
|
const container = accordion = document.createElement('div');
|
||||||
|
container.classList.add('accordion');
|
||||||
|
container.style.setProperty('--max-height', info.nested.length * 48 + 'px');
|
||||||
|
info.nested.forEach((info) => {
|
||||||
|
container.append(...this.createRow(info, true).nodes);
|
||||||
|
});
|
||||||
|
nodes.push(container);
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.classList.add('tgico-down', 'accordion-icon');
|
||||||
|
|
||||||
|
nestedCounter = info.nestedCounter = document.createElement('b');
|
||||||
|
this.setNestedCounter(info);
|
||||||
|
row.title.append(' ', nestedCounter, ' ', span);
|
||||||
|
|
||||||
|
row.container.classList.add('accordion-toggler');
|
||||||
|
row.titleRow.classList.add('with-delimiter');
|
||||||
|
|
||||||
|
row.checkboxField.setValueSilently(this.getNestedCheckedLength(info) === info.nested.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {row, nodes};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getNestedCheckedLength(info: T) {
|
||||||
|
return info.nested.reduce((acc, v) => acc + +v.checkboxField.checked, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setNestedCounter(info: T, count = this.getNestedCheckedLength(info)) {
|
||||||
|
info.nestedCounter.textContent = `${count}/${info.nested.length}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public takeOut() {
|
public takeOut() {
|
||||||
|
@ -128,14 +221,23 @@ export class ChatPermissions {
|
||||||
pFlags: {}
|
pFlags: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IGNORE_FLAGS: Set<ChatRights> = new Set([
|
||||||
|
'send_media'
|
||||||
|
]);
|
||||||
for(const info of this.v) {
|
for(const info of this.v) {
|
||||||
const banned = !info.checkboxField.checked;
|
const banned = !info.checkboxField.checked;
|
||||||
if(banned) {
|
if(!banned) {
|
||||||
info.flags.forEach((flag) => {
|
continue;
|
||||||
// @ts-ignore
|
|
||||||
rights.pFlags[flag] = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.flags.forEach((flag) => {
|
||||||
|
if(IGNORE_FLAGS.has(flag)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
rights.pFlags[flag] = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return rights;
|
return rights;
|
||||||
|
@ -195,7 +297,7 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
|
||||||
const openPermissions = async(peerId: PeerId) => {
|
const openPermissions = async(peerId: PeerId) => {
|
||||||
let participant: AppUserPermissionsTab['participant'];
|
let participant: AppUserPermissionsTab['participant'];
|
||||||
try {
|
try {
|
||||||
participant = await this.managers.appProfileManager.getChannelParticipant(this.chatId, peerId) as any;
|
participant = await this.managers.appProfileManager.getParticipant(this.chatId, peerId);
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
toast('User is no longer participant');
|
toast('User is no longer participant');
|
||||||
return;
|
return;
|
||||||
|
@ -270,37 +372,35 @@ export default class AppGroupPermissionsTab extends SliderSuperTabEventable {
|
||||||
append
|
append
|
||||||
});
|
});
|
||||||
|
|
||||||
setSubtitle(dom, participant);
|
(dom.listEl as any).dialogDom = dom;
|
||||||
|
|
||||||
// dom.titleSpan.innerHTML = 'Chinaza Akachi';
|
setSubtitle(dom, participant);
|
||||||
// dom.lastMessageSpan.innerHTML = 'Can Add Users and Pin Messages';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// this.listenerSetter.add(rootScope)('updateChannelParticipant', (update: Update.updateChannelParticipant) => {
|
this.listenerSetter.add(rootScope)('chat_participant', (update) => {
|
||||||
// const needAdd = update.new_participant?._ === 'channelParticipantBanned' && !update.new_participant.banned_rights.pFlags.view_messages;
|
const needAdd = update.new_participant?._ === 'channelParticipantBanned' &&
|
||||||
// const li = list.querySelector(`[data-peer-id="${update.user_id}"]`);
|
!update.new_participant.banned_rights.pFlags.view_messages;
|
||||||
// if(needAdd) {
|
const li = list.querySelector(`[data-peer-id="${update.user_id}"]`);
|
||||||
// if(!li) {
|
if(needAdd) {
|
||||||
// add(update.new_participant as ChannelParticipant.channelParticipantBanned, false);
|
if(!li) {
|
||||||
// } else {
|
add(update.new_participant as ChannelParticipant.channelParticipantBanned, false);
|
||||||
// setSubtitle(li, update.new_participant as ChannelParticipant.channelParticipantBanned);
|
} else {
|
||||||
// }
|
setSubtitle((li as any).dialogDom, update.new_participant as ChannelParticipant.channelParticipantBanned);
|
||||||
|
}
|
||||||
|
|
||||||
// if(update.prev_participant?._ !== 'channelParticipantBanned') {
|
if(update.prev_participant?._ !== 'channelParticipantBanned') {
|
||||||
// ++exceptionsCount;
|
++exceptionsCount;
|
||||||
// }
|
}
|
||||||
// } else {
|
} else {
|
||||||
// if(li) {
|
li?.remove();
|
||||||
// li.remove();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if(update.prev_participant?._ === 'channelParticipantBanned') {
|
if(update.prev_participant?._ === 'channelParticipantBanned') {
|
||||||
// --exceptionsCount;
|
--exceptionsCount;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// setLength();
|
setLength();
|
||||||
// });
|
});
|
||||||
|
|
||||||
const setLength = () => {
|
const setLength = () => {
|
||||||
replaceContent(addExceptionRow.subtitle, i18n(exceptionsCount ? 'Permissions.ExceptionsCount' : 'Permissions.NoExceptions', [exceptionsCount]));
|
replaceContent(addExceptionRow.subtitle, i18n(exceptionsCount ? 'Permissions.ExceptionsCount' : 'Permissions.NoExceptions', [exceptionsCount]));
|
||||||
|
|
|
@ -7,16 +7,18 @@
|
||||||
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
import {attachClickEvent} from '../../../helpers/dom/clickEvent';
|
||||||
import toggleDisability from '../../../helpers/dom/toggleDisability';
|
import toggleDisability from '../../../helpers/dom/toggleDisability';
|
||||||
import deepEqual from '../../../helpers/object/deepEqual';
|
import deepEqual from '../../../helpers/object/deepEqual';
|
||||||
import {ChannelParticipant} from '../../../layer';
|
import {ChannelParticipant, ChatParticipant} from '../../../layer';
|
||||||
import appDialogsManager from '../../../lib/appManagers/appDialogsManager';
|
import appDialogsManager from '../../../lib/appManagers/appDialogsManager';
|
||||||
import Button from '../../button';
|
import Button from '../../button';
|
||||||
|
import confirmationPopup from '../../confirmationPopup';
|
||||||
import SettingSection from '../../settingSection';
|
import SettingSection from '../../settingSection';
|
||||||
import {SliderSuperTabEventable} from '../../sliderTab';
|
import {SliderSuperTabEventable} from '../../sliderTab';
|
||||||
import getUserStatusString from '../../wrappers/getUserStatusString';
|
import getUserStatusString from '../../wrappers/getUserStatusString';
|
||||||
|
import wrapPeerTitle from '../../wrappers/peerTitle';
|
||||||
import {ChatPermissions} from './groupPermissions';
|
import {ChatPermissions} from './groupPermissions';
|
||||||
|
|
||||||
export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
||||||
public participant: ChannelParticipant;
|
public participant: ChannelParticipant | ChatParticipant;
|
||||||
public chatId: ChatId;
|
public chatId: ChatId;
|
||||||
public userId: UserId;
|
public userId: UserId;
|
||||||
|
|
||||||
|
@ -26,6 +28,8 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
||||||
|
|
||||||
let destroyListener: () => void;
|
let destroyListener: () => void;
|
||||||
|
|
||||||
|
const isChannel = await this.managers.appChatsManager.isChannel(this.chatId);
|
||||||
|
|
||||||
{
|
{
|
||||||
const section = new SettingSection({
|
const section = new SettingSection({
|
||||||
name: 'UserRestrictionsCanDo'
|
name: 'UserRestrictionsCanDo'
|
||||||
|
@ -55,7 +59,6 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
||||||
}, this.managers);
|
}, this.managers);
|
||||||
|
|
||||||
destroyListener = () => {
|
destroyListener = () => {
|
||||||
// appChatsManager.editChatDefaultBannedRights(this.chatId, p.takeOut());
|
|
||||||
const rights = p.takeOut();
|
const rights = p.takeOut();
|
||||||
if(this.participant._ === 'channelParticipantBanned' && deepEqual(this.participant.banned_rights.pFlags, rights.pFlags)) {
|
if(this.participant._ === 'channelParticipantBanned' && deepEqual(this.participant.banned_rights.pFlags, rights.pFlags)) {
|
||||||
return;
|
return;
|
||||||
|
@ -77,7 +80,10 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
||||||
|
|
||||||
attachClickEvent(btnDeleteException, () => {
|
attachClickEvent(btnDeleteException, () => {
|
||||||
const toggle = toggleDisability([btnDeleteException], true);
|
const toggle = toggleDisability([btnDeleteException], true);
|
||||||
this.managers.appChatsManager.clearChannelParticipantBannedRights(this.chatId, this.participant).then(() => {
|
this.managers.appChatsManager.clearChannelParticipantBannedRights(
|
||||||
|
this.chatId,
|
||||||
|
this.participant as ChannelParticipant.channelParticipantBanned
|
||||||
|
).then(() => {
|
||||||
this.eventListener.removeEventListener('destroy', destroyListener);
|
this.eventListener.removeEventListener('destroy', destroyListener);
|
||||||
this.close();
|
this.close();
|
||||||
}, () => {
|
}, () => {
|
||||||
|
@ -90,31 +96,34 @@ export default class AppUserPermissionsTab extends SliderSuperTabEventable {
|
||||||
|
|
||||||
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'deleteuser', text: 'UserRestrictionsBlock'});
|
const btnDelete = Button('btn-primary btn-transparent danger', {icon: 'deleteuser', text: 'UserRestrictionsBlock'});
|
||||||
|
|
||||||
attachClickEvent(btnDelete, () => {
|
attachClickEvent(btnDelete, async() => {
|
||||||
const toggle = toggleDisability([btnDelete], true);
|
const toggle = toggleDisability([btnDelete], true);
|
||||||
this.managers.appChatsManager.kickFromChannel(this.chatId, this.participant).then(() => {
|
|
||||||
this.eventListener.removeEventListener('destroy', destroyListener);
|
|
||||||
this.close();
|
|
||||||
});
|
|
||||||
/* new PopupPeer('popup-group-kick-user', {
|
|
||||||
peerId: -this.chatId,
|
|
||||||
title: 'Ban User?',
|
|
||||||
description: `Are you sure you want to ban <b>${appPeersManager.getPeerTitle(this.userId)}</b>`,
|
|
||||||
buttons: addCancelButton([{
|
|
||||||
text: 'BAN',
|
|
||||||
callback: () => {
|
|
||||||
const toggle = toggleDisability([btnDelete], true);
|
|
||||||
|
|
||||||
appChatsManager.kickFromChannel(this.chatId, this.participant).then(() => {
|
try {
|
||||||
this.eventListener.removeEventListener('destroy', destroyListener);
|
const peerId = this.userId.toPeerId();
|
||||||
this.close();
|
await confirmationPopup({
|
||||||
}, () => {
|
peerId: this.chatId.toPeerId(true),
|
||||||
toggle();
|
descriptionLangKey: 'Permissions.RemoveFromGroup',
|
||||||
});
|
descriptionLangArgs: [await wrapPeerTitle({peerId: peerId})],
|
||||||
},
|
titleLangKey: 'ChannelBlockUser',
|
||||||
isDanger: true
|
button: {
|
||||||
}])
|
langKey: 'Remove',
|
||||||
}).show(); */
|
isDanger: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!isChannel) {
|
||||||
|
await this.managers.appChatsManager.kickFromChat(this.chatId, this.participant);
|
||||||
|
} else {
|
||||||
|
await this.managers.appChatsManager.kickFromChannel(this.chatId, this.participant as ChannelParticipant);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
toggle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventListener.removeEventListener('destroy', destroyListener);
|
||||||
|
this.close();
|
||||||
}, {listenerSetter: this.listenerSetter});
|
}, {listenerSetter: this.listenerSetter});
|
||||||
|
|
||||||
section.content.append(btnDelete);
|
section.content.append(btnDelete);
|
||||||
|
|
|
@ -98,7 +98,7 @@ export default class SwipeHandler {
|
||||||
private onSwipe: (xDiff: number, yDiff: number, e: EE, cancelDrag?: (x: boolean, y: boolean) => void) => boolean | void;
|
private onSwipe: (xDiff: number, yDiff: number, e: EE, cancelDrag?: (x: boolean, y: boolean) => void) => boolean | void;
|
||||||
private verifyTouchTarget: (evt: EE) => boolean | Promise<boolean>;
|
private verifyTouchTarget: (evt: EE) => boolean | Promise<boolean>;
|
||||||
private onFirstSwipe: (e: EE) => void;
|
private onFirstSwipe: (e: EE) => void;
|
||||||
private onReset: () => void;
|
private onReset: (e?: Event) => void;
|
||||||
private onStart: () => void;
|
private onStart: () => void;
|
||||||
private onZoom: (details: ZoomDetails) => void;
|
private onZoom: (details: ZoomDetails) => void;
|
||||||
private onDrag: (e: EE, captureEvent: E, details: {dragOffsetX: number, dragOffsetY: number}, cancelDrag: (x: boolean, y: boolean) => void) => void;
|
private onDrag: (e: EE, captureEvent: E, details: {dragOffsetX: number, dragOffsetY: number}, cancelDrag: (x: boolean, y: boolean) => void) => void;
|
||||||
|
@ -252,7 +252,7 @@ export default class SwipeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.hadMove) {
|
if(this.hadMove) {
|
||||||
this.onReset?.();
|
this.onReset?.(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.releaseWheelDrag?.clearTimeout();
|
this.releaseWheelDrag?.clearTimeout();
|
||||||
|
@ -291,10 +291,14 @@ export default class SwipeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
const e = getEvent(_e);
|
const e = getEvent(_e);
|
||||||
if(Math.max(0, e.button ?? 0) !== 0) {
|
if(![0, 1].includes(Math.max(0, e.button ?? 0))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(e.button === 1) {
|
||||||
|
cancelEvent(_e as any);
|
||||||
|
}
|
||||||
|
|
||||||
if(isSwipingBackSafari(_e as any)) {
|
if(isSwipingBackSafari(_e as any)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export default function wrapAlbum({messages, attachmentDiv, middleware, uploadin
|
||||||
|
|
||||||
// !lowest msgID will be the FIRST in album
|
// !lowest msgID will be the FIRST in album
|
||||||
for(const message of messages) {
|
for(const message of messages) {
|
||||||
const media: Photo.photo | Document.document = getMediaFromMessage(message);
|
const media = getMediaFromMessage(message, true);
|
||||||
|
|
||||||
const size: any = media._ === 'photo' ? choosePhotoSize(media, 480, 480) : {w: media.w, h: media.h};
|
const size: any = media._ === 'photo' ? choosePhotoSize(media, 480, 480) : {w: media.w, h: media.h};
|
||||||
items.push({size, media, message});
|
items.push({size, media, message});
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default async function wrapPhoto({photo, message, container, boxWidth, bo
|
||||||
isOut?: boolean,
|
isOut?: boolean,
|
||||||
lazyLoadQueue?: LazyLoadQueue,
|
lazyLoadQueue?: LazyLoadQueue,
|
||||||
middleware?: Middleware,
|
middleware?: Middleware,
|
||||||
size?: PhotoSize | VideoSize,
|
size?: PhotoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||||
withoutPreloader?: boolean,
|
withoutPreloader?: boolean,
|
||||||
loadPromises?: Promise<any>[],
|
loadPromises?: Promise<any>[],
|
||||||
autoDownloadSize?: number,
|
autoDownloadSize?: number,
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default async function wrapSticker({doc, div, middleware, loadStickerMidd
|
||||||
skipRatio?: number,
|
skipRatio?: number,
|
||||||
static?: boolean,
|
static?: boolean,
|
||||||
managers?: AppManagers,
|
managers?: AppManagers,
|
||||||
fullThumb?: PhotoSize | VideoSize,
|
fullThumb?: PhotoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||||
isOut?: boolean,
|
isOut?: boolean,
|
||||||
noPremium?: boolean,
|
noPremium?: boolean,
|
||||||
withLock?: boolean,
|
withLock?: boolean,
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default function wrapStickerAnimation({
|
||||||
skipRatio?: number,
|
skipRatio?: number,
|
||||||
play: boolean,
|
play: boolean,
|
||||||
managers?: AppManagers,
|
managers?: AppManagers,
|
||||||
fullThumb?: PhotoSize | VideoSize,
|
fullThumb?: PhotoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||||
withRandomOffset?: boolean,
|
withRandomOffset?: boolean,
|
||||||
relativeEffect?: boolean,
|
relativeEffect?: boolean,
|
||||||
loopEffect?: boolean
|
loopEffect?: boolean
|
||||||
|
|
|
@ -82,7 +82,7 @@ export default async function wrapVideo({doc, container, message, boxWidth, boxH
|
||||||
loadPromises?: Promise<any>[],
|
loadPromises?: Promise<any>[],
|
||||||
autoDownload?: ChatAutoDownloadSettings,
|
autoDownload?: ChatAutoDownloadSettings,
|
||||||
photoSize?: PhotoSize,
|
photoSize?: PhotoSize,
|
||||||
videoSize?: VideoSize,
|
videoSize?: Extract<VideoSize, VideoSize.videoSize>,
|
||||||
searchContext?: MediaSearchContext,
|
searchContext?: MediaSearchContext,
|
||||||
managers?: AppManagers,
|
managers?: AppManagers,
|
||||||
noAutoplayAttribute?: boolean
|
noAutoplayAttribute?: boolean
|
||||||
|
|
|
@ -21,7 +21,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.8.6',
|
langPackVersion: '0.9.3',
|
||||||
langPack: 'webk',
|
langPack: 'webk',
|
||||||
langPackCode: 'en',
|
langPackCode: 'en',
|
||||||
domains: MAIN_DOMAINS,
|
domains: MAIN_DOMAINS,
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export type AUDIO_MIME_TYPE = 'audio/mpeg' | 'audio/aac' | 'audio/wav';
|
||||||
|
const AUDIO_MIME_TYPES_SUPPORTED: Set<AUDIO_MIME_TYPE> = new Set([
|
||||||
|
'audio/mpeg',
|
||||||
|
'audio/aac',
|
||||||
|
'audio/wav'
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default AUDIO_MIME_TYPES_SUPPORTED;
|
|
@ -56,7 +56,7 @@ declare global {
|
||||||
|
|
||||||
type LocalFileError = ApiFileManagerError | ReferenceError | StorageError;
|
type LocalFileError = ApiFileManagerError | ReferenceError | StorageError;
|
||||||
type LocalErrorType = LocalFileError | NetworkerError | FiltersError |
|
type LocalErrorType = LocalFileError | NetworkerError | FiltersError |
|
||||||
'UNKNOWN' | 'NO_DOC' | 'MIDDLEWARE' | 'PORT_DISCONNECTED' | 'NO_AUTO_DOWNLOAD';
|
'UNKNOWN' | 'NO_DOC' | 'MIDDLEWARE' | 'PORT_DISCONNECTED' | 'NO_AUTO_DOWNLOAD' | 'CHAT_PRIVATE';
|
||||||
|
|
||||||
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' |
|
||||||
|
@ -64,7 +64,8 @@ declare global {
|
||||||
'VOICE_MESSAGES_FORBIDDEN' | 'PHOTO_INVALID_DIMENSIONS' | 'PHOTO_SAVE_FILE_INVALID' |
|
'VOICE_MESSAGES_FORBIDDEN' | 'PHOTO_INVALID_DIMENSIONS' | 'PHOTO_SAVE_FILE_INVALID' |
|
||||||
'USER_ALREADY_PARTICIPANT' | 'USERNAME_INVALID' | 'USERNAME_PURCHASE_AVAILABLE' | 'USERNAMES_ACTIVE_TOO_MUCH' |
|
'USER_ALREADY_PARTICIPANT' | 'USERNAME_INVALID' | 'USERNAME_PURCHASE_AVAILABLE' | 'USERNAMES_ACTIVE_TOO_MUCH' |
|
||||||
'BOT_INVALID' | 'USERNAME_NOT_OCCUPIED' | 'PINNED_TOO_MUCH' | 'LOCATION_INVALID' |
|
'BOT_INVALID' | 'USERNAME_NOT_OCCUPIED' | 'PINNED_TOO_MUCH' | 'LOCATION_INVALID' |
|
||||||
'FILE_ID_INVALID' | 'CHANNEL_FORUM_MISSING' | 'TRANSCRIPTION_FAILED';
|
'FILE_ID_INVALID' | 'CHANNEL_FORUM_MISSING' | 'TRANSCRIPTION_FAILED' | 'USER_NOT_PARTICIPANT' |
|
||||||
|
'PEER_ID_INVALID';
|
||||||
|
|
||||||
type ErrorType = LocalErrorType | ServerErrorType;
|
type ErrorType = LocalErrorType | ServerErrorType;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,17 @@ import type ListenerSetter from '../listenerSetter';
|
||||||
import IS_TOUCH_SUPPORTED from '../../environment/touchSupport';
|
import IS_TOUCH_SUPPORTED from '../../environment/touchSupport';
|
||||||
import simulateEvent from './dispatchEvent';
|
import simulateEvent from './dispatchEvent';
|
||||||
|
|
||||||
|
let lastMouseDownElement: HTMLElement;
|
||||||
|
document.addEventListener('mousedown', (e) => {
|
||||||
|
lastMouseDownElement = e.target as HTMLElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
export function hasMouseMovedSinceDown(e: Event) {
|
||||||
|
if(e.isTrusted && e.type === 'click' && e.target !== lastMouseDownElement) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const CLICK_EVENT_NAME: 'mousedown' /* | 'touchend' */ | 'click' = (IS_TOUCH_SUPPORTED ? 'mousedown' : 'click') as any;
|
export const CLICK_EVENT_NAME: 'mousedown' /* | 'touchend' */ | 'click' = (IS_TOUCH_SUPPORTED ? 'mousedown' : 'click') as any;
|
||||||
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
|
export type AttachClickOptions = AddEventListenerOptions & Partial<{listenerSetter: ListenerSetter, touchMouseDown: true}>;
|
||||||
export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* TouchEvent | */MouseEvent) => void, options: AttachClickOptions = {}) {
|
export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* TouchEvent | */MouseEvent) => void, options: AttachClickOptions = {}) {
|
||||||
|
@ -42,6 +53,18 @@ export function attachClickEvent(elem: HTMLElement | Window, callback: (e: /* To
|
||||||
} else {
|
} else {
|
||||||
add(CLICK_EVENT_NAME, callback, options);
|
add(CLICK_EVENT_NAME, callback, options);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
if(CLICK_EVENT_NAME === 'click') {
|
||||||
|
const cb = callback;
|
||||||
|
callback = (e) => {
|
||||||
|
if(hasMouseMovedSinceDown(e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
add(CLICK_EVENT_NAME, callback, options);
|
add(CLICK_EVENT_NAME, callback, options);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -8,7 +8,7 @@ import appNavigationController, {NavigationItem} from '../components/appNavigati
|
||||||
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
import IS_TOUCH_SUPPORTED from '../environment/touchSupport';
|
||||||
import {IS_MOBILE_SAFARI} from '../environment/userAgent';
|
import {IS_MOBILE_SAFARI} from '../environment/userAgent';
|
||||||
import cancelEvent from './dom/cancelEvent';
|
import cancelEvent from './dom/cancelEvent';
|
||||||
import {CLICK_EVENT_NAME} from './dom/clickEvent';
|
import {CLICK_EVENT_NAME, hasMouseMovedSinceDown} from './dom/clickEvent';
|
||||||
import findUpAsChild from './dom/findUpAsChild';
|
import findUpAsChild from './dom/findUpAsChild';
|
||||||
import EventListenerBase from './eventListenerBase';
|
import EventListenerBase from './eventListenerBase';
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ export default class OverlayClickHandler extends EventListenerBase<{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onClick = (e: MouseEvent | TouchEvent) => {
|
protected onClick = (e: MouseEvent | TouchEvent) => {
|
||||||
|
if(hasMouseMovedSinceDown(e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(this.element && findUpAsChild(e.target as HTMLElement, this.element)) {
|
if(this.element && findUpAsChild(e.target as HTMLElement, this.element)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
31
src/lang.ts
31
src/lang.ts
|
@ -82,6 +82,7 @@ const lang = {
|
||||||
'Message.Context.Selection.Clear': 'Clear selection',
|
'Message.Context.Selection.Clear': 'Clear selection',
|
||||||
'Message.Context.Selection.Delete': 'Delete selected',
|
'Message.Context.Selection.Delete': 'Delete selected',
|
||||||
'Message.Context.Selection.Forward': 'Forward selected',
|
'Message.Context.Selection.Forward': 'Forward selected',
|
||||||
|
'Message.Context.Selection.Download': 'Download selected',
|
||||||
'Message.Context.Selection.SendNow': 'Send Now selected',
|
'Message.Context.Selection.SendNow': 'Send Now selected',
|
||||||
'Message.Unsupported.Desktop': '__This message is currently not supported on Telegram Web. Try [getdesktop.telegram.org](https://getdesktop.telegram.org/)__',
|
'Message.Unsupported.Desktop': '__This message is currently not supported on Telegram Web. Try [getdesktop.telegram.org](https://getdesktop.telegram.org/)__',
|
||||||
'Message.Unsupported.Mobile': '__This message is currently not supported on Telegram Web. Try [telegram.org/dl](https://telegram.org/dl/)__',
|
'Message.Unsupported.Mobile': '__This message is currently not supported on Telegram Web. Try [telegram.org/dl](https://telegram.org/dl/)__',
|
||||||
|
@ -109,6 +110,7 @@ const lang = {
|
||||||
'one_value': '%d exception',
|
'one_value': '%d exception',
|
||||||
'other_value': '%d exceptions'
|
'other_value': '%d exceptions'
|
||||||
},
|
},
|
||||||
|
'Permissions.RemoveFromGroup': 'Are you sure you want to remove **%s** from the group?',
|
||||||
'PWA.Install': 'Install App',
|
'PWA.Install': 'Install App',
|
||||||
'Link.Available': 'Link is available',
|
'Link.Available': 'Link is available',
|
||||||
'Link.Taken': 'Link is already taken',
|
'Link.Taken': 'Link is already taken',
|
||||||
|
@ -400,6 +402,12 @@ const lang = {
|
||||||
'UserRestrictionsSendMedia': 'Send Media',
|
'UserRestrictionsSendMedia': 'Send Media',
|
||||||
'UserRestrictionsSendPolls': 'Send Polls',
|
'UserRestrictionsSendPolls': 'Send Polls',
|
||||||
'UserRestrictionsSendStickers': 'Send Stickers and GIFs',
|
'UserRestrictionsSendStickers': 'Send Stickers and GIFs',
|
||||||
|
'UserRestrictionsSendPhotos': 'Send Photos',
|
||||||
|
'UserRestrictionsSendVideos': 'Send Videos',
|
||||||
|
'UserRestrictionsSendMusic': 'Send Music',
|
||||||
|
'UserRestrictionsSendFiles': 'Send Files',
|
||||||
|
'UserRestrictionsSendVoices': 'Send Voice Messages',
|
||||||
|
'UserRestrictionsSendRound': 'Send Video Messages',
|
||||||
'UserRestrictionsEmbedLinks': 'Embed Links',
|
'UserRestrictionsEmbedLinks': 'Embed Links',
|
||||||
'UserRestrictionsChangeInfo': 'Change Chat Info',
|
'UserRestrictionsChangeInfo': 'Change Chat Info',
|
||||||
'UserRestrictionsPinMessages': 'Pin Messages',
|
'UserRestrictionsPinMessages': 'Pin Messages',
|
||||||
|
@ -409,6 +417,12 @@ const lang = {
|
||||||
'UserRestrictionsNoSendMedia': 'no media',
|
'UserRestrictionsNoSendMedia': 'no media',
|
||||||
'UserRestrictionsNoSendPolls': 'no polls',
|
'UserRestrictionsNoSendPolls': 'no polls',
|
||||||
'UserRestrictionsNoSendStickers': 'no stickers & GIFs',
|
'UserRestrictionsNoSendStickers': 'no stickers & GIFs',
|
||||||
|
'UserRestrictionsNoSendPhotos': 'no photos',
|
||||||
|
'UserRestrictionsNoSendVideos': 'no videos',
|
||||||
|
'UserRestrictionsNoSendMusic': 'no music',
|
||||||
|
'UserRestrictionsNoSendDocs': 'no files',
|
||||||
|
'UserRestrictionsNoSendVoice': 'no voice',
|
||||||
|
'UserRestrictionsNoSendRound': 'no round',
|
||||||
'UserRestrictionsNoEmbedLinks': 'no embed links',
|
'UserRestrictionsNoEmbedLinks': 'no embed links',
|
||||||
'UserRestrictionsNoChangeInfo': 'can\'t change Info',
|
'UserRestrictionsNoChangeInfo': 'can\'t change Info',
|
||||||
'UserRestrictionsNoPinMessages': 'no pins',
|
'UserRestrictionsNoPinMessages': 'no pins',
|
||||||
|
@ -892,6 +906,22 @@ const lang = {
|
||||||
'ThemeDay': 'Day',
|
'ThemeDay': 'Day',
|
||||||
'ThemeNight': 'Night',
|
'ThemeNight': 'Night',
|
||||||
'AutoNightSystemDefault': 'System Default',
|
'AutoNightSystemDefault': 'System Default',
|
||||||
|
'GlobalAttachPlainRestricted': 'Sending text messages isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachDocumentsRestricted': 'Sending documents isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachMediaRestricted': 'Sending media isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachAudioRestricted': 'Sending music isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachPhotoRestricted': 'Sending photos isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachVideoRestricted': 'Sending videos isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachVoiceRestricted': 'Sending voice isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachRoundRestricted': 'Sending round videos isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachInlineRestricted': 'Sending inline content isn\'t allowed in this group.',
|
||||||
|
'GlobalAttachStickersRestricted': 'Stickers aren\'t allowed in this group.',
|
||||||
|
'GlobalAttachGifRestricted': 'Sending GIFs is not allowed in this group.',
|
||||||
|
'GlobalAttachEmojiRestricted': 'Text messages aren\'t allowed in this group.',
|
||||||
|
'GlobalSendMessageRestricted': 'Sending messages is not allowed in this group.',
|
||||||
|
'ErrorSendRestrictedPollsAll': 'Sorry, sending polls is not allowed in this group.',
|
||||||
|
'Remove': 'Remove',
|
||||||
|
'ChannelBlockUser': 'Remove User',
|
||||||
|
|
||||||
// * macos
|
// * macos
|
||||||
'AccountSettings.Filters': 'Chat Folders',
|
'AccountSettings.Filters': 'Chat Folders',
|
||||||
|
@ -1110,6 +1140,7 @@ const lang = {
|
||||||
'ChatList.Mute.3Days': 'For 3 Days',
|
'ChatList.Mute.3Days': 'For 3 Days',
|
||||||
'ChatList.Mute.Forever': 'Forever',
|
'ChatList.Mute.Forever': 'Forever',
|
||||||
'Channel.DescriptionHolderDescrpiton': 'You can provide an optional description for your channel.',
|
'Channel.DescriptionHolderDescrpiton': 'You can provide an optional description for your channel.',
|
||||||
|
'Channel.Persmission.MessageBlock': 'Text is not Allowed',
|
||||||
'ChannelVisibility.Confirm.MakePrivate.Channel': 'If you make this channel private, the name @%@ will be removed. Anyone else will be able to take it for their public groups or channels.',
|
'ChannelVisibility.Confirm.MakePrivate.Channel': 'If you make this channel private, the name @%@ will be removed. Anyone else will be able to take it for their public groups or channels.',
|
||||||
'ChannelVisibility.Confirm.MakePrivate.Group': 'If you make this group private, the name @%@ will be removed. Anyone else will be able to take it for their public groups or channels.',
|
'ChannelVisibility.Confirm.MakePrivate.Group': 'If you make this group private, the name @%@ will be removed. Anyone else will be able to take it for their public groups or channels.',
|
||||||
'Context.ViewStickerSet': 'View Sticker Set',
|
'Context.ViewStickerSet': 'View Sticker Set',
|
||||||
|
|
|
@ -285,7 +285,8 @@ export namespace InputChatPhoto {
|
||||||
flags?: number,
|
flags?: number,
|
||||||
file?: InputFile,
|
file?: InputFile,
|
||||||
video?: InputFile,
|
video?: InputFile,
|
||||||
video_start_ts?: number
|
video_start_ts?: number,
|
||||||
|
video_emoji_markup?: VideoSize
|
||||||
};
|
};
|
||||||
|
|
||||||
export type inputChatPhoto = {
|
export type inputChatPhoto = {
|
||||||
|
@ -699,6 +700,7 @@ export namespace ChatFull {
|
||||||
pFlags: Partial<{
|
pFlags: Partial<{
|
||||||
can_set_username?: true,
|
can_set_username?: true,
|
||||||
has_scheduled?: true,
|
has_scheduled?: true,
|
||||||
|
translations_disabled?: true,
|
||||||
}>,
|
}>,
|
||||||
id: string | number,
|
id: string | number,
|
||||||
about: string,
|
about: string,
|
||||||
|
@ -733,6 +735,7 @@ export namespace ChatFull {
|
||||||
can_delete_channel?: true,
|
can_delete_channel?: true,
|
||||||
antispam?: true,
|
antispam?: true,
|
||||||
participants_hidden?: true,
|
participants_hidden?: true,
|
||||||
|
translations_disabled?: true,
|
||||||
}>,
|
}>,
|
||||||
flags2?: number,
|
flags2?: number,
|
||||||
id: string | number,
|
id: string | number,
|
||||||
|
@ -1071,7 +1074,7 @@ export namespace MessageMedia {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/MessageAction
|
* @link https://core.telegram.org/type/MessageAction
|
||||||
*/
|
*/
|
||||||
export type MessageAction = MessageAction.messageActionEmpty | MessageAction.messageActionChatCreate | MessageAction.messageActionChatEditTitle | MessageAction.messageActionChatEditPhoto | MessageAction.messageActionChatDeletePhoto | MessageAction.messageActionChatAddUser | MessageAction.messageActionChatDeleteUser | MessageAction.messageActionChatJoinedByLink | MessageAction.messageActionChannelCreate | MessageAction.messageActionChatMigrateTo | MessageAction.messageActionChannelMigrateFrom | MessageAction.messageActionPinMessage | MessageAction.messageActionHistoryClear | MessageAction.messageActionGameScore | MessageAction.messageActionPaymentSentMe | MessageAction.messageActionPaymentSent | MessageAction.messageActionPhoneCall | MessageAction.messageActionScreenshotTaken | MessageAction.messageActionCustomAction | MessageAction.messageActionBotAllowed | MessageAction.messageActionSecureValuesSentMe | MessageAction.messageActionSecureValuesSent | MessageAction.messageActionContactSignUp | MessageAction.messageActionGeoProximityReached | MessageAction.messageActionGroupCall | MessageAction.messageActionInviteToGroupCall | MessageAction.messageActionSetMessagesTTL | MessageAction.messageActionGroupCallScheduled | MessageAction.messageActionSetChatTheme | MessageAction.messageActionChatJoinedByRequest | MessageAction.messageActionWebViewDataSentMe | MessageAction.messageActionWebViewDataSent | MessageAction.messageActionGiftPremium | MessageAction.messageActionTopicCreate | MessageAction.messageActionTopicEdit | MessageAction.messageActionSuggestProfilePhoto | MessageAction.messageActionAttachMenuBotAllowed | MessageAction.messageActionDiscussionStarted | MessageAction.messageActionChatLeave | MessageAction.messageActionChannelDeletePhoto | MessageAction.messageActionChannelEditTitle | MessageAction.messageActionChannelEditPhoto | MessageAction.messageActionChannelEditVideo | MessageAction.messageActionChatEditVideo | MessageAction.messageActionChatAddUsers | MessageAction.messageActionChatJoined | MessageAction.messageActionChatReturn | MessageAction.messageActionChatJoinedYou | MessageAction.messageActionChatReturnYou;
|
export type MessageAction = MessageAction.messageActionEmpty | MessageAction.messageActionChatCreate | MessageAction.messageActionChatEditTitle | MessageAction.messageActionChatEditPhoto | MessageAction.messageActionChatDeletePhoto | MessageAction.messageActionChatAddUser | MessageAction.messageActionChatDeleteUser | MessageAction.messageActionChatJoinedByLink | MessageAction.messageActionChannelCreate | MessageAction.messageActionChatMigrateTo | MessageAction.messageActionChannelMigrateFrom | MessageAction.messageActionPinMessage | MessageAction.messageActionHistoryClear | MessageAction.messageActionGameScore | MessageAction.messageActionPaymentSentMe | MessageAction.messageActionPaymentSent | MessageAction.messageActionPhoneCall | MessageAction.messageActionScreenshotTaken | MessageAction.messageActionCustomAction | MessageAction.messageActionBotAllowed | MessageAction.messageActionSecureValuesSentMe | MessageAction.messageActionSecureValuesSent | MessageAction.messageActionContactSignUp | MessageAction.messageActionGeoProximityReached | MessageAction.messageActionGroupCall | MessageAction.messageActionInviteToGroupCall | MessageAction.messageActionSetMessagesTTL | MessageAction.messageActionGroupCallScheduled | MessageAction.messageActionSetChatTheme | MessageAction.messageActionChatJoinedByRequest | MessageAction.messageActionWebViewDataSentMe | MessageAction.messageActionWebViewDataSent | MessageAction.messageActionGiftPremium | MessageAction.messageActionTopicCreate | MessageAction.messageActionTopicEdit | MessageAction.messageActionSuggestProfilePhoto | MessageAction.messageActionAttachMenuBotAllowed | MessageAction.messageActionRequestedPeer | MessageAction.messageActionDiscussionStarted | MessageAction.messageActionChatLeave | MessageAction.messageActionChannelDeletePhoto | MessageAction.messageActionChannelEditTitle | MessageAction.messageActionChannelEditPhoto | MessageAction.messageActionChannelEditVideo | MessageAction.messageActionChatEditVideo | MessageAction.messageActionChatAddUsers | MessageAction.messageActionChatJoined | MessageAction.messageActionChatReturn | MessageAction.messageActionChatJoinedYou | MessageAction.messageActionChatReturnYou;
|
||||||
|
|
||||||
export namespace MessageAction {
|
export namespace MessageAction {
|
||||||
export type messageActionEmpty = {
|
export type messageActionEmpty = {
|
||||||
|
@ -1296,6 +1299,12 @@ export namespace MessageAction {
|
||||||
_: 'messageActionAttachMenuBotAllowed'
|
_: 'messageActionAttachMenuBotAllowed'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type messageActionRequestedPeer = {
|
||||||
|
_: 'messageActionRequestedPeer',
|
||||||
|
button_id: number,
|
||||||
|
peer: Peer
|
||||||
|
};
|
||||||
|
|
||||||
export type messageActionDiscussionStarted = {
|
export type messageActionDiscussionStarted = {
|
||||||
_: 'messageActionDiscussionStarted'
|
_: 'messageActionDiscussionStarted'
|
||||||
};
|
};
|
||||||
|
@ -1527,7 +1536,7 @@ export namespace GeoPoint {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/auth.SentCode
|
* @link https://core.telegram.org/type/auth.SentCode
|
||||||
*/
|
*/
|
||||||
export type AuthSentCode = AuthSentCode.authSentCode;
|
export type AuthSentCode = AuthSentCode.authSentCode | AuthSentCode.authSentCodeSuccess;
|
||||||
|
|
||||||
export namespace AuthSentCode {
|
export namespace AuthSentCode {
|
||||||
export type authSentCode = {
|
export type authSentCode = {
|
||||||
|
@ -1539,6 +1548,11 @@ export namespace AuthSentCode {
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
phone_number?: string
|
phone_number?: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type authSentCodeSuccess = {
|
||||||
|
_: 'auth.sentCodeSuccess',
|
||||||
|
authorization: AuthAuthorization
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1555,6 +1569,7 @@ export namespace AuthAuthorization {
|
||||||
}>,
|
}>,
|
||||||
otherwise_relogin_days?: number,
|
otherwise_relogin_days?: number,
|
||||||
tmp_sessions?: number,
|
tmp_sessions?: number,
|
||||||
|
future_auth_token?: Uint8Array,
|
||||||
user: User
|
user: User
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1766,6 +1781,7 @@ export namespace UserFull {
|
||||||
has_scheduled?: true,
|
has_scheduled?: true,
|
||||||
video_calls_available?: true,
|
video_calls_available?: true,
|
||||||
voice_messages_forbidden?: true,
|
voice_messages_forbidden?: true,
|
||||||
|
translations_disabled?: true,
|
||||||
}>,
|
}>,
|
||||||
id: string | number,
|
id: string | number,
|
||||||
about?: string,
|
about?: string,
|
||||||
|
@ -2086,7 +2102,7 @@ export namespace MessagesFilter {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/Update
|
* @link https://core.telegram.org/type/Update
|
||||||
*/
|
*/
|
||||||
export type Update = Update.updateNewMessage | Update.updateMessageID | Update.updateDeleteMessages | Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChatParticipants | Update.updateUserStatus | Update.updateUserName | Update.updateNewEncryptedMessage | Update.updateEncryptedChatTyping | Update.updateEncryption | Update.updateEncryptedMessagesRead | Update.updateChatParticipantAdd | Update.updateChatParticipantDelete | Update.updateDcOptions | Update.updateNotifySettings | Update.updateServiceNotification | Update.updatePrivacy | Update.updateUserPhone | Update.updateReadHistoryInbox | Update.updateReadHistoryOutbox | Update.updateWebPage | Update.updateReadMessagesContents | Update.updateChannelTooLong | Update.updateChannel | Update.updateNewChannelMessage | Update.updateReadChannelInbox | Update.updateDeleteChannelMessages | Update.updateChannelMessageViews | Update.updateChatParticipantAdmin | Update.updateNewStickerSet | Update.updateStickerSetsOrder | Update.updateStickerSets | Update.updateSavedGifs | Update.updateBotInlineQuery | Update.updateBotInlineSend | Update.updateEditChannelMessage | Update.updateBotCallbackQuery | Update.updateEditMessage | Update.updateInlineBotCallbackQuery | Update.updateReadChannelOutbox | Update.updateDraftMessage | Update.updateReadFeaturedStickers | Update.updateRecentStickers | Update.updateConfig | Update.updatePtsChanged | Update.updateChannelWebPage | Update.updateDialogPinned | Update.updatePinnedDialogs | Update.updateBotWebhookJSON | Update.updateBotWebhookJSONQuery | Update.updateBotShippingQuery | Update.updateBotPrecheckoutQuery | Update.updatePhoneCall | Update.updateLangPackTooLong | Update.updateLangPack | Update.updateFavedStickers | Update.updateChannelReadMessagesContents | Update.updateContactsReset | Update.updateChannelAvailableMessages | Update.updateDialogUnreadMark | Update.updateMessagePoll | Update.updateChatDefaultBannedRights | Update.updateFolderPeers | Update.updatePeerSettings | Update.updatePeerLocated | Update.updateNewScheduledMessage | Update.updateDeleteScheduledMessages | Update.updateTheme | Update.updateGeoLiveViewed | Update.updateLoginToken | Update.updateMessagePollVote | Update.updateDialogFilter | Update.updateDialogFilterOrder | Update.updateDialogFilters | Update.updatePhoneCallSignalingData | Update.updateChannelMessageForwards | Update.updateReadChannelDiscussionInbox | Update.updateReadChannelDiscussionOutbox | Update.updatePeerBlocked | Update.updateChannelUserTyping | Update.updatePinnedMessages | Update.updatePinnedChannelMessages | Update.updateChat | Update.updateGroupCallParticipants | Update.updateGroupCall | Update.updatePeerHistoryTTL | Update.updateChatParticipant | Update.updateChannelParticipant | Update.updateBotStopped | Update.updateGroupCallConnection | Update.updateBotCommands | Update.updatePendingJoinRequests | Update.updateBotChatInviteRequester | Update.updateMessageReactions | Update.updateAttachMenuBots | Update.updateWebViewResultSent | Update.updateBotMenuButton | Update.updateSavedRingtones | Update.updateTranscribedAudio | Update.updateReadFeaturedEmojiStickers | Update.updateUserEmojiStatus | Update.updateRecentEmojiStatuses | Update.updateRecentReactions | Update.updateMoveStickerSetToTop | Update.updateMessageExtendedMedia | Update.updateChannelPinnedTopic | Update.updateChannelPinnedTopics | Update.updateUser | Update.updateNewDiscussionMessage | Update.updateDeleteDiscussionMessages | Update.updateChannelReload;
|
export type Update = Update.updateNewMessage | Update.updateMessageID | Update.updateDeleteMessages | Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChatParticipants | Update.updateUserStatus | Update.updateUserName | Update.updateNewEncryptedMessage | Update.updateEncryptedChatTyping | Update.updateEncryption | Update.updateEncryptedMessagesRead | Update.updateChatParticipantAdd | Update.updateChatParticipantDelete | Update.updateDcOptions | Update.updateNotifySettings | Update.updateServiceNotification | Update.updatePrivacy | Update.updateUserPhone | Update.updateReadHistoryInbox | Update.updateReadHistoryOutbox | Update.updateWebPage | Update.updateReadMessagesContents | Update.updateChannelTooLong | Update.updateChannel | Update.updateNewChannelMessage | Update.updateReadChannelInbox | Update.updateDeleteChannelMessages | Update.updateChannelMessageViews | Update.updateChatParticipantAdmin | Update.updateNewStickerSet | Update.updateStickerSetsOrder | Update.updateStickerSets | Update.updateSavedGifs | Update.updateBotInlineQuery | Update.updateBotInlineSend | Update.updateEditChannelMessage | Update.updateBotCallbackQuery | Update.updateEditMessage | Update.updateInlineBotCallbackQuery | Update.updateReadChannelOutbox | Update.updateDraftMessage | Update.updateReadFeaturedStickers | Update.updateRecentStickers | Update.updateConfig | Update.updatePtsChanged | Update.updateChannelWebPage | Update.updateDialogPinned | Update.updatePinnedDialogs | Update.updateBotWebhookJSON | Update.updateBotWebhookJSONQuery | Update.updateBotShippingQuery | Update.updateBotPrecheckoutQuery | Update.updatePhoneCall | Update.updateLangPackTooLong | Update.updateLangPack | Update.updateFavedStickers | Update.updateChannelReadMessagesContents | Update.updateContactsReset | Update.updateChannelAvailableMessages | Update.updateDialogUnreadMark | Update.updateMessagePoll | Update.updateChatDefaultBannedRights | Update.updateFolderPeers | Update.updatePeerSettings | Update.updatePeerLocated | Update.updateNewScheduledMessage | Update.updateDeleteScheduledMessages | Update.updateTheme | Update.updateGeoLiveViewed | Update.updateLoginToken | Update.updateMessagePollVote | Update.updateDialogFilter | Update.updateDialogFilterOrder | Update.updateDialogFilters | Update.updatePhoneCallSignalingData | Update.updateChannelMessageForwards | Update.updateReadChannelDiscussionInbox | Update.updateReadChannelDiscussionOutbox | Update.updatePeerBlocked | Update.updateChannelUserTyping | Update.updatePinnedMessages | Update.updatePinnedChannelMessages | Update.updateChat | Update.updateGroupCallParticipants | Update.updateGroupCall | Update.updatePeerHistoryTTL | Update.updateChatParticipant | Update.updateChannelParticipant | Update.updateBotStopped | Update.updateGroupCallConnection | Update.updateBotCommands | Update.updatePendingJoinRequests | Update.updateBotChatInviteRequester | Update.updateMessageReactions | Update.updateAttachMenuBots | Update.updateWebViewResultSent | Update.updateBotMenuButton | Update.updateSavedRingtones | Update.updateTranscribedAudio | Update.updateReadFeaturedEmojiStickers | Update.updateUserEmojiStatus | Update.updateRecentEmojiStatuses | Update.updateRecentReactions | Update.updateMoveStickerSetToTop | Update.updateMessageExtendedMedia | Update.updateChannelPinnedTopic | Update.updateChannelPinnedTopics | Update.updateUser | Update.updateAutoSaveSettings | Update.updateNewDiscussionMessage | Update.updateDeleteDiscussionMessages | Update.updateChannelReload;
|
||||||
|
|
||||||
export namespace Update {
|
export namespace Update {
|
||||||
export type updateNewMessage = {
|
export type updateNewMessage = {
|
||||||
|
@ -2868,6 +2884,10 @@ export namespace Update {
|
||||||
user_id: string | number
|
user_id: string | number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type updateAutoSaveSettings = {
|
||||||
|
_: 'updateAutoSaveSettings'
|
||||||
|
};
|
||||||
|
|
||||||
export type updateNewDiscussionMessage = {
|
export type updateNewDiscussionMessage = {
|
||||||
_: 'updateNewDiscussionMessage',
|
_: 'updateNewDiscussionMessage',
|
||||||
message?: Message
|
message?: Message
|
||||||
|
@ -4325,7 +4345,7 @@ export namespace BotInfo {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/KeyboardButton
|
* @link https://core.telegram.org/type/KeyboardButton
|
||||||
*/
|
*/
|
||||||
export type KeyboardButton = KeyboardButton.keyboardButton | KeyboardButton.keyboardButtonUrl | KeyboardButton.keyboardButtonCallback | KeyboardButton.keyboardButtonRequestPhone | KeyboardButton.keyboardButtonRequestGeoLocation | KeyboardButton.keyboardButtonSwitchInline | KeyboardButton.keyboardButtonGame | KeyboardButton.keyboardButtonBuy | KeyboardButton.keyboardButtonUrlAuth | KeyboardButton.inputKeyboardButtonUrlAuth | KeyboardButton.keyboardButtonRequestPoll | KeyboardButton.inputKeyboardButtonUserProfile | KeyboardButton.keyboardButtonUserProfile | KeyboardButton.keyboardButtonWebView | KeyboardButton.keyboardButtonSimpleWebView;
|
export type KeyboardButton = KeyboardButton.keyboardButton | KeyboardButton.keyboardButtonUrl | KeyboardButton.keyboardButtonCallback | KeyboardButton.keyboardButtonRequestPhone | KeyboardButton.keyboardButtonRequestGeoLocation | KeyboardButton.keyboardButtonSwitchInline | KeyboardButton.keyboardButtonGame | KeyboardButton.keyboardButtonBuy | KeyboardButton.keyboardButtonUrlAuth | KeyboardButton.inputKeyboardButtonUrlAuth | KeyboardButton.keyboardButtonRequestPoll | KeyboardButton.inputKeyboardButtonUserProfile | KeyboardButton.keyboardButtonUserProfile | KeyboardButton.keyboardButtonWebView | KeyboardButton.keyboardButtonSimpleWebView | KeyboardButton.keyboardButtonRequestPeer;
|
||||||
|
|
||||||
export namespace KeyboardButton {
|
export namespace KeyboardButton {
|
||||||
export type keyboardButton = {
|
export type keyboardButton = {
|
||||||
|
@ -4430,6 +4450,13 @@ export namespace KeyboardButton {
|
||||||
text: string,
|
text: string,
|
||||||
url: string
|
url: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type keyboardButtonRequestPeer = {
|
||||||
|
_: 'keyboardButtonRequestPeer',
|
||||||
|
text: string,
|
||||||
|
button_id: number,
|
||||||
|
peer_type: RequestPeerType
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5268,7 +5295,7 @@ export namespace AuthCodeType {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/auth.SentCodeType
|
* @link https://core.telegram.org/type/auth.SentCodeType
|
||||||
*/
|
*/
|
||||||
export type AuthSentCodeType = AuthSentCodeType.authSentCodeTypeApp | AuthSentCodeType.authSentCodeTypeSms | AuthSentCodeType.authSentCodeTypeCall | AuthSentCodeType.authSentCodeTypeFlashCall | AuthSentCodeType.authSentCodeTypeMissedCall | AuthSentCodeType.authSentCodeTypeEmailCode | AuthSentCodeType.authSentCodeTypeSetUpEmailRequired | AuthSentCodeType.authSentCodeTypeFragmentSms;
|
export type AuthSentCodeType = AuthSentCodeType.authSentCodeTypeApp | AuthSentCodeType.authSentCodeTypeSms | AuthSentCodeType.authSentCodeTypeCall | AuthSentCodeType.authSentCodeTypeFlashCall | AuthSentCodeType.authSentCodeTypeMissedCall | AuthSentCodeType.authSentCodeTypeEmailCode | AuthSentCodeType.authSentCodeTypeSetUpEmailRequired | AuthSentCodeType.authSentCodeTypeFragmentSms | AuthSentCodeType.authSentCodeTypeFirebaseSms;
|
||||||
|
|
||||||
export namespace AuthSentCodeType {
|
export namespace AuthSentCodeType {
|
||||||
export type authSentCodeTypeApp = {
|
export type authSentCodeTypeApp = {
|
||||||
|
@ -5323,6 +5350,15 @@ export namespace AuthSentCodeType {
|
||||||
url: string,
|
url: string,
|
||||||
length: number
|
length: number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type authSentCodeTypeFirebaseSms = {
|
||||||
|
_: 'auth.sentCodeTypeFirebaseSms',
|
||||||
|
flags?: number,
|
||||||
|
nonce?: Uint8Array,
|
||||||
|
receipt?: string,
|
||||||
|
push_timeout?: number,
|
||||||
|
length: number
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8090,6 +8126,13 @@ export namespace ChatBannedRights {
|
||||||
invite_users?: true,
|
invite_users?: true,
|
||||||
pin_messages?: true,
|
pin_messages?: true,
|
||||||
manage_topics?: true,
|
manage_topics?: true,
|
||||||
|
send_photos?: true,
|
||||||
|
send_videos?: true,
|
||||||
|
send_roundvideos?: true,
|
||||||
|
send_audios?: true,
|
||||||
|
send_voices?: true,
|
||||||
|
send_docs?: true,
|
||||||
|
send_plain?: true,
|
||||||
}>,
|
}>,
|
||||||
until_date: number
|
until_date: number
|
||||||
};
|
};
|
||||||
|
@ -8149,8 +8192,11 @@ export namespace CodeSettings {
|
||||||
current_number?: true,
|
current_number?: true,
|
||||||
allow_app_hash?: true,
|
allow_app_hash?: true,
|
||||||
allow_missed_call?: true,
|
allow_missed_call?: true,
|
||||||
|
allow_firebase?: true,
|
||||||
}>,
|
}>,
|
||||||
logout_tokens?: Array<Uint8Array>
|
logout_tokens?: Array<Uint8Array>,
|
||||||
|
token?: string,
|
||||||
|
app_sandbox?: boolean
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8857,7 +8903,7 @@ export namespace HelpPromoData {
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/VideoSize
|
* @link https://core.telegram.org/type/VideoSize
|
||||||
*/
|
*/
|
||||||
export type VideoSize = VideoSize.videoSize;
|
export type VideoSize = VideoSize.videoSize | VideoSize.videoSizeEmojiMarkup | VideoSize.videoSizeStickerMarkup;
|
||||||
|
|
||||||
export namespace VideoSize {
|
export namespace VideoSize {
|
||||||
export type videoSize = {
|
export type videoSize = {
|
||||||
|
@ -8869,6 +8915,19 @@ export namespace VideoSize {
|
||||||
size: number,
|
size: number,
|
||||||
video_start_ts?: number
|
video_start_ts?: number
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type videoSizeEmojiMarkup = {
|
||||||
|
_: 'videoSizeEmojiMarkup',
|
||||||
|
emoji_id: string | number,
|
||||||
|
background_colors: Array<number>
|
||||||
|
};
|
||||||
|
|
||||||
|
export type videoSizeStickerMarkup = {
|
||||||
|
_: 'videoSizeStickerMarkup',
|
||||||
|
stickerset: InputStickerSet,
|
||||||
|
sticker_id: string | number,
|
||||||
|
background_colors: Array<number>
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9798,22 +9857,6 @@ export namespace MessagesAvailableReactions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @link https://core.telegram.org/type/messages.TranslatedText
|
|
||||||
*/
|
|
||||||
export type MessagesTranslatedText = MessagesTranslatedText.messagesTranslateNoResult | MessagesTranslatedText.messagesTranslateResultText;
|
|
||||||
|
|
||||||
export namespace MessagesTranslatedText {
|
|
||||||
export type messagesTranslateNoResult = {
|
|
||||||
_: 'messages.translateNoResult'
|
|
||||||
};
|
|
||||||
|
|
||||||
export type messagesTranslateResultText = {
|
|
||||||
_: 'messages.translateResultText',
|
|
||||||
text: string
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/MessagePeerReaction
|
* @link https://core.telegram.org/type/MessagePeerReaction
|
||||||
*/
|
*/
|
||||||
|
@ -10171,6 +10214,7 @@ export namespace InputStorePaymentPurpose {
|
||||||
flags?: number,
|
flags?: number,
|
||||||
pFlags: Partial<{
|
pFlags: Partial<{
|
||||||
restore?: true,
|
restore?: true,
|
||||||
|
upgrade?: true,
|
||||||
}>
|
}>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10212,6 +10256,19 @@ export namespace PaymentFormMethod {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/bots.PremiumGiftsOptions
|
||||||
|
*/
|
||||||
|
export type BotsPremiumGiftsOptions = BotsPremiumGiftsOptions.botsPremiumGiftsOptions;
|
||||||
|
|
||||||
|
export namespace BotsPremiumGiftsOptions {
|
||||||
|
export type botsPremiumGiftsOptions = {
|
||||||
|
_: 'bots.premiumGiftsOptions',
|
||||||
|
name: string,
|
||||||
|
gifts: Array<PremiumGiftOption>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @link https://core.telegram.org/type/EmojiStatus
|
* @link https://core.telegram.org/type/EmojiStatus
|
||||||
*/
|
*/
|
||||||
|
@ -10387,6 +10444,7 @@ export namespace PremiumSubscriptionOption {
|
||||||
current?: true,
|
current?: true,
|
||||||
can_purchase_upgrade?: true,
|
can_purchase_upgrade?: true,
|
||||||
}>,
|
}>,
|
||||||
|
transaction?: string,
|
||||||
months: number,
|
months: number,
|
||||||
currency: string,
|
currency: string,
|
||||||
amount: string | number,
|
amount: string | number,
|
||||||
|
@ -10549,6 +10607,164 @@ export namespace ExportedContactToken {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/RequestPeerType
|
||||||
|
*/
|
||||||
|
export type RequestPeerType = RequestPeerType.requestPeerTypeUser | RequestPeerType.requestPeerTypeChat | RequestPeerType.requestPeerTypeBroadcast;
|
||||||
|
|
||||||
|
export namespace RequestPeerType {
|
||||||
|
export type requestPeerTypeUser = {
|
||||||
|
_: 'requestPeerTypeUser',
|
||||||
|
flags?: number,
|
||||||
|
bot?: boolean,
|
||||||
|
premium?: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
export type requestPeerTypeChat = {
|
||||||
|
_: 'requestPeerTypeChat',
|
||||||
|
flags?: number,
|
||||||
|
pFlags: Partial<{
|
||||||
|
creator?: true,
|
||||||
|
bot_participant?: true,
|
||||||
|
}>,
|
||||||
|
has_username?: boolean,
|
||||||
|
forum?: boolean,
|
||||||
|
user_admin_rights?: ChatAdminRights,
|
||||||
|
bot_admin_rights?: ChatAdminRights
|
||||||
|
};
|
||||||
|
|
||||||
|
export type requestPeerTypeBroadcast = {
|
||||||
|
_: 'requestPeerTypeBroadcast',
|
||||||
|
flags?: number,
|
||||||
|
pFlags: Partial<{
|
||||||
|
creator?: true,
|
||||||
|
}>,
|
||||||
|
has_username?: boolean,
|
||||||
|
user_admin_rights?: ChatAdminRights,
|
||||||
|
bot_admin_rights?: ChatAdminRights
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/EmojiList
|
||||||
|
*/
|
||||||
|
export type EmojiList = EmojiList.emojiListNotModified | EmojiList.emojiList;
|
||||||
|
|
||||||
|
export namespace EmojiList {
|
||||||
|
export type emojiListNotModified = {
|
||||||
|
_: 'emojiListNotModified'
|
||||||
|
};
|
||||||
|
|
||||||
|
export type emojiList = {
|
||||||
|
_: 'emojiList',
|
||||||
|
hash: string | number,
|
||||||
|
document_id: Array<string | number>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/EmojiGroup
|
||||||
|
*/
|
||||||
|
export type EmojiGroup = EmojiGroup.emojiGroup;
|
||||||
|
|
||||||
|
export namespace EmojiGroup {
|
||||||
|
export type emojiGroup = {
|
||||||
|
_: 'emojiGroup',
|
||||||
|
title: string,
|
||||||
|
icon_emoji_id: string | number,
|
||||||
|
emoticons: Array<string>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/messages.EmojiGroups
|
||||||
|
*/
|
||||||
|
export type MessagesEmojiGroups = MessagesEmojiGroups.messagesEmojiGroupsNotModified | MessagesEmojiGroups.messagesEmojiGroups;
|
||||||
|
|
||||||
|
export namespace MessagesEmojiGroups {
|
||||||
|
export type messagesEmojiGroupsNotModified = {
|
||||||
|
_: 'messages.emojiGroupsNotModified'
|
||||||
|
};
|
||||||
|
|
||||||
|
export type messagesEmojiGroups = {
|
||||||
|
_: 'messages.emojiGroups',
|
||||||
|
hash: number,
|
||||||
|
groups: Array<EmojiGroup>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/TextWithEntities
|
||||||
|
*/
|
||||||
|
export type TextWithEntities = TextWithEntities.textWithEntities;
|
||||||
|
|
||||||
|
export namespace TextWithEntities {
|
||||||
|
export type textWithEntities = {
|
||||||
|
_: 'textWithEntities',
|
||||||
|
text: string,
|
||||||
|
entities: Array<MessageEntity>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/messages.TranslatedText
|
||||||
|
*/
|
||||||
|
export type MessagesTranslatedText = MessagesTranslatedText.messagesTranslateResult;
|
||||||
|
|
||||||
|
export namespace MessagesTranslatedText {
|
||||||
|
export type messagesTranslateResult = {
|
||||||
|
_: 'messages.translateResult',
|
||||||
|
result: Array<TextWithEntities>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/AutoSaveSettings
|
||||||
|
*/
|
||||||
|
export type AutoSaveSettings = AutoSaveSettings.autoSaveSettings;
|
||||||
|
|
||||||
|
export namespace AutoSaveSettings {
|
||||||
|
export type autoSaveSettings = {
|
||||||
|
_: 'autoSaveSettings',
|
||||||
|
flags?: number,
|
||||||
|
pFlags: Partial<{
|
||||||
|
photos?: true,
|
||||||
|
videos?: true,
|
||||||
|
}>,
|
||||||
|
video_max_size?: string | number
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/AutoSaveException
|
||||||
|
*/
|
||||||
|
export type AutoSaveException = AutoSaveException.autoSaveException;
|
||||||
|
|
||||||
|
export namespace AutoSaveException {
|
||||||
|
export type autoSaveException = {
|
||||||
|
_: 'autoSaveException',
|
||||||
|
peer: Peer,
|
||||||
|
settings: AutoSaveSettings
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://core.telegram.org/type/account.AutoSaveSettings
|
||||||
|
*/
|
||||||
|
export type AccountAutoSaveSettings = AccountAutoSaveSettings.accountAutoSaveSettings;
|
||||||
|
|
||||||
|
export namespace AccountAutoSaveSettings {
|
||||||
|
export type accountAutoSaveSettings = {
|
||||||
|
_: 'account.autoSaveSettings',
|
||||||
|
users_settings: AutoSaveSettings,
|
||||||
|
chats_settings: AutoSaveSettings,
|
||||||
|
broadcasts_settings: AutoSaveSettings,
|
||||||
|
exceptions: Array<AutoSaveException>,
|
||||||
|
chats: Array<Chat>,
|
||||||
|
users: Array<User>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConstructorDeclMap {
|
export interface ConstructorDeclMap {
|
||||||
'error': Error.error,
|
'error': Error.error,
|
||||||
'inputPeerEmpty': InputPeer.inputPeerEmpty,
|
'inputPeerEmpty': InputPeer.inputPeerEmpty,
|
||||||
|
@ -11495,8 +11711,6 @@ export interface ConstructorDeclMap {
|
||||||
'messages.availableReactions': MessagesAvailableReactions.messagesAvailableReactions,
|
'messages.availableReactions': MessagesAvailableReactions.messagesAvailableReactions,
|
||||||
'messageEntitySpoiler': MessageEntity.messageEntitySpoiler,
|
'messageEntitySpoiler': MessageEntity.messageEntitySpoiler,
|
||||||
'channelAdminLogEventActionChangeAvailableReactions': ChannelAdminLogEventAction.channelAdminLogEventActionChangeAvailableReactions,
|
'channelAdminLogEventActionChangeAvailableReactions': ChannelAdminLogEventAction.channelAdminLogEventActionChangeAvailableReactions,
|
||||||
'messages.translateNoResult': MessagesTranslatedText.messagesTranslateNoResult,
|
|
||||||
'messages.translateResultText': MessagesTranslatedText.messagesTranslateResultText,
|
|
||||||
'messagePeerReaction': MessagePeerReaction.messagePeerReaction,
|
'messagePeerReaction': MessagePeerReaction.messagePeerReaction,
|
||||||
'groupCallStreamChannel': GroupCallStreamChannel.groupCallStreamChannel,
|
'groupCallStreamChannel': GroupCallStreamChannel.groupCallStreamChannel,
|
||||||
'phone.groupCallStreamChannels': PhoneGroupCallStreamChannels.phoneGroupCallStreamChannels,
|
'phone.groupCallStreamChannels': PhoneGroupCallStreamChannels.phoneGroupCallStreamChannels,
|
||||||
|
@ -11557,6 +11771,7 @@ export interface ConstructorDeclMap {
|
||||||
'privacyKeyVoiceMessages': PrivacyKey.privacyKeyVoiceMessages,
|
'privacyKeyVoiceMessages': PrivacyKey.privacyKeyVoiceMessages,
|
||||||
'paymentFormMethod': PaymentFormMethod.paymentFormMethod,
|
'paymentFormMethod': PaymentFormMethod.paymentFormMethod,
|
||||||
'inputWebFileAudioAlbumThumbLocation': InputWebFileLocation.inputWebFileAudioAlbumThumbLocation,
|
'inputWebFileAudioAlbumThumbLocation': InputWebFileLocation.inputWebFileAudioAlbumThumbLocation,
|
||||||
|
'bots.premiumGiftsOptions': BotsPremiumGiftsOptions.botsPremiumGiftsOptions,
|
||||||
'emojiStatusEmpty': EmojiStatus.emojiStatusEmpty,
|
'emojiStatusEmpty': EmojiStatus.emojiStatusEmpty,
|
||||||
'emojiStatus': EmojiStatus.emojiStatus,
|
'emojiStatus': EmojiStatus.emojiStatus,
|
||||||
'emojiStatusUntil': EmojiStatus.emojiStatusUntil,
|
'emojiStatusUntil': EmojiStatus.emojiStatusUntil,
|
||||||
|
@ -11619,6 +11834,26 @@ export interface ConstructorDeclMap {
|
||||||
'messageActionAttachMenuBotAllowed': MessageAction.messageActionAttachMenuBotAllowed,
|
'messageActionAttachMenuBotAllowed': MessageAction.messageActionAttachMenuBotAllowed,
|
||||||
'stickerSetNoCovered': StickerSetCovered.stickerSetNoCovered,
|
'stickerSetNoCovered': StickerSetCovered.stickerSetNoCovered,
|
||||||
'updateUser': Update.updateUser,
|
'updateUser': Update.updateUser,
|
||||||
|
'auth.sentCodeSuccess': AuthSentCode.authSentCodeSuccess,
|
||||||
|
'messageActionRequestedPeer': MessageAction.messageActionRequestedPeer,
|
||||||
|
'requestPeerTypeUser': RequestPeerType.requestPeerTypeUser,
|
||||||
|
'requestPeerTypeChat': RequestPeerType.requestPeerTypeChat,
|
||||||
|
'requestPeerTypeBroadcast': RequestPeerType.requestPeerTypeBroadcast,
|
||||||
|
'keyboardButtonRequestPeer': KeyboardButton.keyboardButtonRequestPeer,
|
||||||
|
'emojiListNotModified': EmojiList.emojiListNotModified,
|
||||||
|
'emojiList': EmojiList.emojiList,
|
||||||
|
'auth.sentCodeTypeFirebaseSms': AuthSentCodeType.authSentCodeTypeFirebaseSms,
|
||||||
|
'emojiGroup': EmojiGroup.emojiGroup,
|
||||||
|
'messages.emojiGroupsNotModified': MessagesEmojiGroups.messagesEmojiGroupsNotModified,
|
||||||
|
'messages.emojiGroups': MessagesEmojiGroups.messagesEmojiGroups,
|
||||||
|
'videoSizeEmojiMarkup': VideoSize.videoSizeEmojiMarkup,
|
||||||
|
'videoSizeStickerMarkup': VideoSize.videoSizeStickerMarkup,
|
||||||
|
'textWithEntities': TextWithEntities.textWithEntities,
|
||||||
|
'messages.translateResult': MessagesTranslatedText.messagesTranslateResult,
|
||||||
|
'autoSaveSettings': AutoSaveSettings.autoSaveSettings,
|
||||||
|
'autoSaveException': AutoSaveException.autoSaveException,
|
||||||
|
'account.autoSaveSettings': AccountAutoSaveSettings.accountAutoSaveSettings,
|
||||||
|
'updateAutoSaveSettings': Update.updateAutoSaveSettings,
|
||||||
'messageEntityEmoji': MessageEntity.messageEntityEmoji,
|
'messageEntityEmoji': MessageEntity.messageEntityEmoji,
|
||||||
'messageEntityHighlight': MessageEntity.messageEntityHighlight,
|
'messageEntityHighlight': MessageEntity.messageEntityHighlight,
|
||||||
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
|
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
|
||||||
|
@ -11998,7 +12233,8 @@ export type PhotosUploadProfilePhoto = {
|
||||||
fallback?: boolean,
|
fallback?: boolean,
|
||||||
file?: InputFile,
|
file?: InputFile,
|
||||||
video?: InputFile,
|
video?: InputFile,
|
||||||
video_start_ts?: number
|
video_start_ts?: number,
|
||||||
|
video_emoji_markup?: VideoSize
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PhotosDeletePhotos = {
|
export type PhotosDeletePhotos = {
|
||||||
|
@ -12353,6 +12589,7 @@ export type ChannelsCreateChannel = {
|
||||||
broadcast?: boolean,
|
broadcast?: boolean,
|
||||||
megagroup?: boolean,
|
megagroup?: boolean,
|
||||||
for_import?: boolean,
|
for_import?: boolean,
|
||||||
|
forum?: boolean,
|
||||||
title: string,
|
title: string,
|
||||||
about: string,
|
about: string,
|
||||||
geo_point?: InputGeoPoint,
|
geo_point?: InputGeoPoint,
|
||||||
|
@ -12807,6 +13044,8 @@ export type StickersCreateStickerSet = {
|
||||||
masks?: boolean,
|
masks?: boolean,
|
||||||
animated?: boolean,
|
animated?: boolean,
|
||||||
videos?: boolean,
|
videos?: boolean,
|
||||||
|
emojis?: boolean,
|
||||||
|
text_color?: boolean,
|
||||||
user_id: InputUser,
|
user_id: InputUser,
|
||||||
title: string,
|
title: string,
|
||||||
short_name: string,
|
short_name: string,
|
||||||
|
@ -14034,9 +14273,8 @@ export type MessagesSetDefaultReaction = {
|
||||||
export type MessagesTranslateText = {
|
export type MessagesTranslateText = {
|
||||||
flags?: number,
|
flags?: number,
|
||||||
peer?: InputPeer,
|
peer?: InputPeer,
|
||||||
msg_id?: number,
|
id?: Array<number>,
|
||||||
text?: string,
|
text?: Array<TextWithEntities>,
|
||||||
from_lang?: string,
|
|
||||||
to_lang: string
|
to_lang: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14392,7 +14630,8 @@ export type PhotosUploadContactProfilePhoto = {
|
||||||
user_id: InputUser,
|
user_id: InputUser,
|
||||||
file?: InputFile,
|
file?: InputFile,
|
||||||
video?: InputFile,
|
video?: InputFile,
|
||||||
video_start_ts?: number
|
video_start_ts?: number,
|
||||||
|
video_emoji_markup?: VideoSize
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ChannelsToggleParticipantsHidden = {
|
export type ChannelsToggleParticipantsHidden = {
|
||||||
|
@ -14400,6 +14639,69 @@ export type ChannelsToggleParticipantsHidden = {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MessagesSendBotRequestedPeer = {
|
||||||
|
peer: InputPeer,
|
||||||
|
msg_id: number,
|
||||||
|
button_id: number,
|
||||||
|
requested_peer: InputPeer
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AccountGetDefaultProfilePhotoEmojis = {
|
||||||
|
hash: string | number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AccountGetDefaultGroupPhotoEmojis = {
|
||||||
|
hash: string | number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AuthRequestFirebaseSms = {
|
||||||
|
flags?: number,
|
||||||
|
phone_number: string,
|
||||||
|
phone_code_hash: string,
|
||||||
|
safety_net_token?: string,
|
||||||
|
ios_push_secret?: string
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessagesGetEmojiGroups = {
|
||||||
|
hash: number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessagesGetEmojiStatusGroups = {
|
||||||
|
hash: number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessagesGetEmojiProfilePhotoGroups = {
|
||||||
|
hash: number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessagesSearchCustomEmoji = {
|
||||||
|
emoticon: string,
|
||||||
|
hash: string | number
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MessagesTogglePeerTranslations = {
|
||||||
|
flags?: number,
|
||||||
|
disabled?: boolean,
|
||||||
|
peer: InputPeer
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AccountGetAutoSaveSettings = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AccountSaveAutoSaveSettings = {
|
||||||
|
flags?: number,
|
||||||
|
users?: boolean,
|
||||||
|
chats?: boolean,
|
||||||
|
broadcasts?: boolean,
|
||||||
|
peer?: InputPeer,
|
||||||
|
settings: AutoSaveSettings
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AccountDeleteAutoSaveExceptions = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
export interface MethodDeclMap {
|
export interface MethodDeclMap {
|
||||||
'invokeAfterMsg': {req: InvokeAfterMsg, res: any},
|
'invokeAfterMsg': {req: InvokeAfterMsg, res: any},
|
||||||
'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any},
|
'invokeAfterMsgs': {req: InvokeAfterMsgs, res: any},
|
||||||
|
@ -14879,5 +15181,17 @@ export interface MethodDeclMap {
|
||||||
'contacts.importContactToken': {req: ContactsImportContactToken, res: User},
|
'contacts.importContactToken': {req: ContactsImportContactToken, res: User},
|
||||||
'photos.uploadContactProfilePhoto': {req: PhotosUploadContactProfilePhoto, res: PhotosPhoto},
|
'photos.uploadContactProfilePhoto': {req: PhotosUploadContactProfilePhoto, res: PhotosPhoto},
|
||||||
'channels.toggleParticipantsHidden': {req: ChannelsToggleParticipantsHidden, res: Updates},
|
'channels.toggleParticipantsHidden': {req: ChannelsToggleParticipantsHidden, res: Updates},
|
||||||
|
'messages.sendBotRequestedPeer': {req: MessagesSendBotRequestedPeer, res: Updates},
|
||||||
|
'account.getDefaultProfilePhotoEmojis': {req: AccountGetDefaultProfilePhotoEmojis, res: EmojiList},
|
||||||
|
'account.getDefaultGroupPhotoEmojis': {req: AccountGetDefaultGroupPhotoEmojis, res: EmojiList},
|
||||||
|
'auth.requestFirebaseSms': {req: AuthRequestFirebaseSms, res: boolean},
|
||||||
|
'messages.getEmojiGroups': {req: MessagesGetEmojiGroups, res: MessagesEmojiGroups},
|
||||||
|
'messages.getEmojiStatusGroups': {req: MessagesGetEmojiStatusGroups, res: MessagesEmojiGroups},
|
||||||
|
'messages.getEmojiProfilePhotoGroups': {req: MessagesGetEmojiProfilePhotoGroups, res: MessagesEmojiGroups},
|
||||||
|
'messages.searchCustomEmoji': {req: MessagesSearchCustomEmoji, res: EmojiList},
|
||||||
|
'messages.togglePeerTranslations': {req: MessagesTogglePeerTranslations, res: boolean},
|
||||||
|
'account.getAutoSaveSettings': {req: AccountGetAutoSaveSettings, res: AccountAutoSaveSettings},
|
||||||
|
'account.saveAutoSaveSettings': {req: AccountSaveAutoSaveSettings, res: boolean},
|
||||||
|
'account.deleteAutoSaveExceptions': {req: AccountDeleteAutoSaveExceptions, res: boolean},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import parseMarkdown from '../richTextProcessor/parseMarkdown';
|
||||||
import ctx from '../../environment/ctx';
|
import ctx from '../../environment/ctx';
|
||||||
import EventListenerBase from '../../helpers/eventListenerBase';
|
import EventListenerBase from '../../helpers/eventListenerBase';
|
||||||
import applyMixins from '../../helpers/applyMixins';
|
import applyMixins from '../../helpers/applyMixins';
|
||||||
|
import tsNow from '../../helpers/tsNow';
|
||||||
|
|
||||||
type UpdatesState = {
|
type UpdatesState = {
|
||||||
pendingPtsUpdates: (Update & {pts: number, pts_count: number})[],
|
pendingPtsUpdates: (Update & {pts: number, pts_count: number})[],
|
||||||
|
@ -311,7 +312,7 @@ class ApiUpdatesManager {
|
||||||
updatesState.date = nextState.date;
|
updatesState.date = nextState.date;
|
||||||
} else {
|
} else {
|
||||||
updatesState.pts = differenceResult.pts;
|
updatesState.pts = differenceResult.pts;
|
||||||
updatesState.date = (Date.now() / 1000 | 0) + this.timeManager.getServerTimeOffset();
|
updatesState.date = tsNow(true) + this.timeManager.getServerTimeOffset();
|
||||||
delete updatesState.seq;
|
delete updatesState.seq;
|
||||||
|
|
||||||
this.channelStates = {};
|
this.channelStates = {};
|
||||||
|
@ -740,7 +741,7 @@ class ApiUpdatesManager {
|
||||||
message,
|
message,
|
||||||
type: 'local',
|
type: 'local',
|
||||||
pFlags: {},
|
pFlags: {},
|
||||||
inbox_date: Date.now() / 1000 | 0,
|
inbox_date: tsNow(true),
|
||||||
media: undefined
|
media: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
import deepEqual from '../../helpers/object/deepEqual';
|
import deepEqual from '../../helpers/object/deepEqual';
|
||||||
import isObject from '../../helpers/object/isObject';
|
import isObject from '../../helpers/object/isObject';
|
||||||
import safeReplaceObject from '../../helpers/object/safeReplaceObject';
|
import safeReplaceObject from '../../helpers/object/safeReplaceObject';
|
||||||
import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatPhoto, ChatReactions, InputChannel, InputChatPhoto, InputFile, InputPeer, MessagesSponsoredMessages, SponsoredMessage, Update, Updates} from '../../layer';
|
import {ChannelParticipant, ChannelsCreateChannel, Chat, ChatAdminRights, ChatBannedRights, ChatInvite, ChatParticipant, ChatPhoto, ChatReactions, InputChannel, InputChatPhoto, InputFile, InputPeer, MessagesSponsoredMessages, SponsoredMessage, Update, Updates} from '../../layer';
|
||||||
import {isRestricted} from '../../helpers/restrictions';
|
import {isRestricted} from '../../helpers/restrictions';
|
||||||
import {AppManager} from './manager';
|
import {AppManager} from './manager';
|
||||||
import hasRights from './utils/chats/hasRights';
|
import hasRights from './utils/chats/hasRights';
|
||||||
|
@ -21,6 +21,7 @@ import {AppStoragesManager} from './appStoragesManager';
|
||||||
import getServerMessageId from './utils/messageId/getServerMessageId';
|
import getServerMessageId from './utils/messageId/getServerMessageId';
|
||||||
import {randomLong} from '../../helpers/random';
|
import {randomLong} from '../../helpers/random';
|
||||||
import generateMessageId from './utils/messageId/generateMessageId';
|
import generateMessageId from './utils/messageId/generateMessageId';
|
||||||
|
import tsNow from '../../helpers/tsNow';
|
||||||
|
|
||||||
export type Channel = Chat.channel;
|
export type Channel = Chat.channel;
|
||||||
export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat' | 'view_participants';
|
export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRights['pFlags'] | 'change_type' | 'change_permissions' | 'delete_chat' | 'view_participants';
|
||||||
|
@ -37,26 +38,9 @@ export class AppChatsManager extends AppManager {
|
||||||
this.clear(true);
|
this.clear(true);
|
||||||
|
|
||||||
this.apiUpdatesManager.addMultipleEventsListeners({
|
this.apiUpdatesManager.addMultipleEventsListeners({
|
||||||
/* updateChannel: (update) => {
|
updateChannelParticipant: this.onUpdateChannelParticipant,
|
||||||
const channelId = update.channel_id;
|
|
||||||
//console.log('updateChannel:', update);
|
|
||||||
rootScope.broadcast('channel_settings', {channelId});
|
|
||||||
}, */
|
|
||||||
|
|
||||||
updateChannelParticipant: (update) => {
|
updateChatDefaultBannedRights: this.onUpdateChatDefaultBannedRights
|
||||||
this.apiManager.clearCache('channels.getParticipants', (params) => {
|
|
||||||
return (params.channel as InputChannel.inputChannel).channel_id === update.channel_id;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChatDefaultBannedRights: (update) => {
|
|
||||||
const chatId = this.appPeersManager.getPeerId(update.peer).toChatId();
|
|
||||||
const chat = this.chats[chatId] as Chat.chat;
|
|
||||||
if(chat) {
|
|
||||||
chat.default_banned_rights = update.default_banned_rights;
|
|
||||||
this.rootScope.dispatchEvent('chat_update', chatId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
@ -535,8 +519,14 @@ export class AppChatsManager extends AppManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public editBanned(id: ChatId, participant: PeerId | ChannelParticipant, banned_rights: ChatBannedRights) {
|
public async editBanned(id: ChatId, participant: PeerId | ChannelParticipant | ChatParticipant, banned_rights: ChatBannedRights) {
|
||||||
const peerId = typeof(participant) !== 'object' ? participant : getParticipantPeerId(participant);
|
const peerId = typeof(participant) !== 'object' ? participant : getParticipantPeerId(participant);
|
||||||
|
const wasChannel = this.isChannel(id);
|
||||||
|
if(!wasChannel) {
|
||||||
|
const channelId = await this.migrateChat(id);
|
||||||
|
id = channelId;
|
||||||
|
}
|
||||||
|
|
||||||
return this.apiManager.invokeApi('channels.editBanned', {
|
return this.apiManager.invokeApi('channels.editBanned', {
|
||||||
channel: this.getChannelInput(id),
|
channel: this.getChannelInput(id),
|
||||||
participant: this.appPeersManager.getInputPeerById(peerId),
|
participant: this.appPeersManager.getInputPeerById(peerId),
|
||||||
|
@ -545,7 +535,7 @@ export class AppChatsManager extends AppManager {
|
||||||
this.onChatUpdated(id, updates);
|
this.onChatUpdated(id, updates);
|
||||||
|
|
||||||
if(typeof(participant) === 'object') {
|
if(typeof(participant) === 'object') {
|
||||||
const timestamp = Date.now() / 1000 | 0;
|
const timestamp = tsNow(true);
|
||||||
this.apiUpdatesManager.processLocalUpdate({
|
this.apiUpdatesManager.processLocalUpdate({
|
||||||
_: 'updateChannelParticipant',
|
_: 'updateChannelParticipant',
|
||||||
channel_id: id,
|
channel_id: id,
|
||||||
|
@ -553,7 +543,7 @@ export class AppChatsManager extends AppManager {
|
||||||
actor_id: undefined,
|
actor_id: undefined,
|
||||||
qts: undefined,
|
qts: undefined,
|
||||||
user_id: peerId,
|
user_id: peerId,
|
||||||
prev_participant: participant,
|
prev_participant: wasChannel ? participant as ChannelParticipant : undefined,
|
||||||
new_participant: Object.keys(banned_rights.pFlags).length ? {
|
new_participant: Object.keys(banned_rights.pFlags).length ? {
|
||||||
_: 'channelParticipantBanned',
|
_: 'channelParticipantBanned',
|
||||||
date: timestamp,
|
date: timestamp,
|
||||||
|
@ -585,9 +575,9 @@ export class AppChatsManager extends AppManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public kickFromChat(id: ChatId, participant: PeerId | ChannelParticipant) {
|
public kickFromChat(id: ChatId, participant: PeerId | ChannelParticipant | ChatParticipant) {
|
||||||
if(this.isChannel(id)) return this.kickFromChannel(id, participant);
|
if(this.isChannel(id)) return this.kickFromChannel(id, participant as ChannelParticipant);
|
||||||
else return this.deleteChatUser(id, (participant as PeerId).toUserId());
|
else return this.deleteChatUser(id, isObject(participant) ? getParticipantPeerId(participant) : (participant as PeerId).toUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public resolveChannel(id: ChatId) {
|
public resolveChannel(id: ChatId) {
|
||||||
|
@ -904,4 +894,21 @@ export class AppChatsManager extends AppManager {
|
||||||
pinned
|
pinned
|
||||||
}).then(this.onChatUpdated.bind(this, chatId));
|
}).then(this.onChatUpdated.bind(this, chatId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onUpdateChannelParticipant = (update: Update.updateChannelParticipant) => {
|
||||||
|
this.apiManager.clearCache('channels.getParticipants', (params) => {
|
||||||
|
return (params.channel as InputChannel.inputChannel).channel_id === update.channel_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rootScope.dispatchEvent('chat_participant', update);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onUpdateChatDefaultBannedRights = (update: Update.updateChatDefaultBannedRights) => {
|
||||||
|
const chatId = this.appPeersManager.getPeerId(update.peer).toChatId();
|
||||||
|
const chat = this.chats[chatId] as Chat.chat;
|
||||||
|
if(chat) {
|
||||||
|
chat.default_banned_rights = update.default_banned_rights;
|
||||||
|
this.rootScope.dispatchEvent('chat_update', chatId);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ type DialogElementOptions = {
|
||||||
wrapOptions?: WrapSomethingOptions,
|
wrapOptions?: WrapSomethingOptions,
|
||||||
isMainList?: boolean
|
isMainList?: boolean
|
||||||
};
|
};
|
||||||
class DialogElement extends Row {
|
export class DialogElement extends Row {
|
||||||
public dom: DialogDom;
|
public dom: DialogDom;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
|
@ -2748,7 +2748,7 @@ export class AppDialogsManager {
|
||||||
let mediaContainer: HTMLElement;
|
let mediaContainer: HTMLElement;
|
||||||
const willPrepend: (Promise<any> | HTMLElement)[] = [];
|
const willPrepend: (Promise<any> | HTMLElement)[] = [];
|
||||||
if(lastMessage && !draftMessage && !isRestricted) {
|
if(lastMessage && !draftMessage && !isRestricted) {
|
||||||
const media: MyDocument | MyPhoto = getMediaFromMessage(lastMessage);
|
const media = getMediaFromMessage(lastMessage, true);
|
||||||
const videoTypes: Set<MyDocument['type']> = new Set(['video', 'gif', 'round']);
|
const videoTypes: Set<MyDocument['type']> = new Set(['video', 'gif', 'round']);
|
||||||
if(media && (media._ === 'photo' || videoTypes.has(media.type))) {
|
if(media && (media._ === 'photo' || videoTypes.has(media.type))) {
|
||||||
const size = choosePhotoSize(media, 20, 20);
|
const size = choosePhotoSize(media, 20, 20);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import getDocumentURL from './utils/docs/getDocumentURL';
|
||||||
import makeError from '../../helpers/makeError';
|
import makeError from '../../helpers/makeError';
|
||||||
import {EXTENSION_MIME_TYPE_MAP} from '../../environment/mimeTypeMap';
|
import {EXTENSION_MIME_TYPE_MAP} from '../../environment/mimeTypeMap';
|
||||||
import {THUMB_TYPE_FULL} from '../mtproto/mtproto_config';
|
import {THUMB_TYPE_FULL} from '../mtproto/mtproto_config';
|
||||||
|
import tsNow from '../../helpers/tsNow';
|
||||||
|
|
||||||
export type MyDocument = Document.document;
|
export type MyDocument = Document.document;
|
||||||
|
|
||||||
|
@ -331,7 +332,7 @@ export class AppDocsManager extends AppManager {
|
||||||
id,
|
id,
|
||||||
mime_type: file.type as MTMimeType,
|
mime_type: file.type as MTMimeType,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
date: Date.now() / 1000,
|
date: tsNow(true),
|
||||||
pFlags: {},
|
pFlags: {},
|
||||||
thumbs: [thumb],
|
thumbs: [thumb],
|
||||||
file_name: file.name
|
file_name: file.name
|
||||||
|
|
|
@ -239,7 +239,7 @@ export class AppDraftsManager extends AppManager {
|
||||||
public setDraft(peerId: PeerId, threadId: number, message: string, entities?: MessageEntity[]) {
|
public setDraft(peerId: PeerId, threadId: number, message: string, entities?: MessageEntity[]) {
|
||||||
const draft: DraftMessage.draftMessage = {
|
const draft: DraftMessage.draftMessage = {
|
||||||
_: 'draftMessage',
|
_: 'draftMessage',
|
||||||
date: Date.now() / 1000 | 0,
|
date: tsNow(true),
|
||||||
message,
|
message,
|
||||||
pFlags: {},
|
pFlags: {},
|
||||||
entities
|
entities
|
||||||
|
|
|
@ -45,6 +45,7 @@ import debounce from '../../helpers/schedulers/debounce';
|
||||||
import pause from '../../helpers/schedulers/pause';
|
import pause from '../../helpers/schedulers/pause';
|
||||||
import {InternalLink, InternalLinkTypeMap, INTERNAL_LINK_TYPE} from './internalLink';
|
import {InternalLink, InternalLinkTypeMap, INTERNAL_LINK_TYPE} from './internalLink';
|
||||||
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
import MEDIA_MIME_TYPES_SUPPORTED from '../../environment/mediaMimeTypesSupport';
|
||||||
|
import IMAGE_MIME_TYPES_SUPPORTED from '../../environment/imageMimeTypesSupport';
|
||||||
import {NULL_PEER_ID} from '../mtproto/mtproto_config';
|
import {NULL_PEER_ID} from '../mtproto/mtproto_config';
|
||||||
import telegramMeWebManager from '../mtproto/telegramMeWebManager';
|
import telegramMeWebManager from '../mtproto/telegramMeWebManager';
|
||||||
import {ONE_DAY} from '../../helpers/date';
|
import {ONE_DAY} from '../../helpers/date';
|
||||||
|
@ -102,6 +103,8 @@ import findUpTag from '../../helpers/dom/findUpTag';
|
||||||
import {MTAppConfig} from '../mtproto/appConfig';
|
import {MTAppConfig} from '../mtproto/appConfig';
|
||||||
import PopupForward from '../../components/popups/forward';
|
import PopupForward from '../../components/popups/forward';
|
||||||
import AppBackgroundTab from '../../components/sidebarLeft/tabs/background';
|
import AppBackgroundTab from '../../components/sidebarLeft/tabs/background';
|
||||||
|
import partition from '../../helpers/array/partition';
|
||||||
|
import indexOfAndSplice from '../../helpers/array/indexOfAndSplice';
|
||||||
|
|
||||||
export type ChatSavedPosition = {
|
export type ChatSavedPosition = {
|
||||||
mids: number[],
|
mids: number[],
|
||||||
|
@ -1728,24 +1731,43 @@ export class AppImManager extends EventListenerBase<{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rights = await PopupNewMedia.canSend(this.chat.peerId, true);
|
||||||
|
|
||||||
const _dropsContainer = newMediaPopup ? mediaDropsContainer : dropsContainer;
|
const _dropsContainer = newMediaPopup ? mediaDropsContainer : dropsContainer;
|
||||||
const _drops = newMediaPopup ? mediaDrops : drops;
|
const _drops = newMediaPopup ? mediaDrops : drops;
|
||||||
|
|
||||||
if(mount && !_drops.length) {
|
if(mount && !_drops.length) {
|
||||||
const force = isFiles && !types.length; // * can't get file items not from 'drop' on Safari
|
const force = isFiles && !types.length; // * can't get file items not from 'drop' on Safari
|
||||||
|
|
||||||
const foundMedia = types.filter((t) => MEDIA_MIME_TYPES_SUPPORTED.has(t)).length;
|
const [foundMedia, foundDocuments] = partition(types, (t) => MEDIA_MIME_TYPES_SUPPORTED.has(t));
|
||||||
// const foundDocuments = types.length - foundMedia;
|
const [foundPhotos, foundVideos] = partition(foundMedia, (t) => IMAGE_MIME_TYPES_SUPPORTED.has(t));
|
||||||
|
|
||||||
this.log('drag files', types);
|
if(!rights.send_docs) {
|
||||||
|
foundDocuments.length = 0;
|
||||||
|
} else {
|
||||||
|
foundDocuments.push(...foundMedia);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rights.send_photos) {
|
||||||
|
foundPhotos.forEach((mimeType) => indexOfAndSplice(foundMedia, mimeType));
|
||||||
|
foundPhotos.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!rights.send_videos) {
|
||||||
|
foundVideos.forEach((mimeType) => indexOfAndSplice(foundMedia, mimeType));
|
||||||
|
foundVideos.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log('drag files', types, foundMedia, foundDocuments, foundPhotos, foundVideos);
|
||||||
|
|
||||||
if(newMediaPopup) {
|
if(newMediaPopup) {
|
||||||
newMediaPopup.appendDrops(_dropsContainer);
|
newMediaPopup.appendDrops(_dropsContainer);
|
||||||
|
|
||||||
if(types.length || force) {
|
const length = (rights.send_docs ? [foundDocuments] : [foundPhotos, foundVideos]).reduce((acc, v) => acc + v.length, 0);
|
||||||
|
if(length || force) {
|
||||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||||
header: 'Preview.Dragging.AddItems',
|
header: 'Preview.Dragging.AddItems',
|
||||||
headerArgs: [types.length],
|
headerArgs: [length],
|
||||||
onDrop: (e: DragEvent) => {
|
onDrop: (e: DragEvent) => {
|
||||||
toggle(e, false);
|
toggle(e, false);
|
||||||
this.log('drop', e);
|
this.log('drop', e);
|
||||||
|
@ -1754,7 +1776,7 @@ export class AppImManager extends EventListenerBase<{
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(types.length || force) {
|
if(foundDocuments.length || force) {
|
||||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||||
icon: 'dragfiles',
|
icon: 'dragfiles',
|
||||||
header: 'Chat.DropTitle',
|
header: 'Chat.DropTitle',
|
||||||
|
@ -1767,8 +1789,7 @@ export class AppImManager extends EventListenerBase<{
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if((foundMedia && !foundDocuments) || force) {
|
if(foundMedia.length || force) {
|
||||||
if(foundMedia || force) {
|
|
||||||
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
_drops.push(new ChatDragAndDrop(_dropsContainer, {
|
||||||
icon: 'dragmedia',
|
icon: 'dragmedia',
|
||||||
header: 'Chat.DropTitle',
|
header: 'Chat.DropTitle',
|
||||||
|
|
|
@ -524,7 +524,7 @@ export class AppMessagesManager extends AppManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public transcribeAudio(message: Message.message): Promise<MessagesTranscribedAudio> {
|
public async transcribeAudio(message: Message.message): Promise<MessagesTranscribedAudio> {
|
||||||
const {id, peerId} = message;
|
const {id, peerId} = message;
|
||||||
|
|
||||||
const process = (result: MessagesTranscribedAudio) => {
|
const process = (result: MessagesTranscribedAudio) => {
|
||||||
|
@ -3916,16 +3916,12 @@ export class AppMessagesManager extends AppManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMessages(peerId: PeerId, mids: number[], revoke?: boolean) {
|
public async deleteMessages(peerId: PeerId, mids: number[], revoke?: boolean, isRecursion?: boolean) {
|
||||||
let promise: Promise<any>;
|
let promise: Promise<any>;
|
||||||
|
|
||||||
const config = await this.apiManager.getConfig();
|
const isChannel = this.appPeersManager.isChannel(peerId);
|
||||||
const overflowMids = mids.splice(config.forwarded_count_max, mids.length - config.forwarded_count_max);
|
const channelId = isChannel && peerId.toChatId();
|
||||||
|
if(isChannel && !isRecursion) {
|
||||||
const localMessageIds = mids.map((mid) => getServerMessageId(mid));
|
|
||||||
|
|
||||||
if(peerId.isAnyChat() && this.appPeersManager.isChannel(peerId)) {
|
|
||||||
const channelId = peerId.toChatId();
|
|
||||||
const channel = this.appChatsManager.getChat(channelId) as Chat.channel;
|
const channel = this.appChatsManager.getChat(channelId) as Chat.channel;
|
||||||
if(!channel.pFlags.creator && !channel.admin_rights?.pFlags?.delete_messages) {
|
if(!channel.pFlags.creator && !channel.admin_rights?.pFlags?.delete_messages) {
|
||||||
mids = mids.filter((mid) => {
|
mids = mids.filter((mid) => {
|
||||||
|
@ -3937,10 +3933,21 @@ export class AppMessagesManager extends AppManager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await this.apiManager.getConfig();
|
||||||
|
const overflowMids = mids.splice(config.forwarded_count_max, mids.length - config.forwarded_count_max);
|
||||||
|
|
||||||
|
const serverMessageIds = mids.map((mid) => {
|
||||||
|
const messageId = getServerMessageId(mid);
|
||||||
|
// filter outgoing messages
|
||||||
|
return generateMessageId(messageId) === mid && messageId;
|
||||||
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
if(isChannel) {
|
||||||
promise = this.apiManager.invokeApi('channels.deleteMessages', {
|
promise = this.apiManager.invokeApi('channels.deleteMessages', {
|
||||||
channel: this.appChatsManager.getChannelInput(channelId),
|
channel: this.appChatsManager.getChannelInput(channelId),
|
||||||
id: localMessageIds
|
id: serverMessageIds
|
||||||
}).then((affectedMessages) => {
|
}).then((affectedMessages) => {
|
||||||
this.apiUpdatesManager.processLocalUpdate({
|
this.apiUpdatesManager.processLocalUpdate({
|
||||||
_: 'updateDeleteChannelMessages',
|
_: 'updateDeleteChannelMessages',
|
||||||
|
@ -3953,7 +3960,7 @@ export class AppMessagesManager extends AppManager {
|
||||||
} else {
|
} else {
|
||||||
promise = this.apiManager.invokeApi('messages.deleteMessages', {
|
promise = this.apiManager.invokeApi('messages.deleteMessages', {
|
||||||
revoke,
|
revoke,
|
||||||
id: localMessageIds
|
id: serverMessageIds
|
||||||
}).then((affectedMessages) => {
|
}).then((affectedMessages) => {
|
||||||
this.apiUpdatesManager.processLocalUpdate({
|
this.apiUpdatesManager.processLocalUpdate({
|
||||||
_: 'updateDeleteMessages',
|
_: 'updateDeleteMessages',
|
||||||
|
@ -3966,7 +3973,7 @@ export class AppMessagesManager extends AppManager {
|
||||||
|
|
||||||
const promises: (typeof promise)[] = [promise];
|
const promises: (typeof promise)[] = [promise];
|
||||||
if(overflowMids.length) {
|
if(overflowMids.length) {
|
||||||
promises.push(this.deleteMessages(peerId, overflowMids, revoke));
|
promises.push(this.deleteMessages(peerId, overflowMids, revoke, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises).then(noop);
|
return Promise.all(promises).then(noop);
|
||||||
|
|
|
@ -20,6 +20,8 @@ import getParticipantPeerId from './utils/chats/getParticipantPeerId';
|
||||||
import ctx from '../../environment/ctx';
|
import ctx from '../../environment/ctx';
|
||||||
import {ReferenceContext} from '../mtproto/referenceDatabase';
|
import {ReferenceContext} from '../mtproto/referenceDatabase';
|
||||||
import generateMessageId from './utils/messageId/generateMessageId';
|
import generateMessageId from './utils/messageId/generateMessageId';
|
||||||
|
import assumeType from '../../helpers/assumeType';
|
||||||
|
import makeError from '../../helpers/makeError';
|
||||||
|
|
||||||
export type UserTyping = Partial<{userId: UserId, action: SendMessageAction, timeout: number}>;
|
export type UserTyping = Partial<{userId: UserId, action: SendMessageAction, timeout: number}>;
|
||||||
|
|
||||||
|
@ -34,56 +36,11 @@ export class AppProfileManager extends AppManager {
|
||||||
|
|
||||||
protected after() {
|
protected after() {
|
||||||
this.apiUpdatesManager.addMultipleEventsListeners({
|
this.apiUpdatesManager.addMultipleEventsListeners({
|
||||||
updateChatParticipants: (update) => {
|
updateChatParticipants: this.onUpdateChatParticipants,
|
||||||
const participants = update.participants;
|
|
||||||
if(participants._ === 'chatParticipants') {
|
|
||||||
const chatId = participants.chat_id;
|
|
||||||
const chatFull = this.chatsFull[chatId] as ChatFull.chatFull;
|
|
||||||
if(chatFull !== undefined) {
|
|
||||||
chatFull.participants = participants;
|
|
||||||
this.rootScope.dispatchEvent('chat_full_update', chatId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChatParticipantAdd: (update) => {
|
updateChatParticipantAdd: this.onUpdateChatParticipantAdd,
|
||||||
const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;
|
|
||||||
if(chatFull !== undefined) {
|
|
||||||
const _participants = chatFull.participants as ChatParticipants.chatParticipants;
|
|
||||||
const participants = _participants.participants || [];
|
|
||||||
for(let i = 0, length = participants.length; i < length; i++) {
|
|
||||||
if(participants[i].user_id === update.user_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
participants.push({
|
updateChatParticipantDelete: this.onUpdateChatParticipantDelete,
|
||||||
_: 'chatParticipant',
|
|
||||||
user_id: update.user_id,
|
|
||||||
inviter_id: update.inviter_id,
|
|
||||||
date: tsNow(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
_participants.version = update.version;
|
|
||||||
this.rootScope.dispatchEvent('chat_full_update', update.chat_id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateChatParticipantDelete: (update) => {
|
|
||||||
const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;
|
|
||||||
if(chatFull !== undefined) {
|
|
||||||
const _participants = chatFull.participants as ChatParticipants.chatParticipants;
|
|
||||||
const participants = _participants.participants || [];
|
|
||||||
for(let i = 0, length = participants.length; i < length; i++) {
|
|
||||||
if(participants[i].user_id === update.user_id) {
|
|
||||||
participants.splice(i, 1);
|
|
||||||
_participants.version = update.version;
|
|
||||||
this.rootScope.dispatchEvent('chat_full_update', update.chat_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateUserTyping: this.onUpdateUserTyping,
|
updateUserTyping: this.onUpdateUserTyping,
|
||||||
updateChatUserTyping: this.onUpdateUserTyping,
|
updateChatUserTyping: this.onUpdateUserTyping,
|
||||||
|
@ -332,7 +289,68 @@ export class AppProfileManager extends AppManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getChannelParticipants(id: ChatId, filter: ChannelParticipantsFilter = {_: 'channelParticipantsRecent'}, limit = 200, offset = 0) {
|
public getParticipants(
|
||||||
|
id: ChatId,
|
||||||
|
filter: ChannelParticipantsFilter = {_: 'channelParticipantsRecent'},
|
||||||
|
limit = 200,
|
||||||
|
offset = 0
|
||||||
|
) {
|
||||||
|
if(this.appChatsManager.isChannel(id)) {
|
||||||
|
return this.getChannelParticipants(id, filter, limit, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(this.getChatFull(id)).then((chatFull) => {
|
||||||
|
const chatParticipants = (chatFull as ChatFull.chatFull).participants;
|
||||||
|
if(chatParticipants._ !== 'chatParticipants') {
|
||||||
|
throw makeError('CHAT_PRIVATE');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(filter._ === 'channelParticipantsSearch' && filter.q.trim()) {
|
||||||
|
const index = this.appUsersManager.createSearchIndex();
|
||||||
|
chatParticipants.participants.forEach((chatParticipant) => {
|
||||||
|
const userId = chatParticipant.user_id;
|
||||||
|
index.indexObject(userId, this.appUsersManager.getUserSearchText(userId));
|
||||||
|
});
|
||||||
|
|
||||||
|
const found = index.search(filter.q);
|
||||||
|
const filteredParticipants = chatParticipants.participants.filter((chatParticipant) => {
|
||||||
|
return found.has(chatParticipant.user_id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {...chatParticipants, participants: filteredParticipants};
|
||||||
|
}
|
||||||
|
|
||||||
|
return chatParticipants;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParticipant(id: ChatId, peerId: PeerId) {
|
||||||
|
if(this.appChatsManager.isChannel(id)) {
|
||||||
|
return this.getChannelParticipant(id, peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getParticipants(id).then((chatParticipants) => {
|
||||||
|
assumeType<ChatParticipants.chatParticipants>(chatParticipants);
|
||||||
|
const found = chatParticipants.participants.find((chatParticipant) => {
|
||||||
|
if(getParticipantPeerId(chatParticipant) === peerId) {
|
||||||
|
return chatParticipant;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!found) {
|
||||||
|
throw makeError('USER_NOT_PARTICIPANT');
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public getChannelParticipants(
|
||||||
|
id: ChatId,
|
||||||
|
filter: ChannelParticipantsFilter = {_: 'channelParticipantsRecent'},
|
||||||
|
limit = 200,
|
||||||
|
offset = 0
|
||||||
|
) {
|
||||||
if(filter._ === 'channelParticipantsRecent') {
|
if(filter._ === 'channelParticipantsRecent') {
|
||||||
const chat = this.appChatsManager.getChat(id);
|
const chat = this.appChatsManager.getChat(id);
|
||||||
if(chat?.pFlags && (
|
if(chat?.pFlags && (
|
||||||
|
@ -647,6 +665,67 @@ export class AppProfileManager extends AppManager {
|
||||||
return peerId + (threadId ? `_${threadId}` : '');
|
return peerId + (threadId ? `_${threadId}` : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getPeerTypings(peerId: PeerId, threadId?: number) {
|
||||||
|
return this.typingsInPeer[this.getTypingsKey(peerId, threadId)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private onUpdateChatParticipants = (update: Update.updateChatParticipants) => {
|
||||||
|
const participants = update.participants;
|
||||||
|
if(participants._ !== 'chatParticipants') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatId = participants.chat_id;
|
||||||
|
const chatFull = this.chatsFull[chatId] as ChatFull.chatFull;
|
||||||
|
if(chatFull !== undefined) {
|
||||||
|
chatFull.participants = participants;
|
||||||
|
this.rootScope.dispatchEvent('chat_full_update', chatId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private onUpdateChatParticipantAdd = (update: Update.updateChatParticipantAdd) => {
|
||||||
|
const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;
|
||||||
|
if(chatFull === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _participants = chatFull.participants as ChatParticipants.chatParticipants;
|
||||||
|
const participants = _participants.participants || [];
|
||||||
|
for(let i = 0, length = participants.length; i < length; i++) {
|
||||||
|
if(participants[i].user_id === update.user_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
participants.push({
|
||||||
|
_: 'chatParticipant',
|
||||||
|
user_id: update.user_id,
|
||||||
|
inviter_id: update.inviter_id,
|
||||||
|
date: tsNow(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
_participants.version = update.version;
|
||||||
|
this.rootScope.dispatchEvent('chat_full_update', update.chat_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onUpdateChatParticipantDelete = (update: Update.updateChatParticipantDelete) => {
|
||||||
|
const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;
|
||||||
|
if(chatFull === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _participants = chatFull.participants as ChatParticipants.chatParticipants;
|
||||||
|
const participants = _participants.participants || [];
|
||||||
|
for(let i = 0, length = participants.length; i < length; i++) {
|
||||||
|
if(participants[i].user_id === update.user_id) {
|
||||||
|
participants.splice(i, 1);
|
||||||
|
_participants.version = update.version;
|
||||||
|
this.rootScope.dispatchEvent('chat_full_update', update.chat_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {
|
private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {
|
||||||
const fromId = (update as Update.updateUserTyping).user_id ?
|
const fromId = (update as Update.updateUserTyping).user_id ?
|
||||||
(update as Update.updateUserTyping).user_id.toPeerId() :
|
(update as Update.updateUserTyping).user_id.toPeerId() :
|
||||||
|
@ -741,8 +820,4 @@ export class AppProfileManager extends AppManager {
|
||||||
|
|
||||||
this.rootScope.dispatchEvent('peer_block', {peerId, blocked: update.blocked});
|
this.rootScope.dispatchEvent('peer_block', {peerId, blocked: update.blocked});
|
||||||
};
|
};
|
||||||
|
|
||||||
public getPeerTypings(peerId: PeerId, threadId?: number) {
|
|
||||||
return this.typingsInPeer[this.getTypingsKey(peerId, threadId)];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import type {MyDocument} from './appDocsManager';
|
import type {MyDocument} from './appDocsManager';
|
||||||
import type {DownloadOptions} from '../mtproto/apiFileManager';
|
import type {DownloadOptions} from '../mtproto/apiFileManager';
|
||||||
import {Document, InputFileLocation, InputStickerSet, MessagesAllStickers, MessagesFavedStickers, MessagesFeaturedStickers, MessagesFoundStickerSets, MessagesRecentStickers, MessagesStickers, MessagesStickerSet, PhotoSize, StickerPack, StickerSet, StickerSetCovered, Update} from '../../layer';
|
import {Document, InputFileLocation, InputStickerSet, MessagesAllStickers, MessagesFavedStickers, MessagesFeaturedStickers, MessagesFoundStickerSets, MessagesRecentStickers, MessagesStickers, MessagesStickerSet, PhotoSize, StickerPack, StickerSet, StickerSetCovered, Update, VideoSize} from '../../layer';
|
||||||
import {Modify} from '../../types';
|
import {Modify} from '../../types';
|
||||||
import AppStorage from '../storage';
|
import AppStorage from '../storage';
|
||||||
import DATABASE_STATE from '../../config/databases/state';
|
import DATABASE_STATE from '../../config/databases/state';
|
||||||
|
@ -20,6 +20,7 @@ import ctx from '../../environment/ctx';
|
||||||
import {getEnvironment} from '../../environment/utils';
|
import {getEnvironment} from '../../environment/utils';
|
||||||
import getDocumentInput from './utils/docs/getDocumentInput';
|
import getDocumentInput from './utils/docs/getDocumentInput';
|
||||||
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
|
import getStickerEffectThumb from './utils/stickers/getStickerEffectThumb';
|
||||||
|
import tsNow from '../../helpers/tsNow';
|
||||||
|
|
||||||
const CACHE_TIME = 3600e3;
|
const CACHE_TIME = 3600e3;
|
||||||
|
|
||||||
|
@ -406,7 +407,7 @@ export class AppStickersManager extends AppManager {
|
||||||
|
|
||||||
public preloadSticker(docId: DocId, isPremiumEffect?: boolean) {
|
public preloadSticker(docId: DocId, isPremiumEffect?: boolean) {
|
||||||
const doc = this.appDocsManager.getDoc(docId);
|
const doc = this.appDocsManager.getDoc(docId);
|
||||||
return this.apiFileManager.downloadMedia({media: doc, thumb: isPremiumEffect ? doc.video_thumbs?.[0] : undefined});
|
return this.apiFileManager.downloadMedia({media: doc, thumb: isPremiumEffect ? doc.video_thumbs?.[0] as Extract<VideoSize, VideoSize.videoSize> : undefined});
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: DocId) {
|
private saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: DocId) {
|
||||||
|
@ -592,7 +593,7 @@ export class AppStickersManager extends AppManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
if(res) {
|
if(res) {
|
||||||
set.installed_date = Date.now() / 1000 | 0;
|
set.installed_date = tsNow(true);
|
||||||
this.rootScope.dispatchEvent('stickers_installed', set);
|
this.rootScope.dispatchEvent('stickers_installed', set);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,7 +431,7 @@ export class AppUsersManager extends AppManager {
|
||||||
return index.search(query).has(user.id);
|
return index.search(query).has(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createSearchIndex() {
|
public createSearchIndex() {
|
||||||
return new SearchIndex<UserId>({
|
return new SearchIndex<UserId>({
|
||||||
clearBadChars: true,
|
clearBadChars: true,
|
||||||
ignoreCase: true,
|
ignoreCase: true,
|
||||||
|
|
|
@ -35,8 +35,8 @@ import DEBUG from '../../config/debug';
|
||||||
|
|
||||||
// const key2 = [('00000' + sentCount).slice(-5), key].join('-');
|
// const key2 = [('00000' + sentCount).slice(-5), key].join('-');
|
||||||
|
|
||||||
// let byManager = stats[manager] ??= {};
|
// const byManager = stats[manager] ??= {};
|
||||||
// let byMethod = byManager[method] ??= {times: [], byArgs: {}};
|
// const byMethod = byManager[method] ??= {times: [], byArgs: {}};
|
||||||
|
|
||||||
// const perf = performance.now();
|
// const perf = performance.now();
|
||||||
// promise.catch(noop).finally(() => {
|
// promise.catch(noop).finally(() => {
|
||||||
|
@ -53,8 +53,9 @@ import DEBUG from '../../config/debug';
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// setInterval(() => {
|
// setInterval(() => {
|
||||||
// // console.log(dT(), '[PROXY] stats', stats, sentCount, sentMethods, sentMethods2);
|
// console.log(dT(), '[PROXY] stats', stats, sentCount, sentMethods, sentMethods2);
|
||||||
// sentCount = 0;
|
// sentCount = 0;
|
||||||
|
// stats = {};
|
||||||
// sentMethods = {};
|
// sentMethods = {};
|
||||||
// sentMethods2 = {};
|
// sentMethods2 = {};
|
||||||
// }, 2000);
|
// }, 2000);
|
||||||
|
|
|
@ -365,7 +365,7 @@ export class UiNotificationsManager {
|
||||||
avatarContext.textBaseline = 'middle';
|
avatarContext.textBaseline = 'middle';
|
||||||
avatarContext.textAlign = 'center';
|
avatarContext.textAlign = 'center';
|
||||||
avatarContext.fillStyle = 'white';
|
avatarContext.fillStyle = 'white';
|
||||||
avatarContext.fillText(abbreviation.text, avatarCanvas.width / 2, avatarCanvas.height * (window.devicePixelRatio > 1 ? .5625 : 5));
|
avatarContext.fillText(abbreviation.text, avatarCanvas.width / 2, avatarCanvas.height * (window.devicePixelRatio > 1 || true ? .5625 : .5));
|
||||||
|
|
||||||
notification.image = avatarCanvas.toDataURL();
|
notification.image = avatarCanvas.toDataURL();
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,14 @@ export default function hasRights(chat: Exclude<Chat, Chat.chatEmpty>, action: C
|
||||||
case 'send_media':
|
case 'send_media':
|
||||||
case 'send_messages':
|
case 'send_messages':
|
||||||
case 'send_polls':
|
case 'send_polls':
|
||||||
case 'send_stickers': {
|
case 'send_stickers':
|
||||||
|
case 'send_photos':
|
||||||
|
case 'send_videos':
|
||||||
|
case 'send_roundvideos':
|
||||||
|
case 'send_audios':
|
||||||
|
case 'send_voices':
|
||||||
|
case 'send_docs':
|
||||||
|
case 'send_plain': {
|
||||||
if(!isThread && chat.pFlags.left) {
|
if(!isThread && chat.pFlags.left) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,12 @@ import type {Document, PhotoSize, VideoSize} from '../../../../layer';
|
||||||
import type {DownloadOptions} from '../../../mtproto/apiFileManager';
|
import type {DownloadOptions} from '../../../mtproto/apiFileManager';
|
||||||
import getDocumentInputFileLocation from './getDocumentInputFileLocation';
|
import getDocumentInputFileLocation from './getDocumentInputFileLocation';
|
||||||
|
|
||||||
export default function getDocumentDownloadOptions(doc: Document.document, thumb?: PhotoSize.photoSize | VideoSize, queueId?: number, onlyCache?: boolean): DownloadOptions {
|
export default function getDocumentDownloadOptions(
|
||||||
|
doc: Document.document,
|
||||||
|
thumb?: PhotoSize.photoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||||
|
queueId?: number,
|
||||||
|
onlyCache?: boolean
|
||||||
|
): DownloadOptions {
|
||||||
const inputFileLocation = getDocumentInputFileLocation(doc, thumb?.type);
|
const inputFileLocation = getDocumentInputFileLocation(doc, thumb?.type);
|
||||||
|
|
||||||
let mimeType: MTMimeType;
|
let mimeType: MTMimeType;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import {Document, Message, MessageAction, MessageExtendedMedia, MessageMedia, Photo, WebPage} from '../../../../layer';
|
import {Document, Game, Message, MessageAction, MessageExtendedMedia, MessageMedia, Photo, WebPage} from '../../../../layer';
|
||||||
|
|
||||||
export default function getMediaFromMessage(message: Message) {
|
export default function getMediaFromMessage(message: Message, onlyInner: true): Photo.photo | Document.document;
|
||||||
|
export default function getMediaFromMessage(message: Message, onlyInner?: false): Photo.photo | Document.document | Game.game | WebPage.webPage;
|
||||||
|
export default function getMediaFromMessage(message: Message, onlyInner = false): Photo.photo | Document.document | Game.game | WebPage.webPage {
|
||||||
if(!message) return;
|
if(!message) return;
|
||||||
|
|
||||||
let media: any;
|
let media: any;
|
||||||
|
@ -15,8 +17,9 @@ export default function getMediaFromMessage(message: Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
media = (messageMedia as MessageMedia.messageMediaDocument).document ||
|
media = (messageMedia as MessageMedia.messageMediaDocument).document ||
|
||||||
(messageMedia as MessageMedia.messageMediaPhoto).photo;
|
(messageMedia as MessageMedia.messageMediaPhoto).photo ||
|
||||||
|
(onlyInner ? undefined : (messageMedia as MessageMedia.messageMediaGame).game || messageMedia);
|
||||||
}
|
}
|
||||||
|
|
||||||
return media as Photo.photo | Document.document;
|
return media as any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {MyDocument} from '../../appDocsManager';
|
import {MyDocument} from '../../appDocsManager';
|
||||||
|
import {VideoSize} from '../../../../layer';
|
||||||
|
|
||||||
export default function getStickerEffectThumb(doc: MyDocument) {
|
export default function getStickerEffectThumb(doc: MyDocument) {
|
||||||
return doc.video_thumbs?.[0];
|
return doc.video_thumbs?.[0] as Extract<VideoSize, VideoSize.videoSize>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ export type DownloadOptions = {
|
||||||
|
|
||||||
export type DownloadMediaOptions = {
|
export type DownloadMediaOptions = {
|
||||||
media: Photo.photo | Document.document | WebDocument,
|
media: Photo.photo | Document.document | WebDocument,
|
||||||
thumb?: PhotoSize | VideoSize,
|
thumb?: PhotoSize | Extract<VideoSize, VideoSize.videoSize>,
|
||||||
queueId?: number,
|
queueId?: number,
|
||||||
onlyCache?: boolean,
|
onlyCache?: boolean,
|
||||||
downloadId?: string
|
downloadId?: string
|
||||||
|
|
|
@ -37,6 +37,7 @@ import pause from '../../helpers/schedulers/pause';
|
||||||
import ApiManagerMethods from './api_methods';
|
import ApiManagerMethods from './api_methods';
|
||||||
import {getEnvironment} from '../../environment/utils';
|
import {getEnvironment} from '../../environment/utils';
|
||||||
import toggleStorages from '../../helpers/toggleStorages';
|
import toggleStorages from '../../helpers/toggleStorages';
|
||||||
|
import tsNow from '../../helpers/tsNow';
|
||||||
|
|
||||||
/* class RotatableArray<T> {
|
/* class RotatableArray<T> {
|
||||||
public array: Array<T> = [];
|
public array: Array<T> = [];
|
||||||
|
@ -250,7 +251,7 @@ export class ApiManager extends ApiManagerMethods {
|
||||||
|
|
||||||
public async setUserAuth(userAuth: UserAuth | UserId) {
|
public async setUserAuth(userAuth: UserAuth | UserId) {
|
||||||
if(typeof(userAuth) === 'string' || typeof(userAuth) === 'number') {
|
if(typeof(userAuth) === 'string' || typeof(userAuth) === 'number') {
|
||||||
userAuth = {dcID: 0, date: Date.now() / 1000 | 0, id: userAuth.toPeerId(false)};
|
userAuth = {dcID: 0, date: tsNow(true), id: userAuth.toPeerId(false)};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rootScope.dispatchEvent('user_auth', userAuth);
|
this.rootScope.dispatchEvent('user_auth', userAuth);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -28,6 +28,7 @@ export type BroadcastEvents = {
|
||||||
'chat_full_update': ChatId,
|
'chat_full_update': ChatId,
|
||||||
'chat_update': ChatId,
|
'chat_update': ChatId,
|
||||||
'chat_toggle_forum': {chatId: ChatId, enabled: boolean},
|
'chat_toggle_forum': {chatId: ChatId, enabled: boolean},
|
||||||
|
'chat_participant': Update.updateChannelParticipant,
|
||||||
|
|
||||||
'channel_update': ChatId,
|
'channel_update': ChatId,
|
||||||
|
|
||||||
|
@ -179,10 +180,7 @@ export class RootScope extends EventListenerBase<BroadcastEventsListeners> {
|
||||||
|
|
||||||
this.addEventListener('premium_toggle_private', ({isNew, isPremium}) => {
|
this.addEventListener('premium_toggle_private', ({isNew, isPremium}) => {
|
||||||
this.premium = isPremium;
|
this.premium = isPremium;
|
||||||
|
this.dispatchEventSingle('premium_toggle', isPremium);
|
||||||
if(!isNew) {
|
|
||||||
this.dispatchEventSingle('premium_toggle', isPremium);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addEventListener('connection_status_change', (status) => {
|
this.addEventListener('connection_status_change', (status) => {
|
||||||
|
|
|
@ -163,6 +163,17 @@ const onFirstMount = () => {
|
||||||
}).then((code) => {
|
}).then((code) => {
|
||||||
// console.log('got code', code);
|
// console.log('got code', code);
|
||||||
|
|
||||||
|
if(code._ === 'auth.sentCodeSuccess') {
|
||||||
|
const {authorization} = code;
|
||||||
|
if(authorization._ === 'auth.authorization') {
|
||||||
|
rootScope.managers.apiManager.setUser(authorization.user);
|
||||||
|
|
||||||
|
import('./pageIm').then((m) => {
|
||||||
|
m.default.mount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import('./pageAuthCode').then((m) => m.default.mount(Object.assign(code, {phone_number: phone_number})));
|
import('./pageAuthCode').then((m) => m.default.mount(Object.assign(code, {phone_number: phone_number})));
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
toggle();
|
toggle();
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -382,7 +382,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
&-background {
|
&-background {
|
||||||
rect {
|
.audio-waveform-bar {
|
||||||
opacity: .3;
|
opacity: .3;
|
||||||
|
|
||||||
@include hover() {
|
@include hover() {
|
||||||
|
@ -408,7 +408,7 @@
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
rect {
|
&-bar {
|
||||||
fill: var(--primary-color);
|
fill: var(--primary-color);
|
||||||
//overflow: visible!important;
|
//overflow: visible!important;
|
||||||
}
|
}
|
||||||
|
@ -599,7 +599,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.is-out) .audio-toggle:not(.playing) + .audio-waveform-container .audio-waveform-background rect {
|
&:not(.is-out) .audio-toggle:not(.playing) + .audio-waveform-container .audio-waveform-background .audio-waveform-bar {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2437,6 +2437,7 @@ $bubble-border-radius-big: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-transcribed-text {
|
.audio-transcribed-text {
|
||||||
|
margin-bottom: .75rem;
|
||||||
margin-top: .25rem;
|
margin-top: .25rem;
|
||||||
|
|
||||||
&.is-error {
|
&.is-error {
|
||||||
|
@ -2464,9 +2465,9 @@ $bubble-border-radius-big: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.document-message + .audio-transcribed-text {
|
// .document-message + .audio-transcribed-text {
|
||||||
margin-bottom: .75rem;
|
// margin-bottom: .75rem;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes audio-dots {
|
@keyframes audio-dots {
|
||||||
|
@ -2949,7 +2950,7 @@ $bubble-border-radius-big: 12px;
|
||||||
|
|
||||||
.audio {
|
.audio {
|
||||||
&-waveform {
|
&-waveform {
|
||||||
rect {
|
&-bar {
|
||||||
fill: var(--message-out-primary-color);
|
fill: var(--message-out-primary-color);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
|
@ -2974,7 +2975,7 @@ $bubble-border-radius-big: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-unread {
|
&.is-unread {
|
||||||
rect {
|
.audio-waveform-bar {
|
||||||
fill: var(--message-out-primary-color);
|
fill: var(--message-out-primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,9 @@
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-photo, .media-video, .media-poster {
|
.media-photo,
|
||||||
|
.media-video,
|
||||||
|
.media-poster {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -114,4 +116,23 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-cant-send {
|
||||||
|
display: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cant-send {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 3rem;
|
||||||
|
|
||||||
|
.scrollable-y {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-helper-cant-send {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,8 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
line-height: var(--line-height);
|
line-height: var(--line-height);
|
||||||
|
|
||||||
|
@include text-overflow(false);
|
||||||
|
|
||||||
@include animation-level(0) {
|
@include animation-level(0) {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
@ -385,7 +387,17 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
@include animation-level(2) {
|
@include animation-level(2) {
|
||||||
transition: border-color .1s, transform .1s cubic-bezier(.22, .75, .7, 1.44);
|
transition: border-color .1s, transform .1s cubic-bezier(.22, .75, .7, 1.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-restriction {
|
||||||
|
.checkbox-toggle {
|
||||||
|
background-color: var(--danger-color);
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
border-color: var(--danger-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,14 @@ $row-border-radius: $border-radius-medium;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-row {
|
||||||
|
&.with-delimiter {
|
||||||
|
.row-title:first-child {
|
||||||
|
border-right: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-title-right,
|
&-title-right,
|
||||||
|
@ -321,4 +329,10 @@ $row-border-radius: $border-radius-medium;
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.accordion-toggler {
|
||||||
|
.checkbox-field-toggle {
|
||||||
|
pointer-events: all !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-create-contact {
|
.popup-create-contact {
|
||||||
|
.popup-container {
|
||||||
|
padding: 0 1rem 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.name-fields {
|
.name-fields {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -50,9 +50,10 @@
|
||||||
.checkbox-field {
|
.checkbox-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 3rem;
|
min-height: 3rem;
|
||||||
|
height: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 1.125rem;
|
padding: .25rem 1.125rem;
|
||||||
|
|
||||||
.checkbox-box {
|
.checkbox-box {
|
||||||
left: auto;
|
left: auto;
|
||||||
|
|
|
@ -2102,3 +2102,34 @@ hr {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accordion {
|
||||||
|
overflow: hidden;
|
||||||
|
height: 0;
|
||||||
|
|
||||||
|
&.is-expanded {
|
||||||
|
height: var(--max-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: height var(--transition-standard-in);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
transform: rotate(0deg) translateY(4px);
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 0;
|
||||||
|
|
||||||
|
@include animation-level(2) {
|
||||||
|
transition: transform var(--transition-standard-in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-toggler-expanded {
|
||||||
|
.accordion-icon {
|
||||||
|
transform: translateY(4px) rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue