Report messages

This commit is contained in:
Eduard Kuzmenko 2021-08-26 01:05:29 +03:00
parent 81a68880e9
commit 6df79ad7b8
13 changed files with 228 additions and 14 deletions

View File

@ -26,6 +26,7 @@ import cancelSelection from "../../helpers/dom/cancelSelection";
import { attachClickEvent } from "../../helpers/dom/clickEvent";
import isSelectionEmpty from "../../helpers/dom/isSelectionEmpty";
import { Message } from "../../layer";
import PopupReportMessages from "../popups/reportMessages";
export default class ChatContextMenu {
private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[];
@ -322,6 +323,15 @@ export default class ChatContextMenu {
!this.chat.selection.selectionForwardBtn.hasAttribute('disabled'),
notDirect: () => true,
withSelection: true
}, {
icon: 'flag',
text: 'ReportChat',
onClick: () => {
new PopupReportMessages(this.peerId, [this.mid]);
},
verify: () => !this.message.pFlags.out && !this.message.pFlags.is_outgoing && this.appPeersManager.isChannel(this.peerId),
notDirect: () => true,
withSelection: true
}, {
icon: 'select',
text: 'Message.Context.Select',

View File

@ -95,7 +95,7 @@ class InputField {
this.container.classList.add('input-field');
if(options.maxLength) {
options.showLengthOn = Math.round(options.maxLength / 3);
options.showLengthOn = Math.min(40, Math.round(options.maxLength / 3));
}
const {placeholder, maxLength, showLengthOn, name, plainText} = options;

View File

@ -36,6 +36,7 @@ export default class PopupElement {
protected btnClose: HTMLElement;
protected btnConfirm: HTMLElement;
protected body: HTMLElement;
protected buttons: HTMLElement;
protected onClose: () => void;
protected onCloseAfterTimeout: () => void;
@ -91,7 +92,7 @@ export default class PopupElement {
}
if(buttons && buttons.length) {
const buttonsDiv = document.createElement('div');
const buttonsDiv = this.buttons = document.createElement('div');
buttonsDiv.classList.add('popup-buttons');
if(buttons.length === 2) {

View File

@ -18,15 +18,16 @@ export type PopupPeerOptions = PopupOptions & Partial<{
title: string,
titleLangKey?: LangPackKey,
titleLangArgs?: any[],
noTitle?: boolean,
description: string,
descriptionLangKey?: LangPackKey,
descriptionLangArgs?: any[],
buttons: Array<Omit<PopupButton, 'callback'> & Partial<{callback: PopupPeerButtonCallback}>>,
buttons?: Array<Omit<PopupButton, 'callback'> & Partial<{callback: PopupPeerButtonCallback}>>,
checkboxes: Array<PopupPeerCheckboxOptions>
}>;
export default class PopupPeer extends PopupElement {
constructor(private className: string, options: PopupPeerOptions = {}) {
super('popup-peer' + (className ? ' ' + className : ''), addCancelButton(options.buttons), {overlayClosable: true, ...options});
super('popup-peer' + (className ? ' ' + className : ''), options.buttons && addCancelButton(options.buttons), {overlayClosable: true, ...options});
if(options.peerId) {
let avatarEl = new AvatarElement();
@ -36,16 +37,21 @@ export default class PopupPeer extends PopupElement {
this.header.prepend(avatarEl);
}
if(options.titleLangKey || !options.title) this.title.append(i18n(options.titleLangKey || 'AppName', options.titleLangArgs));
else this.title.innerText = options.title || '';
let p = document.createElement('p');
p.classList.add('popup-description');
if(options.descriptionLangKey) p.append(i18n(options.descriptionLangKey, options.descriptionLangArgs));
else p.innerHTML = options.description;
if(!options.noTitle) {
if(options.titleLangKey || !options.title) this.title.append(i18n(options.titleLangKey || 'AppName', options.titleLangArgs));
else this.title.innerText = options.title || '';
}
const fragment = document.createDocumentFragment();
fragment.append(p);
if(options.descriptionLangKey || options.description) {
const p = document.createElement('p');
p.classList.add('popup-description');
if(options.descriptionLangKey) p.append(i18n(options.descriptionLangKey, options.descriptionLangArgs));
else if(options.description) p.innerHTML = options.description;
fragment.append(p);
}
if(options.checkboxes) {
this.container.classList.add('have-checkbox');

View File

@ -0,0 +1,54 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import findUpClassName from "../../helpers/dom/findUpClassName";
import whichChild from "../../helpers/dom/whichChild";
import { ReportReason } from "../../layer";
import appStickersManager from "../../lib/appManagers/appStickersManager";
import { LangPackKey } from "../../lib/langPack";
import Button from "../button";
import PopupPeer from "./peer";
import PopupReportMessagesConfirm from "./reportMessagesConfirm";
export default class PopupReportMessages extends PopupPeer {
constructor(peerId: number, mids: number[], onConfirm?: () => void) {
super('popup-report-messages', {titleLangKey: 'ChatTitle.ReportMessages', buttons: [], body: true});
mids = mids.slice();
const buttons: [LangPackKey, ReportReason['_']][] = [
['ReportChatSpam', 'inputReportReasonSpam'],
['ReportChatViolence', 'inputReportReasonViolence'],
['ReportChatChild', 'inputReportReasonChildAbuse'],
['ReportChatPornography', 'inputReportReasonPornography'],
['ReportChatOther', 'inputReportReasonOther']
];
const className = 'btn-primary btn-transparent';
buttons.forEach(b => {
const button = Button(className, {/* icon: 'edit', */text: b[0]});
this.body.append(button);
});
const preloadStickerPromise = appStickersManager.preloadAnimatedEmojiSticker(PopupReportMessagesConfirm.STICKER_EMOJI);
this.body.addEventListener('click', (e) => {
const target = findUpClassName(e.target, 'btn-primary');
const reason = buttons[whichChild(target)][1];
preloadStickerPromise.then(() => {
this.hide();
new PopupReportMessagesConfirm(peerId, mids, reason, onConfirm);
});
});
this.body.style.margin = '0 -1rem';
this.buttons.style.marginTop = '.5rem';
this.show();
}
}

View File

@ -0,0 +1,66 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { ReportReason } from "../../layer";
import appMessagesManager from "../../lib/appManagers/appMessagesManager";
import appStickersManager from "../../lib/appManagers/appStickersManager";
import InputField from "../inputField";
import { toastNew } from "../toast";
import { wrapSticker } from "../wrappers";
import PopupPeer from "./peer";
export default class PopupReportMessagesConfirm extends PopupPeer {
public static STICKER_EMOJI = '👮‍♀️';
constructor(peerId: number, mids: number[], reason: ReportReason['_'], onConfirm?: () => void) {
super('popup-report-messages-confirm', {
noTitle: true,
descriptionLangKey: 'ReportInfo',
buttons: [{
langKey: 'ReportChat',
callback: () => {
if(!inputField.isValid()) {
return;
}
onConfirm && onConfirm();
appMessagesManager.reportMessages(peerId, mids, reason, inputField.value).then(bool => {
if(!bool) return;
toastNew({
langPackKey: 'ReportSentInfo'
});
});
}
}],
body: true
});
const div = document.createElement('div');
const doc = appStickersManager.getAnimatedEmojiSticker(PopupReportMessagesConfirm.STICKER_EMOJI);
const size = 100;
wrapSticker({
doc,
div,
emoji: PopupReportMessagesConfirm.STICKER_EMOJI,
width: size,
height: size,
loop: false,
play: true
}).finally(() => {
this.show();
});
this.header.append(div);
const inputField = new InputField({
label: 'ReportHint',
maxLength: 512,
placeholder: 'ReportChatDescription'
});
this.body.append(inputField.container);
}
}

View File

@ -549,6 +549,17 @@ const lang = {
"Create": "Create",
"ViewDiscussion": "View discussion",
"MessageScheduledUntilOnline": "Scheduled until online",
"ReportChat": "Report",
"ReportChatSpam": "Spam",
// "ReportChatFakeAccount": "Fake Account",
"ReportChatViolence": "Violence",
"ReportChatPornography": "Pornography",
"ReportChatChild": "Child Abuse",
"ReportChatOther": "Other",
"ReportChatDescription": "Description",
"ReportInfo": "Please enter any additional details relevant to your report.",
"ReportSentInfo": "Telegram moderators will review your report.\nThank you for your cooperation!",
"ReportHint": "Additional details...",
// * macos
"AccountSettings.Filters": "Chat Folders",
@ -609,6 +620,7 @@ const lang = {
"one_value": "%d Comment",
"other_value": "%d Comments"
},
"ChatTitle.ReportMessages": "Report Messages",
"Chat.Send.WithoutSound": "Send Without Sound",
"Chat.Send.SetReminder": "Set a Reminder",
"Chat.Send.ScheduledMessage": "Schedule Message",

View File

@ -17,7 +17,7 @@ import { createPosterForVideo } from "../../helpers/files";
import { copy, getObjectKeysAndSort } from "../../helpers/object";
import { randomLong } from "../../helpers/random";
import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/string";
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo, Updates, ReplyMarkup, InputPeer, InputPhoto, InputDocument, InputGeoPoint, WebPage, GeoPoint } from "../../layer";
import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo, Updates, ReplyMarkup, InputPeer, InputPhoto, InputDocument, InputGeoPoint, WebPage, GeoPoint, ReportReason } from "../../layer";
import { InvokeApiOptions } from "../../types";
import I18n, { i18n, join, langPack, LangPackKey, _i18n } from "../langPack";
import { logger, LogTypes } from "../logger";
@ -2866,6 +2866,17 @@ export class AppMessagesManager {
}
}
public reportMessages(peerId: number, mids: number[], reason: ReportReason['_'], message?: string) {
return apiManager.invokeApiSingle('messages.report', {
peer: appPeersManager.getInputPeerById(peerId),
id: mids.map(mid => appMessagesIdsManager.getServerMessageId(mid)),
reason: {
_: reason
},
message
});
}
public startBot(botId: number, chatId: number, startParam: string) {
const peerId = chatId ? -chatId : botId;
if(startParam) {

View File

@ -32,7 +32,7 @@ export class AppStickersManager {
private getGreetingStickersPromise: Promise<void>;
constructor() {
this.getStickerSet({id: 'emoji'}, {saveById: true});
this.getAnimatedEmojiStickerSet();
rootScope.addMultipleEventsListeners({
updateNewStickerSet: (update) => {
@ -121,6 +121,10 @@ export class AppStickersManager {
});
}
public getAnimatedEmojiStickerSet() {
return this.getStickerSet({id: 'emoji'}, {saveById: true});
}
public async getRecentStickers(): Promise<Modify<MessagesRecentStickers.messagesRecentStickers, {
stickers: Document[]
}>> {
@ -139,6 +143,15 @@ export class AppStickersManager {
const pack = stickerSet.packs.find(p => p.emoticon === emoji);
return pack ? appDocsManager.getDoc(pack.documents[0]) : undefined;
}
public preloadAnimatedEmojiSticker(emoji: string) {
return this.getAnimatedEmojiStickerSet().then(() => {
const doc = this.getAnimatedEmojiSticker(emoji);
if(doc) {
return appDocsManager.downloadDoc(doc);
}
});
}
public saveStickerSet(res: Omit<MessagesStickerSet.messagesStickerSet, '_'>, id: string) {
//console.log('stickers save set', res);w

View File

@ -1,3 +1,9 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.popup-instance-deactivated {
background-color: rgba(0, 0, 0, .6);

View File

@ -1,3 +1,9 @@
/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
.popup-join-chat-invite {
user-select: none;

View File

@ -0,0 +1,28 @@
.popup-report-messages-confirm {
user-select: none;
.media-sticker-wrapper {
width: 100px;
height: 100px;
position: relative;
margin: 0 auto;
}
.popup-body {
margin: 1em -.5rem .375rem -.5rem;
overflow: unset;
}
.popup-description {
font-size: .875rem;
text-align: center;
}
.popup-buttons {
margin-top: .625rem;
}
.input-field {
width: 100%;
}
}

View File

@ -276,6 +276,7 @@ html.night {
@import "partials/popups/forward";
@import "partials/popups/instanceDeactivated";
@import "partials/popups/joinChatInvite";
@import "partials/popups/reportMessages";
@import "partials/pages/pages";
@import "partials/pages/authCode";