diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 4a9ab089..b8f1ad79 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -12,6 +12,7 @@ import PopupForward from "../popups/forward"; import PopupPinMessage from "../popups/unpinMessage"; import { copyTextToClipboard } from "../../helpers/clipboard"; import PopupSendNow from "../popups/sendNow"; +import { toast } from "../toast"; export default class ChatContextMenu { private buttons: (ButtonMenuItemOptions & {verify: () => boolean, notDirect?: () => boolean, withSelection?: true})[]; @@ -195,6 +196,11 @@ export default class ChatContextMenu { verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.chat.getMessage(mid).message), notDirect: () => true, withSelection: true + }, { + icon: 'link', + text: 'Copy Link', + onClick: this.onCopyLinkClick, + verify: () => this.appPeersManager.isChannel(this.peerId) }, { icon: 'pin', text: 'Pin', @@ -300,6 +306,21 @@ export default class ChatContextMenu { copyTextToClipboard(str); }; + private onCopyLinkClick = () => { + const username = this.appPeersManager.getPeerUsername(this.peerId); + const msgId = this.appMessagesManager.getServerMessageId(this.mid); + let url = 'https://t.me/'; + if(username) { + url += username + '/' + msgId; + toast('Link copied to clipboard.'); + } else { + url += 'c/' + Math.abs(this.peerId) + '/' + msgId; + toast('This link will only work for chat members.'); + } + + copyTextToClipboard(url); + }; + private onPinClick = () => { new PopupPinMessage(this.peerId, this.mid); }; diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 28fea860..b57435f2 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -90,13 +90,13 @@ export class AppChatsManager { case 'updateUserTyping': case 'updateChatUserTyping': { - if(rootScope.myId == update.user_id) { + if(rootScope.myId === update.user_id) { return; } - const peerId = update._ == 'updateUserTyping' ? update.user_id : -update.chat_id; + const peerId = update._ === 'updateUserTyping' ? update.user_id : -update.chat_id; const typings = this.typingsInPeer[peerId] ?? (this.typingsInPeer[peerId] = []); - let typing = typings.find(t => t.userId == update.user_id); + let typing = typings.find(t => t.userId === update.user_id); if(!typing) { typing = { userId: update.user_id @@ -110,7 +110,7 @@ export class AppChatsManager { typing.action = update.action; if(!appUsersManager.hasUser(update.user_id)) { - if(update._ == 'updateChatUserTyping') { + if(update._ === 'updateChatUserTyping') { if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) { appProfileManager.getChatFull(update.chat_id); } @@ -125,7 +125,7 @@ export class AppChatsManager { typing.timeout = window.setTimeout(() => { delete typing.timeout; - typings.findAndSplice(t => t.userId == update.user_id); + typings.findAndSplice(t => t.userId === update.user_id); rootScope.broadcast('peer_typings', {peerId, typings}); @@ -174,7 +174,7 @@ export class AppChatsManager { } } - if(chat._ == 'channel' && + if(chat._ === 'channel' && chat.participants_count === undefined && oldChat !== undefined && oldChat.participants_count) { @@ -217,10 +217,10 @@ export class AppChatsManager { public hasRights(id: number, action: ChatRights, flag?: keyof ChatBannedRights['pFlags']) { const chat = this.getChat(id); - if(chat._ == 'chatEmpty') return false; + if(chat._ === 'chatEmpty') return false; - if(chat._ == 'chatForbidden' || - chat._ == 'channelForbidden' || + if(chat._ === 'chatForbidden' || + chat._ === 'channelForbidden' || chat.pFlags.kicked || (chat.pFlags.left && !chat.pFlags.megagroup)) { return false; @@ -241,7 +241,7 @@ export class AppChatsManager { return false; } - if(chat._ == 'channel') { + if(chat._ === 'channel') { if((!chat.pFlags.megagroup && !myFlags?.post_messages)) { return false; } @@ -252,7 +252,7 @@ export class AppChatsManager { // good case 'deleteRevoke': { - if(chat._ == 'channel') { + if(chat._ === 'channel') { return !!myFlags?.delete_messages; } else if(!chat.pFlags.admin) { return false; @@ -263,7 +263,7 @@ export class AppChatsManager { // good case 'pin': { - if(chat._ == 'channel') { + if(chat._ === 'channel') { return chat.admin_rights ? !!myFlags.pin_messages || !!myFlags.post_messages : myFlags && !myFlags.pin_messages; } else { if(myFlags?.pin_messages && !chat.pFlags.admin) { @@ -277,9 +277,9 @@ export class AppChatsManager { case 'edit_title': case 'edit_photo': case 'invite': { - if(chat._ == 'channel') { + if(chat._ === 'channel') { if(chat.pFlags.megagroup) { - if(!(action == 'invite' && chat.pFlags.democracy)) { + if(!(action === 'invite' && chat.pFlags.democracy)) { return false; } } else { @@ -314,7 +314,7 @@ export class AppChatsManager { public isChannel(id: number) { if(id < 0) id = -id; const chat = this.chats[id]; - if(chat && (chat._ == 'channel' || chat._ == 'channelForbidden')/* || this.channelAccess[id] */) { + if(chat && (chat._ === 'channel' || chat._ === 'channelForbidden')/* || this.channelAccess[id] */) { return true; } return false; @@ -326,7 +326,7 @@ export class AppChatsManager { } */ const chat = this.chats[id]; - if(chat && chat._ == 'channel' && chat.pFlags.megagroup) { + if(chat && chat._ === 'channel' && chat.pFlags.megagroup) { return true; } return false; @@ -410,7 +410,7 @@ export class AppChatsManager { } if(chatFull.participants && - chatFull.participants._ == 'chatParticipants') { + chatFull.participants._ === 'chatParticipants') { chatFull.participants.participants = this.wrapParticipants(id, chatFull.participants.participants); } @@ -430,8 +430,8 @@ export class AppChatsManager { if(this.isChannel(id)) { const isAdmin = chat.pFlags.creator; participants.forEach((participant) => { - participant.canLeave = myId == participant.user_id; - participant.canKick = isAdmin && participant._ == 'channelParticipant'; + participant.canLeave = myId === participant.user_id; + participant.canKick = isAdmin && participant._ === 'channelParticipant'; // just for order by last seen participant.user = appUsersManager.getUser(participant.user_id); @@ -439,10 +439,10 @@ export class AppChatsManager { } else { const isAdmin = chat.pFlags.creator || chat.pFlags.admins_enabled && chat.pFlags.admin; participants.forEach((participant) => { - participant.canLeave = myId == participant.user_id; + participant.canLeave = myId === participant.user_id; participant.canKick = !participant.canLeave && ( chat.pFlags.creator || - participant._ == 'chatParticipant' && (isAdmin || myId == participant.inviter_id) + participant._ === 'chatParticipant' && (isAdmin || myId === participant.inviter_id) ); // just for order by last seen @@ -541,7 +541,7 @@ export class AppChatsManager { return participants.reduce((acc: number, participant) => { const user = appUsersManager.getUser(participant.user_id); - if(user && user.status && user.status._ == 'userStatusOnline') { + if(user && user.status && user.status._ === 'userStatusOnline') { return acc + 1; } diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 7a860e04..7c7aec3d 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -136,7 +136,7 @@ export class AppImManager { switch(p[0]) { case '@': { appUsersManager.resolveUsername(p).then(peer => { - const isUser = peer._ == 'user'; + const isUser = peer._ === 'user'; const peerId = isUser ? peer.id : -peer.id; this.setInnerPeer(peerId, postId); @@ -365,7 +365,7 @@ export class AppImManager { const drops: ChatDragAndDrop[] = []; let mounted = false; const toggle = async(e: DragEvent, mount: boolean) => { - if(mount == mounted) return; + if(mount === mounted) return; const _types = e.dataTransfer.types; // @ts-ignore @@ -489,7 +489,7 @@ export class AppImManager { getFilesFromEvent(e).then((files: File[]) => { if(files.length) { - if(attachType == 'media' && files.find(file => !['image', 'video'].includes(file.type.split('/')[0]))) { + if(attachType === 'media' && files.find(file => !['image', 'video'].includes(file.type.split('/')[0]))) { attachType = 'document'; } @@ -704,7 +704,7 @@ export class AppImManager { } else { // user const user = appUsersManager.getUser(peerId); - if(rootScope.myId == peerId) { + if(rootScope.myId === peerId) { return ''; } else if(user) { subtitle = appUsersManager.getUserStatusString(user.id); @@ -713,7 +713,7 @@ export class AppImManager { const typings = appChatsManager.typingsInPeer[peerId]; if(typings && typings.length) { return 'typing...'; - } else if(subtitle == 'online') { + } else if(subtitle === 'online') { return `${subtitle}`; } else { return subtitle; diff --git a/src/lib/appManagers/appPeersManager.ts b/src/lib/appManagers/appPeersManager.ts index ac96d906..f375f057 100644 --- a/src/lib/appManagers/appPeersManager.ts +++ b/src/lib/appManagers/appPeersManager.ts @@ -118,7 +118,7 @@ export class AppPeersManager { else if(isObject(peerString)) return peerString.user_id ? peerString.user_id : -(peerString.channel_id || peerString.chat_id); else if(!peerString) return 0; - const isUser = peerString.charAt(0) == 'u'; + const isUser = peerString.charAt(0) === 'u'; const peerParams = peerString.substr(1).split('_'); return isUser ? peerParams[0] : -peerParams[0] || 0; @@ -238,7 +238,7 @@ export class AppPeersManager { } else if(peerId < 0) { return 'group'; } else { - return peerId == rootScope.myId ? 'saved' : 'chat'; + return peerId === rootScope.myId ? 'saved' : 'chat'; } } diff --git a/src/lib/richtextprocessor.ts b/src/lib/richtextprocessor.ts index cd2393b1..e434b396 100644 --- a/src/lib/richtextprocessor.ts +++ b/src/lib/richtextprocessor.ts @@ -95,7 +95,7 @@ namespace RichTextProcessor { export function getEmojiSpritesheetCoords(emojiCode: string) { let unified = encodeEmoji(emojiCode)/* .replace(/(-fe0f|fe0f)/g, '') */; - if(unified == '1f441-200d-1f5e8') { + if(unified === '1f441-200d-1f5e8') { unified = '1f441-fe0f-200d-1f5e8-fe0f'; } @@ -236,7 +236,7 @@ namespace RichTextProcessor { if(text.match(/^`*$/)) { newText.push(match[0]); } else if(match[3]) { // pre - if(match[5] == '\n') { + if(match[5] === '\n') { match[5] = ''; rawOffset -= 1; } @@ -251,7 +251,7 @@ namespace RichTextProcessor { rawOffset -= match[2].length + match[4].length; } else if(match[7]) { // code|italic|bold - const isSOH = match[6] == '\x01'; + const isSOH = match[6] === '\x01'; if(!isSOH) { newText.push(match[6] + text + match[9]); } else { @@ -311,7 +311,7 @@ namespace RichTextProcessor { export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[]) { currentEntities = currentEntities.slice(); - const filtered = newEntities.filter(e => !currentEntities.find(_e => e._ == _e._ && e.offset == _e.offset && e.length == _e.length)); + const filtered = newEntities.filter(e => !currentEntities.find(_e => e._ === _e._ && e.offset === _e.offset && e.length === _e.length)); currentEntities.push(...filtered); currentEntities.sort((a, b) => a.offset - b.offset); return currentEntities; @@ -496,7 +496,7 @@ namespace RichTextProcessor { let inner: string; let url: string; - if(entity._ == 'messageEntityTextUrl') { + if(entity._ === 'messageEntityTextUrl') { url = (entity as MessageEntity.messageEntityTextUrl).url; url = wrapUrl(url, true); //inner = wrapRichNestedText(entityText, entity.nested, options); @@ -659,7 +659,7 @@ namespace RichTextProcessor { export function wrapEmojiText(text: string) { if(!text) return ''; - let entities = parseEntities(text).filter(e => e._ == 'messageEntityEmoji'); + let entities = parseEntities(text).filter(e => e._ === 'messageEntityEmoji'); return wrapRichText(text, {entities}); } @@ -670,7 +670,7 @@ namespace RichTextProcessor { let tgMeMatch; let telescoPeMatch; - /* if(unsafe == 2) { + /* if(unsafe === 2) { url = 'tg://unsafe_url?url=' + encodeURIComponent(url); } else */if((tgMeMatch = url.match(/^https?:\/\/t(?:elegram)?\.me\/(.+)/))) { const fullPath = tgMeMatch[1]; @@ -733,7 +733,7 @@ namespace RichTextProcessor { const first = [...splitted[0]][0]; - if(onlyFirst || splitted.length == 1) return wrapEmojiText(first); + if(onlyFirst || splitted.length === 1) return wrapEmojiText(first); const last = [...splitted[splitted.length - 1]][0];