tweb/public/6.c9f92f16f3feb387034b.chunk.js.map
Eduard Kuzmenko 7381aaa97b Build
2021-07-18 16:42:20 +03:00

1 line
648 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{"version":3,"sources":["webpack:///./src/helpers/slicedArray.ts","webpack:///./src/lib/appManagers/appPollsManager.ts","webpack:///./src/lib/appManagers/appDraftsManager.ts","webpack:///./src/lib/appManagers/appAvatarsManager.ts","webpack:///./src/helpers/middleware.ts","webpack:///./src/helpers/assumeType.ts","webpack:///./src/components/middleEllipsis.ts","webpack:///./src/lib/mtproto/referenceDatabase.ts","webpack:///./src/helpers/heavyQueue.ts","webpack:///./src/helpers/blur.ts","webpack:///./src/lib/opusDecodeController.ts","webpack:///./src/lib/appManagers/appWebPagesManager.ts","webpack:///./src/helpers/dom/htmlToDocumentFragment.ts","webpack:///./src/vendor/leemon.ts","webpack:///./src/lib/mtproto/bin_utils.ts","webpack:///./src/lib/storages/dialogs.ts","webpack:///./src/lib/storages/filters.ts","webpack:///./src/helpers/formatCallDuration.ts","webpack:///./src/helpers/formatDuration.ts","webpack:///./src/lib/appManagers/appMessagesManager.ts","webpack:///./src/lib/appManagers/appPhotosManager.ts","webpack:///./src/components/peerTitle.ts","webpack:///./src/lib/mtproto/serverTimeManager.ts","webpack:///./src/lib/appManagers/appDocsManager.ts","webpack:///./src/lib/appManagers/appProfileManager.ts","webpack:///./src/helpers/bytes.ts","webpack:///./src/components/visibilityIntersector.ts","webpack:///./src/components/lazyLoadQueue.ts","webpack:///./src/helpers/dom/renderImageFromUrl.ts","webpack:///./src/lib/appManagers/appNotificationsManager.ts","webpack:///./src/components/singleTransition.ts","webpack:///./src/components/preloader.ts","webpack:///./src/helpers/files.ts"],"names":["SliceEnd","SlicedArray","this","sliceConstructor","getSliceConstructor","first","constructSlice","slices","slicedArray","Array","end","None","side","isEnd","Top","slice","last","includes","length","Bottom","Both","setEnd","start","deleteCount","items","ret","super","splice","idx","indexOf","unsetEnd","i","flatten","push","lowerBound","upperBound","foundSlice","lowerIndex","upperIndex","foundSliceIndex","sliced","unshift","insertIndex","s","prevSlice","nextSlice","insertSlice","item","index","maxId","offset","offsetId","add_offset","limit","sliceOffset","pos","findSliceOffset","sliceStart","Math","max","sliceEnd","topWasMeantToLoad","bottomWasMeantToLoad","abs","topFulfilled","bottomFulfilled","offsetIdOffset","fulfilled","found","findSlice","appPollsManager","polls","results","log","Error","addMultipleEventsListeners","updateMessagePoll","update","poll","poll_id","savePoll","dispatchEvent","id","Object","assign","saveResults","rQuestion","wrapEmojiText","question","rReply","chosenIndexes","pFlags","min","forEach","answer","chosen","pollId","correctAnswers","solution","solutionEntities","parseMarkdown","undefined","_","correct_answers","solution_entities","message","optionIds","media","options","map","answers","option","messageId","mid","peerId","inputPeer","getInputPeerById","is_outgoing","invokeAfterMessageIsSent","sendVote","invokeApi","peer","msg_id","getServerMessageId","then","updates","processUpdateMessage","votesList","saveApiUsers","users","closed","Promise","resolve","newPoll","editMessage","newMedia","getInputMediaPoll","err","error","appDraftsManager","drafts","getAllDraftPromise","get","updateDraftMessage","peerID","getPeerId","saveDraft","threadId","draft","notify","getKey","getAllDrafts","key","getDialogOnly","reloadConversation","updatesState","syncLoading","apiDraft","processApiDraft","set","draft1","draft2","reply_to_msg_id","entities","no_webpage","myEntities","parseEntities","apiEntities","totalEntities","mergeEntities","rMessage","wrapDraftText","generateMessageId","localDraft","saveOnServer","serverDraft","getDraft","draftsAreEqual","draftObj","params","isEmptyDraft","getInputEntities","saveLocalDraft","date","serverTimeOffset","bool","splitted","split","appAvatarsManager","savedAvatarURLs","photo","size","getAvatarPromise","cached","saved","peerPhotoFileLocation","photo_id","big","downloadOptions","dcId","dc_id","location","promise","download","blob","URL","createObjectURL","loadPromise","div","img","Image","onlyThumb","renderThumbPromise","callback","loadAvatar","dataset","color","animate","settings","animationsEnabled","thumbImage","classList","add","stripped_thumb","url","getPreviewURLFromBytes","append","setTimeout","childElementCount","mutateElement","remove","renderPromise","isDialog","title","getPeerPhoto","avatarAvailable","avatarRendered","firstElementChild","contains","myId","innerText","user","getUser","deleted","getPeerColorById","abbr","getAbbreviation","getPeer","initials","innerHTML","putAvatar","getMiddleware","cleanupObj","cleaned","clean","_cleanupObj","assumeType","x","Map","testQueue","Set","fontFamily","timeoutId","setTestQueue","cancelAnimationFrame","window","requestAnimationFrame","testQueueElements","testElement","clear","addEventListener","capture","passive","element","mapped","firstTime","text","textLength","from","multiplier","font","textWidth","elementWidth","textContent","fontWeight","getTextWidth","getBoundingClientRect","width","newElementWidth","widthChanged","setAttribute","smallerText","smallerWidth","smallerTextLength","half","half1","substr","replace","half2","removeAttribute","context","canvas","document","createElement","getContext","measureText","MiddleEllipsisElement","HTMLElement","delete","customElements","define","referenceDatabase","contexts","links","addTaskListener","task","bytes","payload","originalPayload","refreshReference","getReferenceByLink","postMessage","reference","getContexts","_context","values","next","value","c","reject","type","wrapSingleMessage","console","warn","hex","newHex","deleteContext","newContext","heavyQueue","processingQueue","addHeavyTask","queue","method","processHeavyQueue","todo","f","performance","now","possiblePromise","process","apply","shift","realResult","timedChunk","finally","isFilterAvailable","requireBlurPromise","fastBlurFunc","processBlurNext","radius","iterations","height","ctx","alpha","filter","drawImage","toDataURL","m","default","blurPromises","dataUri","has","onload","src","opusDecodeController","sampleRate","tasks","keepAlive","isPlaySupportedResult","audio","canPlayType","wavWorker","Worker","e","data","page","onTaskEnd","worker","command","waveform","buffers","typedArray","buffer","loadWorker","loadWavWorker","terminateWorkers","result","clearTimeout","timeout","executeNewTask","kill","terminate","decoderSampleRate","outputBufferSampleRate","wavBitDepth","wavSampleRate","pages","withWaveform","pushDecodeTask","dataBlob","Blob","webpages","pendingWebPages","updateWebPage","saveWebPage","webpage","apiWebPage","mediaContext","savePhoto","saveDoc","siteName","site_name","shortTitle","author","rTitle","wrapRichText","noLinks","noLinebreaks","contextHashtag","matches","match","shortDescriptionText","description","rDescription","contextSite","msgs","msgId","webPage","keys","htmlToDocumentFragment","html","template","trim","content","bpe","mask","int2bigInt","t","bits","minSize","k","ceil","buff","copyInt_","n","len","isObject","object","appMessagesManager","appChatsManager","appPeersManager","appUsersManager","appNotificationsManager","appStateManager","apiUpdatesManager","serverTimeManager","onUpdateFolderPeers","folder_peers","folderPeer","folder_id","dialog","dropDialog","pinned","pinnedOrders","findAndSplice","p","pushToState","generateIndexForDialog","pushDialog","scheduleHandleNewDialogs","onUpdateDialogPinned","folderId","onUpdatePinnedDialogs","handleOrder","order","reverse","newPinned","getFolder","dialogsResult","applyDialogs","dialogs","d","storage","storages","getCache","getSelf","peerText","getPeerSearchText","dialogsIndex","indexObject","updateFolderPeers","updateDialogPinned","updatePinnedDialogs","getState","state","storagesResults","top_message","topMessage","saveMessages","saveDialog","getMessageByPeer","allDialogsLoaded","loaded","init","byFolders","dialogsOffsetDate","0","1","dialogsNum","cachedResults","query","count","skipMigrated","arr","migratedTo","filtersStorage","filters","testDialogForFilter","pinnedIndex","pinned_peers","generateDialogIndex","generateDialogPinnedDateByIndex","sort","a","b","folders","folder","skipped","justReturn","channelId","isChannel","topDate","generateDialogPinnedDate","channel","getChat","Date","foundIndex","historyStorage","getHistoryStorage","history","concat","incomingMessage","fromId","viaBotId","requestPeer","pts","newPts","getChannelState","offsetDate","findIndex","setDialogToState","foundDialog","getDialog","_peerId","keepPeerSingle","saveApiChats","chats","messages","updatedDialogs","topPendingMessage","pendingTopMsgs","dropped","newUpdatesAfterReloadToHandle","saveUpdate","saveOffset","chat","left","kicked","generateTempMessageId","from_id","getOutputPeer","peer_id","out","isOutgoing","migrated_to","deactivated","migratedToPeer","migratedFromTo","migratedToFrom","wasDialogBefore","read_inbox_max_id","read_outbox_max_id","hasOwnProperty","unread","mergeReplyKeyboard","readMaxId","readOutboxMaxId","savePeerSettings","notify_settings","addChannelState","offsetIndex","realFolderId","curDialogStorage","search","d1","d2","loadedAll","isDialogsLoaded","getTopMessages","rootScope","onUpdateDialogFilter","saveDialogFilter","onUpdateDialogFilterOrder","orderIndex","filterId","setOrderIndex","updateDialogFilter","updateDialogFilters","oldFilters","getDialogFilters","_filterId","find","updateDialogFilterOrder","exclude_peers","include_peers","exclude_archived","exclude_read","unread_count","unread_mark","exclude_muted","isPeerLocalMuted","broadcasts","isBroadcast","groups","isAnyGroup","isBot","bots","non_contacts","isContact","contacts","config","pinned_infolder_count_max","flags","getOutputDialogFilter","overwrite","invokeApiSingle","CALL_DURATION_LANG_KEYS","h","w","formatCallDuration","duration","elements","showLast","o","modulus","formatDuration","fragment","pendingByRandomId","pendingByMessageId","pendingAfterMsgs","tempNum","tempFinalizeCallbacks","sendSmthLazyLoadQueue","needSingleMessages","fetchSingleMessagesPromise","maxSeenId","newMessagesHandleTimeout","newMessagesToHandle","newDialogsToHandle","notificationsHandlePromise","notificationsToHandle","reloadConversationsPeers","logger","Debug","Log","Warn","groupedTempId","typings","handleNewMessages","handleNewDialogs","newMaxSeenId","obj","dialogsStorage","incrementMaxSeenId","handleNotifications","idle","isIDLE","notifyPeerToHandle","all","getNotifyPeerTypeSettings","getNotifySettings","getInputNotifyPeerById","peerTypeNotifySettings","notifyAboutMessage","fwdCount","onUpdateMessageId","randomId","random_id","pendingData","tempId","getMessageFromStorage","Boolean","finalizePendingMessageCallbacks","onUpdateNewMessage","getMessagePeer","getMessagesStorage","isLocalThreadUpdate","threadKey","getThreadKey","threadsStorage","good","isInChat","pendingMessage","checkPendingMessage","updateMessageRepliesIfNeeded","firstSlice","forceUserOnline","action","user_id","channel_id","top_msg_id","chat_id","processLocalUpdate","handleNewMessage","inboxUnread","setDialogTopMessage","notifyPeer","fwd_from","onUpdateDialogUnreadMark","onUpdateEditMessage","oldMessage","newMessage","handleEditedMessage","isTopMessage","clear_history","grouped_id","onUpdateReadHistory","max_id","read_max_id","isOut","stillUnreadCount","still_unread_count","newUnreadCount","foundAffected","repliesKey","threadsToReplies","updateMessage","replyTo","reply_to","reply_to_top_id","cancel","getReadMaxIdIfUnread","threadKeyPart","onUpdateReadMessagesContents","mids","getMessageById","media_unread","setDialogToStateIfMessageIsTop","onUpdateChannelAvailableMessages","available_min_id","onUpdateDeleteMessages","clearCache","threadKeys","historyUpdated","handleDeletedMessages","threadsStorages","onUpdateChannel","needDialog","username","historiesStorage","onUpdateChannelReload","onUpdateChannelMessageViews","views","onUpdateServiceNotification","inbox_date","hasUser","verified","access_hash","first_name","phone","onUpdatePinnedMessages","missingMessages","werePinned","pinnedMessages","hiddenPinnedMessages","onUpdateNotifySettings","onUpdateNewScheduledMessage","scheduledMessagesStorage","isScheduled","onUpdateDeleteScheduledMessages","updateMessageID","updateNewDiscussionMessage","updateNewMessage","updateNewChannelMessage","updateDialogUnreadMark","updateEditMessage","updateEditChannelMessage","updateReadChannelDiscussionInbox","updateReadChannelDiscussionOutbox","updateReadHistoryInbox","updateReadHistoryOutbox","updateReadChannelInbox","updateReadChannelOutbox","updateChannelReadMessagesContents","updateReadMessagesContents","updateChannelAvailableMessages","updateDeleteMessages","updateDeleteChannelMessages","updateChannel","updateChannelReload","updateChannelMessageViews","updateServiceNotification","updatePinnedMessages","updatePinnedChannelMessages","updateNotifySettings","updateNewScheduledMessage","updateDeleteScheduledMessages","getConversationsAll","filterFunc","eventData","appWebPagesManager","getWebPage","maxSeenMsgId","middleware","messagesStorageByPeerId","groupedMessagesStorage","searchesStorage","threadsServiceMessagesIdsStorage","sendEntites","entity","getUserInput","callbackName","finalize","deferred","schedule_date","scheduleDate","is_scheduled","noWebPage","handled","replyToMsgId","MAX_LENGTH","message_length_max","sendText","getPeerMigratedTo","generateOutgoingMessage","toggleError","on","send","sentRequestOptions","apiPromise","afterMessageId","invokeApiAfter","query_id","queryId","resultId","clear_draft","clearDraft","silent","wrapMessageEntities","seq","pts_count","local","beforeMessageSending","file","attachType","apiFileName","fileType","mime_type","fileName","File","name","isDocument","caption","attributes","isPhoto","actionName","isVoiceMessage","attribute","voice","isMedia","photoSize","sizes","cacheContext","appDownloadManager","getCacheContext","downloaded","objectURL","appPhotosManager","videoAttribute","round_message","isRoundMessage","file_name","thumbs","thumb","thumbURL","thumbBlob","thumbCacheContext","appDocsManager","preloader","attachMethod","tryAgainOnFail","isUpload","sentDeferred","attachPromise","catch","uploaded","cancelPendingMessage","setTyping","uploadPromise","file_reference","inputMedia","load","thumbUploadPromise","upload","notifyAll","done","total","inputFile","addNotifyListener","progress","percents","floor","isGroupedItem","background","code","files","sendFile","sendFileDetails","groupId","details","syncDraft","invoke","multiMedia","multi_media","promises","messageMedia","getInput","doc","getMediaInput","inputSingleMedia","inputs","total_voters","getPoll","getScheduledMessagesStorage","generateFromId","generateFlags","random","generateReplyHeader","via_bot_id","reply_markup","replies","generateReplies","pending","replyToTopId","header","channelFull","appProfileManager","chatsFull","linked_chat_id","comments","replies_pts","admin_rights","anonymous","post","originalMessage","fwdHeader","from_name","post_author","channel_post","saved_from_msg_id","saved_from_peer","Number","MAX_SAFE_INTEGER","outDialogs","getConversations","getDialogs","chatHistoryStorage","getOffsetDate","offset_date","offset_id","offset_peer","hash","noErrorBox","resetPinnedOrder","telegramMeWebManager","setAuthorized","maxSeenIdIncremented","hasPrepend","noIdsDialogs","dialogsLength","setDialogsLoaded","fromPeerId","newMessages","generateForwardHeader","group","from_peer","to_peer","with_my_score","withMyScore","createMessageStorage","reloadConversationsPromise","peers","getInputDialogPeerById","just_clear","revoke","affectedHistory","doFlushHistory","justClear","getHistory","historyResult","getChannelInput","getPinnedMessage","getSearch","inputFilter","unpin","pm_oneside","unpinAll","unpinAllMessages","foundMessages","getMidsByAlbum","verify","temp","q","MESSAGE_ID_OFFSET","num","MESSAGE_ID_INCREMENT","l","used","increment","reply_to_mid","overwriting","savedFromPeerId","savedFromMid","savedFrom","fwdFromId","ttl_seconds","migrateFrom","migrateTo","suffix","video_sizes","reason","migrateChecks","fixEmoji","usingMids","plain","highlightWord","parts","addPart","langKey","part","format","el","usingFullAlbum","getMidsByMessage","getAlbumText","emoticon","prefix","game","stickerEmojiRaw","stickerEmoji","actionWrapped","wrapMessageActionTextNew","regExp","RegExp","exec","messageWrapped","noTextFormat","join","createDocumentFragment","peerTitle","senderTitle","getPeerTitle","wrapPlainText","langPackKey","args","getNameDivHTML","endsWith","peerIds","today","daysToStart","getTime","tomorrowDate","setDate","getDate","_args","IntlDateElement","day","month","year","htmlToSpan","userId","anchorHTML","domain","langPack","toggleDialogPin","pinned_dialogs_count_max","getPinnedOrders","getDialogPeer","read","hasChat","fromChat","kind","goodMedias","sticker","canMessageBeEdited","edit_time_limit","hasRights","messageReplyMarkup","lastReplyMarkup","selective","maxOutId","single_use","hidden","canCache","invokeApiCacheable","bind","func","nextRate","backLimit","minDate","maxDate","foundMsgs","filtering","neededContents","neededDocTypes","excludeDocTypes","goodEntities","matchUrl","next_rate","offset_id_offset","min_date","max_date","min_id","offsetPeerId","offsetMessage","offset_rate","searchResult","foundCount","getDiscussionMessage","maxMessageId","serviceStartMessage","is_single","filterMessages","generateThreadServiceStartMessage","newDialogsHandlePromise","localMessageIds","creator","editor","megagroup","goodMsgIds","affectedMessages","force","triedToReadMaxId","readPromise","soundReset","getPeerString","readHistory","msgIds","threadMessage","broadcastEventName","finalizePendingMessage","mute","mute_until","canSendToUser","finalMessage","callbacks","getPhoto","newPhoto","newPhotoSize","oldCacheContext","getPhotoDownloadOptions","fakeDownload","getDoc","newDoc","getInputFileName","tempMessage","handleReleasingMessage","notification","peerString","notificationMessage","show_previews","wrapMessageForReply","onclick","tag","peerPhoto","image","canWriteToPeer","isFetchIntervalNeeded","haveSlice","sliceMe","fillHistoryStorage","requestHistory","isTopEnd","isBottomEnd","oldestMessage","_historyResult","getMessagesResult","fetchSingleMessages","typing","smth","deleteWebPageFromPending","groupedStorage","albums","peerMessagesToHandle","deletedMids","AppPhotosManager","photos","windowW","windowH","visualViewport","innerWidth","innerHeight","oldPhoto","saveContext","boxWidth","boxHeight","useBytes","devicePixelRatio","bestPhotoSize","inputUser","cacheSeconds","photosResult","photoIds","isSticker","mimeType","Uint8Array","jpegHeader","jpegTail","path","useBlur","getPreviewURLFromThumb","noZoom","choosePhotoSize","boxSize","aspect","isFit","aspectCovered","style","ignoreCache","getImageFromStrippedThumb","queueId","onlyCache","thumb_size","photoId","fullWidth","fullHeight","getDownload","fullPhotoSize","downloadToDisc","weakMap","WeakMap","peerTitleWeakMap","querySelectorAll","PeerTitle","plainText","onlyFirstName","to","docs","savingLottiePreview","onServiceWorkerFail","supportsStreaming","oldDoc","audioTitle","audioPerformer","performer","alt","stickerset","stickerSetInput","isWebpSupported","animated","monthAsNumber","leadingZero","isServiceWorkerOnline","getFileURL","docId","thumbSize","inputFileLocation","getFileDownloadOptions","preloadPhoto","tryNotToUseBytes","getThumbURL","originalPromise","isPlaySupported","reader","FileReader","onloadend","uint8","target","decode","readAsArrayBuffer","toneIndex","stickerCachedThumbs","toBlob","downloadDoc","createDownloadAnchor","AppProfileManager","usersFull","fullPromises","onUpdateUserTyping","typingsInPeer","cancelAction","getChatFull","updateChatParticipants","participants","chatId","chatFull","updateChatParticipantAdd","_participants","inviter_id","version","updateChatParticipantDelete","updateUserTyping","updateChatUserTyping","updateChannelUserTyping","fullChat","emptyPhoto","chat_photo","invalidateChannelParticipants","megagroupOnlines","override","userFull","saveApiUser","profile_photo","about","rAbout","getProfile","getProfileByPeerId","profile","getChannelFull","full_chat","exported_invite","link","exportedInvite","broadcast","participant","channelParticipant","fullChannel","processUserIds","userIds","getUserSearchText","getChannelParticipants","cP","getParticipantPeerId","last_name","updateResult","previous","participants_count","isMegagroup","timestamp","onlines","res","getChannelInputPeer","reduce","acc","status","bytesToHex","toString","bytesFromHex","hexString","parseInt","charAt","bytesToBase64","mod3","nLen","nUint24","nIdx","String","fromCharCode","uint6ToBase64","nUint6","bytesCmp","bytes1","bytes2","bufferConcats","v","byteLength","tmp","lastLength","ArrayBuffer","VisibilityIntersector","onVisibilityChange","locked","observer","IntersectionObserver","entries","changed","entry","isIntersecting","visible","getVisible","disconnect","targets","observe","unobserve","unlock","refresh","parallelLimit","inProcess","lockPromise","unlockResolve","processQueue","_processQueue","loadItem","getItem","processItem","addElement","lock","intersector","unlockAndRefresh","intersectorTimeout","wasSeen","setProcessQueueTimeout","_queue","spliced","loadedURLs","elem","HTMLImageElement","HTMLVideoElement","SVGImageElement","setAttributeNS","backgroundImage","renderImageFromUrl","useCache","isImage","loader","renderImageFromUrlPromise","notificationsShown","notificationIndex","notificationsCount","soundsPlayed","vibrateSupport","navigator","vibrate","peerSettings","notifyUsers","notifyChats","notifyBroadcasts","faviconEl","head","querySelector","titleBackup","titleChanged","stopped","pushInited","updateLocalSettings","updSettings","nodesktop","volume","novibrate","nopreview","nopush","needPush","isAvailable","registeredDevice","subscribe","unsubscribe","setSettings","nosound","notifications","sound","requestPermission","Notification","removeEventListener","mozVibrate","webkitVibrate","notificationsUiSupport","topMessagesDeferred","notifySoundEl","body","stop","newVal","toggleToggler","tokenData","unregisterDevice","registerDevice","once","notificationData","period","custom","enable","resetTitle","setFavicon","clearInterval","titleInterval","setInterval","beginPath","arc","PI","fillStyle","fill","fontSize","str","textBaseline","textAlign","fillText","getNotifyPeerTypePromise","inputKey","compare_sound","notifyContactsSignUp","href","prevFavicon","cloneNode","parentNode","replaceChild","peerNotifySettings","isMuted","respectType","notifySettings","inputNotify","typeNotifySettings","getPeerLocalSettings","permission","testSound","icon","setLocalNotificationsDisabled","close","focus","onclose","show","hide","nextSoundAt","prevSoundVolume","filename","autoplay","hidePushNotifications","token_type","tokenType","token","tokenValue","other_uids","app_sandbox","secret","className","forwards","onTransitionEnd","afterTimeout","toggle","ProgressivePreloader","detached","cancelable","streamable","onClick","loadFunc","bold","constructContainer","construct","downloadSvg","lastElementChild","cancelSvg","previousElementSibling","circle","setProgress","startTime","onEnd","elapsedTime","delay","TRANSITION_TIME","detach","setManual","reset","parentElement","totalLength","getTotalLength","strokeDasharray","createPosterFromVideo","video","onseeked","videoWidth","videoHeight","onerror","currentTime","createPosterForVideo","onloadedmetadata","preloadVideo","race","onVideoLoad","readyState","HAVE_METADATA","getFilesFromEvent","onlyTypes","scanFiles","isDirectory","directoryReader","createReader","readEntries","itemFile","getAsFile","DataTransferItem","DragEvent","dataTransfer","clipboardData","originalEvent","webkitGetAsEntry","requestFile","accept","input","display","click"],"mappings":"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","file":"6.c9f92f16f3feb387034b.chunk.js","sourcesContent":["/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { MOUNT_CLASS_TO } from \"../config/debug\";\r\n\r\n/**\r\n * Descend sorted storage\r\n */\r\n\r\ntype ItemType = number;\r\n\r\nexport enum SliceEnd {\r\n None = 0,\r\n Top = 1,\r\n Bottom = 2,\r\n Both = SliceEnd.Top | SliceEnd.Bottom\r\n};\r\n\r\nexport interface Slice extends Array<ItemType> {\r\n //slicedArray: SlicedArray;\r\n end: SliceEnd;\r\n\r\n isEnd: (side: SliceEnd) => boolean;\r\n setEnd: (side: SliceEnd) => void;\r\n unsetEnd: (side: SliceEnd) => void;\r\n\r\n slice: (from?: number, to?: number) => Slice;\r\n splice: (start: number, deleteCount: number, ...items: ItemType[]) => Slice;\r\n}\r\n\r\nexport interface SliceConstructor {\r\n new(...items: ItemType[]): Slice;\r\n}\r\n\r\n// TODO: Clear empty arrays after deleting items\r\nexport default class SlicedArray {\r\n private slices: Slice[]/* = [[7,6,5],[4,3,2],[1,0,-1]] */;\r\n private sliceConstructor: SliceConstructor;\r\n \r\n constructor() {\r\n // @ts-ignore\r\n this.sliceConstructor = SlicedArray.getSliceConstructor(this);\r\n\r\n const first = this.constructSlice();\r\n //first.setEnd(SliceEnd.Bottom);\r\n this.slices = [first];\r\n }\r\n\r\n private static getSliceConstructor(slicedArray: SlicedArray) {\r\n return class Slice extends Array<ItemType> implements Slice {\r\n //slicedArray: SlicedArray;\r\n end: SliceEnd = SliceEnd.None;\r\n \r\n /* constructor(...items: ItemType[]) {\r\n super(...items);\r\n //this.slicedArray = slicedArray;\r\n } */\r\n \r\n isEnd(side: SliceEnd): boolean {\r\n if((this.end & side) === side) {\r\n return true;\r\n }/* else if(!this.slicedArray) {\r\n return false;\r\n } */\r\n \r\n let isEnd = false;\r\n if(side === SliceEnd.Top) {\r\n const slice = slicedArray.last;\r\n isEnd = slice.end & side ? this.includes(slice[slice.length - 1])/* || !slice.length */ : false;\r\n } else if(side === SliceEnd.Bottom) {\r\n const slice = slicedArray.first;\r\n isEnd = slice.end & side ? this.includes(slice[0])/* || !slice.length */ : false;\r\n } else if(side === SliceEnd.Both) {\r\n return this.isEnd(SliceEnd.Top) && this.isEnd(SliceEnd.Bottom);\r\n }\r\n\r\n if(isEnd) {\r\n this.setEnd(side);\r\n }\r\n \r\n return isEnd;\r\n }\r\n \r\n setEnd(side: SliceEnd) {\r\n this.end |= side;\r\n }\r\n\r\n unsetEnd(side: SliceEnd) {\r\n this.end ^= side;\r\n }\r\n\r\n splice(start: number, deleteCount: number, ...items: ItemType[]) {\r\n const ret = super.splice(start, deleteCount, ...items);\r\n\r\n if(!this.length) {\r\n const slices = slicedArray.slices as number[][];\r\n const idx = slices.indexOf(this);\r\n if(idx !== -1) {\r\n if(slices.length === 1) { // left empty slice without ends\r\n this.unsetEnd(SliceEnd.Both);\r\n } else { // delete this slice\r\n slices.splice(idx, 1);\r\n }\r\n }\r\n }\r\n\r\n return ret;\r\n }\r\n }\r\n }\r\n\r\n public constructSlice(...items: ItemType[]) {\r\n //const slice = new Slice(this, ...items);\r\n // can't pass items directly to constructor because first argument is length\r\n const slice = new this.sliceConstructor(items.length);\r\n for(let i = 0, length = items.length; i < length; ++i) {\r\n slice[i] = items[i];\r\n }\r\n return slice;\r\n \r\n // ! code below will slow execution in 15 times\r\n /* const self = this;\r\n const p: Slice = new Proxy(slice, {\r\n get: function(target, name: any) {\r\n if(name === 'constructor') {\r\n const p = new Proxy(Slice, {\r\n construct: (target, args) => {\r\n return self.constructSlice(...args);\r\n }\r\n });\r\n\r\n return p;\r\n }\r\n\r\n return target[name];\r\n }\r\n });\r\n\r\n return p; */\r\n\r\n /*\r\n var p = slicedArray.constructSlice();\r\n p.length = 100000;\r\n p.fill(255);\r\n\r\n var a = new Array(100000);\r\n a.fill(255);\r\n\r\n var b = 0;\r\n var perf = performance.now();\r\n for(var i = 0; i < p.length; ++i) {\r\n b += p[i];\r\n }\r\n\r\n console.log('perf 1', performance.now() - perf);\r\n\r\n b = 0;\r\n perf = performance.now();\r\n for(var i = 0; i < a.length; ++i) {\r\n b += a[i];\r\n }\r\n\r\n console.log('perf 2', performance.now() - perf);\r\n */\r\n }\r\n\r\n public insertSlice(slice: ItemType[], flatten = true) {\r\n if(!slice.length) {\r\n return;\r\n }\r\n\r\n const first = this.slices[0];\r\n if(!first.length) {\r\n first.push(...slice);\r\n return first;\r\n }\r\n\r\n const lowerBound = slice[slice.length - 1];\r\n const upperBound = slice[0];\r\n\r\n let foundSlice: Slice, lowerIndex = -1, upperIndex = -1, foundSliceIndex = 0;\r\n for(; foundSliceIndex < this.slices.length; ++foundSliceIndex) {\r\n foundSlice = this.slices[foundSliceIndex];\r\n lowerIndex = foundSlice.indexOf(lowerBound);\r\n upperIndex = foundSlice.indexOf(upperBound);\r\n \r\n if(upperIndex !== -1 && -1 !== lowerIndex) {\r\n break;\r\n } else if(upperIndex !== -1 || -1 !== lowerIndex) {\r\n break;\r\n }\r\n }\r\n\r\n if(upperIndex !== -1 && -1 !== lowerIndex) {\r\n\r\n } else if(upperIndex !== -1) { // ([1, 2, 3] | [1, 2, 3, 4, 5]) -> [1, 2, 3, 4, 5]\r\n const sliced = slice.slice(foundSlice.length - upperIndex);\r\n foundSlice.push(...sliced);\r\n } else if(lowerIndex !== -1) { // ([1, 2, 3] | [-1, 0, 1]) -> [-1, 0, 1, 2, 3]\r\n const sliced = slice.slice(0, slice.length - lowerIndex - 1);\r\n foundSlice.unshift(...sliced);\r\n } else {\r\n let insertIndex = 0;\r\n for(const length = this.slices.length; insertIndex < length; ++insertIndex) { // * maybe should iterate from the end, could be faster ?\r\n const s = this.slices[insertIndex];\r\n if(slice[0] > s[0]) {\r\n break;\r\n }\r\n }\r\n\r\n this.slices.splice(insertIndex, 0, this.constructSlice(...slice));\r\n foundSliceIndex = insertIndex;\r\n }\r\n\r\n if(flatten) {\r\n return this.flatten(foundSliceIndex);\r\n }\r\n }\r\n\r\n private flatten(foundSliceIndex: number) {\r\n if(this.slices.length >= 2) {\r\n for(let i = 0, length = this.slices.length; i < (length - 1); ++i) {\r\n const prevSlice = this.slices[i];\r\n const nextSlice = this.slices[i + 1];\r\n \r\n const upperIndex = prevSlice.indexOf(nextSlice[0]);\r\n if(upperIndex !== -1) {\r\n prevSlice.setEnd(nextSlice.end);\r\n this.slices.splice(i + 1, 1);\r\n\r\n if(i < foundSliceIndex) {\r\n --foundSliceIndex;\r\n }\r\n\r\n --length; // respect array bounds\r\n --i; // repeat from the same place\r\n \r\n this.insertSlice(nextSlice, false);\r\n }\r\n }\r\n }\r\n\r\n return this.slices[foundSliceIndex];\r\n }\r\n\r\n // * \r\n \r\n get first() {\r\n return this.slices[0];\r\n }\r\n \r\n get last() {\r\n return this.slices[this.slices.length - 1];\r\n }\r\n\r\n get slice() {\r\n return this.first;\r\n }\r\n\r\n get length() {\r\n return this.slice.length;\r\n }\r\n\r\n public findSlice(item: ItemType) {\r\n for(let i = 0, length = this.slices.length; i < length; ++i) {\r\n const slice = this.slices[i];\r\n const index = slice.indexOf(item);\r\n if(index !== -1) {\r\n return {slice, index};\r\n }\r\n }\r\n \r\n return undefined;\r\n }\r\n\r\n public findSliceOffset(maxId: number) {\r\n let slice: Slice;\r\n for(let i = 0; i < this.slices.length; ++i) {\r\n let offset = 0;\r\n slice = this.slices[i];\r\n if(slice.length < 2) {\r\n continue;\r\n }\r\n \r\n for(; offset < slice.length; offset++) {\r\n if(maxId >= slice[offset]) {\r\n /* if(!offset) { // because can't find 3 in [[5,4], [2,1]]\r\n return undefined;\r\n } */\r\n\r\n return {\r\n slice, \r\n offset: maxId === slice[offset] ? offset : offset - 1\r\n };\r\n }\r\n }\r\n }\r\n\r\n if(slice && slice.isEnd(SliceEnd.Top)) {\r\n return {\r\n slice,\r\n offset: slice.length\r\n };\r\n }\r\n\r\n return undefined;\r\n }\r\n\r\n // * https://core.telegram.org/api/offsets\r\n public sliceMe(offsetId: number, add_offset: number, limit: number) {\r\n let slice = this.slice;\r\n let offset = 0;\r\n let sliceOffset = 0;\r\n\r\n if(offsetId) {\r\n const pos = this.findSliceOffset(offsetId);\r\n if(!pos) {\r\n return undefined;\r\n }\r\n\r\n slice = pos.slice;\r\n offset = sliceOffset = pos.offset;\r\n\r\n if(slice.includes(offsetId)) {\r\n sliceOffset += 1;\r\n }\r\n\r\n /* if(slice.includes(offsetId) && add_offset < 0) {\r\n add_offset += 1;\r\n } */\r\n }\r\n\r\n let sliceStart = Math.max(sliceOffset + add_offset, 0);\r\n let sliceEnd = sliceOffset + add_offset + limit;\r\n //const fixHalfBackLimit = add_offset && !(limit / add_offset % 2) && (sliceEnd % 2) ? 1 : 0;\r\n //sliceEnd += fixHalfBackLimit;\r\n\r\n const sliced = slice.slice(sliceStart, sliceEnd) as Slice;\r\n\r\n const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;\r\n const bottomWasMeantToLoad = Math.abs(add_offset);\r\n\r\n // can use 'slice' here to check because if it's end, then 'sliced' is out of 'slice'\r\n // useful when there is only 1 message in chat on its reopening\r\n const topFulfilled = (slice.length - sliceOffset) >= topWasMeantToLoad || (slice.isEnd(SliceEnd.Top) ? (sliced.setEnd(SliceEnd.Top), true) : false);\r\n const bottomFulfilled = (sliceOffset - bottomWasMeantToLoad) >= 0 || (slice.isEnd(SliceEnd.Bottom) ? (sliced.setEnd(SliceEnd.Bottom), true) : false);\r\n\r\n //console.log('sliceMe', topFulfilled, bottomFulfilled);\r\n\r\n return {\r\n slice: sliced, \r\n offsetIdOffset: offset,\r\n fulfilled: SliceEnd.None | (topFulfilled && bottomFulfilled ? SliceEnd.Both : ((topFulfilled ? SliceEnd.Top : SliceEnd.None) | (bottomFulfilled ? SliceEnd.Bottom : SliceEnd.None)))\r\n };\r\n }\r\n\r\n public unshift(...items: ItemType[]) {\r\n let slice = this.first;\r\n if(!slice.length) {\r\n slice.setEnd(SliceEnd.Bottom);\r\n } else if(!slice.isEnd(SliceEnd.Bottom)) {\r\n slice = this.constructSlice();\r\n slice.setEnd(SliceEnd.Bottom);\r\n this.slices.unshift(slice);\r\n }\r\n\r\n slice.unshift(...items);\r\n }\r\n\r\n public push(...items: ItemType[]) {\r\n let slice = this.last;\r\n if(!slice.length) {\r\n slice.setEnd(SliceEnd.Top);\r\n } else if(!slice.isEnd(SliceEnd.Top)) {\r\n slice = this.constructSlice();\r\n slice.setEnd(SliceEnd.Top);\r\n this.slices.push(slice);\r\n }\r\n\r\n slice.push(...items);\r\n }\r\n\r\n public delete(item: ItemType) {\r\n const found = this.findSlice(item);\r\n if(found) {\r\n found.slice.splice(found.index, 1);\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n\r\nMOUNT_CLASS_TO && (MOUNT_CLASS_TO.SlicedArray = SlicedArray);\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport { copy } from \"../../helpers/object\";\r\nimport { InputMedia, MessageEntity } from \"../../layer\";\r\nimport { logger, LogTypes } from \"../logger\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport { RichTextProcessor } from \"../richtextprocessor\";\r\nimport rootScope from \"../rootScope\";\r\nimport apiUpdatesManager from \"./apiUpdatesManager\";\r\nimport appMessagesManager from './appMessagesManager';\r\nimport appPeersManager from './appPeersManager';\r\nimport appUsersManager from \"./appUsersManager\";\r\n\r\nexport type PollAnswer = {\r\n _: 'pollAnswer',\r\n text: string,\r\n option: Uint8Array\r\n};\r\n\r\nexport type PollAnswerVoters = {\r\n _: 'pollAnswerVoters',\r\n flags: number,\r\n option: Uint8Array,\r\n voters: number,\r\n\r\n pFlags: Partial<{\r\n chosen: true,\r\n correct: true\r\n }>\r\n};\r\n\r\nexport type PollResult = {\r\n _: 'pollAnswerVoters',\r\n flags: number,\r\n option: Uint8Array,\r\n voters: number,\r\n\r\n pFlags?: Partial<{chosen: true, correct: true}>\r\n};\r\n\r\nexport type PollResults = {\r\n _: 'pollResults',\r\n flags: number,\r\n results?: Array<PollResult>,\r\n total_voters?: number,\r\n recent_voters?: number[],\r\n solution?: string,\r\n solution_entities?: any[],\r\n\r\n pFlags: Partial<{\r\n min: true\r\n }>,\r\n};\r\n\r\nexport type Poll = {\r\n _: 'poll',\r\n question: string,\r\n id: string,\r\n answers: Array<PollAnswer>,\r\n close_period?: number,\r\n close_date?: number\r\n\r\n pFlags?: Partial<{\r\n closed: true,\r\n public_voters: true,\r\n multiple_choice: true,\r\n quiz: true\r\n }>,\r\n rQuestion?: string,\r\n rReply?: string,\r\n chosenIndexes?: number[]\r\n};\r\n\r\nexport class AppPollsManager {\r\n public polls: {[id: string]: Poll} = {};\r\n public results: {[id: string]: PollResults} = {};\r\n\r\n private log = logger('POLLS', LogTypes.Error);\r\n\r\n constructor() {\r\n rootScope.addMultipleEventsListeners({\r\n updateMessagePoll: (update) => {\r\n this.log('updateMessagePoll:', update);\r\n\r\n let poll: Poll = update.poll || this.polls[update.poll_id];\r\n if(!poll) {\r\n return;\r\n }\r\n\r\n poll = this.savePoll(poll, update.results as any);\r\n rootScope.dispatchEvent('poll_update', {poll, results: update.results as any});\r\n }\r\n });\r\n }\r\n\r\n public savePoll(poll: Poll, results: PollResults) {\r\n const id = poll.id;\r\n if(this.polls[id]) {\r\n poll = Object.assign(this.polls[id], poll);\r\n this.saveResults(poll, results);\r\n return poll;\r\n }\r\n\r\n this.polls[id] = poll;\r\n\r\n poll.rQuestion = RichTextProcessor.wrapEmojiText(poll.question);\r\n poll.rReply = RichTextProcessor.wrapEmojiText('📊') + ' ' + (poll.rQuestion || 'poll');\r\n poll.chosenIndexes = [];\r\n this.saveResults(poll, results);\r\n return poll;\r\n }\r\n\r\n public saveResults(poll: Poll, results: PollResults) {\r\n if(this.results[poll.id]) {\r\n results = Object.assign(this.results[poll.id], results);\r\n } else {\r\n this.results[poll.id] = results;\r\n }\r\n\r\n if(!results.pFlags.min) { // ! https://core.telegram.org/constructor/pollResults - min\r\n poll.chosenIndexes.length = 0;\r\n if(results?.results?.length) {\r\n results.results.forEach((answer, idx) => {\r\n if(answer.pFlags?.chosen) {\r\n poll.chosenIndexes.push(idx);\r\n }\r\n });\r\n }\r\n }\r\n }\r\n\r\n public getPoll(pollId: string): {poll: Poll, results: PollResults} {\r\n return {\r\n poll: this.polls[pollId], \r\n results: this.results[pollId]\r\n };\r\n }\r\n\r\n public getInputMediaPoll(poll: Poll, correctAnswers?: Uint8Array[], solution?: string, solutionEntities?: MessageEntity[]): InputMedia.inputMediaPoll {\r\n if(solution) {\r\n if(!solutionEntities) {\r\n solutionEntities = [];\r\n }\r\n\r\n solution = RichTextProcessor.parseMarkdown(solution, solutionEntities);\r\n } else {\r\n solution = undefined; // can be string here\r\n }\r\n\r\n return {\r\n _: 'inputMediaPoll',\r\n poll,\r\n correct_answers: correctAnswers,\r\n solution,\r\n solution_entities: solution ? solutionEntities : undefined\r\n };\r\n }\r\n\r\n public sendVote(message: any, optionIds: number[]): Promise<void> {\r\n const poll: Poll = message.media.poll;\r\n\r\n const options: Uint8Array[] = optionIds.map(index => {\r\n return poll.answers[index].option;\r\n });\r\n \r\n const messageId = message.mid;\r\n const peerId = message.peerId;\r\n const inputPeer = appPeersManager.getInputPeerById(peerId);\r\n\r\n if(message.pFlags.is_outgoing) {\r\n return appMessagesManager.invokeAfterMessageIsSent(messageId, 'sendVote', (message) => {\r\n this.log('invoke sendVote callback');\r\n return this.sendVote(message, optionIds);\r\n });\r\n }\r\n\r\n return apiManager.invokeApi('messages.sendVote', {\r\n peer: inputPeer,\r\n msg_id: appMessagesManager.getServerMessageId(message.mid),\r\n options\r\n }).then(updates => {\r\n this.log('sendVote updates:', updates);\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n }\r\n\r\n public getResults(message: any) {\r\n const inputPeer = appPeersManager.getInputPeerById(message.peerId);\r\n\r\n return apiManager.invokeApi('messages.getPollResults', {\r\n peer: inputPeer,\r\n msg_id: appMessagesManager.getServerMessageId(message.mid)\r\n }).then(updates => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n this.log('getResults updates:', updates);\r\n });\r\n }\r\n\r\n public getVotes(message: any, option?: Uint8Array, offset?: string, limit = 20) {\r\n return apiManager.invokeApi('messages.getPollVotes', {\r\n peer: appPeersManager.getInputPeerById(message.peerId),\r\n id: appMessagesManager.getServerMessageId(message.mid),\r\n option,\r\n offset,\r\n limit\r\n }).then((votesList) => {\r\n this.log('getPollVotes messages:', votesList);\r\n\r\n appUsersManager.saveApiUsers(votesList.users);\r\n\r\n return votesList;\r\n });\r\n }\r\n\r\n public stopPoll(message: any) {\r\n const poll: Poll = message.media.poll;\r\n \r\n if(poll.pFlags.closed) return Promise.resolve();\r\n\r\n const newPoll = copy(poll);\r\n newPoll.pFlags.closed = true;\r\n return appMessagesManager.editMessage(message, undefined, {\r\n newMedia: this.getInputMediaPoll(newPoll)\r\n }).then(() => {\r\n //console.log('stopped poll');\r\n }, err => {\r\n this.log.error('stopPoll error:', err);\r\n });\r\n }\r\n}\r\n\r\nconst appPollsManager = new AppPollsManager();\r\nMOUNT_CLASS_TO.appPollsManager = appPollsManager;\r\nexport default appPollsManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport rootScope from \"../rootScope\";\r\nimport appPeersManager from \"./appPeersManager\";\r\nimport appMessagesManager from \"./appMessagesManager\";\r\nimport apiUpdatesManager from \"./apiUpdatesManager\";\r\nimport RichTextProcessor from \"../richtextprocessor\";\r\nimport serverTimeManager from \"../mtproto/serverTimeManager\";\r\nimport { MessageEntity, DraftMessage, MessagesSaveDraft } from \"../../layer\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport { tsNow } from \"../../helpers/date\";\r\nimport { deepEqual } from \"../../helpers/object\";\r\nimport { isObject } from \"../mtproto/bin_utils\";\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport stateStorage from \"../stateStorage\";\r\n\r\nexport type MyDraftMessage = DraftMessage.draftMessage;\r\n\r\nexport class AppDraftsManager {\r\n private drafts: {[peerIdAndThreadId: string]: MyDraftMessage} = {};\r\n private getAllDraftPromise: Promise<void> = null;\r\n\r\n constructor() {\r\n stateStorage.get('drafts').then(drafts => {\r\n this.drafts = drafts || {};\r\n });\r\n\r\n rootScope.addMultipleEventsListeners({\r\n updateDraftMessage: (update) => {\r\n const peerID = appPeersManager.getPeerId(update.peer);\r\n this.saveDraft(peerID, (update as any).threadId, update.draft, {notify: true});\r\n }\r\n });\r\n }\r\n\r\n private getKey(peerId: number, threadId?: number) {\r\n return '' + peerId + (threadId ? '_' + threadId : '');\r\n }\r\n\r\n public getDraft(peerId: number, threadId?: number) {\r\n return this.drafts[this.getKey(peerId, threadId)];\r\n }\r\n\r\n public addMissedDialogs() {\r\n return this.getAllDrafts().then(() => {\r\n for(const key in this.drafts) {\r\n if(key.indexOf('_') !== -1) { // exclude threads\r\n continue;\r\n }\r\n\r\n const peerId = +key;\r\n const dialog = appMessagesManager.getDialogOnly(peerId);\r\n if(!dialog) {\r\n appMessagesManager.reloadConversation(peerId);\r\n /* const dialog = appMessagesManager.generateDialog(peerId);\r\n dialog.draft = this.drafts[key];\r\n appMessagesManager.saveConversation(dialog);\r\n appMessagesManager.newDialogsToHandle[peerId] = dialog;\r\n appMessagesManager.scheduleHandleNewDialogs(); */\r\n }\r\n }\r\n });\r\n }\r\n\r\n public getAllDrafts() {\r\n return this.getAllDraftPromise || (this.getAllDraftPromise = new Promise((resolve) => {\r\n apiManager.invokeApi('messages.getAllDrafts').then((updates) => {\r\n const p = apiUpdatesManager.updatesState.syncLoading || Promise.resolve();\r\n p.then(() => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n \r\n resolve();\r\n });\r\n }));\r\n }\r\n\r\n public saveDraft(peerId: number, threadId: number, apiDraft: DraftMessage, options: Partial<{\r\n notify: boolean\r\n }> = {}) {\r\n const draft = this.processApiDraft(apiDraft);\r\n\r\n const key = this.getKey(peerId, threadId);\r\n if(draft) {\r\n this.drafts[key] = draft;\r\n } else {\r\n delete this.drafts[key];\r\n }\r\n\r\n stateStorage.set({\r\n drafts: this.drafts\r\n });\r\n\r\n if(options.notify) {\r\n // console.warn(dT(), 'save draft', peerId, apiDraft, options)\r\n rootScope.dispatchEvent('draft_updated', {\r\n peerId,\r\n threadId,\r\n draft\r\n });\r\n }\r\n\r\n return draft;\r\n }\r\n\r\n public draftsAreEqual(draft1: DraftMessage, draft2: DraftMessage) {\r\n if(typeof(draft1) !== typeof(draft2)) {\r\n return false;\r\n }\r\n\r\n if(!isObject(draft1)) {\r\n return true;\r\n }\r\n\r\n if(draft1._ !== draft2._) {\r\n return false;\r\n }\r\n \r\n if(draft1._ === 'draftMessage' && draft2._ === draft1._) {\r\n if(draft1.reply_to_msg_id !== draft2.reply_to_msg_id) {\r\n return false;\r\n }\r\n \r\n if(!deepEqual(draft1.entities, draft2.entities)) {\r\n return false;\r\n }\r\n \r\n if(draft1.message !== draft2.message) {\r\n return false;\r\n }\r\n \r\n if(draft1.pFlags.no_webpage !== draft2.pFlags.no_webpage) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public isEmptyDraft(draft: DraftMessage) {\r\n if(!draft || draft._ === 'draftMessageEmpty') {\r\n return true;\r\n }\r\n \r\n if(draft.reply_to_msg_id > 0) {\r\n return false;\r\n }\r\n \r\n if(!draft.message.length) {\r\n return true;\r\n }\r\n \r\n return false;\r\n }\r\n\r\n public processApiDraft(draft: DraftMessage): MyDraftMessage {\r\n if(!draft || draft._ !== 'draftMessage') {\r\n return undefined;\r\n }\r\n\r\n const myEntities = RichTextProcessor.parseEntities(draft.message);\r\n const apiEntities = draft.entities || [];\r\n const totalEntities = RichTextProcessor.mergeEntities(apiEntities.slice(), myEntities); // ! only in this order, otherwise bold and emoji formatting won't work\r\n\r\n draft.rMessage = RichTextProcessor.wrapDraftText(draft.message, {entities: totalEntities});\r\n //draft.rReply = appMessagesManager.getRichReplyText(draft);\r\n if(draft.reply_to_msg_id) {\r\n draft.reply_to_msg_id = appMessagesManager.generateMessageId(draft.reply_to_msg_id);\r\n }\r\n\r\n return draft;\r\n }\r\n\r\n public async syncDraft(peerId: number, threadId: number, localDraft?: MyDraftMessage, saveOnServer = true) {\r\n // console.warn(dT(), 'sync draft', peerID)\r\n const serverDraft = this.getDraft(peerId, threadId);\r\n if(this.draftsAreEqual(serverDraft, localDraft)) {\r\n // console.warn(dT(), 'equal drafts', localDraft, serverDraft)\r\n return true;\r\n }\r\n\r\n // console.warn(dT(), 'changed draft', localDraft, serverDraft)\r\n let params: MessagesSaveDraft = {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n message: ''\r\n };\r\n\r\n let draftObj: DraftMessage;\r\n if(this.isEmptyDraft(localDraft)) {\r\n draftObj = {_: 'draftMessageEmpty'};\r\n } else {\r\n let message = localDraft.message;\r\n let entities: MessageEntity[] = localDraft.entities;\r\n\r\n if(localDraft.reply_to_msg_id) {\r\n params.reply_to_msg_id = appMessagesManager.getServerMessageId(localDraft.reply_to_msg_id);\r\n }\r\n\r\n if(entities?.length) {\r\n params.entities = appMessagesManager.getInputEntities(entities);\r\n }\r\n\r\n if(localDraft.pFlags.no_webpage) {\r\n params.no_webpage = localDraft.pFlags.no_webpage;\r\n }\r\n\r\n params.message = message;\r\n }\r\n\r\n const saveLocalDraft = draftObj || localDraft;\r\n saveLocalDraft.date = tsNow(true) + serverTimeManager.serverTimeOffset;\r\n\r\n this.saveDraft(peerId, threadId, saveLocalDraft, {notify: true});\r\n\r\n if(saveOnServer && !threadId) {\r\n return apiManager.invokeApi('messages.saveDraft', params);\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public clearAllDrafts() {\r\n return apiManager.invokeApi('messages.clearAllDrafts').then(bool => {\r\n if(!bool) {\r\n return;\r\n }\r\n\r\n for(const peerId in this.drafts) {\r\n const splitted = peerId.split('_');\r\n const threadId = splitted[1];\r\n rootScope.dispatchEvent('draft_updated', {\r\n peerId: +splitted[0],\r\n threadId: threadId ? +threadId : undefined,\r\n draft: undefined\r\n });\r\n }\r\n });\r\n }\r\n}\r\n\r\nconst appDraftsManager = new AppDraftsManager();\r\nMOUNT_CLASS_TO.appDraftsManager = appDraftsManager;\r\nexport default appDraftsManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport renderImageFromUrl, { renderImageFromUrlPromise } from \"../../helpers/dom/renderImageFromUrl\";\r\nimport replaceContent from \"../../helpers/dom/replaceContent\";\r\nimport sequentialDom from \"../../helpers/sequentialDom\";\r\nimport { UserProfilePhoto, ChatPhoto, InputFileLocation } from \"../../layer\";\r\nimport RichTextProcessor from \"../richtextprocessor\";\r\nimport rootScope from \"../rootScope\";\r\nimport appDownloadManager from \"./appDownloadManager\";\r\nimport appPeersManager from \"./appPeersManager\";\r\nimport appPhotosManager from \"./appPhotosManager\";\r\nimport appUsersManager from \"./appUsersManager\";\r\n\r\ntype PeerPhotoSize = 'photo_small' | 'photo_big';\r\n\r\nexport class AppAvatarsManager {\r\n private savedAvatarURLs: {\r\n [peerId: number]: {\r\n [size in PeerPhotoSize]?: string | Promise<string>\r\n }\r\n } = {};\r\n \r\n public removeFromAvatarsCache(peerId: number) {\r\n if(this.savedAvatarURLs[peerId]) {\r\n delete this.savedAvatarURLs[peerId];\r\n }\r\n }\r\n\r\n public loadAvatar(peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize) {\r\n const inputPeer = appPeersManager.getInputPeerById(peerId);\r\n\r\n let cached = false;\r\n let getAvatarPromise: Promise<string>;\r\n let saved = this.savedAvatarURLs[peerId];\r\n if(!saved || !saved[size]) {\r\n if(!saved) {\r\n saved = this.savedAvatarURLs[peerId] = {};\r\n }\r\n\r\n //console.warn('will invoke downloadSmallFile:', peerId);\r\n const peerPhotoFileLocation: InputFileLocation.inputPeerPhotoFileLocation = {\r\n _: 'inputPeerPhotoFileLocation', \r\n pFlags: {},\r\n peer: inputPeer, \r\n photo_id: photo.photo_id\r\n };\r\n\r\n if(size === 'photo_big') {\r\n peerPhotoFileLocation.pFlags.big = true;\r\n }\r\n\r\n const downloadOptions = {dcId: photo.dc_id, location: peerPhotoFileLocation};\r\n\r\n /* let str: string;\r\n const time = Date.now();\r\n if(peerId === 0) {\r\n str = `download avatar ${peerId}`;\r\n } */\r\n\r\n const promise = appDownloadManager.download(downloadOptions);\r\n getAvatarPromise = saved[size] = promise.then(blob => {\r\n return saved[size] = URL.createObjectURL(blob);\r\n\r\n /* if(str) {\r\n console.log(str, Date.now() / 1000, Date.now() - time);\r\n } */\r\n });\r\n } else if(typeof(saved[size]) !== 'string') {\r\n getAvatarPromise = saved[size] as Promise<any>;\r\n } else {\r\n getAvatarPromise = Promise.resolve(saved[size]);\r\n cached = true;\r\n }\r\n\r\n return {cached, loadPromise: getAvatarPromise};\r\n }\r\n\r\n public putAvatar(div: HTMLElement, peerId: number, photo: UserProfilePhoto.userProfilePhoto | ChatPhoto.chatPhoto, size: PeerPhotoSize, img = new Image(), onlyThumb = false) {\r\n let {cached, loadPromise} = this.loadAvatar(peerId, photo, size);\r\n\r\n let renderThumbPromise: Promise<void>;\r\n let callback: () => void;\r\n if(cached) {\r\n // смотри в misc.ts: renderImageFromUrl\r\n callback = () => {\r\n replaceContent(div, img);\r\n div.dataset.color = '';\r\n };\r\n } else {\r\n const animate = rootScope.settings.animationsEnabled;\r\n if(animate) {\r\n img.classList.add('fade-in');\r\n }\r\n\r\n let thumbImage: HTMLImageElement;\r\n if(photo.stripped_thumb) {\r\n thumbImage = new Image();\r\n div.classList.add('avatar-relative');\r\n thumbImage.classList.add('avatar-photo', 'avatar-photo-thumbnail');\r\n img.classList.add('avatar-photo');\r\n const url = appPhotosManager.getPreviewURLFromBytes(photo.stripped_thumb);\r\n renderThumbPromise = renderImageFromUrlPromise(thumbImage, url).then(() => {\r\n replaceContent(div, thumbImage);\r\n });\r\n }\r\n\r\n callback = () => {\r\n if(photo.stripped_thumb) {\r\n div.append(img);\r\n } else {\r\n replaceContent(div, img);\r\n }\r\n\r\n setTimeout(() => {\r\n if(div.childElementCount) {\r\n sequentialDom.mutateElement(img, () => {\r\n div.dataset.color = '';\r\n \r\n if(animate) {\r\n img.classList.remove('fade-in');\r\n }\r\n\r\n if(thumbImage) {\r\n thumbImage.remove();\r\n }\r\n });\r\n }\r\n }, animate ? 200 : 0);\r\n };\r\n }\r\n\r\n const renderPromise = loadPromise\r\n .then((url) => renderImageFromUrlPromise(img, url/* , false */))\r\n .then(() => callback());\r\n\r\n return {cached, loadPromise: renderThumbPromise || renderPromise};\r\n }\r\n\r\n // peerId === peerId || title\r\n public putPhoto(div: HTMLElement, peerId: number, isDialog = false, title = '', onlyThumb = false) {\r\n const photo = appPeersManager.getPeerPhoto(peerId);\r\n\r\n const size: PeerPhotoSize = 'photo_small';\r\n const avatarAvailable = !!photo;\r\n const avatarRendered = div.firstElementChild && !(div.firstElementChild as HTMLElement).classList.contains('emoji');\r\n \r\n const myId = rootScope.myId;\r\n\r\n //console.log('loadDialogPhoto location:', location, inputPeer);\r\n if(peerId === myId && isDialog) {\r\n div.innerText = '';\r\n div.dataset.color = '';\r\n div.classList.add('tgico-saved');\r\n div.classList.remove('tgico-deletedaccount');\r\n return;\r\n }\r\n\r\n if(peerId > 0) {\r\n const user = appUsersManager.getUser(peerId);\r\n if(user && user.pFlags && user.pFlags.deleted) {\r\n div.innerText = '';\r\n div.dataset.color = appPeersManager.getPeerColorById(peerId);\r\n div.classList.add('tgico-deletedaccount');\r\n div.classList.remove('tgico-saved');\r\n return;\r\n }\r\n }\r\n\r\n if(!avatarAvailable || !avatarRendered || !this.savedAvatarURLs[peerId]) {\r\n let color = '';\r\n if(peerId && (peerId !== myId || !isDialog)) {\r\n color = appPeersManager.getPeerColorById(peerId);\r\n }\r\n \r\n div.innerText = '';\r\n div.classList.remove('tgico-saved', 'tgico-deletedaccount');\r\n div.dataset.color = color;\r\n\r\n let abbr: string;\r\n if(!title) {\r\n const peer = appPeersManager.getPeer(peerId);\r\n abbr = peer.initials ?? '';\r\n } else {\r\n abbr = RichTextProcessor.getAbbreviation(title);\r\n }\r\n\r\n div.innerHTML = abbr;\r\n //return Promise.resolve(true);\r\n }\r\n\r\n if(avatarAvailable/* && false */) {\r\n return this.putAvatar(div, peerId, photo, size, undefined, onlyThumb);\r\n }\r\n }\r\n}\r\n\r\nconst appAvatarsManager = new AppAvatarsManager();\r\nexport default appAvatarsManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\n// * will change .cleaned and new instance will be created\r\nexport const getMiddleware = () => {\r\n let cleanupObj = {cleaned: false};\r\n return {\r\n clean: () => {\r\n cleanupObj.cleaned = true;\r\n cleanupObj = {cleaned: false};\r\n },\r\n get: () => {\r\n const _cleanupObj = cleanupObj;\r\n return () => {\r\n return !_cleanupObj.cleaned;\r\n };\r\n }\r\n };\r\n};\r\n","export default function assumeType<T>(x: unknown): asserts x is T {\r\n return; // ¯\\_(ツ)_/¯\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\n// Thanks to https://stackoverflow.com/a/49349813\r\nimport { clamp } from \"../helpers/number\";\r\n\r\n/**\r\n * Attibute modifier to create middle ellipsis\r\n * When the attribute value is left blank the ellipsis will be in the middle\r\n * When positive the attribute value will be used as a percentage\r\n * When negative the attribute value will be used as character index counted from the end\r\n * @example\r\n * <div data-middle-ellipsis>A Javascript solution to middle ellipsis</div>\r\n * <div data-middle-ellipsis=\"20\">A Javascript solution to middle ellipsis</div>\r\n * <div data-middle-ellipsis=\"-3\">A Javascript solution to middle ellipsis</div>\r\n */\r\nconst ellipsis = '…';\r\nconst map: Map<HTMLElement, {\r\n text: string,\r\n textLength: number,\r\n from: number,\r\n multiplier: number,\r\n font: string,\r\n textWidth: number,\r\n elementWidth: number\r\n}> = new Map();\r\n\r\nconst testQueue: Set<HTMLElement> = new Set();\r\nexport const fontFamily = 'Roboto, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif';\r\nconst fontSize = '16px';\r\nlet timeoutId: number;\r\n\r\nconst setTestQueue = () => {\r\n cancelAnimationFrame(timeoutId);\r\n timeoutId = window.requestAnimationFrame(testQueueElements);\r\n};\r\n\r\nconst testQueueElements = () => {\r\n testQueue.forEach(testElement);\r\n testQueue.clear();\r\n};\r\n\r\nwindow.addEventListener('resize', () => {\r\n for(const [key] of map) {\r\n testQueue.add(key);\r\n }\r\n \r\n setTestQueue();\r\n}, {capture: true, passive: true});\r\n\r\nconst testElement = (element: HTMLElement) => {\r\n //const perf = performance.now();\r\n // do not recalculate variables a second time\r\n let mapped = map.get(element);\r\n const firstTime = !mapped;\r\n\r\n let {text, textLength, from, multiplier, font, textWidth, elementWidth} = mapped || {};\r\n //console.log('[MEE] testElement got mapped', mapped);\r\n\r\n if(firstTime) {\r\n text = element.textContent;\r\n textLength = text.length;\r\n from = /* parseFloat(element.getAttribute(attributeName)) || */50;\r\n multiplier = from > 0 && from / 100;\r\n\r\n //const perf = performance.now();\r\n font = `${element.dataset.fontWeight || 400} ${fontSize} ${fontFamily}`;\r\n /* const computedStyle = window.getComputedStyle(elm, null);\r\n font = `${computedStyle.getPropertyValue('font-weight')} ${computedStyle.getPropertyValue('font-size')} ${computedStyle.getPropertyValue('font-family')}`; */\r\n //console.log('testMiddleEllipsis get computed style:', performance.now() - perf, font);\r\n\r\n textWidth = getTextWidth(text, font);\r\n //const perf = performance.now();\r\n elementWidth = element.getBoundingClientRect().width;\r\n //console.log('testMiddleEllipsis get offsetWidth:', performance.now() - perf, font);\r\n mapped = {text, textLength, from, multiplier, font, textWidth, elementWidth};\r\n map.set(element, mapped);\r\n\r\n //console.log('[MEE] testElement map set', element);\r\n }\r\n \r\n const newElementWidth = element.getBoundingClientRect().width;\r\n const widthChanged = firstTime || elementWidth !== newElementWidth;\r\n !firstTime && widthChanged && (mapped.elementWidth = elementWidth = newElementWidth);\r\n \r\n if(widthChanged) {\r\n if(textWidth > elementWidth) {\r\n element.setAttribute('title', text);\r\n let smallerText = text;\r\n let smallerWidth = elementWidth;\r\n while(smallerText.length > 3) {\r\n let smallerTextLength = smallerText.length;\r\n const half = multiplier &&\r\n clamp(multiplier * smallerTextLength << 0, 1, smallerTextLength - 2) ||\r\n Math.max(smallerTextLength + from - 1, 1);\r\n const half1 = smallerText.substr(0, half).replace(/\\s*$/,'');\r\n const half2 = smallerText.substr(half + 1).replace(/^\\s*/,'');\r\n smallerText = half1 + half2;\r\n smallerWidth = getTextWidth(smallerText + ellipsis, font);\r\n if(smallerWidth < elementWidth) {\r\n element.textContent = half1 + ellipsis + half2;\r\n break;\r\n }\r\n }\r\n\r\n // * set new width after cutting text\r\n mapped.elementWidth = element.getBoundingClientRect().width;\r\n //mapped.textWidth = smallerWidth;\r\n } else {\r\n element.removeAttribute('title');\r\n }\r\n }\r\n\r\n //console.log('testMiddleEllipsis for element:', elm, performance.now() - perf);\r\n};\r\n\r\nlet context: CanvasRenderingContext2D;\r\n/**\r\n * Get the text width\r\n * @param {string} text\r\n * @param {string} font\r\n */\r\nfunction getTextWidth(text: string, font: string) {\r\n //const perf = performance.now();\r\n if(!context) {\r\n const canvas = document.createElement('canvas');\r\n context = canvas.getContext('2d');\r\n context.font = font;\r\n }\r\n\r\n //context.font = font;\r\n const metrics = context.measureText(text);\r\n //console.log('getTextWidth perf:', performance.now() - perf);\r\n return metrics.width;\r\n //return Math.round(metrics.width);\r\n}\r\n\r\nexport class MiddleEllipsisElement extends HTMLElement {\r\n constructor() {\r\n super();\r\n }\r\n\r\n connectedCallback() {\r\n //console.log('[MEE]: connectedCallback before', map.has(this), testQueue.has(this), map.size, this.textContent, map);\r\n\r\n map.set(this, null);\r\n testQueue.add(this);\r\n setTestQueue();\r\n //testElement(this);\r\n\r\n //console.log('[MEE]: connectedCallback after', map.has(this), map.size, testQueue.has(this), testQueue.size);\r\n }\r\n\r\n disconnectedCallback() {\r\n const deleted = map.delete(this);\r\n //console.log('[MEE]: disconnectedCallback', deleted, map.has(this), map.size, this.textContent, map);\r\n }\r\n}\r\n\r\ncustomElements.define(\"middle-ellipsis-element\", MiddleEllipsisElement);\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { RefreshReferenceTask, RefreshReferenceTaskResponse } from \"./apiFileManager\";\r\nimport appMessagesManager from \"../appManagers/appMessagesManager\";\r\nimport { Photo } from \"../../layer\";\r\nimport { bytesToHex } from \"../../helpers/bytes\";\r\nimport { deepEqual } from \"../../helpers/object\";\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport apiManager from \"./mtprotoworker\";\r\nimport assumeType from \"../../helpers/assumeType\";\r\n\r\nexport type ReferenceContext = ReferenceContext.referenceContextProfilePhoto | ReferenceContext.referenceContextMessage;\r\nexport namespace ReferenceContext {\r\n export type referenceContextProfilePhoto = {\r\n type: 'profilePhoto',\r\n peerId: number\r\n };\r\n\r\n export type referenceContextMessage = {\r\n type: 'message',\r\n peerId: number,\r\n messageId: number\r\n };\r\n}\r\n\r\nexport type ReferenceBytes = Photo.photo['file_reference'];\r\nexport type ReferenceContexts = Set<ReferenceContext>;\r\n\r\n//type ReferenceBytes = Uint8Array;\r\n\r\nclass ReferenceDatabase {\r\n private contexts: Map<ReferenceBytes, ReferenceContexts> = new Map();\r\n //private references: Map<ReferenceBytes, number[]> = new Map();\r\n private links: {[hex: string]: ReferenceBytes} = {};\r\n\r\n constructor() {\r\n apiManager.addTaskListener('refreshReference', (task: RefreshReferenceTask) => {\r\n const bytes = task.payload;\r\n\r\n assumeType<RefreshReferenceTaskResponse>(task);\r\n task.originalPayload = bytes;\r\n\r\n this.refreshReference(bytes).then(() => {\r\n task.payload = this.getReferenceByLink(bytes);\r\n apiManager.postMessage(task);\r\n }, (err) => {\r\n task.error = err;\r\n apiManager.postMessage(task);\r\n });\r\n });\r\n }\r\n\r\n public saveContext(reference: ReferenceBytes, context: ReferenceContext, contexts?: ReferenceContexts) {\r\n [contexts, reference] = this.getContexts(reference);\r\n if(!contexts) {\r\n contexts = new Set();\r\n this.contexts.set(reference, contexts);\r\n }\r\n \r\n this.links[bytesToHex(reference)] = reference;\r\n for(const _context of contexts) {\r\n if(deepEqual(_context, context)) {\r\n return;\r\n }\r\n }\r\n\r\n contexts.add(context);\r\n }\r\n\r\n public getReferenceByLink(reference: ReferenceBytes) {\r\n return this.links[bytesToHex(reference)];\r\n }\r\n\r\n public getContexts(reference: ReferenceBytes): [ReferenceContexts, ReferenceBytes] {\r\n const contexts = this.contexts.get(reference) || (reference = this.getReferenceByLink(reference) || reference, this.contexts.get(reference));\r\n return [contexts, reference];\r\n }\r\n\r\n public getContext(reference: ReferenceBytes): [ReferenceContext, ReferenceBytes] {\r\n const contexts = this.getContexts(reference);\r\n return contexts[0] ? [contexts[0].values().next().value, contexts[1]] : undefined;\r\n }\r\n\r\n public deleteContext(reference: ReferenceBytes, context: ReferenceContext, contexts?: ReferenceContexts) {\r\n [contexts, reference] = this.getContexts(reference);\r\n if(contexts) {\r\n for(const _context of contexts) {\r\n if(deepEqual(_context, context)) {\r\n contexts.delete(_context);\r\n if(!contexts.size) {\r\n this.contexts.delete(reference);\r\n delete this.links[bytesToHex(reference)];\r\n }\r\n return true;\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n public refreshReference(reference: ReferenceBytes, context?: ReferenceContext): Promise<void> {\r\n if(!context) {\r\n const c = this.getContext(reference);\r\n if(!c) {\r\n return Promise.reject('NO_CONTEXT');\r\n }\r\n\r\n [context, reference] = c;\r\n }\r\n\r\n let promise: Promise<any>;\r\n switch(context?.type) {\r\n case 'message': {\r\n promise = appMessagesManager.wrapSingleMessage(context.peerId, context.messageId, true);\r\n break; \r\n // .then(() => {\r\n // console.log('FILE_REFERENCE_EXPIRED: got message', context, appMessagesManager.getMessage((context as ReferenceContext.referenceContextMessage).messageId).media, reference);\r\n // });\r\n }\r\n\r\n default: {\r\n console.warn('FILE_REFERENCE_EXPIRED: not implemented context', context);\r\n return Promise.reject();\r\n }\r\n }\r\n\r\n const hex = bytesToHex(reference);\r\n return promise.then(() => {\r\n const newHex = bytesToHex(reference);\r\n if(hex !== newHex) {\r\n return;\r\n }\r\n\r\n this.deleteContext(reference, context);\r\n\r\n const newContext = this.getContext(reference);\r\n if(newContext) {\r\n return this.refreshReference(reference, newContext[0]);\r\n }\r\n\r\n throw 'NO_NEW_CONTEXT';\r\n });\r\n }\r\n\r\n /* handleReferenceError = (reference: ReferenceBytes, error: ApiError) => {\r\n switch(error.type) {\r\n case 'FILE_REFERENCE_EXPIRED': {\r\n return this.refreshReference(reference);\r\n }\r\n\r\n default:\r\n return Promise.reject(error);\r\n }\r\n }; */\r\n\r\n /* public replaceReference(oldReference: ReferenceBytes, newReference: ReferenceBytes) {\r\n const contexts = this.contexts.get(oldReference);\r\n if(contexts) {\r\n this.contexts.delete(oldReference);\r\n this.contexts.set(newReference, contexts);\r\n }\r\n } */\r\n}\r\n\r\nconst referenceDatabase = new ReferenceDatabase();\r\nMOUNT_CLASS_TO.referenceDatabase = referenceDatabase;\r\nexport default referenceDatabase;","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { CancellablePromise, deferredPromise } from \"./cancellablePromise\";\r\nimport { getHeavyAnimationPromise } from \"../hooks/useHeavyAnimationCheck\";\r\nimport { fastRaf } from \"./schedulers\";\r\n\r\ntype HeavyQueue<T> = {\r\n items: any[], \r\n process: (...args: any[]) => T,\r\n context: any,\r\n promise?: CancellablePromise<ReturnType<HeavyQueue<T>['process']>[]>\r\n};\r\nconst heavyQueue: HeavyQueue<any>[] = [];\r\nlet processingQueue = false;\r\n\r\nexport default function addHeavyTask<T>(queue: HeavyQueue<T>, method: 'push' | 'unshift' = 'push') {\r\n if(!queue.items.length) {\r\n return Promise.resolve([]);\r\n }\r\n \r\n queue.promise = deferredPromise<T[]>();\r\n heavyQueue[method](queue);\r\n processHeavyQueue();\r\n\r\n return queue.promise;\r\n}\r\n\r\nfunction processHeavyQueue() {\r\n if(!processingQueue) {\r\n const queue = heavyQueue.shift();\r\n timedChunk(queue).finally(() => {\r\n processingQueue = false;\r\n if(heavyQueue.length) {\r\n processHeavyQueue();\r\n }\r\n });\r\n }\r\n}\r\n\r\nfunction timedChunk<T>(queue: HeavyQueue<T>) {\r\n if(!queue.items.length) {\r\n queue.promise.resolve([]);\r\n return Promise.resolve([]);\r\n }\r\n\r\n const todo = queue.items.slice();\r\n const results: T[] = [];\r\n\r\n return new Promise<T[]>((resolve, reject) => {\r\n const f = async() => {\r\n const start = performance.now();\r\n\r\n do {\r\n await getHeavyAnimationPromise();\r\n const possiblePromise = queue.process.apply(queue.context, todo.shift());\r\n let realResult: T;\r\n if(possiblePromise instanceof Promise) {\r\n try {\r\n realResult = await possiblePromise;\r\n } catch(err) {\r\n reject(err);\r\n return;\r\n }\r\n } else {\r\n realResult = possiblePromise;\r\n }\r\n\r\n results.push(realResult);\r\n } while(todo.length > 0 && (performance.now() - start) < 6);\r\n\r\n if(todo.length > 0) {\r\n fastRaf(f);\r\n //setTimeout(f, 25);\r\n } else {\r\n resolve(results);\r\n }\r\n };\r\n\r\n fastRaf(f);\r\n //setTimeout(f, 25);\r\n }).then(queue.promise.resolve, queue.promise.reject);\r\n}","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport type fastBlur from '../vendor/fastBlur';\r\nimport addHeavyTask from './heavyQueue';\r\n\r\nconst RADIUS = 2;\r\nconst ITERATIONS = 2;\r\n\r\nconst isFilterAvailable = 'filter' in (document.createElement('canvas').getContext('2d') || {});\r\nlet requireBlurPromise: Promise<any>;\r\nlet fastBlurFunc: typeof fastBlur;\r\nif(!isFilterAvailable) {\r\n requireBlurPromise = import('../vendor/fastBlur').then(m => {\r\n fastBlurFunc = m.default;\r\n });\r\n} else {\r\n requireBlurPromise = Promise.resolve();\r\n}\r\n\r\nfunction processBlurNext(img: HTMLImageElement, radius: number, iterations: number) {\r\n return new Promise<string>((resolve) => {\r\n const canvas = document.createElement('canvas');\r\n canvas.width = img.width;\r\n canvas.height = img.height;\r\n \r\n const ctx = canvas.getContext('2d', {alpha: false});\r\n if(isFilterAvailable) {\r\n ctx.filter = `blur(${radius}px)`;\r\n ctx.drawImage(img, -radius * 2, -radius * 2, canvas.width + radius * 4, canvas.height + radius * 4);\r\n } else {\r\n ctx.drawImage(img, 0, 0);\r\n fastBlurFunc(ctx, 0, 0, canvas.width, canvas.height, radius, iterations);\r\n }\r\n \r\n resolve(canvas.toDataURL());\r\n /* if(DEBUG) {\r\n console.log(`[blur] end, radius: ${radius}, iterations: ${iterations}, time: ${performance.now() - perf}`);\r\n } */\r\n\r\n /* canvas.toBlob(blob => {\r\n resolve(URL.createObjectURL(blob));\r\n \r\n if(DEBUG) {\r\n console.log(`[blur] end, radius: ${radius}, iterations: ${iterations}, time: ${performance.now() - perf}`);\r\n }\r\n }); */\r\n });\r\n}\r\n\r\nconst blurPromises: Map<string, Promise<string>> = new Map();\r\nconst CACHE_SIZE = 1000;\r\n\r\nexport default function blur(dataUri: string, radius: number = RADIUS, iterations: number = ITERATIONS) {\r\n if(!dataUri) {\r\n console.error('no dataUri for blur', dataUri);\r\n return Promise.resolve(dataUri);\r\n }\r\n\r\n if(blurPromises.size > CACHE_SIZE) {\r\n blurPromises.clear();\r\n }\r\n \r\n if(blurPromises.has(dataUri)) return blurPromises.get(dataUri);\r\n const promise = new Promise<string>((resolve) => {\r\n //return resolve(dataUri);\r\n requireBlurPromise.then(() => {\r\n const img = new Image();\r\n img.onload = () => {\r\n if(isFilterAvailable) {\r\n processBlurNext(img, radius, iterations).then(resolve);\r\n } else {\r\n addHeavyTask({\r\n items: [[img, radius, iterations]],\r\n context: null,\r\n process: processBlurNext\r\n }, 'unshift').then(results => {\r\n resolve(results[0]);\r\n });\r\n }\r\n };\r\n img.src = dataUri;\r\n\r\n /* addHeavyTask({\r\n items: [[dataUri, radius, iterations]],\r\n context: null,\r\n process: processBlur\r\n }, 'unshift').then(results => {\r\n resolve(results[0]);\r\n }); */\r\n });\r\n });\r\n\r\n blurPromises.set(dataUri, promise);\r\n\r\n return promise;\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { MOUNT_CLASS_TO } from \"../config/debug\";\r\nimport { isSafari } from \"../helpers/userAgent\";\r\nimport { logger, LogTypes } from \"./logger\";\r\n\r\ntype Result = {\r\n bytes: Uint8Array, \r\n waveform?: Uint8Array\r\n};\r\n\r\ntype Task = {\r\n pages: Uint8Array,\r\n withWaveform: boolean,\r\n waveform?: Uint8Array,\r\n callback: {resolve: (result: Result) => void, reject: (err: any) => void},\r\n timeout: number\r\n};\r\n\r\nexport class OpusDecodeController {\r\n private worker: Worker;\r\n private wavWorker : Worker;\r\n private sampleRate = 48000;\r\n private tasks: Array<Task> = [];\r\n private keepAlive = false;\r\n private isPlaySupportedResult: boolean;\r\n private log = logger('OPUS', LogTypes.Error);\r\n\r\n public isPlaySupported() {\r\n if(this.isPlaySupportedResult !== undefined) return this.isPlaySupportedResult;\r\n\r\n const audio = document.createElement('audio');\r\n return this.isPlaySupportedResult = !!(audio.canPlayType && audio.canPlayType('audio/ogg;').replace(/no/, ''))/* && false */;\r\n }\r\n\r\n public loadWavWorker() {\r\n if(this.wavWorker) return;\r\n\r\n this.wavWorker = new Worker('waveWorker.min.js');\r\n this.wavWorker.addEventListener('message', (e) => {\r\n const data = e.data;\r\n\r\n this.log('[WAV] got message:', data);\r\n if(data && data.page) {\r\n const bytes = data.page;\r\n this.onTaskEnd(this.tasks.shift(), bytes);\r\n }\r\n });\r\n }\r\n\r\n public loadWorker() {\r\n if(this.worker) return;\r\n\r\n this.worker = new Worker('decoderWorker.min.js');\r\n this.worker.addEventListener('message', (e) => {\r\n const data = e.data;\r\n \r\n this.log('[DECODER] got message', data);\r\n if(data.type === 'done') {\r\n //this.log('[DECODER] send done to wav');\r\n this.wavWorker.postMessage({command: 'done'});\r\n\r\n if(data.waveform) {\r\n this.tasks[0].waveform = data.waveform;\r\n }\r\n } else { // e.data contains decoded buffers as float32 values\r\n //this.log('[DECODER] send encode to wav');\r\n this.wavWorker.postMessage({\r\n command: 'encode',\r\n buffers: e.data\r\n }, isSafari ? undefined : data.map((typedArray: Uint8Array) => typedArray.buffer));\r\n }\r\n });\r\n }\r\n\r\n public setKeepAlive(keepAlive: boolean) {\r\n this.keepAlive = keepAlive;\r\n if(this.keepAlive) {\r\n this.loadWorker();\r\n this.loadWavWorker();\r\n } else if(!this.tasks.length) {\r\n this.terminateWorkers();\r\n }\r\n }\r\n\r\n public onTaskEnd(task: Task, result?: Uint8Array) {\r\n if(!result) {\r\n task.callback.reject('timeout');\r\n } else {\r\n clearTimeout(task.timeout);\r\n task.callback.resolve({bytes: result, waveform: task.waveform});\r\n }\r\n\r\n if(this.tasks.length) {\r\n this.executeNewTask(this.tasks[0]);\r\n }\r\n\r\n this.terminateWorkers();\r\n }\r\n\r\n public terminateWorkers(kill = false) {\r\n if((this.keepAlive || this.tasks.length) && !kill) return;\r\n\r\n if(this.worker) {\r\n this.worker.terminate();\r\n this.worker = null;\r\n }\r\n \r\n if(this.wavWorker) {\r\n this.wavWorker.terminate();\r\n this.wavWorker = null;\r\n }\r\n }\r\n\r\n public executeNewTask(task: Task) {\r\n this.worker.postMessage({ \r\n command: 'init',\r\n decoderSampleRate: this.sampleRate,\r\n outputBufferSampleRate: this.sampleRate\r\n });\r\n\r\n this.wavWorker.postMessage({ \r\n command: 'init',\r\n wavBitDepth: 16,\r\n wavSampleRate: this.sampleRate\r\n });\r\n\r\n //console.log('sending command to worker:', task);\r\n //setTimeout(() => {\r\n this.log('[DECODER] send decode');\r\n this.worker.postMessage({\r\n command: 'decode',\r\n pages: task.pages,\r\n waveform: task.withWaveform\r\n }, isSafari ? undefined : [task.pages.buffer]);\r\n //}, 1e3);\r\n\r\n task.timeout = window.setTimeout(() => {\r\n this.log.error('decode timeout'/* , task */);\r\n\r\n this.terminateWorkers(true);\r\n if(this.tasks.length) {\r\n this.loadWorker();\r\n this.loadWavWorker();\r\n }\r\n\r\n this.onTaskEnd(this.tasks.shift());\r\n }, 10e3);\r\n }\r\n\r\n public pushDecodeTask(pages: Uint8Array, withWaveform: boolean) {\r\n return new Promise<Result>((resolve, reject) => {\r\n const task = {\r\n pages,\r\n withWaveform,\r\n callback: {resolve, reject},\r\n timeout: 0\r\n };\r\n\r\n this.loadWorker();\r\n this.loadWavWorker();\r\n\r\n if(this.tasks.push(task) === 1) {\r\n this.executeNewTask(task);\r\n }\r\n });\r\n }\r\n\r\n public async decode(typedArray: Uint8Array, withWaveform = false) {\r\n return this.pushDecodeTask(typedArray, withWaveform).then(result => {\r\n const dataBlob = new Blob([result.bytes], {type: \"audio/wav\"});\r\n return {url: URL.createObjectURL(dataBlob), waveform: result.waveform};\r\n });\r\n }\r\n}\r\n\r\nconst opusDecodeController = new OpusDecodeController();\r\nMOUNT_CLASS_TO.opusDecodeController = opusDecodeController;\r\nexport default opusDecodeController;","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport appPhotosManager from \"./appPhotosManager\";\r\nimport appDocsManager from \"./appDocsManager\";\r\nimport { RichTextProcessor } from \"../richtextprocessor\";\r\nimport { ReferenceContext } from \"../mtproto/referenceDatabase\";\r\nimport rootScope from \"../rootScope\";\r\nimport { safeReplaceObject } from \"../../helpers/object\";\r\nimport { limitSymbols } from \"../../helpers/string\";\r\n\r\nexport class AppWebPagesManager {\r\n private webpages: any = {};\r\n private pendingWebPages: {\r\n [webPageId: string]: {\r\n [mid: string]: true\r\n }\r\n } = {};\r\n \r\n constructor() {\r\n rootScope.addMultipleEventsListeners({\r\n updateWebPage: (update) => {\r\n this.saveWebPage(update.webpage);\r\n }\r\n });\r\n }\r\n \r\n public saveWebPage(apiWebPage: any, mid?: number, mediaContext?: ReferenceContext) {\r\n if(apiWebPage.photo && apiWebPage.photo._ === 'photo') {\r\n //appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);\r\n apiWebPage.photo = appPhotosManager.savePhoto(apiWebPage.photo, mediaContext);\r\n } else {\r\n delete apiWebPage.photo;\r\n }\r\n\r\n if(apiWebPage.document && apiWebPage.document._ === 'document') {\r\n apiWebPage.document = appDocsManager.saveDoc(apiWebPage.document, mediaContext); // warning 11.04.2020\r\n } else {\r\n if(apiWebPage.type === 'document') {\r\n delete apiWebPage.type;\r\n }\r\n\r\n delete apiWebPage.document;\r\n }\r\n \r\n const siteName = apiWebPage.site_name;\r\n let shortTitle = apiWebPage.title || apiWebPage.author || siteName || '';\r\n if(siteName && shortTitle === siteName) {\r\n delete apiWebPage.site_name;\r\n }\r\n\r\n shortTitle = limitSymbols(shortTitle, 80, 100);\r\n\r\n apiWebPage.rTitle = RichTextProcessor.wrapRichText(shortTitle, {noLinks: true, noLinebreaks: true});\r\n let contextHashtag = '';\r\n if(siteName === 'GitHub') {\r\n const matches = apiWebPage.url.match(/(https?:\\/\\/github\\.com\\/[^\\/]+\\/[^\\/]+)/);\r\n if(matches) {\r\n contextHashtag = matches[0] + '/issues/{1}';\r\n }\r\n }\r\n\r\n // delete apiWebPage.description\r\n const shortDescriptionText = limitSymbols(apiWebPage.description || '', 150, 180);\r\n apiWebPage.rDescription = RichTextProcessor.wrapRichText(shortDescriptionText, {\r\n contextSite: siteName || 'external',\r\n contextHashtag: contextHashtag\r\n });\r\n \r\n if(apiWebPage.type !== 'photo' &&\r\n apiWebPage.type !== 'video' &&\r\n apiWebPage.type !== 'gif' &&\r\n apiWebPage.type !== 'document' &&\r\n !apiWebPage.description &&\r\n apiWebPage.photo) {\r\n apiWebPage.type = 'photo';\r\n }\r\n \r\n if(mid) {\r\n if(this.pendingWebPages[apiWebPage.id] === undefined) {\r\n this.pendingWebPages[apiWebPage.id] = {};\r\n }\r\n\r\n this.pendingWebPages[apiWebPage.id][mid] = true;\r\n }\r\n \r\n if(this.webpages[apiWebPage.id] === undefined) {\r\n this.webpages[apiWebPage.id] = apiWebPage;\r\n } else {\r\n safeReplaceObject(this.webpages[apiWebPage.id], apiWebPage);\r\n }\r\n \r\n if(!mid && this.pendingWebPages[apiWebPage.id] !== undefined) {\r\n const msgs: number[] = [];\r\n for(const msgId in this.pendingWebPages[apiWebPage.id]) {\r\n msgs.push(+msgId);\r\n }\r\n\r\n rootScope.dispatchEvent('webpage_updated', {\r\n id: apiWebPage.id,\r\n msgs\r\n });\r\n }\r\n\r\n return apiWebPage;\r\n }\r\n\r\n public deleteWebPageFromPending(webPage: any, mid: number) {\r\n const id = webPage.id;\r\n if(this.pendingWebPages[id] && this.pendingWebPages[id][mid]) {\r\n delete this.pendingWebPages[id][mid];\r\n\r\n if(!Object.keys(this.pendingWebPages[id]).length) {\r\n delete this.pendingWebPages[id];\r\n }\r\n }\r\n }\r\n\r\n public getWebPage(id: string) {\r\n return this.webpages[id];\r\n }\r\n}\r\n\r\nexport default new AppWebPagesManager();\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nexport default function htmlToDocumentFragment(html: string) {\r\n var template = document.createElement('template');\r\n html = html.trim(); // Never return a text node of whitespace as the result\r\n template.innerHTML = html;\r\n return template.content;\r\n}\r\n","'use strict'\r\n//@flow\r\n\r\n/** * * * * * * * * * *\r\n * Big Integer Library *\r\n * Created 2000 *\r\n * Leemon Baird *\r\n * www.leemon.com *\r\n * * * * * * * * * * * */\r\n\r\n////////////////////////////////////////////////////////////////////////////////////////\r\n// These functions are designed to avoid frequent dynamic memory allocation in the inner loop.\r\n// For most functions, if it needs a BigInt as a local variable it will actually use\r\n// a global, and will only allocate to it only when it's not the right size. This ensures\r\n// that when a function is called repeatedly with same-sized parameters, it only allocates\r\n// memory on the first call.\r\n//\r\n// Note that for cryptographic purposes, the calls to Math.random() must\r\n// be replaced with calls to a better pseudorandom number generator.\r\n//\r\n// In the following, \"bigInt\" means a bigInt with at least one leading zero element,\r\n// and \"integer\" means a nonnegative integer less than radix. In some cases, integer\r\n// can be negative. Negative bigInts are 2s complement.\r\n//\r\n// The following functions do not modify their inputs.\r\n// Those returning a bigInt, string, or Array will dynamically allocate memory for that value.\r\n// Those returning a boolean will return the integer 0 (false) or 1 (true).\r\n// Those returning boolean or int will not allocate memory except possibly on the first\r\n// time they're called with a given parameter size.\r\n//\r\n// bigInt add(x,y) //return (x+y) for bigInts x and y.\r\n// bigInt addInt(x,n) //return (x+n) where x is a bigInt and n is an integer.\r\n// string bigInt2str(x,base) //return a string form of bigInt x in a given base, with 2 <= base <= 95\r\n// int bitSize(x) //return how many bits long the bigInt x is, not counting leading zeros\r\n// bigInt dup(x) //return a copy of bigInt x\r\n// boolean equals(x,y) //is the bigInt x equal to the bigint y?\r\n// boolean equalsInt(x,y) //is bigint x equal to integer y?\r\n// bigInt expand(x,n) //return a copy of x with at least n elements, adding leading zeros if needed\r\n// Array findPrimes(n) //return array of all primes less than integer n\r\n// bigInt GCD(x,y) //return greatest common divisor of bigInts x and y (each with same number of elements).\r\n// boolean greater(x,y) //is x>y? (x and y are nonnegative bigInts)\r\n// boolean greaterShift(x,y,shift)//is (x <<(shift*bpe)) > y?\r\n// bigInt int2bigInt(t,n,m) //return a bigInt equal to integer t, with at least n bits and m array elements\r\n// bigInt inverseMod(x,n) //return (x**(-1) mod n) for bigInts x and n. If no inverse exists, it returns null\r\n// int inverseModInt(x,n) //return x**(-1) mod n, for integers x and n. Return 0 if there is no inverse\r\n// boolean isZero(x) //is the bigInt x equal to zero?\r\n// boolean millerRabin(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is bigInt, 1<b<x)\r\n// boolean millerRabinInt(x,b) //does one round of Miller-Rabin base integer b say that bigInt x is possibly prime? (b is int, 1<b<x)\r\n// bigInt mod(x,n) //return a new bigInt equal to (x mod n) for bigInts x and n.\r\n// int modInt(x,n) //return x mod n for bigInt x and integer n.\r\n// bigInt mult(x,y) //return x*y for bigInts x and y. This is faster when y<x.\r\n// bigInt multMod(x,y,n) //return (x*y mod n) for bigInts x,y,n. For greater speed, let y<x.\r\n// boolean negative(x) //is bigInt x negative?\r\n// bigInt powMod(x,y,n) //return (x**y mod n) where x,y,n are bigInts and ** is exponentiation. 0**0=1. Faster for odd n.\r\n// bigInt randBigInt(n,s) //return an n-bit random BigInt (n>=1). If s=1, then the most significant of those n bits is set to 1.\r\n// bigInt randTruePrime(k) //return a new, random, k-bit, true prime bigInt using Maurer's algorithm.\r\n// bigInt randProbPrime(k) //return a new, random, k-bit, probable prime bigInt (probability it's composite less than 2^-80).\r\n// bigInt str2bigInt(s,b,n,m) //return a bigInt for number represented in string s in base b with at least n bits and m array elements\r\n// bigInt sub(x,y) //return (x-y) for bigInts x and y. Negative answers will be 2s complement\r\n// bigInt trim(x,k) //return a copy of x with exactly k leading zero elements\r\n//\r\n//\r\n// The following functions each have a non-underscored version, which most users should call instead.\r\n// These functions each write to a single parameter, and the caller is responsible for ensuring the array\r\n// passed in is large enough to hold the result.\r\n//\r\n// void addInt_(x,n) //do x=x+n where x is a bigInt and n is an integer\r\n// void add_(x,y) //do x=x+y for bigInts x and y\r\n// void copy_(x,y) //do x=y on bigInts x and y\r\n// void copyInt_(x,n) //do x=n on bigInt x and integer n\r\n// void GCD_(x,y) //set x to the greatest common divisor of bigInts x and y, (y is destroyed). (This never overflows its array).\r\n// boolean inverseMod_(x,n) //do x=x**(-1) mod n, for bigInts x and n. Returns 1 (0) if inverse does (doesn't) exist\r\n// void mod_(x,n) //do x=x mod n for bigInts x and n. (This never overflows its array).\r\n// void mult_(x,y) //do x=x*y for bigInts x and y.\r\n// void multMod_(x,y,n) //do x=x*y mod n for bigInts x,y,n.\r\n// void powMod_(x,y,n) //do x=x**y mod n, where x,y,n are bigInts (n is odd) and ** is exponentiation. 0**0=1.\r\n// void randBigInt_(b,n,s) //do b = an n-bit random BigInt. if s=1, then nth bit (most significant bit) is set to 1. n>=1.\r\n// void randTruePrime_(ans,k) //do ans = a random k-bit true random prime (not just probable prime) with 1 in the msb.\r\n// void sub_(x,y) //do x=x-y for bigInts x and y. Negative answers will be 2s complement.\r\n//\r\n// The following functions do NOT have a non-underscored version.\r\n// They each write a bigInt result to one or more parameters. The caller is responsible for\r\n// ensuring the arrays passed in are large enough to hold the results.\r\n//\r\n// void addShift_(x,y,ys) //do x=x+(y<<(ys*bpe))\r\n// void carry_(x) //do carries and borrows so each element of the bigInt x fits in bpe bits.\r\n// void divide_(x,y,q,r) //divide x by y giving quotient q and remainder r\r\n// int divInt_(x,n) //do x=floor(x/n) for bigInt x and integer n, and return the remainder. (This never overflows its array).\r\n// void eGCD_(x,y,d,a,b) //sets a,b,d to positive bigInts such that d = GCD_(x,y) = a*x-b*y\r\n// void halve_(x) //do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement. (This never overflows its array).\r\n// void leftShift_(x,n) //left shift bigInt x by n bits. n<bpe.\r\n// void linComb_(x,y,a,b) //do x=a*x+b*y for bigInts x and y and integers a and b\r\n// void linCombShift_(x,y,b,ys) //do x=x+b*(y<<(ys*bpe)) for bigInts x and y, and integers b and ys\r\n// void mont_(x,y,n,np) //Montgomery multiplication (see comments where the function is defined)\r\n// void multInt_(x,n) //do x=x*n where x is a bigInt and n is an integer.\r\n// void rightShift_(x,n) //right shift bigInt x by n bits. 0 <= n < bpe. (This never overflows its array).\r\n// void squareMod_(x,n) //do x=x*x mod n for bigInts x,n\r\n// void subShift_(x,y,ys) //do x=x-(y<<(ys*bpe)). Negative answers will be 2s complement.\r\n//\r\n// The following functions are based on algorithms from the _Handbook of Applied Cryptography_\r\n// powMod_() = algorithm 14.94, Montgomery exponentiation\r\n// eGCD_,inverseMod_() = algorithm 14.61, Binary extended GCD_\r\n// GCD_() = algorothm 14.57, Lehmer's algorithm\r\n// mont_() = algorithm 14.36, Montgomery multiplication\r\n// divide_() = algorithm 14.20 Multiple-precision division\r\n// squareMod_() = algorithm 14.16 Multiple-precision squaring\r\n// randTruePrime_() = algorithm 4.62, Maurer's algorithm\r\n// millerRabin() = algorithm 4.24, Miller-Rabin algorithm\r\n//\r\n// Profiling shows:\r\n// randTruePrime_() spends:\r\n// 10% of its time in calls to powMod_()\r\n// 85% of its time in calls to millerRabin()\r\n// millerRabin() spends:\r\n// 99% of its time in calls to powMod_() (always with a base of 2)\r\n// powMod_() spends:\r\n// 94% of its time in calls to mont_() (almost always with x==y)\r\n//\r\n// This suggests there are several ways to speed up this library slightly:\r\n// - convert powMod_ to use a Montgomery form of k-ary window (or maybe a Montgomery form of sliding window)\r\n// -- this should especially focus on being fast when raising 2 to a power mod n\r\n// - convert randTruePrime_() to use a minimum r of 1/3 instead of 1/2 with the appropriate change to the test\r\n// - tune the parameters in randTruePrime_(), including c, m, and recLimit\r\n// - speed up the single loop in mont_() that takes 95% of the runtime, perhaps by reducing checking\r\n// within the loop when all the parameters are the same length.\r\n//\r\n// There are several ideas that look like they wouldn't help much at all:\r\n// - replacing trial division in randTruePrime_() with a sieve (that speeds up something taking almost no time anyway)\r\n// - increase bpe from 15 to 30 (that would help if we had a 32*32->64 multiplier, but not with JavaScript's 32*32->32)\r\n// - speeding up mont_(x,y,n,np) when x==y by doing a non-modular, non-Montgomery square\r\n// followed by a Montgomery reduction. The intermediate answer will be twice as long as x, so that\r\n// method would be slower. This is unfortunate because the code currently spends almost all of its time\r\n// doing mont_(x,x,...), both for randTruePrime_() and powMod_(). A faster method for Montgomery squaring\r\n// would have a large impact on the speed of randTruePrime_() and powMod_(). HAC has a couple of poorly-worded\r\n// sentences that seem to imply it's faster to do a non-modular square followed by a single\r\n// Montgomery reduction, but that's obviously wrong.\r\n////////////////////////////////////////////////////////////////////////////////////////\r\n\r\nexport type Bool = 1 | 0\r\n\r\n//globals\r\nexport var bpe = 0 //bits stored per array element\r\nvar mask = 0 //AND this with an array element to chop it down to bpe bits\r\nvar radix = mask + 1 //equals 2^bpe. A single 1 bit to the left of the last bit of mask.\r\n\r\n//the digits for converting to different bases\r\nvar digitsStr =\r\n '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_=!@#$%^&*()[]{}|;:,.<>/?`~ \\\\\\'\"+-'\r\n\r\n//initialize the global variables\r\n\r\n//bpe=number of bits in the mantissa on this platform\r\nfor (bpe = 0; 1 << (bpe + 1) > 1 << bpe; bpe++);\r\nbpe >>= 1 //bpe=number of bits in one element of the array representing the bigInt\r\nmask = (1 << bpe) - 1 //AND the mask with an integer to get its bpe least significant bits\r\nradix = mask + 1 //2^bpe. a single 1 bit to the left of the first bit of mask\r\nexport var one = int2bigInt(1, 1, 1) //constant used in powMod_()\r\nexport var zero = int2bigInt(0, 1, 1)\r\n\r\n//the following global variables are scratchpad memory to\r\n//reduce dynamic memory allocation in the inner loop\r\nvar t: number[] | number = new Array(0)\r\nvar ss = t //used in mult_()\r\nvar s0 = t //used in multMod_(), squareMod_()\r\n// var s1=t; //used in powMod_(), multMod_(), squareMod_()\r\n// var s2=t; //used in powMod_(), multMod_()\r\nvar s3 = t //used in powMod_()\r\nvar s4 = t,\r\n s5 = t //used in mod_()\r\nvar s6 = t //used in bigInt2str()\r\nvar s7 = t //used in powMod_()\r\nvar T = t //used in GCD_()\r\nvar sa = t //used in mont_()\r\nvar mr_x1 = t,\r\n mr_r = t,\r\n mr_a = t, //used in millerRabin()\r\n eg_v = t,\r\n eg_u = t,\r\n eg_A = t,\r\n eg_B = t,\r\n eg_C = t,\r\n eg_D = t, //used in eGCD_(), inverseMod_()\r\n //, md_q1=t, md_q2=t, md_q3=t, md_r=t, md_r1=t, md_r2=t, md_tt=t, //used in mod_()\r\n\r\n primes = t,\r\n pows = t,\r\n s_i = t,\r\n s_i2 = t,\r\n s_R = t,\r\n s_rm = t,\r\n s_q = t,\r\n s_n1 = t,\r\n s_a = t,\r\n s_r2 = t,\r\n s_n = t,\r\n s_b = t,\r\n s_d = t,\r\n s_x1 = t,\r\n s_x2 = t,\r\n s_aa = t, //used in randTruePrime_()\r\n rpprb = t //used in randProbPrimeRounds() (which also uses \"primes\")\r\n\r\n////////////////////////////////////////////////////////////////////////////////////////\r\n\r\nvar k, buff\r\n\r\n/**\r\n * return array of all primes less than integer n\r\n *\r\n * @param {number} n\r\n * @returns {number[]}\r\n */\r\nexport function findPrimes(n: number): number[] {\r\n var i, s, p, ans\r\n s = new Array(n)\r\n for (i = 0; i < n; i++) s[i] = 0\r\n s[0] = 2\r\n p = 0 //first p elements of s are primes, the rest are a sieve\r\n for (; s[p] < n; ) {\r\n //s[p] is the pth prime\r\n for (\r\n i = s[p] * s[p];\r\n i < n;\r\n i += s[p] //mark multiples of s[p]\r\n )\r\n s[i] = 1\r\n p++\r\n s[p] = s[p - 1] + 1\r\n for (; s[p] < n && s[s[p]]; s[p]++); //find next prime (where s[p]==0)\r\n }\r\n ans = new Array(p)\r\n for (i = 0; i < p; i++) ans[i] = s[i]\r\n return ans\r\n}\r\n\r\n/**\r\n * does a single round of Miller-Rabin base b consider x to be a possible prime?\r\n *\r\n * x is a bigInt, and b is an integer, with b<x\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} b\r\n * @returns {(0 | 1)}\r\n */\r\nexport function millerRabinInt(x: number[], b: number): Bool {\r\n if (mr_x1.length !== x.length) {\r\n mr_x1 = dup(x)\r\n mr_r = dup(x)\r\n mr_a = dup(x)\r\n }\r\n\r\n copyInt_(mr_a, b)\r\n return millerRabin(x, mr_a)\r\n}\r\n\r\n/**\r\n * does a single round of Miller-Rabin base b consider x to be a possible prime?\r\n *\r\n * x and b are bigInts with b<x\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} b\r\n * @returns {(0 | 1)}\r\n */\r\nexport function millerRabin(x: number[], b: number[]): Bool {\r\n var i, j, k, s\r\n\r\n if (mr_x1.length !== x.length) {\r\n mr_x1 = dup(x)\r\n mr_r = dup(x)\r\n mr_a = dup(x)\r\n }\r\n\r\n copy_(mr_a, b)\r\n copy_(mr_r, x)\r\n copy_(mr_x1, x)\r\n\r\n addInt_(mr_r, -1)\r\n addInt_(mr_x1, -1)\r\n\r\n //s=the highest power of two that divides mr_r\r\n k = 0\r\n for (i = 0; i < mr_r.length; i++)\r\n for (j = 1; j < mask; j <<= 1)\r\n if (x[i] & j) {\r\n s = k < mr_r.length + bpe ? k : 0\r\n i = mr_r.length\r\n j = mask\r\n } else k++\r\n\r\n if (s) rightShift_(mr_r, s)\r\n\r\n powMod_(mr_a, mr_r, x)\r\n\r\n if (!equalsInt(mr_a, 1) && !equals(mr_a, mr_x1)) {\r\n j = 1\r\n //$off\r\n while (j <= s - 1 && !equals(mr_a, mr_x1)) {\r\n squareMod_(mr_a, x)\r\n if (equalsInt(mr_a, 1)) {\r\n return 0\r\n }\r\n j++\r\n }\r\n if (!equals(mr_a, mr_x1)) {\r\n return 0\r\n }\r\n }\r\n return 1\r\n}\r\n\r\n/**\r\n * returns how many bits long the bigInt is, not counting leading zeros.\r\n *\r\n * @param {number[]} x\r\n * @returns {number}\r\n */\r\nexport function bitSize(x: number[]): number {\r\n var j, z, w\r\n for (j = x.length - 1; x[j] == 0 && j > 0; j--);\r\n for (z = 0, w = x[j]; w; w >>= 1, z++);\r\n z += bpe * j\r\n return z\r\n}\r\n\r\n/**\r\n * return a copy of x with at least n elements, adding leading zeros if needed\r\n *\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {number[]}\r\n */\r\nexport function expand(x: number[], n: number): number[] {\r\n var ans = int2bigInt(0, (x.length > n ? x.length : n) * bpe, 0)\r\n copy_(ans, x)\r\n return ans\r\n}\r\n\r\n/**\r\n * return a k-bit true random prime using Maurer's algorithm.\r\n *\r\n * @export\r\n * @param {number} k\r\n * @returns {number[]}\r\n */\r\nexport function randTruePrime(k: number): number[] {\r\n var ans = int2bigInt(0, k, 0)\r\n randTruePrime_(ans, k)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return a k-bit random probable prime with probability of error < 2^-80\r\n *\r\n * @export\r\n * @param {number} k\r\n * @returns {number[]}\r\n */\r\nexport function randProbPrime(k: number): number[] {\r\n if (k >= 600) return randProbPrimeRounds(k, 2) //numbers from HAC table 4.3\r\n if (k >= 550) return randProbPrimeRounds(k, 4)\r\n if (k >= 500) return randProbPrimeRounds(k, 5)\r\n if (k >= 400) return randProbPrimeRounds(k, 6)\r\n if (k >= 350) return randProbPrimeRounds(k, 7)\r\n if (k >= 300) return randProbPrimeRounds(k, 9)\r\n if (k >= 250) return randProbPrimeRounds(k, 12) //numbers from HAC table 4.4\r\n if (k >= 200) return randProbPrimeRounds(k, 15)\r\n if (k >= 150) return randProbPrimeRounds(k, 18)\r\n if (k >= 100) return randProbPrimeRounds(k, 27)\r\n return randProbPrimeRounds(k, 40) //number from HAC remark 4.26 (only an estimate)\r\n}\r\n\r\n/**\r\n * return a k-bit probable random prime using n rounds of Miller Rabin\r\n * (after trial division with small primes)\r\n *\r\n * @export\r\n * @param {number} k\r\n * @param {number} n\r\n * @returns {number[]}\r\n */\r\nexport function randProbPrimeRounds(k: number, n: number): number[] {\r\n var ans, i, divisible, B\r\n B = 30000 //B is largest prime to use in trial division\r\n ans = int2bigInt(0, k, 0)\r\n\r\n //optimization: try larger and smaller B to find the best limit.\r\n\r\n if (primes.length === 0) primes = findPrimes(30000) //check for divisibility by primes <=30000\r\n\r\n if (rpprb.length !== ans.length) rpprb = dup(ans)\r\n\r\n for (;;) {\r\n //keep trying random values for ans until one appears to be prime\r\n //optimization: pick a random number times L=2*3*5*...*p, plus a\r\n // random element of the list of all numbers in [0,L) not divisible by any prime up to p.\r\n // This can reduce the amount of random number generation.\r\n\r\n randBigInt_(ans, k, 0) //ans = a random odd number to check\r\n ans[0] |= 1\r\n divisible = 0\r\n\r\n //check ans for divisibility by small primes up to B\r\n for (i = 0; i < primes.length && primes[i] <= B; i++)\r\n if (modInt(ans, primes[i]) === 0 && !equalsInt(ans, primes[i])) {\r\n divisible = 1\r\n break\r\n }\r\n\r\n //optimization: change millerRabin so the base can be bigger than the number being checked, then eliminate the while here.\r\n\r\n //do n rounds of Miller Rabin, with random bases less than ans\r\n for (i = 0; i < n && !divisible; i++) {\r\n randBigInt_(rpprb, k, 0)\r\n while (\r\n !greater(ans, rpprb) //pick a random rpprb that's < ans\r\n )\r\n randBigInt_(rpprb, k, 0)\r\n if (!millerRabin(ans, rpprb)) divisible = 1\r\n }\r\n\r\n if (!divisible) return ans\r\n }\r\n /*::\r\n declare var never: empty\r\n return never\r\n */\r\n}\r\n\r\n/**\r\n * return a new bigInt equal to (x mod n) for bigInts x and n.\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} n\r\n * @returns {number[]}\r\n */\r\nexport function mod(x: number[], n: number[]): number[] {\r\n var ans = dup(x)\r\n mod_(ans, n)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return (x+n) where x is a bigInt and n is an integer.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {number[]}\r\n */\r\nexport function addInt(x: number[], n: number): number[] {\r\n var ans = expand(x, x.length + 1)\r\n addInt_(ans, n)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return x*y for bigInts x and y. This is faster when y<x.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {number[]}\r\n */\r\nexport function mult(x: number[], y: number[]): number[] {\r\n var ans = expand(x, x.length + y.length)\r\n mult_(ans, y)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return (x**y mod n) where x,y,n are bigInts and ** is exponentiation.\r\n *\r\n * 0**0=1.\r\n *\r\n * Faster for odd n.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} n\r\n * @returns {number[]}\r\n */\r\nexport function powMod(x: number[], y: number[], n: number[]): number[] {\r\n var ans = expand(x, n.length)\r\n powMod_(\r\n //this should work without the trim, but doesn't\r\n ans,\r\n trim(y, 2),\r\n trim(n, 2),\r\n )\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * Simple pow with no optimizations (in 40x times slower than jsbn's pow)\r\n * @param x bigInt\r\n * @param e\r\n */\r\nexport function pow(x: number[], e: number) {\r\n let ans = dup(x);\r\n e -= 1;\r\n for(let i = 0; i < e; ++i) {\r\n ans = mult(ans, x);\r\n }\r\n return trim(ans, 1);\r\n}\r\n\r\n/**\r\n * return (x-y) for bigInts x and y\r\n *\r\n * Negative answers will be 2s complement\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {number[]}\r\n */\r\nexport function sub(x: number[], y: number[]): number[] {\r\n var ans = expand(x, x.length > y.length ? x.length + 1 : y.length + 1)\r\n sub_(ans, y)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return (x+y) for bigInts x and y\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {number[]}\r\n */\r\nexport function add(x: number[], y: number[]): number[] {\r\n var ans = expand(x, x.length > y.length ? x.length + 1 : y.length + 1)\r\n add_(ans, y)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * return (x**(-1) mod n) for bigInts x and n.\r\n *\r\n * If no inverse exists, it returns null\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} n\r\n * @returns {(number[] | null)}\r\n */\r\nexport function inverseMod(x: number[], n: number[]): number[] | null {\r\n var ans = expand(x, n.length)\r\n var s = inverseMod_(ans, n)\r\n return s ? trim(ans, 1) : null\r\n}\r\n\r\n/**\r\n * return (x*y mod n) for bigInts x,y,n.\r\n *\r\n * For greater speed, let y<x.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} n\r\n * @returns {number[]}\r\n */\r\nexport function multMod(x: number[], y: number[], n: number[]): number[] {\r\n var ans = expand(x, n.length)\r\n multMod_(ans, y, n)\r\n return trim(ans, 1)\r\n}\r\n\r\n/**\r\n * generate a k-bit true random prime using Maurer's algorithm, and put it into ans.\r\n *\r\n * The bigInt ans must be large enough to hold it.\r\n *\r\n * @export\r\n * @param {number[]} ans\r\n * @param {number} k\r\n * @return {void}\r\n */\r\nexport function randTruePrime_(ans: number[], k: number): void {\r\n var c, m, pm, dd, j, r, B, divisible, z, zz, recSize\r\n var w\r\n if (primes.length == 0) primes = findPrimes(30000) //check for divisibility by primes <=30000\r\n\r\n if (pows.length == 0) {\r\n pows = new Array(512)\r\n for (j = 0; j < 512; j++) {\r\n pows[j] = Math.pow(2, j / 511 - 1)\r\n }\r\n }\r\n\r\n //c and m should be tuned for a particular machine and value of k, to maximize speed\r\n c = 0.1 //c=0.1 in HAC\r\n m = 20 //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits\r\n var recLimit = 20 //stop recursion when k <=recLimit. Must have recLimit >= 2\r\n\r\n if (s_i2.length != ans.length) {\r\n s_i2 = dup(ans)\r\n s_R = dup(ans)\r\n s_n1 = dup(ans)\r\n s_r2 = dup(ans)\r\n s_d = dup(ans)\r\n s_x1 = dup(ans) //TODO Seems like a bug in eslint, reports as unused\r\n s_x2 = dup(ans)\r\n s_b = dup(ans)\r\n s_n = dup(ans)\r\n s_i = dup(ans)\r\n s_rm = dup(ans)\r\n s_q = dup(ans)\r\n s_a = dup(ans)\r\n s_aa = dup(ans)\r\n }\r\n\r\n if (k <= recLimit) {\r\n //generate small random primes by trial division up to its square root\r\n pm = (1 << ((k + 2) >> 1)) - 1 //pm is binary number with all ones, just over sqrt(2^k)\r\n copyInt_(ans, 0)\r\n for (dd = 1; dd; ) {\r\n dd = 0\r\n ans[0] = 1 | (1 << (k - 1)) | Math.floor(Math.random() * (1 << k)) //random, k-bit, odd integer, with msb 1\r\n for (j = 1; j < primes.length && (primes[j] & pm) == primes[j]; j++) {\r\n //trial division by all primes 3...sqrt(2^k)\r\n if (0 == ans[0] % primes[j]) {\r\n dd = 1\r\n break\r\n }\r\n }\r\n }\r\n carry_(ans)\r\n return\r\n }\r\n\r\n B = c * k * k //try small primes up to B (or all the primes[] array if the largest is less than B).\r\n if (k > 2 * m)\r\n //generate this k-bit number by first recursively generating a number that has between k/2 and k-m bits\r\n for (r = 1; k - k * r <= m; ) r = pows[Math.floor(Math.random() * 512)] //r=Math.pow(2,Math.random()-1);\r\n else r = 0.5\r\n\r\n //simulation suggests the more complex algorithm using r=.333 is only slightly faster.\r\n\r\n recSize = Math.floor(r * k) + 1\r\n\r\n randTruePrime_(s_q, recSize)\r\n copyInt_(s_i2, 0)\r\n s_i2[Math.floor((k - 2) / bpe)] |= 1 << ((k - 2) % bpe) //s_i2=2^(k-2)\r\n divide_(s_i2, s_q, s_i, s_rm) //s_i=floor((2^(k-1))/(2q))\r\n\r\n z = bitSize(s_i)\r\n\r\n for (;;) {\r\n for (;;) {\r\n //generate z-bit numbers until one falls in the range [0,s_i-1]\r\n randBigInt_(s_R, z, 0)\r\n if (greater(s_i, s_R)) break\r\n } //now s_R is in the range [0,s_i-1]\r\n addInt_(s_R, 1) //now s_R is in the range [1,s_i]\r\n add_(s_R, s_i) //now s_R is in the range [s_i+1,2*s_i]\r\n\r\n copy_(s_n, s_q)\r\n mult_(s_n, s_R)\r\n multInt_(s_n, 2)\r\n addInt_(s_n, 1) //s_n=2*s_R*s_q+1\r\n\r\n copy_(s_r2, s_R)\r\n multInt_(s_r2, 2) //s_r2=2*s_R\r\n\r\n //check s_n for divisibility by small primes up to B\r\n for (divisible = 0, j = 0; j < primes.length && primes[j] < B; j++)\r\n if (modInt(s_n, primes[j]) == 0 && !equalsInt(s_n, primes[j])) {\r\n divisible = 1\r\n break\r\n }\r\n\r\n if (!divisible)\r\n if (!millerRabinInt(s_n, 2))\r\n //if it passes small primes check, then try a single Miller-Rabin base 2\r\n //this line represents 75% of the total runtime for randTruePrime_\r\n divisible = 1\r\n\r\n if (!divisible) {\r\n //if it passes that test, continue checking s_n\r\n addInt_(s_n, -3)\r\n for (j = s_n.length - 1; s_n[j] == 0 && j > 0; j--); //strip leading zeros\r\n for (zz = 0, w = s_n[j]; w; w >>= 1, zz++);\r\n zz += bpe * j //zz=number of bits in s_n, ignoring leading zeros\r\n for (;;) {\r\n //generate z-bit numbers until one falls in the range [0,s_n-1]\r\n randBigInt_(s_a, zz, 0)\r\n if (greater(s_n, s_a)) break\r\n } //now s_a is in the range [0,s_n-1]\r\n addInt_(s_n, 3) //now s_a is in the range [0,s_n-4]\r\n addInt_(s_a, 2) //now s_a is in the range [2,s_n-2]\r\n copy_(s_b, s_a)\r\n copy_(s_n1, s_n)\r\n addInt_(s_n1, -1)\r\n powMod_(s_b, s_n1, s_n) //s_b=s_a^(s_n-1) modulo s_n\r\n addInt_(s_b, -1)\r\n if (isZero(s_b)) {\r\n copy_(s_b, s_a)\r\n powMod_(s_b, s_r2, s_n)\r\n addInt_(s_b, -1)\r\n copy_(s_aa, s_n)\r\n copy_(s_d, s_b)\r\n GCD_(s_d, s_n) //if s_b and s_n are relatively prime, then s_n is a prime\r\n if (equalsInt(s_d, 1)) {\r\n copy_(ans, s_aa)\r\n return //if we've made it this far, then s_n is absolutely guaranteed to be prime\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Return an n-bit random BigInt (n>=1). If s=1, then the most significant of those n bits is set to 1.\r\n *\r\n * @export\r\n * @param {number} n\r\n * @param {number} s\r\n * @returns {number[]}\r\n */\r\nexport function randBigInt(n: number, s: number): number[] {\r\n var a, b\r\n a = Math.floor((n - 1) / bpe) + 2 //# array elements to hold the BigInt with a leading 0 element\r\n b = int2bigInt(0, 0, a)\r\n randBigInt_(b, n, s)\r\n return b\r\n}\r\n\r\n/**\r\n * Set b to an n-bit random BigInt. If s=1, then the most significant of those n bits is set to 1.\r\n *\r\n * Array b must be big enough to hold the result. Must have n>=1\r\n *\r\n * @export\r\n * @param {number[]} b\r\n * @param {number} n\r\n * @param {number} s\r\n * @return {void}\r\n */\r\nexport function randBigInt_(b: number[], n: number, s: number): void {\r\n var i, a\r\n for (i = 0; i < b.length; i++) b[i] = 0\r\n a = Math.floor((n - 1) / bpe) + 1 //# array elements to hold the BigInt\r\n for (i = 0; i < a; i++) {\r\n b[i] = Math.floor(Math.random() * (1 << (bpe - 1)))\r\n }\r\n b[a - 1] &= (2 << ((n - 1) % bpe)) - 1\r\n if (s == 1) b[a - 1] |= 1 << ((n - 1) % bpe)\r\n}\r\n\r\n/**\r\n * Return the greatest common divisor of bigInts x and y (each with same number of elements).\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {number[]}\r\n */\r\nexport function GCD(x: number[], y: number[]): number[] {\r\n var xc, yc\r\n xc = dup(x)\r\n yc = dup(y)\r\n GCD_(xc, yc)\r\n return xc\r\n}\r\n\r\n/**\r\n * set x to the greatest common divisor of bigInts x and y (each with same number of elements).\r\n *\r\n * y is destroyed.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n */\r\nexport function GCD_(x: number[], y: number[]): void {\r\n var i: number, xp: number, yp: number, A: number, B, C: number, D: number, q, sing\r\n var qp\r\n if (T.length !== x.length) T = dup(x)\r\n\r\n sing = 1\r\n while (sing) {\r\n //while y has nonzero elements other than y[0]\r\n sing = 0\r\n for (\r\n i = 1;\r\n i < y.length;\r\n i++ //check if y has nonzero elements other than 0\r\n )\r\n if (y[i]) {\r\n sing = 1\r\n break\r\n }\r\n if (!sing) break //quit when y all zero elements except possibly y[0]\r\n\r\n for (i = x.length; !x[i] && i >= 0; i--); //find most significant element of x\r\n xp = x[i]\r\n yp = y[i]\r\n A = 1\r\n B = 0\r\n C = 0\r\n D = 1\r\n while (yp + C && yp + D) {\r\n q = Math.floor((xp + A) / (yp + C))\r\n qp = Math.floor((xp + B) / (yp + D))\r\n if (q != qp) break\r\n t = A - q * C\r\n A = C\r\n C = t // do (A,B,xp, C,D,yp) = (C,D,yp, A,B,xp) - q*(0,0,0, C,D,yp)\r\n t = B - q * D\r\n B = D\r\n D = t\r\n t = xp - q * yp\r\n xp = yp\r\n yp = t\r\n }\r\n if (B) {\r\n copy_(T, x)\r\n linComb_(x, y, A, B) //x=A*x+B*y\r\n linComb_(y, T, D, C) //y=D*y+C*T\r\n } else {\r\n mod_(x, y)\r\n copy_(T, x)\r\n copy_(x, y)\r\n copy_(y, T)\r\n }\r\n }\r\n if (y[0] === 0) return\r\n t = modInt(x, y[0])\r\n copyInt_(x, y[0])\r\n y[0] = t\r\n while (y[0]) {\r\n x[0] %= y[0]\r\n t = x[0]\r\n x[0] = y[0]\r\n y[0] = t\r\n }\r\n}\r\n\r\n/**\r\n * do x=x**(-1) mod n, for bigInts x and n.\r\n *\r\n * If no inverse exists, it sets x to zero and returns 0, else it returns 1.\r\n * The x array must be at least as large as the n array.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} n\r\n * @returns {(0 | 1)}\r\n */\r\nexport function inverseMod_(x: number[], n: number[]): Bool {\r\n var k = 1 + 2 * Math.max(x.length, n.length)\r\n\r\n if (!(x[0] & 1) && !(n[0] & 1)) {\r\n //if both inputs are even, then inverse doesn't exist\r\n copyInt_(x, 0)\r\n return 0\r\n }\r\n\r\n if (eg_u.length != k) {\r\n eg_u = new Array(k)\r\n eg_v = new Array(k)\r\n eg_A = new Array(k)\r\n eg_B = new Array(k)\r\n eg_C = new Array(k)\r\n eg_D = new Array(k)\r\n }\r\n\r\n copy_(eg_u, x)\r\n copy_(eg_v, n)\r\n copyInt_(eg_A, 1)\r\n copyInt_(eg_B, 0)\r\n copyInt_(eg_C, 0)\r\n copyInt_(eg_D, 1)\r\n for (;;) {\r\n while (!(eg_u[0] & 1)) {\r\n //while eg_u is even\r\n halve_(eg_u)\r\n if (!(eg_A[0] & 1) && !(eg_B[0] & 1)) {\r\n //if eg_A==eg_B==0 mod 2\r\n halve_(eg_A)\r\n halve_(eg_B)\r\n } else {\r\n add_(eg_A, n)\r\n halve_(eg_A)\r\n sub_(eg_B, x)\r\n halve_(eg_B)\r\n }\r\n }\r\n\r\n while (!(eg_v[0] & 1)) {\r\n //while eg_v is even\r\n halve_(eg_v)\r\n if (!(eg_C[0] & 1) && !(eg_D[0] & 1)) {\r\n //if eg_C==eg_D==0 mod 2\r\n halve_(eg_C)\r\n halve_(eg_D)\r\n } else {\r\n add_(eg_C, n)\r\n halve_(eg_C)\r\n sub_(eg_D, x)\r\n halve_(eg_D)\r\n }\r\n }\r\n\r\n if (!greater(eg_v, eg_u)) {\r\n //eg_v <= eg_u\r\n sub_(eg_u, eg_v)\r\n sub_(eg_A, eg_C)\r\n sub_(eg_B, eg_D)\r\n } else {\r\n //eg_v > eg_u\r\n sub_(eg_v, eg_u)\r\n sub_(eg_C, eg_A)\r\n sub_(eg_D, eg_B)\r\n }\r\n\r\n if (equalsInt(eg_u, 0)) {\r\n while (\r\n negative(eg_C) //make sure answer is nonnegative\r\n )\r\n add_(eg_C, n)\r\n copy_(x, eg_C)\r\n\r\n if (!equalsInt(eg_v, 1)) {\r\n //if GCD_(x,n)!=1, then there is no inverse\r\n copyInt_(x, 0)\r\n return 0\r\n }\r\n return 1\r\n }\r\n }\r\n /*::\r\n declare var never: empty\r\n return never\r\n */\r\n}\r\n\r\n/**\r\n * return x**(-1) mod n, for integers x and n.\r\n *\r\n * Return 0 if there is no inverse\r\n *\r\n * @param {number} x\r\n * @param {number} n\r\n * @returns {number}\r\n */\r\nexport function inverseModInt(x: number, n: number): number {\r\n var a = 1,\r\n b = 0,\r\n t\r\n for (;;) {\r\n if (x === 1) return a\r\n if (x === 0) return 0\r\n b -= a * Math.floor(n / x)\r\n //$off\r\n n %= x\r\n\r\n if (n === 1) return b //to avoid negatives, change this b to n-b, and each -= to +=\r\n if (n === 0) return 0\r\n a -= b * Math.floor(x / n)\r\n //$off\r\n x %= n\r\n }\r\n /*::\r\n declare var never: empty\r\n return never\r\n */\r\n}\r\n\r\n//this deprecated function is for backward compatibility only.\r\nfunction inverseModInt_(x: number, n: number) {\r\n return inverseModInt(x, n)\r\n}\r\n\r\n/**\r\n * Given positive bigInts x and y, change the bigints v, a, and b to positive bigInts such that:\r\n *\r\n * v = GCD_(x,y) = a*x-b*y\r\n *\r\n * The bigInts v, a, b, must have exactly as many elements as the larger of x and y.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} v\r\n * @param {number[]} a\r\n * @param {number[]} b\r\n * @return {void}\r\n */\r\nexport function eGCD_(\r\n x: number[],\r\n y: number[],\r\n v: number[],\r\n a: number[],\r\n b: number[],\r\n): void {\r\n var g = 0\r\n var k = Math.max(x.length, y.length)\r\n if (eg_u.length != k) {\r\n eg_u = new Array(k)\r\n eg_A = new Array(k)\r\n eg_B = new Array(k)\r\n eg_C = new Array(k)\r\n eg_D = new Array(k)\r\n }\r\n while (!(x[0] & 1) && !(y[0] & 1)) {\r\n //while x and y both even\r\n halve_(x)\r\n halve_(y)\r\n g++\r\n }\r\n copy_(eg_u, x)\r\n copy_(v, y)\r\n copyInt_(eg_A, 1)\r\n copyInt_(eg_B, 0)\r\n copyInt_(eg_C, 0)\r\n copyInt_(eg_D, 1)\r\n for (;;) {\r\n while (!(eg_u[0] & 1)) {\r\n //while u is even\r\n halve_(eg_u)\r\n if (!(eg_A[0] & 1) && !(eg_B[0] & 1)) {\r\n //if A==B==0 mod 2\r\n halve_(eg_A)\r\n halve_(eg_B)\r\n } else {\r\n add_(eg_A, y)\r\n halve_(eg_A)\r\n sub_(eg_B, x)\r\n halve_(eg_B)\r\n }\r\n }\r\n\r\n while (!(v[0] & 1)) {\r\n //while v is even\r\n halve_(v)\r\n if (!(eg_C[0] & 1) && !(eg_D[0] & 1)) {\r\n //if C==D==0 mod 2\r\n halve_(eg_C)\r\n halve_(eg_D)\r\n } else {\r\n add_(eg_C, y)\r\n halve_(eg_C)\r\n sub_(eg_D, x)\r\n halve_(eg_D)\r\n }\r\n }\r\n\r\n if (!greater(v, eg_u)) {\r\n //v<=u\r\n sub_(eg_u, v)\r\n sub_(eg_A, eg_C)\r\n sub_(eg_B, eg_D)\r\n } else {\r\n //v>u\r\n sub_(v, eg_u)\r\n sub_(eg_C, eg_A)\r\n sub_(eg_D, eg_B)\r\n }\r\n if (equalsInt(eg_u, 0)) {\r\n while (negative(eg_C)) {\r\n //make sure a (C) is nonnegative\r\n add_(eg_C, y)\r\n sub_(eg_D, x)\r\n }\r\n multInt_(eg_D, -1) ///make sure b (D) is nonnegative\r\n copy_(a, eg_C)\r\n copy_(b, eg_D)\r\n leftShift_(v, g)\r\n return\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * is bigInt x negative?\r\n *\r\n * @param {number[]} x\r\n * @returns {(1 | 0)}\r\n */\r\nexport function negative(x: number[]) {\r\n //TODO Flow Bool type inference\r\n return (x[x.length - 1] >> (bpe - 1)) & 1\r\n}\r\n\r\n/**\r\n * is (x << (shift*bpe)) > y?\r\n *\r\n * x and y are nonnegative bigInts\r\n * shift is a nonnegative integer\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number} shift\r\n * @returns {(1 | 0)}\r\n */\r\nexport function greaterShift(x: number[], y: number[], shift: number): Bool {\r\n var i,\r\n kx = x.length,\r\n ky = y.length\r\n k = kx + shift < ky ? kx + shift : ky\r\n for (i = ky - 1 - shift; i < kx && i >= 0; i++) if (x[i] > 0) return 1 //if there are nonzeros in x to the left of the first column of y, then x is bigger\r\n for (i = kx - 1 + shift; i < ky; i++) if (y[i] > 0) return 0 //if there are nonzeros in y to the left of the first column of x, then x is not bigger\r\n for (i = k - 1; i >= shift; i--)\r\n if (x[i - shift] > y[i]) return 1\r\n else if (x[i - shift] < y[i]) return 0\r\n return 0\r\n}\r\n\r\n/**\r\n * is x > y?\r\n *\r\n * x and y both nonnegative\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {(1 | 0)}\r\n */\r\nexport function greater(x: number[], y: number[]): Bool {\r\n var i\r\n var k = x.length < y.length ? x.length : y.length\r\n\r\n for (i = x.length; i < y.length; i++) if (y[i]) return 0 //y has more digits\r\n\r\n for (i = y.length; i < x.length; i++) if (x[i]) return 1 //x has more digits\r\n\r\n for (i = k - 1; i >= 0; i--)\r\n if (x[i] > y[i]) return 1\r\n else if (x[i] < y[i]) return 0\r\n return 0\r\n}\r\n\r\n/**\r\n * divide x by y giving quotient q and remainder r.\r\n *\r\n * q = floor(x/y)\r\n * r = x mod y\r\n *\r\n * All 4 are bigints.\r\n *\r\n * * x must have at least one leading zero element.\r\n * * y must be nonzero.\r\n * * q and r must be arrays that are exactly the same length as x. (Or q can have more).\r\n * * Must have x.length >= y.length >= 2.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} q\r\n * @param {number[]} r\r\n * @return {void}\r\n */\r\nexport function divide_(\r\n x: number[],\r\n y: number[],\r\n q: number[],\r\n r: number[],\r\n): void {\r\n var kx, ky\r\n var i, j, y1, y2, c, a, b\r\n copy_(r, x)\r\n for (ky = y.length; y[ky - 1] === 0; ky--); //ky is number of elements in y, not including leading zeros\r\n\r\n //normalize: ensure the most significant element of y has its highest bit set\r\n b = y[ky - 1]\r\n for (a = 0; b; a++) b >>= 1\r\n a = bpe - a //a is how many bits to shift so that the high order bit of y is leftmost in its array element\r\n leftShift_(y, a) //multiply both by 1<<a now, then divide both by that at the end\r\n leftShift_(r, a)\r\n\r\n //Rob Visser discovered a bug: the following line was originally just before the normalization.\r\n for (kx = r.length; r[kx - 1] === 0 && kx > ky; kx--); //kx is number of elements in normalized x, not including leading zeros\r\n\r\n copyInt_(q, 0) // q=0\r\n while (!greaterShift(y, r, kx - ky)) {\r\n // while (leftShift_(y,kx-ky) <= r) {\r\n subShift_(r, y, kx - ky) // r=r-leftShift_(y,kx-ky)\r\n q[kx - ky]++ // q[kx-ky]++;\r\n } // }\r\n\r\n for (i = kx - 1; i >= ky; i--) {\r\n if (r[i] == y[ky - 1]) q[i - ky] = mask\r\n else q[i - ky] = Math.floor((r[i] * radix + r[i - 1]) / y[ky - 1])\r\n\r\n //The following for(;;) loop is equivalent to the commented while loop,\r\n //except that the uncommented version avoids overflow.\r\n //The commented loop comes from HAC, which assumes r[-1]==y[-1]==0\r\n // while (q[i-ky]*(y[ky-1]*radix+y[ky-2]) > r[i]*radix*radix+r[i-1]*radix+r[i-2])\r\n // q[i-ky]--;\r\n for (;;) {\r\n y2 = (ky > 1 ? y[ky - 2] : 0) * q[i - ky]\r\n c = y2 >> bpe\r\n y2 = y2 & mask\r\n y1 = c + q[i - ky] * y[ky - 1]\r\n c = y1 >> bpe\r\n y1 = y1 & mask\r\n\r\n if (\r\n c == r[i]\r\n ? y1 == r[i - 1] ? y2 > (i > 1 ? r[i - 2] : 0) : y1 > r[i - 1]\r\n : c > r[i]\r\n )\r\n q[i - ky]--\r\n else break\r\n }\r\n\r\n linCombShift_(r, y, -q[i - ky], i - ky) //r=r-q[i-ky]*leftShift_(y,i-ky)\r\n if (negative(r)) {\r\n addShift_(r, y, i - ky) //r=r+leftShift_(y,i-ky)\r\n q[i - ky]--\r\n }\r\n }\r\n\r\n rightShift_(y, a) //undo the normalization step\r\n rightShift_(r, a) //undo the normalization step\r\n}\r\n\r\n/**\r\n * do carries and borrows so each element of the bigInt x fits in bpe bits.\r\n *\r\n * @param {number[]} x\r\n */\r\nexport function carry_(x: number[]): void {\r\n var i, k, c, b\r\n k = x.length\r\n c = 0\r\n for (i = 0; i < k; i++) {\r\n c += x[i]\r\n b = 0\r\n if (c < 0) {\r\n b = -(c >> bpe)\r\n c += b * radix\r\n }\r\n x[i] = c & mask\r\n c = (c >> bpe) - b\r\n }\r\n}\r\n\r\n/**\r\n * return x mod n for bigInt x and integer n.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {number}\r\n */\r\nexport function modInt(x: number[], n: number): number {\r\n var i,\r\n c = 0\r\n for (i = x.length - 1; i >= 0; i--) c = (c * radix + x[i]) % n\r\n return c\r\n}\r\n\r\n/**\r\n * convert the integer t into a bigInt with at least the given number of bits.\r\n * the returned array stores the bigInt in bpe-bit chunks, little endian (buff[0] is least significant word)\r\n * Pad the array with leading zeros so that it has at least minSize elements.\r\n *\r\n * There will always be at least one leading 0 element.\r\n *\r\n * @export\r\n * @param {number} t\r\n * @param {number} bits\r\n * @param {number} minSize\r\n * @returns {number[]}\r\n */\r\nexport function int2bigInt(t: number, bits: number, minSize: number): number[] {\r\n var i, k\r\n k = Math.ceil(bits / bpe) + 1\r\n k = minSize > k ? minSize : k\r\n var buff = new Array(k)\r\n copyInt_(buff, t)\r\n return buff\r\n}\r\n\r\n/**\r\n * return the bigInt given a string representation in a given base.\r\n * Pad the array with leading zeros so that it has at least minSize elements.\r\n * If base=-1, then it reads in a space-separated list of array elements in decimal.\r\n *\r\n * The array will always have at least one leading zero, unless base=-1.\r\n *\r\n * @export\r\n * @param {string} s\r\n * @param {number} base\r\n * @param {number} [minSize]\r\n * @returns {number[]}\r\n */\r\nexport function str2bigInt(\r\n s: string,\r\n base: number,\r\n minSize?: number,\r\n): number[] {\r\n var d, i, x, y, kk\r\n var k = s.length\r\n if (base === -1) {\r\n //comma-separated list of array elements in decimal\r\n x = new Array(0)\r\n for (;;) {\r\n y = new Array(x.length + 1)\r\n for (i = 0; i < x.length; i++) y[i + 1] = x[i]\r\n y[0] = parseInt(s, 10) //TODO PERF Should we replace that with ~~ (not not)? https://jsperf.com/number-vs-parseint-vs-plus/7\r\n x = y\r\n d = s.indexOf(',', 0)\r\n if (d < 1) break\r\n //$off\r\n s = s.substring(d + 1)\r\n if (s.length == 0) break\r\n }\r\n //$off\r\n if (x.length < minSize) {\r\n //$off\r\n y = new Array(minSize)\r\n copy_(y, x)\r\n return y\r\n }\r\n return x\r\n }\r\n\r\n x = int2bigInt(0, base * k, 0)\r\n for (i = 0; i < k; i++) {\r\n d = digitsStr.indexOf(s.substring(i, i + 1), 0)\r\n if (base <= 36 && d >= 36)\r\n //convert lowercase to uppercase if base<=36\r\n d -= 26\r\n if (d >= base || d < 0) {\r\n //stop at first illegal character\r\n break\r\n }\r\n multInt_(x, base)\r\n addInt_(x, d)\r\n }\r\n\r\n for (k = x.length; k > 0 && !x[k - 1]; k--); //strip off leading zeros\r\n //$off\r\n k = minSize > k + 1 ? minSize : k + 1\r\n //$off\r\n y = new Array(k)\r\n //$off\r\n kk = k < x.length ? k : x.length\r\n //$off\r\n for (i = 0; i < kk; i++) y[i] = x[i]\r\n //$off\r\n for (; i < k; i++) y[i] = 0\r\n return y\r\n}\r\n\r\n//return the bigInt given a string representation in a given base.\r\n//Pad the array with leading zeros so that it has at least minSize elements.\r\n//If base=-1, then it reads in a space-separated list of array elements in decimal.\r\n//The array will always have at least one leading zero, unless base=-1.\r\n// function str2bigInt(s,b,minSize) {\r\n// var d, i, j, base, str, x, y, kk;\r\n// if (typeof b === 'string') {\r\n// base = b.length;\r\n// str = b;\r\n// } else {\r\n// base = b;\r\n// str = digitsStr;\r\n// }\r\n// var k=s.length;\r\n// if (base==-1) { //comma-separated list of array elements in decimal\r\n// x=new Array(0);\r\n// for (;;) {\r\n// y=new Array(x.length+1);\r\n// for (i=0;i<x.length;i++)\r\n// y[i+1]=x[i];\r\n// y[0]=parseInt(s,10);\r\n// x=y;\r\n// d=s.indexOf(',',0);\r\n// if (d<1)\r\n// break;\r\n// s=s.substring(d+1);\r\n// if (s.length==0)\r\n// break;\r\n// }\r\n// if (x.length<minSize) {\r\n// y=new Array(minSize);\r\n// copy_(y,x);\r\n// return y;\r\n// }\r\n// return x;\r\n// }\r\n\r\n// x=int2bigInt(0,base*k,0);\r\n// for (i=0;i<k;i++) {\r\n// d=str.indexOf(s.substring(i,i+1),0);\r\n// if (base<=36 && d>=36) { //convert lowercase to uppercase if base<=36\r\n// d-=26;\r\n// }\r\n// if (d>=base || d<0) { //ignore illegal characters\r\n// continue;\r\n// }\r\n// multInt_(x,base);\r\n// addInt_(x,d);\r\n// }\r\n\r\n// for (k=x.length;k>0 && !x[k-1];k--); //strip off leading zeros\r\n// k=minSize>k+1 ? minSize : k+1;\r\n// y=new Array(k);\r\n// kk=k<x.length ? k : x.length;\r\n// for (i=0;i<kk;i++)\r\n// y[i]=x[i];\r\n// for (;i<k;i++)\r\n// y[i]=0;\r\n// return y;\r\n// }\r\n\r\n/**\r\n * is bigint x equal to integer y?\r\n *\r\n * y must have less than bpe bits\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} y\r\n * @returns {(1 | 0)}\r\n */\r\nexport function equalsInt(x: number[], y: number): Bool {\r\n var i\r\n if (x[0] != y) return 0\r\n for (i = 1; i < x.length; i++) if (x[i]) return 0\r\n return 1\r\n}\r\n\r\n/**\r\n * are bigints x and y equal?\r\n *\r\n * this works even if x and y are different lengths and have arbitrarily many leading zeros\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {(1 | 0)}\r\n */\r\nexport function equals(x: number[], y: number[]): Bool {\r\n var i\r\n var k = x.length < y.length ? x.length : y.length\r\n for (i = 0; i < k; i++) if (x[i] !== y[i]) return 0\r\n if (x.length > y.length) {\r\n for (; i < x.length; i++) if (x[i]) return 0\r\n } else {\r\n for (; i < y.length; i++) if (y[i]) return 0\r\n }\r\n return 1\r\n}\r\n\r\n/**\r\n * is the bigInt x equal to zero?\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @returns {(1 | 0)}\r\n */\r\nexport function isZero(x: number[]): Bool {\r\n var i\r\n for (i = 0; i < x.length; i++) if (x[i]) return 0\r\n return 1\r\n}\r\n\r\n/**\r\n * Convert a bigInt into a string in a given base, from base 2 up to base 95.\r\n *\r\n * Base -1 prints the contents of the array representing the number.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} base\r\n * @returns {string}\r\n */\r\nexport function bigInt2str(x: number[], base: number): string {\r\n var i,\r\n t,\r\n s = ''\r\n\r\n if (s6.length !== x.length) s6 = dup(x)\r\n else copy_(s6, x)\r\n\r\n if (base === -1) {\r\n //return the list of array contents\r\n for (i = x.length - 1; i > 0; i--) s += x[i] + ','\r\n s += x[0]\r\n } else {\r\n //return it in the given base\r\n while (!isZero(s6)) {\r\n t = divInt_(s6, base) //t=s6 % base; s6=floor(s6/base);\r\n s = digitsStr.substring(t, t + 1) + s\r\n }\r\n }\r\n if (s.length === 0) s = '0'\r\n return s\r\n}\r\n\r\n/**\r\n * Convert a bigInt into bytes\r\n * @param x bigInt\r\n * @param littleEndian byte order by default\r\n */\r\nexport function bigInt2bytes(x: number[], littleEndian = true) {\r\n if(s6.length !== x.length) s6 = dup(x);\r\n else copy_(s6, x);\r\n\r\n const out: number[] = [];\r\n\r\n //console.log('bigInt2bytes');\r\n while(!isZero(s6)) {\r\n t = divInt_(s6, 256); //t=s6 % base; s6=floor(s6/base);\r\n out.push(t);\r\n //console.log('bigInt2bytes', t);\r\n }\r\n\r\n if(littleEndian) {\r\n out.reverse();\r\n }\r\n\r\n //console.log('bigInt2bytes', out);\r\n\r\n return out;\r\n}\r\n\r\n/**\r\n * Compare two bigInts and return -1 if x is less, 0 if equals, 1 if greater\r\n * @param x bigInt\r\n * @param y bigInt\r\n */\r\nexport function cmp(x: number[], y: number[]) {\r\n return greater(x, y) ? 1 : (equals(x, y) ? 0 : -1);\r\n}\r\n\r\n/* Object.assign(self, {\r\n cmp,\r\n str2bigInt,\r\n int2bigInt,\r\n bigInt2str,\r\n one,\r\n divide_,\r\n divInt_,\r\n dup,\r\n negative\r\n}); */\r\n\r\n/**\r\n * Returns a duplicate of bigInt x\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @returns {number[]}\r\n */\r\nexport function dup(x: number[]): number[] {\r\n var i\r\n buff = Array(x.length)\r\n copy_(buff, x)\r\n return buff\r\n}\r\n\r\n/**\r\n * do x=y on bigInts x and y.\r\n *\r\n * x must be an array at least as big as y (not counting the leading zeros in y).\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @returns {void}\r\n */\r\nexport function copy_(x: number[], y: number[]): void {\r\n var i\r\n var k = x.length < y.length ? x.length : y.length\r\n for (i = 0; i < k; i++) x[i] = y[i]\r\n for (i = k; i < x.length; i++) x[i] = 0\r\n}\r\n\r\n/**\r\n * do x=y on bigInt x and integer y.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {void}\r\n */\r\nexport function copyInt_(x: number[], n: number): void {\r\n var i, c\r\n var len = x.length //TODO .length in for loop have perfomance costs. Bench this\r\n for (c = n, i = 0; i < len; i++) {\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x+n where x is a bigInt and n is an integer.\r\n *\r\n * x must be large enough to hold the result.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {void}\r\n */\r\nexport function addInt_(x: number[], n: number): void {\r\n var i, k, c, b\r\n x[0] += n\r\n k = x.length\r\n c = 0\r\n for (i = 0; i < k; i++) {\r\n c += x[i]\r\n b = 0\r\n if (c < 0) {\r\n b = -(c >> bpe)\r\n c += b * radix\r\n }\r\n x[i] = c & mask\r\n c = (c >> bpe) - b\r\n if (!c) return //stop carrying as soon as the carry is zero\r\n }\r\n}\r\n\r\n/**\r\n * right shift bigInt x by n bits.\r\n *\r\n * 0 <= n < bpe.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n */\r\nexport function rightShift_(x: number[], n: number): void {\r\n var i\r\n var k = Math.floor(n / bpe)\r\n if (k) {\r\n for (\r\n i = 0;\r\n i < x.length - k;\r\n i++ //right shift x by k elements\r\n )\r\n x[i] = x[i + k]\r\n for (; i < x.length; i++) x[i] = 0\r\n //$off\r\n n %= bpe\r\n }\r\n for (i = 0; i < x.length - 1; i++) {\r\n x[i] = mask & ((x[i + 1] << (bpe - n)) | (x[i] >> n))\r\n }\r\n x[i] >>= n\r\n}\r\n\r\n/**\r\n * do x=floor(|x|/2)*sgn(x) for bigInt x in 2's complement\r\n *\r\n * @param {number[]} x\r\n * @returns {void}\r\n */\r\nexport function halve_(x: number[]): void {\r\n var i\r\n for (i = 0; i < x.length - 1; i++) {\r\n x[i] = mask & ((x[i + 1] << (bpe - 1)) | (x[i] >> 1))\r\n }\r\n x[i] = (x[i] >> 1) | (x[i] & (radix >> 1)) //most significant bit stays the same\r\n}\r\n\r\n/**\r\n * left shift bigInt x by n bits\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {void}\r\n */\r\nexport function leftShift_(x: number[], n: number): void {\r\n var i\r\n var k = Math.floor(n / bpe)\r\n if (k) {\r\n for (\r\n i = x.length;\r\n i >= k;\r\n i-- //left shift x by k elements\r\n )\r\n x[i] = x[i - k]\r\n for (; i >= 0; i--) x[i] = 0\r\n //$off\r\n n %= bpe\r\n }\r\n if (!n) return\r\n for (i = x.length - 1; i > 0; i--) {\r\n x[i] = mask & ((x[i] << n) | (x[i - 1] >> (bpe - n)))\r\n }\r\n x[i] = mask & (x[i] << n)\r\n}\r\n\r\n/**\r\n * do x=x*n where x is a bigInt and n is an integer.\r\n *\r\n * x must be large enough to hold the result.\r\n *\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {void}\r\n */\r\nexport function multInt_(x: number[], n: number): void {\r\n var i, k, c, b\r\n if (!n) return\r\n k = x.length\r\n c = 0\r\n for (i = 0; i < k; i++) {\r\n c += x[i] * n\r\n b = 0\r\n if (c < 0) {\r\n b = -(c >> bpe)\r\n c += b * radix\r\n }\r\n x[i] = c & mask\r\n c = (c >> bpe) - b\r\n }\r\n}\r\n\r\n/**\r\n * do x=floor(x/n) for bigInt x and integer n, and return the remainder\r\n *\r\n * @param {number[]} x\r\n * @param {number} n\r\n * @returns {number} remainder\r\n */\r\nexport function divInt_(x: number[], n: number): number {\r\n var i,\r\n r = 0,\r\n s\r\n for (i = x.length - 1; i >= 0; i--) {\r\n s = r * radix + x[i]\r\n x[i] = Math.floor(s / n)\r\n r = s % n\r\n }\r\n return r\r\n}\r\n\r\n/**\r\n * do the linear combination x=a*x+b*y for bigInts x and y, and integers a and b.\r\n *\r\n * x must be large enough to hold the answer.\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number} a\r\n * @param {number} b\r\n * @returns {void}\r\n */\r\nexport function linComb_(x: number[], y: number[], a: number, b: number): void {\r\n var i, c, k, kk\r\n k = x.length < y.length ? x.length : y.length\r\n kk = x.length\r\n for (c = 0, i = 0; i < k; i++) {\r\n c += a * x[i] + b * y[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; i < kk; i++) {\r\n c += a * x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do the linear combination x=a*x+b*(y<<(ys*bpe)) for bigInts x and y, and integers a, b and ys.\r\n *\r\n * x must be large enough to hold the answer.\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number} b\r\n * @param {number} ys\r\n * @returns {void}\r\n */\r\nexport function linCombShift_(\r\n x: number[],\r\n y: number[],\r\n b: number,\r\n ys: number,\r\n): void {\r\n var i, c, k, kk\r\n k = x.length < ys + y.length ? x.length : ys + y.length\r\n kk = x.length\r\n for (c = 0, i = ys; i < k; i++) {\r\n c += x[i] + b * y[i - ys]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; c && i < kk; i++) {\r\n c += x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x+(y<<(ys*bpe)) for bigInts x and y, and integer ys.\r\n *\r\n * x must be large enough to hold the answer.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number} ys\r\n * @return {void}\r\n */\r\nexport function addShift_(x: number[], y: number[], ys: number): void {\r\n var i, c, k, kk\r\n k = x.length < ys + y.length ? x.length : ys + y.length\r\n kk = x.length\r\n for (c = 0, i = ys; i < k; i++) {\r\n c += x[i] + y[i - ys]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; c && i < kk; i++) {\r\n c += x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x-(y<<(ys*bpe)) for bigInts x and y, and integer ys\r\n *\r\n * x must be large enough to hold the answer\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number} ys\r\n * @return {void}\r\n */\r\nexport function subShift_(x: number[], y: number[], ys: number): void {\r\n var i, c, k, kk\r\n k = x.length < ys + y.length ? x.length : ys + y.length\r\n kk = x.length\r\n for (c = 0, i = ys; i < k; i++) {\r\n c += x[i] - y[i - ys]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; c && i < kk; i++) {\r\n c += x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x-y for bigInts x and y\r\n *\r\n * x must be large enough to hold the answer\r\n *\r\n * negative answers will be 2s complement\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @return {void}\r\n */\r\nexport function sub_(x: number[], y: number[]): void {\r\n var i, c, k, kk\r\n k = x.length < y.length ? x.length : y.length\r\n for (c = 0, i = 0; i < k; i++) {\r\n c += x[i] - y[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; c && i < x.length; i++) {\r\n c += x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x+y for bigInts x and y\r\n *\r\n * x must be large enough to hold the answer\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @return {void}\r\n */\r\nexport function add_(x: number[], y: number[]): void {\r\n var i, c, k, kk\r\n k = x.length < y.length ? x.length : y.length\r\n for (c = 0, i = 0; i < k; i++) {\r\n c += x[i] + y[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n for (i = k; c && i < x.length; i++) {\r\n c += x[i]\r\n x[i] = c & mask\r\n c >>= bpe\r\n }\r\n}\r\n\r\n/**\r\n * do x=x*y for bigInts x and y.\r\n *\r\n * This is faster when y<x.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @return {void}\r\n */\r\nexport function mult_(x: number[], y: number[]): void {\r\n var i\r\n if (ss.length != 2 * x.length) ss = new Array(2 * x.length)\r\n copyInt_(ss, 0)\r\n for (i = 0; i < y.length; i++) if (y[i]) linCombShift_(ss, x, y[i], i) //ss=1*ss+y[i]*(x<<(i*bpe))\r\n copy_(x, ss)\r\n}\r\n\r\n/**\r\n * do x=x mod n for bigInts x and n\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} n\r\n * @return {void}\r\n */\r\nexport function mod_(x: number[], n: number[]): void {\r\n if (s4.length !== x.length) s4 = dup(x)\r\n else copy_(s4, x)\r\n if (s5.length !== x.length) s5 = dup(x)\r\n divide_(s4, n, s5, x) //x = remainder of s4 / n\r\n}\r\n\r\n/**\r\n * do x=x*y mod n for bigInts x,y,n.\r\n *\r\n * for greater speed, let y<x.\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} n\r\n * @return {void}\r\n */\r\nexport function multMod_(x: number[], y: number[], n: number[]): void {\r\n var i\r\n if (s0.length != 2 * x.length) s0 = new Array(2 * x.length)\r\n copyInt_(s0, 0)\r\n for (i = 0; i < y.length; i++) if (y[i]) linCombShift_(s0, x, y[i], i) //s0=1*s0+y[i]*(x<<(i*bpe))\r\n mod_(s0, n)\r\n copy_(x, s0)\r\n}\r\n\r\n/**\r\n * do x=x*x mod n for bigInts x,n.\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} n\r\n * @return {void}\r\n */\r\nexport function squareMod_(x: number[], n: number[]): void {\r\n var i, j, d, c, kx, kn, k\r\n for (kx = x.length; kx > 0 && !x[kx - 1]; kx--); //ignore leading zeros in x\r\n k = kx > n.length ? 2 * kx : 2 * n.length //k=# elements in the product, which is twice the elements in the larger of x and n\r\n if (s0.length != k) s0 = new Array(k)\r\n copyInt_(s0, 0)\r\n for (i = 0; i < kx; i++) {\r\n c = s0[2 * i] + x[i] * x[i]\r\n s0[2 * i] = c & mask\r\n c >>= bpe\r\n for (j = i + 1; j < kx; j++) {\r\n c = s0[i + j] + 2 * x[i] * x[j] + c\r\n s0[i + j] = c & mask\r\n c >>= bpe\r\n }\r\n s0[i + kx] = c\r\n }\r\n mod_(s0, n)\r\n copy_(x, s0)\r\n}\r\n\r\n/**\r\n * return x with exactly k leading zero elements\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number} k\r\n * @returns {number[]}\r\n */\r\nexport function trim(x: number[], k: number): number[] {\r\n var i, y\r\n for (i = x.length; i > 0 && !x[i - 1]; i--);\r\n y = new Array(i + k)\r\n copy_(y, x)\r\n return y\r\n}\r\n\r\n/**\r\n * do `x=x**y mod n`, where x,y,n are bigInts and `**` is exponentiation. `0**0=1`.\r\n *\r\n * this is faster when n is odd.\r\n *\r\n * x usually needs to have as many elements as n.\r\n *\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} n\r\n * @return {void}\r\n */\r\nexport function powMod_(x: number[], y: number[], n: number[]): void {\r\n var k1, k2, kn, np\r\n if (s7.length != n.length) s7 = dup(n)\r\n\r\n //for even modulus, use a simple square-and-multiply algorithm,\r\n //rather than using the more complex Montgomery algorithm.\r\n if ((n[0] & 1) == 0) {\r\n copy_(s7, x)\r\n copyInt_(x, 1)\r\n while (!equalsInt(y, 0)) {\r\n if (y[0] & 1) multMod_(x, s7, n)\r\n divInt_(y, 2)\r\n squareMod_(s7, n)\r\n }\r\n return\r\n }\r\n\r\n //calculate np from n for the Montgomery multiplications\r\n copyInt_(s7, 0)\r\n for (kn = n.length; kn > 0 && !n[kn - 1]; kn--);\r\n np = radix - inverseModInt(modInt(n, radix), radix)\r\n s7[kn] = 1\r\n multMod_(x, s7, n) // x = x * 2**(kn*bp) mod n\r\n\r\n if (s3.length != x.length) s3 = dup(x)\r\n else copy_(s3, x)\r\n //$off\r\n // @ts-ignore\r\n for (k1 = y.length - 1; (k1 > 0) & !y[k1]; k1--); //k1=first nonzero element of y\r\n if (y[k1] == 0) {\r\n //anything to the 0th power is 1\r\n copyInt_(x, 1)\r\n return\r\n }\r\n for (k2 = 1 << (bpe - 1); k2 && !(y[k1] & k2); k2 >>= 1); //k2=position of first 1 bit in y[k1]\r\n for (;;) {\r\n if (!(k2 >>= 1)) {\r\n //look at next bit of y\r\n k1--\r\n if (k1 < 0) {\r\n mont_(x, one, n, np)\r\n return\r\n }\r\n k2 = 1 << (bpe - 1)\r\n }\r\n mont_(x, x, n, np)\r\n\r\n if (k2 & y[k1])\r\n //if next bit is a 1\r\n mont_(x, s3, n, np)\r\n }\r\n}\r\n\r\n/**\r\n * do x=x*y*Ri mod n for bigInts x,y,n,\r\n * where Ri = 2**(-kn*bpe) mod n, and kn is the\r\n * number of elements in the n array, not\r\n * counting leading zeros.\r\n *\r\n * x array must have at least as many elemnts as the n array\r\n * It's OK if x and y are the same variable.\r\n *\r\n * must have:\r\n * * x,y < n\r\n * * n is odd\r\n * * np = -(n^(-1)) mod radix\r\n *\r\n * @export\r\n * @param {number[]} x\r\n * @param {number[]} y\r\n * @param {number[]} n\r\n * @param {number} np\r\n * @return {void}\r\n */\r\nexport function mont_(x: number[], y: number[], n: number[], np: number): void {\r\n var i, j, c, ui, t, ks\r\n var kn = n.length\r\n var ky = y.length\r\n\r\n if (sa.length != kn) sa = new Array(kn)\r\n\r\n copyInt_(sa, 0)\r\n\r\n for (; kn > 0 && n[kn - 1] == 0; kn--); //ignore leading zeros of n\r\n for (; ky > 0 && y[ky - 1] == 0; ky--); //ignore leading zeros of y\r\n ks = sa.length - 1 //sa will never have more than this many nonzero elements.\r\n\r\n //the following loop consumes 95% of the runtime for randTruePrime_() and powMod_() for large numbers\r\n for (i = 0; i < kn; i++) {\r\n t = sa[0] + x[i] * y[0]\r\n ui = ((t & mask) * np) & mask //the inner \"& mask\" was needed on Safari (but not MSIE) at one time\r\n c = (t + ui * n[0]) >> bpe\r\n t = x[i]\r\n\r\n //do sa=(sa+x[i]*y+ui*n)/b where b=2**bpe. Loop is unrolled 5-fold for speed\r\n j = 1\r\n for (; j < ky - 4; ) {\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n }\r\n for (; j < ky; ) {\r\n c += sa[j] + ui * n[j] + t * y[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n }\r\n for (; j < kn - 4; ) {\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n }\r\n for (; j < kn; ) {\r\n c += sa[j] + ui * n[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n }\r\n for (; j < ks; ) {\r\n c += sa[j]\r\n sa[j - 1] = c & mask\r\n c >>= bpe\r\n j++\r\n }\r\n sa[j - 1] = c & mask\r\n }\r\n\r\n if (!greater(n, sa)) sub_(sa, n)\r\n copy_(x, sa)\r\n}","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { bufferConcats } from '../../helpers/bytes';\r\nimport { add_, bigInt2str, cmp, leftShift_, str2bigInt } from '../../vendor/leemon';\r\nimport { nextRandomInt } from '../../helpers/random';\r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\nexport function isObject(object: any) {\r\n return typeof(object) === 'object' && object !== null;\r\n}\r\n\r\n/* export function bigint(num: number) {\r\n return new BigInteger(num.toString(16), 16);\r\n} */\r\n\r\n/* export function bigStringInt(strNum: string) {\r\n return new BigInteger(strNum, 10);\r\n} */\r\n\r\n/* export function base64ToBlob(base64str: string, mimeType: string) {\r\n var sliceSize = 1024;\r\n var byteCharacters = atob(base64str);\r\n var bytesLength = byteCharacters.length;\r\n var slicesCount = Math.ceil(bytesLength / sliceSize);\r\n var byteArrays = new Array(slicesCount);\r\n\r\n for(var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {\r\n var begin = sliceIndex * sliceSize;\r\n var end = Math.min(begin + sliceSize, bytesLength);\r\n\r\n var bytes = new Array(end - begin);\r\n for(var offset = begin, i = 0; offset < end; ++i, ++offset) {\r\n bytes[i] = byteCharacters[offset].charCodeAt(0);\r\n }\r\n byteArrays[sliceIndex] = new Uint8Array(bytes);\r\n }\r\n\r\n return blobConstruct(byteArrays, mimeType);\r\n}\r\n\r\nexport function dataUrlToBlob(url: string) {\r\n // var name = 'b64blob ' + url.length\r\n // console.time(name)\r\n var urlParts = url.split(',');\r\n var base64str = urlParts[1];\r\n var mimeType = urlParts[0].split(':')[1].split(';')[0];\r\n var blob = base64ToBlob(base64str, mimeType);\r\n // console.timeEnd(name)\r\n return blob;\r\n} */\r\n\r\nexport function intToUint(val: number) {\r\n // return val < 0 ? val + 4294967296 : val; // 0 <= val <= Infinity\r\n return val >>> 0; // (4294967296 >>> 0) === 0; 0 <= val <= 4294967295\r\n}\r\n\r\n/* export function bytesFromBigInt(bigInt: BigInteger, len?: number) {\r\n var bytes = bigInt.toByteArray();\r\n\r\n if(len && bytes.length < len) {\r\n var padding = [];\r\n for(var i = 0, needPadding = len - bytes.length; i < needPadding; i++) {\r\n padding[i] = 0;\r\n }\r\n if(bytes instanceof ArrayBuffer) {\r\n bytes = bufferConcat(padding, bytes);\r\n } else {\r\n bytes = padding.concat(bytes);\r\n }\r\n } else {\r\n while (!bytes[0] && (!len || bytes.length > len)) {\r\n bytes = bytes.slice(1);\r\n }\r\n }\r\n\r\n return bytes;\r\n} */\r\n\r\nexport function longFromInts(high: number, low: number): string {\r\n //let perf = performance.now();\r\n //let str = bigint(high).shiftLeft(32).add(bigint(low)).toString(10);\r\n //console.log('longFromInts jsbn', performance.now() - perf);\r\n high = intToUint(high);\r\n low = intToUint(low);\r\n \r\n //perf = performance.now();\r\n const bigInt = str2bigInt(high.toString(16), 16, 32);//int2bigInt(high, 64, 64);\r\n //console.log('longFromInts construct high', bigint(high).toString(10), bigInt2str(bigInt, 10));\r\n leftShift_(bigInt, 32);\r\n //console.log('longFromInts shiftLeft', bigint(high).shiftLeft(32).toString(10), bigInt2str(bigInt, 10));\r\n add_(bigInt, str2bigInt(low.toString(16), 16, 32));\r\n const _str = bigInt2str(bigInt, 10);\r\n\r\n //console.log('longFromInts leemon', performance.now() - perf);\r\n\r\n //console.log('longFromInts', high, low, str, _str, str === _str);\r\n\r\n return _str;\r\n}\r\n\r\nexport function sortLongsArray(arr: string[]) {\r\n return arr.map(long => {\r\n return str2bigInt(long, 10);\r\n }).sort((a, b) => {\r\n return cmp(a, b);\r\n }).map(bigInt => {\r\n return bigInt2str(bigInt, 10);\r\n });\r\n}\r\n\r\nexport function addPadding<T extends number[] | ArrayBuffer | Uint8Array>(\r\n bytes: T, \r\n blockSize: number = 16, \r\n zeroes?: boolean, \r\n blockSizeAsTotalLength = false, \r\n prepend = false\r\n): T {\r\n const len = (bytes as ArrayBuffer).byteLength || (bytes as Uint8Array).length;\r\n const needPadding = blockSizeAsTotalLength ? blockSize - len : blockSize - (len % blockSize);\r\n if(needPadding > 0 && needPadding < blockSize) {\r\n ////console.log('addPadding()', len, blockSize, needPadding);\r\n const padding: number[] = new Array(needPadding);\r\n if(zeroes) {\r\n for(let i = 0; i < needPadding; ++i) {\r\n padding[i] = 0;\r\n }\r\n } else {\r\n for(let i = 0; i < needPadding; ++i) {\r\n padding[i] = nextRandomInt(255);\r\n }\r\n }\r\n\r\n if(bytes instanceof ArrayBuffer) {\r\n return (prepend ? bufferConcats(padding, bytes) : bufferConcats(bytes, padding)).buffer as T;\r\n } else if(bytes instanceof Uint8Array) {\r\n return (prepend ? bufferConcats(padding, bytes) : bufferConcats(bytes, padding)) as T;\r\n } else {\r\n // @ts-ignore\r\n return (prepend ? padding.concat(bytes) : bytes.concat(padding)) as T;\r\n }\r\n }\r\n\r\n return bytes;\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport type { Chat, DialogPeer, Message, MessagesPeerDialogs, Update } from \"../../layer\";\r\nimport type { AppChatsManager } from \"../appManagers/appChatsManager\";\r\nimport type { AppMessagesManager, Dialog, MyMessage } from \"../appManagers/appMessagesManager\";\r\nimport type { AppPeersManager } from \"../appManagers/appPeersManager\";\r\nimport type { AppUsersManager } from \"../appManagers/appUsersManager\";\r\nimport type { AppDraftsManager } from \"../appManagers/appDraftsManager\";\r\nimport type { AppNotificationsManager } from \"../appManagers/appNotificationsManager\";\r\nimport type { ApiUpdatesManager } from \"../appManagers/apiUpdatesManager\";\r\nimport type { ServerTimeManager } from \"../mtproto/serverTimeManager\";\r\nimport { tsNow } from \"../../helpers/date\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport SearchIndex from \"../searchIndex\";\r\nimport { forEachReverse, insertInDescendSortedArray } from \"../../helpers/array\";\r\nimport rootScope from \"../rootScope\";\r\nimport { safeReplaceObject } from \"../../helpers/object\";\r\nimport { AppStateManager } from \"../appManagers/appStateManager\";\r\nimport { SliceEnd } from \"../../helpers/slicedArray\";\r\n\r\nexport default class DialogsStorage {\r\n private storage: AppStateManager['storages']['dialogs'];\r\n \r\n private dialogs: {[peerId: string]: Dialog};\r\n public byFolders: {[folderId: number]: Dialog[]};\r\n\r\n private allDialogsLoaded: {[folder_id: number]: boolean};\r\n private dialogsOffsetDate: {[folder_id: number]: number};\r\n private pinnedOrders: {[folder_id: number]: number[]};\r\n private dialogsNum: number;\r\n\r\n private dialogsIndex: SearchIndex<number>;\r\n\r\n private cachedResults: {\r\n query: string,\r\n count: number,\r\n dialogs: Dialog[],\r\n folderId: number\r\n };\r\n\r\n constructor(private appMessagesManager: AppMessagesManager, \r\n private appChatsManager: AppChatsManager, \r\n private appPeersManager: AppPeersManager, \r\n private appUsersManager: AppUsersManager,\r\n private appDraftsManager: AppDraftsManager,\r\n private appNotificationsManager: AppNotificationsManager,\r\n private appStateManager: AppStateManager,\r\n private apiUpdatesManager: ApiUpdatesManager,\r\n private serverTimeManager: ServerTimeManager\r\n ) {\r\n this.storage = this.appStateManager.storages.dialogs;\r\n this.dialogs = this.storage.getCache();\r\n this.clear(true);\r\n\r\n rootScope.addEventListener('language_change', (e) => {\r\n const peerId = appUsersManager.getSelf().id;\r\n const dialog = this.getDialogOnly(peerId);\r\n if(dialog) {\r\n const peerText = appPeersManager.getPeerSearchText(peerId);\r\n this.dialogsIndex.indexObject(peerId, peerText);\r\n }\r\n });\r\n\r\n rootScope.addMultipleEventsListeners({\r\n updateFolderPeers: this.onUpdateFolderPeers,\r\n\r\n updateDialogPinned: this.onUpdateDialogPinned,\r\n\r\n updatePinnedDialogs: this.onUpdatePinnedDialogs,\r\n });\r\n\r\n appStateManager.getState().then((state) => {\r\n this.pinnedOrders = state.pinnedOrders || {};\r\n if(!this.pinnedOrders[0]) this.pinnedOrders[0] = [];\r\n if(!this.pinnedOrders[1]) this.pinnedOrders[1] = [];\r\n \r\n const dialogs = appStateManager.storagesResults.dialogs;\r\n if(dialogs.length) {\r\n for(let i = 0, length = dialogs.length; i < length; ++i) {\r\n const dialog = dialogs[i];\r\n if(dialog) {\r\n dialog.top_message = this.appMessagesManager.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog\r\n\r\n if(dialog.topMessage) {\r\n this.appMessagesManager.saveMessages([dialog.topMessage]);\r\n }\r\n \r\n this.saveDialog(dialog, undefined, true);\r\n\r\n // ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись\r\n const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);\r\n if(message.deleted) {\r\n this.appMessagesManager.reloadConversation(dialog.peerId);\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.allDialogsLoaded = state.allDialogsLoaded || {};\r\n });\r\n }\r\n\r\n public isDialogsLoaded(folderId: number) {\r\n return !!this.allDialogsLoaded[folderId];\r\n }\r\n\r\n public setDialogsLoaded(folderId: number, loaded: boolean) {\r\n this.allDialogsLoaded[folderId] = loaded;\r\n this.appStateManager.pushToState('allDialogsLoaded', this.allDialogsLoaded);\r\n }\r\n\r\n public clear(init = false) {\r\n if(!init) {\r\n const dialogs = this.appStateManager.storagesResults.dialogs;\r\n dialogs.length = 0;\r\n this.storage.clear();\r\n }\r\n\r\n this.byFolders = {};\r\n this.allDialogsLoaded = {};\r\n this.dialogsOffsetDate = {};\r\n this.pinnedOrders = {\r\n 0: [],\r\n 1: []\r\n };\r\n this.dialogsNum = 0;\r\n this.dialogsIndex = new SearchIndex<number>();\r\n this.cachedResults = {\r\n query: '',\r\n count: 0,\r\n dialogs: [],\r\n folderId: 0\r\n };\r\n }\r\n\r\n public resetPinnedOrder(folderId: number) {\r\n this.pinnedOrders[folderId] = [];\r\n }\r\n\r\n public getPinnedOrders(folderId: number) {\r\n return this.pinnedOrders[folderId];\r\n }\r\n\r\n public getOffsetDate(folderId: number) {\r\n return this.dialogsOffsetDate[folderId] || 0;\r\n }\r\n\r\n public getFolder(id: number, skipMigrated = true) {\r\n if(id <= 1) {\r\n const arr = this.byFolders[id] ?? (this.byFolders[id] = []);\r\n return skipMigrated ? arr.filter(dialog => dialog.migratedTo === undefined) : arr;\r\n }\r\n\r\n const dialogs: {dialog: Dialog, index: number}[] = [];\r\n const filter = this.appMessagesManager.filtersStorage.filters[id];\r\n\r\n for(const peerId in this.dialogs) {\r\n const dialog = this.dialogs[peerId];\r\n if(this.appMessagesManager.filtersStorage.testDialogForFilter(dialog, filter) && (!skipMigrated || dialog.migratedTo === undefined)) {\r\n let index: number;\r\n\r\n const pinnedIndex = filter.pinned_peers.indexOf(dialog.peerId);\r\n if(pinnedIndex !== -1) {\r\n index = this.generateDialogIndex(this.generateDialogPinnedDateByIndex(filter.pinned_peers.length - 1 - pinnedIndex));\r\n } else if(dialog.pFlags?.pinned) {\r\n index = this.generateIndexForDialog(dialog, true);\r\n } else {\r\n index = dialog.index;\r\n }\r\n\r\n dialogs.push({dialog, index});\r\n }\r\n }\r\n\r\n dialogs.sort((a, b) => b.index - a.index);\r\n return dialogs.map(d => d.dialog);\r\n }\r\n\r\n public getDialog(peerId: number, folderId?: number, skipMigrated = true): [Dialog, number] | [] {\r\n const folders: Dialog[][] = [];\r\n\r\n if(folderId === undefined) {\r\n const dialogs = this.byFolders;\r\n for(const folderId in dialogs) {\r\n folders.push(dialogs[folderId]);\r\n }\r\n } else {\r\n folders.push(this.getFolder(folderId, skipMigrated));\r\n }\r\n\r\n for(let folder of folders) {\r\n let i = 0, skipped = 0;\r\n for(let length = folder.length; i < length; ++i) {\r\n const dialog = folder[i];\r\n if(dialog.peerId === peerId) {\r\n return [dialog, i - skipped];\r\n } else if(skipMigrated && dialog.migratedTo !== undefined) {\r\n ++skipped;\r\n }\r\n }\r\n }\r\n\r\n return [];\r\n }\r\n\r\n public getDialogOnly(peerId: number) {\r\n return this.dialogs[peerId];\r\n }\r\n\r\n /*\r\n var date = Date.now() / 1000 | 0;\r\n var m = date * 0x10000;\r\n\r\n var k = (date + 1) * 0x10000;\r\n k - m;\r\n 65536\r\n */\r\n public generateDialogIndex(date?: number) {\r\n if(date === undefined) {\r\n date = tsNow(true) + this.serverTimeManager.serverTimeOffset;\r\n }\r\n\r\n return (date * 0x10000) + ((++this.dialogsNum) & 0xFFFF);\r\n }\r\n\r\n public generateIndexForDialog(dialog: Dialog, justReturn = false, message?: MyMessage) {\r\n const channelId = this.appPeersManager.isChannel(dialog.peerId) ? -dialog.peerId : 0;\r\n \r\n let topDate = 0;\r\n if(dialog.pFlags.pinned && !justReturn) {\r\n topDate = this.generateDialogPinnedDate(dialog);\r\n } else {\r\n if(!message) {\r\n message = this.appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message);\r\n }\r\n\r\n topDate = (message as Message.message).date || topDate;\r\n if(channelId) {\r\n const channel = this.appChatsManager.getChat(channelId);\r\n if(!topDate || (channel.date && channel.date > topDate)) {\r\n topDate = channel.date;\r\n }\r\n }\r\n \r\n if(dialog.draft?._ === 'draftMessage' && dialog.draft.date > topDate) {\r\n topDate = dialog.draft.date;\r\n }\r\n }\r\n\r\n if(!topDate) {\r\n topDate = Date.now() / 1000;\r\n }\r\n\r\n const index = this.generateDialogIndex(topDate);\r\n if(justReturn) return index;\r\n dialog.index = index;\r\n }\r\n\r\n public generateDialogPinnedDateByIndex(pinnedIndex: number) {\r\n return 0x7fff0000 + (pinnedIndex & 0xFFFF); // 0xFFFF - потому что в папках может быть бесконечное число пиннедов\r\n }\r\n\r\n public generateDialogPinnedDate(dialog: Dialog) {\r\n const order = this.pinnedOrders[dialog.folder_id];\r\n\r\n const foundIndex = order.indexOf(dialog.peerId);\r\n let pinnedIndex = foundIndex;\r\n if(foundIndex === -1) {\r\n pinnedIndex = order.push(dialog.peerId) - 1;\r\n this.appStateManager.pushToState('pinnedOrders', this.pinnedOrders);\r\n }\r\n\r\n return this.generateDialogPinnedDateByIndex(pinnedIndex);\r\n }\r\n\r\n /* public generateDialog(peerId: number) {\r\n const dialog: Dialog = {\r\n _: 'dialog',\r\n pFlags: {},\r\n peer: this.appPeersManager.getOutputPeer(peerId),\r\n top_message: 0,\r\n read_inbox_max_id: 0,\r\n read_outbox_max_id: 0,\r\n unread_count: 0,\r\n unread_mentions_count: 0,\r\n notify_settings: {\r\n _: 'peerNotifySettings',\r\n },\r\n };\r\n\r\n return dialog;\r\n } */\r\n\r\n public setDialogToState(dialog: Dialog) {\r\n const historyStorage = this.appMessagesManager.getHistoryStorage(dialog.peerId);\r\n const history = [].concat(historyStorage.history.slice);\r\n let incomingMessage: any;\r\n for(let i = 0, length = history.length; i < length; ++i) {\r\n const mid = history[i];\r\n const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, mid);\r\n if(!message.pFlags.is_outgoing) {\r\n incomingMessage = message;\r\n \r\n const fromId = message.viaBotId || message.fromId;\r\n if(fromId !== dialog.peerId) {\r\n this.appStateManager.requestPeer(fromId, 'topMessage_' + dialog.peerId, 1);\r\n }\r\n\r\n break;\r\n }\r\n }\r\n\r\n dialog.topMessage = incomingMessage;\r\n\r\n if(dialog.peerId < 0 && dialog.pts) {\r\n const newPts = this.apiUpdatesManager.getChannelState(-dialog.peerId, dialog.pts).pts;\r\n dialog.pts = newPts;\r\n }\r\n\r\n this.storage.set({\r\n [dialog.peerId]: dialog\r\n });\r\n\r\n this.appStateManager.requestPeer(dialog.peerId, 'dialog_' + dialog.peerId, 1);\r\n }\r\n\r\n public pushDialog(dialog: Dialog, offsetDate?: number) {\r\n const dialogs = this.getFolder(dialog.folder_id, false);\r\n const pos = dialogs.findIndex(d => d.peerId === dialog.peerId);\r\n if(pos !== -1) {\r\n dialogs.splice(pos, 1);\r\n }\r\n\r\n //if(!this.dialogs[dialog.peerId]) {\r\n this.dialogs[dialog.peerId] = dialog;\r\n\r\n this.setDialogToState(dialog);\r\n //}\r\n\r\n if(offsetDate &&\r\n !dialog.pFlags.pinned &&\r\n (!this.dialogsOffsetDate[dialog.folder_id] || offsetDate < this.dialogsOffsetDate[dialog.folder_id])) {\r\n if(pos !== -1) {\r\n // So the dialog jumped to the last position\r\n return false;\r\n }\r\n this.dialogsOffsetDate[dialog.folder_id] = offsetDate;\r\n }\r\n\r\n insertInDescendSortedArray(dialogs, dialog, 'index', pos);\r\n }\r\n\r\n public dropDialog(peerId: number): [Dialog, number] | [] {\r\n const foundDialog = this.getDialog(peerId, undefined, false);\r\n if(foundDialog[0]) {\r\n this.byFolders[foundDialog[0].folder_id].splice(foundDialog[1], 1);\r\n this.pinnedOrders[foundDialog[0].folder_id].findAndSplice(_peerId => peerId === _peerId);\r\n this.dialogsIndex.indexObject(peerId, '');\r\n delete this.dialogs[peerId];\r\n\r\n // clear from state\r\n this.appStateManager.keepPeerSingle(0, 'topMessage_' + peerId);\r\n this.appStateManager.keepPeerSingle(0, 'dialog_' + peerId);\r\n this.storage.delete(peerId);\r\n }\r\n\r\n return foundDialog;\r\n }\r\n\r\n public applyDialogs(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) {\r\n // * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages\r\n\r\n // ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0\r\n forEachReverse(dialogsResult.dialogs, (dialog, idx) => {\r\n if(dialog._ === 'dialogFolder') {\r\n dialogsResult.dialogs.splice(idx, 1);\r\n }\r\n });\r\n\r\n this.appUsersManager.saveApiUsers(dialogsResult.users);\r\n this.appChatsManager.saveApiChats(dialogsResult.chats);\r\n this.appMessagesManager.saveMessages(dialogsResult.messages);\r\n\r\n this.appMessagesManager.log('applyConversation', dialogsResult);\r\n\r\n const updatedDialogs: {[peerId: number]: Dialog} = {};\r\n (dialogsResult.dialogs as Dialog[]).forEach((dialog) => {\r\n const peerId = this.appPeersManager.getPeerId(dialog.peer);\r\n let topMessage = dialog.top_message;\r\n\r\n const topPendingMessage = this.appMessagesManager.pendingTopMsgs[peerId];\r\n if(topPendingMessage) {\r\n if(!topMessage \r\n || (this.appMessagesManager.getMessageByPeer(peerId, topPendingMessage) as MyMessage).date > (this.appMessagesManager.getMessageByPeer(peerId, topMessage) as MyMessage).date) {\r\n dialog.top_message = topMessage = topPendingMessage;\r\n this.appMessagesManager.getHistoryStorage(peerId).maxId = topPendingMessage;\r\n }\r\n }\r\n\r\n /* const d = Object.assign({}, dialog);\r\n if(peerId === 239602833) {\r\n this.log.error('applyConversation lun', dialog, d);\r\n } */\r\n\r\n if(topMessage || (dialog.draft && dialog.draft._ === 'draftMessage')) {\r\n this.saveDialog(dialog);\r\n updatedDialogs[peerId] = dialog;\r\n } else {\r\n const dropped = this.dropDialog(peerId);\r\n if(dropped.length) {\r\n rootScope.dispatchEvent('dialog_drop', {peerId, dialog: dropped[0]});\r\n }\r\n }\r\n\r\n const updates = this.appMessagesManager.newUpdatesAfterReloadToHandle[peerId];\r\n if(updates !== undefined) {\r\n for(const update of updates) {\r\n this.apiUpdatesManager.saveUpdate(update);\r\n }\r\n\r\n delete this.appMessagesManager.newUpdatesAfterReloadToHandle[peerId];\r\n }\r\n });\r\n\r\n if(Object.keys(updatedDialogs).length) {\r\n rootScope.dispatchEvent('dialogs_multiupdate', updatedDialogs);\r\n }\r\n }\r\n\r\n /**\r\n * Won't save migrated from peer, forbidden peers, left and kicked\r\n */\r\n public saveDialog(dialog: Dialog, folderId = 0, saveOffset = false) {\r\n const peerId = this.appPeersManager.getPeerId(dialog.peer);\r\n if(!peerId) {\r\n console.error('saveConversation no peerId???', dialog, folderId);\r\n return;\r\n }\r\n\r\n if(dialog._ !== 'dialog'/* || peerId === 239602833 */) {\r\n console.error('saveConversation not regular dialog', dialog, Object.assign({}, dialog));\r\n }\r\n \r\n const channelId = this.appPeersManager.isChannel(peerId) ? -peerId : 0;\r\n\r\n if(peerId < 0) {\r\n const chat: Chat = this.appChatsManager.getChat(-peerId);\r\n // ! chatForbidden stays for chat where you're kicked\r\n if(chat._ === 'channelForbidden' /* || chat._ === 'chatForbidden' */ || (chat as Chat.chat).pFlags.left || (chat as Chat.chat).pFlags.kicked) {\r\n return;\r\n }\r\n }\r\n\r\n const peerText = this.appPeersManager.getPeerSearchText(peerId);\r\n this.dialogsIndex.indexObject(peerId, peerText);\r\n\r\n let mid: number, message;\r\n if(dialog.top_message) {\r\n mid = this.appMessagesManager.generateMessageId(dialog.top_message);//dialog.top_message;\r\n message = this.appMessagesManager.getMessageByPeer(peerId, mid);\r\n } else {\r\n mid = this.appMessagesManager.generateTempMessageId(peerId);\r\n message = {\r\n _: 'message',\r\n id: mid,\r\n mid,\r\n from_id: this.appPeersManager.getOutputPeer(this.appUsersManager.getSelf().id),\r\n peer_id: this.appPeersManager.getOutputPeer(peerId),\r\n deleted: true,\r\n pFlags: {out: true},\r\n date: 0,\r\n message: ''\r\n };\r\n this.appMessagesManager.saveMessages([message], {isOutgoing: true});\r\n }\r\n\r\n if(!message?.pFlags) {\r\n this.appMessagesManager.log.error('saveConversation no message:', dialog, message);\r\n }\r\n\r\n if(!channelId && peerId < 0) {\r\n const chat = this.appChatsManager.getChat(-peerId);\r\n if(chat && chat.migrated_to && chat.pFlags.deactivated) {\r\n const migratedToPeer = this.appPeersManager.getPeerId(chat.migrated_to);\r\n this.appMessagesManager.migratedFromTo[peerId] = migratedToPeer;\r\n this.appMessagesManager.migratedToFrom[migratedToPeer] = peerId;\r\n dialog.migratedTo = migratedToPeer;\r\n //return;\r\n }\r\n }\r\n\r\n const wasDialogBefore = this.getDialogOnly(peerId);\r\n\r\n dialog.top_message = mid;\r\n dialog.read_inbox_max_id = this.appMessagesManager.generateMessageId(wasDialogBefore && !dialog.read_inbox_max_id ? wasDialogBefore.read_inbox_max_id : dialog.read_inbox_max_id);\r\n dialog.read_outbox_max_id = this.appMessagesManager.generateMessageId(wasDialogBefore && !dialog.read_outbox_max_id ? wasDialogBefore.read_outbox_max_id : dialog.read_outbox_max_id);\r\n\r\n if(!dialog.hasOwnProperty('folder_id')) {\r\n if(dialog._ === 'dialog') {\r\n // ! СЛОЖНО ! СМОТРИ В getTopMessages\r\n dialog.folder_id = wasDialogBefore ? wasDialogBefore.folder_id : folderId;\r\n }/* else if(dialog._ === 'dialogFolder') {\r\n dialog.folder_id = dialog.folder.id;\r\n } */\r\n }\r\n\r\n dialog.draft = this.appDraftsManager.saveDraft(peerId, 0, dialog.draft);\r\n dialog.peerId = peerId;\r\n\r\n // Because we saved message without dialog present\r\n if(message.pFlags.is_outgoing) {\r\n if(mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true;\r\n else delete message.pFlags.unread;\r\n }\r\n\r\n const historyStorage = this.appMessagesManager.getHistoryStorage(peerId);\r\n const slice = historyStorage.history.slice;\r\n /* if(historyStorage === undefined) { // warning\r\n historyStorage.history.push(mid);\r\n } else */if(!slice.length) {\r\n historyStorage.history.unshift(mid);\r\n if(this.appMessagesManager.mergeReplyKeyboard(historyStorage, message)) {\r\n rootScope.dispatchEvent('history_reply_markup', {peerId});\r\n }\r\n } else if(!slice.isEnd(SliceEnd.Bottom)) { // * this will probably never happen, however, if it does, then it will fix slice with top_message\r\n const slice = historyStorage.history.insertSlice([mid]);\r\n slice.setEnd(SliceEnd.Bottom);\r\n if(this.appMessagesManager.mergeReplyKeyboard(historyStorage, message)) {\r\n rootScope.dispatchEvent('history_reply_markup', {peerId});\r\n }\r\n }\r\n\r\n historyStorage.maxId = mid;\r\n historyStorage.readMaxId = dialog.read_inbox_max_id;\r\n historyStorage.readOutboxMaxId = dialog.read_outbox_max_id;\r\n\r\n this.appNotificationsManager.savePeerSettings(peerId, dialog.notify_settings);\r\n\r\n if(channelId && dialog.pts) {\r\n this.apiUpdatesManager.addChannelState(channelId, dialog.pts);\r\n }\r\n\r\n this.generateIndexForDialog(dialog);\r\n\r\n if(wasDialogBefore) {\r\n safeReplaceObject(wasDialogBefore, dialog);\r\n }\r\n\r\n this.pushDialog(dialog, saveOffset && message.date);\r\n }\r\n\r\n public getDialogs(query = '', offsetIndex?: number, limit = 20, folderId = 0) {\r\n const realFolderId = folderId > 1 ? 0 : folderId;\r\n let curDialogStorage = this.getFolder(folderId, false);\r\n\r\n if(query) {\r\n if(!limit || this.cachedResults.query !== query || this.cachedResults.folderId !== folderId) {\r\n this.cachedResults.query = query;\r\n this.cachedResults.folderId = folderId;\r\n\r\n const results = this.dialogsIndex.search(query);\r\n\r\n this.cachedResults.dialogs = [];\r\n\r\n for(const peerId in this.dialogs) {\r\n const dialog = this.dialogs[peerId];\r\n if(results.has(dialog.peerId) && dialog.folder_id === folderId) {\r\n this.cachedResults.dialogs.push(dialog);\r\n }\r\n }\r\n\r\n this.cachedResults.dialogs.sort((d1, d2) => d2.index - d1.index);\r\n\r\n this.cachedResults.count = this.cachedResults.dialogs.length;\r\n }\r\n\r\n curDialogStorage = this.cachedResults.dialogs;\r\n } else {\r\n this.cachedResults.query = '';\r\n }\r\n\r\n let offset = 0;\r\n if(offsetIndex > 0) {\r\n for(; offset < curDialogStorage.length; offset++) {\r\n if(offsetIndex > curDialogStorage[offset].index) {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n const loadedAll = this.isDialogsLoaded(realFolderId);\r\n if(query || loadedAll || curDialogStorage.length >= offset + limit) {\r\n return Promise.resolve({\r\n dialogs: curDialogStorage.slice(offset, offset + limit),\r\n count: loadedAll ? curDialogStorage.length : null,\r\n isEnd: loadedAll && (offset + limit) >= curDialogStorage.length\r\n });\r\n }\r\n\r\n return this.appMessagesManager.getTopMessages(limit, realFolderId).then(result => {\r\n //const curDialogStorage = this[folderId];\r\n\r\n offset = 0;\r\n if(offsetIndex > 0) {\r\n for(; offset < curDialogStorage.length; offset++) {\r\n if(offsetIndex > curDialogStorage[offset].index) {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n //this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogs.length);\r\n\r\n return {\r\n dialogs: curDialogStorage.slice(offset, offset + limit),\r\n count: result.count === undefined ? curDialogStorage.length : result.count,\r\n // isEnd: this.isDialogsLoaded(realFolderId) && (offset + limit) >= curDialogStorage.length\r\n isEnd: result.isEnd\r\n };\r\n });\r\n }\r\n\r\n // only 0 and 1 folders\r\n private onUpdateFolderPeers = (update: Update.updateFolderPeers) => {\r\n //this.log('updateFolderPeers', update);\r\n const peers = update.folder_peers;\r\n\r\n peers.forEach((folderPeer) => {\r\n const {folder_id, peer} = folderPeer;\r\n\r\n const peerId = this.appPeersManager.getPeerId(peer);\r\n const dialog = this.dropDialog(peerId)[0];\r\n if(dialog) {\r\n if(dialog.pFlags?.pinned) {\r\n delete dialog.pFlags.pinned;\r\n this.pinnedOrders[folder_id].findAndSplice(p => p === dialog.peerId);\r\n this.appStateManager.pushToState('pinnedOrders', this.pinnedOrders);\r\n }\r\n\r\n dialog.folder_id = folder_id;\r\n this.generateIndexForDialog(dialog);\r\n this.pushDialog(dialog); // need for simultaneously updatePinnedDialogs\r\n }\r\n\r\n this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog);\r\n });\r\n };\r\n\r\n private onUpdateDialogPinned = (update: Update.updateDialogPinned) => {\r\n const folderId = update.folder_id ?? 0;\r\n //this.log('updateDialogPinned', update);\r\n const peerId = this.appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer);\r\n const dialog = this.getDialogOnly(peerId);\r\n\r\n // этот код внизу никогда не сработает, в папках за пиннед отвечает updateDialogFilter\r\n /* if(update.folder_id > 1) {\r\n const filter = this.filtersStorage.filters[update.folder_id];\r\n if(update.pFlags.pinned) {\r\n filter.pinned_peers.unshift(peerId);\r\n } else {\r\n filter.pinned_peers.findAndSplice(p => p === peerId);\r\n }\r\n } */\r\n\r\n if(dialog) {\r\n if(!update.pFlags.pinned) {\r\n delete dialog.pFlags.pinned;\r\n this.pinnedOrders[folderId].findAndSplice(p => p === dialog.peerId);\r\n this.appStateManager.pushToState('pinnedOrders', this.pinnedOrders);\r\n } else { // means set\r\n dialog.pFlags.pinned = true;\r\n }\r\n\r\n this.generateIndexForDialog(dialog);\r\n } \r\n\r\n this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog);\r\n };\r\n\r\n private onUpdatePinnedDialogs = (update: Update.updatePinnedDialogs) => {\r\n const folderId = update.folder_id ?? 0;\r\n \r\n const handleOrder = (order: number[]) => {\r\n this.pinnedOrders[folderId].length = 0;\r\n order.reverse(); // index must be higher\r\n order.forEach((peerId) => {\r\n newPinned[peerId] = true;\r\n \r\n const dialog = this.getDialogOnly(peerId);\r\n this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog);\r\n if(!dialog) {\r\n return;\r\n }\r\n \r\n dialog.pFlags.pinned = true;\r\n this.generateIndexForDialog(dialog);\r\n });\r\n \r\n this.getFolder(folderId, false).forEach(dialog => {\r\n const peerId = dialog.peerId;\r\n if(dialog.pFlags.pinned && !newPinned[peerId]) {\r\n this.appMessagesManager.scheduleHandleNewDialogs(peerId);\r\n }\r\n });\r\n };\r\n\r\n //this.log('updatePinnedDialogs', update);\r\n const newPinned: {[peerId: number]: true} = {};\r\n if(!update.order) {\r\n apiManager.invokeApi('messages.getPinnedDialogs', {\r\n folder_id: folderId\r\n }).then((dialogsResult) => {\r\n // * for test reordering and rendering\r\n // dialogsResult.dialogs.reverse();\r\n\r\n this.applyDialogs(dialogsResult);\r\n\r\n handleOrder(dialogsResult.dialogs.map(d => d.peerId));\r\n\r\n /* dialogsResult.dialogs.forEach((dialog) => {\r\n newPinned[dialog.peerId] = true;\r\n });\r\n\r\n this.dialogsStorage.getFolder(folderId).forEach((dialog) => {\r\n const peerId = dialog.peerId;\r\n if(dialog.pFlags.pinned && !newPinned[peerId]) {\r\n this.newDialogsToHandle[peerId] = {reload: true};\r\n this.scheduleHandleNewDialogs();\r\n }\r\n }); */\r\n });\r\n\r\n return;\r\n }\r\n\r\n //this.log('before order:', this.dialogsStorage[0].map(d => d.peerId));\r\n\r\n handleOrder(update.order.map(peer => this.appPeersManager.getPeerId((peer as DialogPeer.dialogPeer).peer)));\r\n };\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { copy, safeReplaceObject } from \"../../helpers/object\";\r\nimport type { DialogFilter, Update } from \"../../layer\";\r\nimport type { Modify } from \"../../types\";\r\nimport type { AppPeersManager } from \"../appManagers/appPeersManager\";\r\nimport type { AppUsersManager } from \"../appManagers/appUsersManager\";\r\n//import type { ApiManagerProxy } from \"../mtproto/mtprotoworker\";\r\nimport type _rootScope from \"../rootScope\";\r\nimport type {AppMessagesManager, Dialog} from '../appManagers/appMessagesManager';\r\nimport type {AppNotificationsManager} from \"../appManagers/appNotificationsManager\";\r\nimport type { ApiUpdatesManager } from \"../appManagers/apiUpdatesManager\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport { forEachReverse } from \"../../helpers/array\";\r\nimport { AppStateManager } from \"../appManagers/appStateManager\";\r\n\r\nexport type MyDialogFilter = Modify<DialogFilter, {\r\n pinned_peers: number[],\r\n include_peers: number[],\r\n exclude_peers: number[],\r\n orderIndex?: number\r\n}>;\r\n\r\n// ! because 0 index is 'All Chats'\r\nconst START_ORDER_INDEX = 1;\r\n\r\nexport default class FiltersStorage {\r\n public filters: {[filterId: string]: MyDialogFilter};\r\n private orderIndex: number;\r\n\r\n constructor(private appMessagesManager: AppMessagesManager,\r\n private appPeersManager: AppPeersManager, \r\n private appUsersManager: AppUsersManager, \r\n private appNotificationsManager: AppNotificationsManager, \r\n private appStateManager: AppStateManager,\r\n private apiUpdatesManager: ApiUpdatesManager, \r\n /* private apiManager: ApiManagerProxy, */ \r\n private rootScope: typeof _rootScope) {\r\n this.clear();\r\n this.filters = {};\r\n\r\n this.appStateManager.getState().then((state) => {\r\n safeReplaceObject(this.filters, state.filters);\r\n\r\n for(const filterId in this.filters) {\r\n const filter = this.filters[filterId];\r\n if(filter.hasOwnProperty('orderIndex') && filter.orderIndex >= this.orderIndex) {\r\n this.orderIndex = filter.orderIndex + 1;\r\n }\r\n }\r\n });\r\n\r\n rootScope.addMultipleEventsListeners({\r\n updateDialogFilter: this.onUpdateDialogFilter,\r\n\r\n updateDialogFilters: (update) => {\r\n //console.warn('updateDialogFilters', update);\r\n\r\n const oldFilters = copy(this.filters);\r\n\r\n this.getDialogFilters(true).then(filters => {\r\n for(const _filterId in oldFilters) {\r\n const filterId = +_filterId;\r\n if(!filters.find(filter => filter.id === filterId)) { // * deleted\r\n this.onUpdateDialogFilter({_: 'updateDialogFilter', id: filterId});\r\n }\r\n }\r\n\r\n this.onUpdateDialogFilterOrder({_: 'updateDialogFilterOrder', order: filters.map(filter => filter.id)});\r\n });\r\n },\r\n\r\n updateDialogFilterOrder: this.onUpdateDialogFilterOrder\r\n });\r\n }\r\n\r\n public clear(init = false) {\r\n if(!init) {\r\n safeReplaceObject(this.filters, {});\r\n }\r\n\r\n this.orderIndex = START_ORDER_INDEX;\r\n }\r\n\r\n private onUpdateDialogFilter = (update: Update.updateDialogFilter) => {\r\n if(update.filter) {\r\n this.saveDialogFilter(update.filter as any);\r\n } else if(this.filters[update.id]) { // Папка удалена\r\n //this.getDialogFilters(true);\r\n this.rootScope.dispatchEvent('filter_delete', this.filters[update.id]);\r\n delete this.filters[update.id];\r\n }\r\n\r\n this.appStateManager.pushToState('filters', this.filters);\r\n };\r\n\r\n private onUpdateDialogFilterOrder = (update: Update.updateDialogFilterOrder) => {\r\n //console.log('updateDialogFilterOrder', update);\r\n\r\n this.orderIndex = START_ORDER_INDEX;\r\n update.order.forEach((filterId, idx) => {\r\n const filter = this.filters[filterId];\r\n delete filter.orderIndex;\r\n this.setOrderIndex(filter);\r\n });\r\n\r\n this.rootScope.dispatchEvent('filter_order', update.order);\r\n\r\n this.appStateManager.pushToState('filters', this.filters);\r\n };\r\n\r\n public testDialogForFilter(dialog: Dialog, filter: MyDialogFilter) {\r\n // exclude_peers\r\n for(const peerId of filter.exclude_peers) {\r\n if(peerId === dialog.peerId) {\r\n return false;\r\n }\r\n }\r\n\r\n // include_peers\r\n for(const peerId of filter.include_peers) {\r\n if(peerId === dialog.peerId) {\r\n return true;\r\n }\r\n }\r\n\r\n const pFlags = filter.pFlags;\r\n\r\n // exclude_archived\r\n if(pFlags.exclude_archived && dialog.folder_id === 1) {\r\n return false;\r\n }\r\n\r\n // exclude_read\r\n if(pFlags.exclude_read && !dialog.unread_count && !dialog.pFlags.unread_mark) {\r\n return false;\r\n }\r\n\r\n // exclude_muted\r\n if(pFlags.exclude_muted) {\r\n const isMuted = this.appNotificationsManager.isPeerLocalMuted(dialog.peerId);\r\n if(isMuted) {\r\n return false;\r\n }\r\n }\r\n\r\n const peerId = dialog.peerId;\r\n if(peerId < 0) {\r\n // broadcasts\r\n if(pFlags.broadcasts && this.appPeersManager.isBroadcast(peerId)) {\r\n return true;\r\n }\r\n\r\n // groups\r\n if(pFlags.groups && this.appPeersManager.isAnyGroup(peerId)) {\r\n return true;\r\n }\r\n } else {\r\n // bots\r\n if(this.appPeersManager.isBot(peerId)) {\r\n return !!pFlags.bots;\r\n }\r\n \r\n // non_contacts\r\n if(pFlags.non_contacts && !this.appUsersManager.isContact(peerId)) {\r\n return true;\r\n }\r\n\r\n // contacts\r\n if(pFlags.contacts && this.appUsersManager.isContact(peerId)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n public toggleDialogPin(peerId: number, filterId: number) {\r\n const filter = this.filters[filterId];\r\n\r\n const wasPinned = filter.pinned_peers.findAndSplice(p => p === peerId);\r\n if(!wasPinned) {\r\n if(filter.pinned_peers.length >= this.rootScope.config.pinned_infolder_count_max) {\r\n return Promise.reject({type: 'PINNED_DIALOGS_TOO_MUCH'});\r\n }\r\n \r\n filter.pinned_peers.unshift(peerId);\r\n }\r\n \r\n return this.updateDialogFilter(filter);\r\n }\r\n\r\n public createDialogFilter(filter: MyDialogFilter) {\r\n let maxId = Math.max(1, ...Object.keys(this.filters).map(i => +i));\r\n filter = copy(filter);\r\n filter.id = maxId + 1;\r\n return this.updateDialogFilter(filter);\r\n }\r\n\r\n public updateDialogFilter(filter: MyDialogFilter, remove = false) {\r\n const flags = remove ? 0 : 1;\r\n\r\n return apiManager.invokeApi('messages.updateDialogFilter', {\r\n flags,\r\n id: filter.id,\r\n filter: remove ? undefined : this.getOutputDialogFilter(filter)\r\n }).then((bool: boolean) => { // возможно нужна проверка и откат, если результат не ТРУ\r\n //console.log('updateDialogFilter bool:', bool);\r\n\r\n if(bool) {\r\n /* if(!this.filters[filter.id]) {\r\n this.saveDialogFilter(filter);\r\n }\r\n\r\n rootScope.$broadcast('filter_update', filter); */\r\n\r\n this.onUpdateDialogFilter({\r\n _: 'updateDialogFilter',\r\n id: filter.id,\r\n filter: remove ? undefined : filter as any\r\n });\r\n }\r\n\r\n return bool;\r\n });\r\n }\r\n\r\n public getOutputDialogFilter(filter: MyDialogFilter) {\r\n const c: MyDialogFilter = copy(filter);\r\n ['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {\r\n // @ts-ignore\r\n c[key] = c[key].map((peerId: number) => this.appPeersManager.getInputPeerById(peerId));\r\n });\r\n\r\n forEachReverse(c.include_peers, (peerId, idx) => {\r\n if(c.pinned_peers.includes(peerId)) {\r\n c.include_peers.splice(idx, 1);\r\n }\r\n });\r\n\r\n return c as any as DialogFilter;\r\n }\r\n\r\n public async getDialogFilters(overwrite = false): Promise<MyDialogFilter[]> {\r\n const keys = Object.keys(this.filters);\r\n if(keys.length && !overwrite) {\r\n return keys.map(filterId => this.filters[filterId]).sort((a, b) => a.orderIndex - b.orderIndex);\r\n }\r\n\r\n const filters: MyDialogFilter[] = await apiManager.invokeApiSingle('messages.getDialogFilters') as any;\r\n for(const filter of filters) {\r\n this.saveDialogFilter(filter, overwrite);\r\n }\r\n\r\n //console.log(this.filters);\r\n return filters;\r\n }\r\n\r\n public saveDialogFilter(filter: MyDialogFilter, update = true) {\r\n ['pinned_peers', 'exclude_peers', 'include_peers'].forEach(key => {\r\n // @ts-ignore\r\n filter[key] = filter[key].map((peer: any) => this.appPeersManager.getPeerId(peer));\r\n });\r\n\r\n forEachReverse(filter.include_peers, (peerId, idx) => {\r\n if(filter.pinned_peers.includes(peerId)) {\r\n filter.include_peers.splice(idx, 1);\r\n }\r\n });\r\n \r\n filter.include_peers = filter.pinned_peers.concat(filter.include_peers);\r\n\r\n if(this.filters[filter.id]) {\r\n Object.assign(this.filters[filter.id], filter);\r\n } else {\r\n this.filters[filter.id] = filter;\r\n }\r\n\r\n this.setOrderIndex(filter);\r\n\r\n if(update) {\r\n this.rootScope.dispatchEvent('filter_update', filter);\r\n }\r\n }\r\n\r\n public setOrderIndex(filter: MyDialogFilter) {\r\n if(filter.hasOwnProperty('orderIndex')) {\r\n if(filter.orderIndex >= this.orderIndex) {\r\n this.orderIndex = filter.orderIndex + 1;\r\n }\r\n } else {\r\n filter.orderIndex = this.orderIndex++;\r\n }\r\n\r\n this.appStateManager.pushToState('filters', this.filters);\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { i18n, join, LangPackKey } from \"../lib/langPack\";\r\nimport formatDuration, { DurationType } from \"./formatDuration\";\r\n\r\nconst CALL_DURATION_LANG_KEYS: {[type in DurationType]: LangPackKey} = {\r\n s: 'Seconds',\r\n m: 'Minutes',\r\n h: 'Hours',\r\n d: 'Days',\r\n w: 'Weeks'\r\n};\r\nexport default function formatCallDuration(duration: number) {\r\n const a = formatDuration(duration, 2);\r\n const elements = a.map(d => i18n(CALL_DURATION_LANG_KEYS[d.type], [d.duration]));\r\n\r\n const fragment = document.createElement('span');\r\n fragment.append(...join(elements, false));\r\n\r\n return fragment;\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nexport type DurationType = 's' | 'm' | 'h' | 'd' | 'w';\r\nexport default function formatDuration(duration: number, showLast = 2) {\r\n if(!duration) {\r\n duration = 1;\r\n }\r\n\r\n let d: {duration: number, type: DurationType}[] = [];\r\n const p = [\r\n {m: 1, t: 's'},\r\n {m: 60, t: 'm'}, \r\n {m: 60, t: 'h'}, \r\n {m: 24, t: 'd'}, \r\n {m: 7, t: 'w'}\r\n ] as Array<{m?: number, t: DurationType}>\r\n const s = 1;\r\n let t = s;\r\n p.forEach((o, idx) => {\r\n t *= o.m;\r\n\r\n if(duration < t) {\r\n return;\r\n }\r\n\r\n const modulus = p[idx === (p.length - 1) ? idx : idx + 1].m;\r\n d.push({\r\n duration: (duration / t % modulus | 0),\r\n type: o.t\r\n });\r\n });\r\n\r\n const out = d.slice(-showLast).reverse();\r\n for(let i = out.length - 1; i >= 0; --i) {\r\n if(out[i].duration === 0) {\r\n out.splice(i, 1);\r\n }\r\n }\r\n \r\n return out;\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { LazyLoadQueueBase } from \"../../components/lazyLoadQueue\";\r\nimport ProgressivePreloader from \"../../components/preloader\";\r\nimport { CancellablePromise, deferredPromise } from \"../../helpers/cancellablePromise\";\r\nimport { formatTime, tsNow } from \"../../helpers/date\";\r\nimport { createPosterForVideo } from \"../../helpers/files\";\r\nimport { copy, getObjectKeysAndSort } from \"../../helpers/object\";\r\nimport { randomLong } from \"../../helpers/random\";\r\nimport { splitStringByLength, limitSymbols, escapeRegExp } from \"../../helpers/string\";\r\nimport { 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 } from \"../../layer\";\r\nimport { InvokeApiOptions } from \"../../types\";\r\nimport I18n, { i18n, join, langPack, LangPackKey, _i18n } from \"../langPack\";\r\nimport { logger, LogTypes } from \"../logger\";\r\nimport type { ApiFileManager } from '../mtproto/apiFileManager';\r\n//import apiManager from '../mtproto/apiManager';\r\nimport apiManager from '../mtproto/mtprotoworker';\r\nimport referenceDatabase, { ReferenceContext } from \"../mtproto/referenceDatabase\";\r\nimport serverTimeManager from \"../mtproto/serverTimeManager\";\r\nimport { RichTextProcessor } from \"../richtextprocessor\";\r\nimport rootScope from \"../rootScope\";\r\nimport DialogsStorage from \"../storages/dialogs\";\r\nimport FiltersStorage from \"../storages/filters\";\r\n//import { telegramMeWebService } from \"../mtproto/mtproto\";\r\nimport apiUpdatesManager from \"./apiUpdatesManager\";\r\nimport appChatsManager from \"./appChatsManager\";\r\nimport appDocsManager, { MyDocument } from \"./appDocsManager\";\r\nimport appDownloadManager from \"./appDownloadManager\";\r\nimport appPeersManager from \"./appPeersManager\";\r\nimport appPhotosManager, { MyPhoto } from \"./appPhotosManager\";\r\nimport appPollsManager from \"./appPollsManager\";\r\nimport appStateManager from \"./appStateManager\";\r\nimport appUsersManager from \"./appUsersManager\";\r\nimport appWebPagesManager from \"./appWebPagesManager\";\r\nimport appDraftsManager from \"./appDraftsManager\";\r\nimport { getFileNameByLocation } from \"../../helpers/fileName\";\r\nimport appProfileManager from \"./appProfileManager\";\r\nimport DEBUG, { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport SlicedArray, { Slice, SliceEnd } from \"../../helpers/slicedArray\";\r\nimport appNotificationsManager, { NotifyOptions } from \"./appNotificationsManager\";\r\nimport PeerTitle from \"../../components/peerTitle\";\r\nimport { forEachReverse } from \"../../helpers/array\";\r\nimport htmlToDocumentFragment from \"../../helpers/dom/htmlToDocumentFragment\";\r\nimport htmlToSpan from \"../../helpers/dom/htmlToSpan\";\r\nimport { REPLIES_PEER_ID } from \"../mtproto/mtproto_config\";\r\nimport formatCallDuration from \"../../helpers/formatCallDuration\";\r\nimport appAvatarsManager from \"./appAvatarsManager\";\r\nimport telegramMeWebManager from \"../mtproto/telegramMeWebManager\";\r\nimport { getMiddleware } from \"../../helpers/middleware\";\r\nimport assumeType from \"../../helpers/assumeType\";\r\n\r\n//console.trace('include');\r\n// TODO: если удалить сообщение в непрогруженном диалоге, то при обновлении, из-за стейта, последнего сообщения в чатлисте не будет\r\n// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках\r\n\r\nconst APITIMEOUT = 0;\r\n\r\nexport type HistoryStorage = {\r\n count: number | null,\r\n history: SlicedArray,\r\n\r\n maxId?: number,\r\n readPromise?: Promise<void>,\r\n readMaxId?: number,\r\n readOutboxMaxId?: number,\r\n triedToReadMaxId?: number,\r\n\r\n maxOutId?: number,\r\n reply_markup?: Exclude<ReplyMarkup, ReplyMarkup.replyInlineMarkup>\r\n};\r\n\r\nexport type HistoryResult = {\r\n count: number,\r\n history: Slice,\r\n offsetIdOffset?: number,\r\n};\r\n\r\nexport type Dialog = MTDialog.dialog;\r\n\r\nexport type MyMessage = Message.message | Message.messageService;\r\nexport type MyInputMessagesFilter = 'inputMessagesFilterEmpty' \r\n | 'inputMessagesFilterPhotos' \r\n | 'inputMessagesFilterPhotoVideo' \r\n | 'inputMessagesFilterVideo' \r\n | 'inputMessagesFilterDocument' \r\n | 'inputMessagesFilterVoice' \r\n | 'inputMessagesFilterRoundVoice' \r\n | 'inputMessagesFilterRoundVideo' \r\n | 'inputMessagesFilterMusic' \r\n | 'inputMessagesFilterUrl' \r\n | 'inputMessagesFilterMyMentions'\r\n | 'inputMessagesFilterChatPhotos'\r\n | 'inputMessagesFilterPinned';\r\n\r\nexport type PinnedStorage = Partial<{\r\n promise: Promise<PinnedStorage>,\r\n count: number,\r\n maxId: number\r\n}>;\r\nexport type MessagesStorage = {\r\n //generateIndex: (message: any) => void\r\n [mid: string]: any\r\n};\r\n\r\nexport type MyMessageActionType = Message.messageService['action']['_'];\r\n\r\ntype PendingAfterMsg = Partial<InvokeApiOptions & {\r\n afterMessageId: string,\r\n messageId: string\r\n}>;\r\n\r\nexport class AppMessagesManager {\r\n private static MESSAGE_ID_INCREMENT = 0x10000;\r\n private static MESSAGE_ID_OFFSET = 0xFFFFFFFF;\r\n\r\n private messagesStorageByPeerId: {[peerId: string]: MessagesStorage};\r\n public groupedMessagesStorage: {[groupId: string]: MessagesStorage}; // will be used for albums\r\n private scheduledMessagesStorage: {[peerId: string]: MessagesStorage};\r\n private historiesStorage: {\r\n [peerId: string]: HistoryStorage\r\n };\r\n private threadsStorage: {\r\n [peerId: string]: {\r\n [threadId: string]: HistoryStorage\r\n }\r\n };\r\n private searchesStorage: {\r\n [peerId: string]: Partial<{\r\n [inputFilter in MyInputMessagesFilter]: {\r\n count?: number,\r\n history: number[]\r\n }\r\n }>\r\n };\r\n public pinnedMessages: {[peerId: string]: PinnedStorage};\r\n\r\n public threadsServiceMessagesIdsStorage: {[peerId_threadId: string]: number};\r\n private threadsToReplies: {\r\n [peerId_threadId: string]: string;\r\n };\r\n\r\n private pendingByRandomId: {\r\n [randomId: string]: {\r\n peerId: number,\r\n tempId: number,\r\n threadId: number,\r\n storage: MessagesStorage\r\n }\r\n } = {};\r\n private pendingByMessageId: {[mid: string]: string} = {};\r\n private pendingAfterMsgs: {[peerId: string]: PendingAfterMsg} = {};\r\n public pendingTopMsgs: {[peerId: string]: number} = {};\r\n private tempNum = 0;\r\n private tempFinalizeCallbacks: {\r\n [tempId: string]: {\r\n [callbackName: string]: Partial<{\r\n deferred: CancellablePromise<void>, \r\n callback: (message: any) => Promise<any>\r\n }>\r\n }\r\n } = {};\r\n \r\n private sendSmthLazyLoadQueue = new LazyLoadQueueBase(1);\r\n\r\n private needSingleMessages: {[peerId: string]: number[]} = {};\r\n private fetchSingleMessagesPromise: Promise<void> = null;\r\n\r\n private maxSeenId = 0;\r\n\r\n public migratedFromTo: {[peerId: number]: number} = {};\r\n public migratedToFrom: {[peerId: number]: number} = {};\r\n\r\n private newMessagesHandleTimeout = 0;\r\n private newMessagesToHandle: {[peerId: string]: Set<number>} = {};\r\n private newDialogsHandlePromise: Promise<any>;\r\n private newDialogsToHandle: {[peerId: string]: Dialog} = {};\r\n public newUpdatesAfterReloadToHandle: {[peerId: string]: Set<Update>} = {};\r\n\r\n private notificationsHandlePromise = 0;\r\n private notificationsToHandle: {[peerId: string]: {\r\n fwdCount: number,\r\n fromId: number,\r\n topMessage?: MyMessage\r\n }} = {};\r\n\r\n private reloadConversationsPromise: Promise<void>;\r\n private reloadConversationsPeers: Set<number> = new Set();\r\n\r\n public log = logger('MESSAGES', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn);\r\n\r\n public dialogsStorage: DialogsStorage;\r\n public filtersStorage: FiltersStorage;\r\n\r\n private groupedTempId = 0;\r\n\r\n private typings: {[peerId: string]: {type: SendMessageAction['_'], timeout?: number}} = {};\r\n\r\n private middleware: ReturnType<typeof getMiddleware>;\r\n\r\n constructor() {\r\n this.clear();\r\n\r\n rootScope.addMultipleEventsListeners({\r\n updateMessageID: this.onUpdateMessageId,\r\n\r\n updateNewDiscussionMessage: this.onUpdateNewMessage,\r\n updateNewMessage: this.onUpdateNewMessage,\r\n updateNewChannelMessage: this.onUpdateNewMessage,\r\n\r\n updateDialogUnreadMark: this.onUpdateDialogUnreadMark,\r\n\r\n updateEditMessage: this.onUpdateEditMessage,\r\n updateEditChannelMessage: this.onUpdateEditMessage,\r\n\r\n updateReadChannelDiscussionInbox: this.onUpdateReadHistory,\r\n updateReadChannelDiscussionOutbox: this.onUpdateReadHistory,\r\n updateReadHistoryInbox: this.onUpdateReadHistory,\r\n updateReadHistoryOutbox: this.onUpdateReadHistory,\r\n updateReadChannelInbox: this.onUpdateReadHistory,\r\n updateReadChannelOutbox: this.onUpdateReadHistory,\r\n\r\n updateChannelReadMessagesContents: this.onUpdateReadMessagesContents,\r\n updateReadMessagesContents: this.onUpdateReadMessagesContents,\r\n\r\n updateChannelAvailableMessages: this.onUpdateChannelAvailableMessages,\r\n\r\n updateDeleteMessages: this.onUpdateDeleteMessages,\r\n updateDeleteChannelMessages: this.onUpdateDeleteMessages,\r\n\r\n updateChannel: this.onUpdateChannel,\r\n\r\n // @ts-ignore\r\n updateChannelReload: this.onUpdateChannelReload,\r\n\r\n updateChannelMessageViews: this.onUpdateChannelMessageViews,\r\n\r\n updateServiceNotification: this.onUpdateServiceNotification,\r\n\r\n updatePinnedMessages: this.onUpdatePinnedMessages,\r\n updatePinnedChannelMessages: this.onUpdatePinnedMessages,\r\n\r\n updateNotifySettings: this.onUpdateNotifySettings,\r\n\r\n updateNewScheduledMessage: this.onUpdateNewScheduledMessage,\r\n\r\n updateDeleteScheduledMessages: this.onUpdateDeleteScheduledMessages\r\n });\r\n\r\n // ! Invalidate notify settings, can optimize though\r\n rootScope.addEventListener('notify_peer_type_settings', ({key, settings}) => {\r\n this.getConversationsAll().then(dialogs => {\r\n let filterFunc: (dialog: Dialog) => boolean;\r\n if(key === 'notifyUsers') filterFunc = (dialog) => dialog.peerId > 0;\r\n else if(key === 'notifyBroadcasts') filterFunc = (dialog) => appChatsManager.isBroadcast(-dialog.peerId);\r\n else filterFunc = (dialog) => appPeersManager.isAnyGroup(dialog.peerId);\r\n\r\n dialogs\r\n .filter(filterFunc)\r\n .forEach(dialog => {\r\n rootScope.dispatchEvent('dialog_notify_settings', dialog);\r\n });\r\n });\r\n });\r\n\r\n rootScope.addEventListener('webpage_updated', (e) => {\r\n const eventData = e;\r\n eventData.msgs.forEach((mid) => {\r\n const message = this.getMessageById(mid) as Message.message;\r\n if(!message) return;\r\n message.media = {\r\n _: 'messageMediaWebPage', \r\n webpage: appWebPagesManager.getWebPage(eventData.id)\r\n };\r\n\r\n const peerId = this.getMessagePeer(message);\r\n const storage = this.getMessagesStorage(peerId);\r\n rootScope.dispatchEvent('message_edit', {\r\n storage,\r\n peerId,\r\n mid\r\n });\r\n });\r\n });\r\n\r\n rootScope.addEventListener('draft_updated', (e) => {\r\n const {peerId, threadId, draft} = e;\r\n\r\n if(threadId) return;\r\n\r\n const dialog = this.getDialogOnly(peerId);\r\n if(dialog && !threadId) {\r\n dialog.draft = draft;\r\n this.dialogsStorage.generateIndexForDialog(dialog);\r\n this.dialogsStorage.pushDialog(dialog);\r\n\r\n rootScope.dispatchEvent('dialog_draft', {\r\n peerId,\r\n draft,\r\n index: dialog.index\r\n });\r\n } else {\r\n this.reloadConversation(peerId);\r\n }\r\n });\r\n \r\n appStateManager.getState().then(state => {\r\n if(state.maxSeenMsgId) {\r\n this.maxSeenId = state.maxSeenMsgId;\r\n }\r\n });\r\n }\r\n\r\n public clear() {\r\n if(this.middleware) {\r\n this.middleware.clean();\r\n } else {\r\n this.middleware = getMiddleware();\r\n }\r\n\r\n this.messagesStorageByPeerId = {};\r\n this.groupedMessagesStorage = {};\r\n this.scheduledMessagesStorage = {};\r\n this.historiesStorage = {};\r\n this.threadsStorage = {};\r\n this.searchesStorage = {};\r\n this.pinnedMessages = {};\r\n this.threadsServiceMessagesIdsStorage = {};\r\n this.threadsToReplies = {};\r\n\r\n this.dialogsStorage && this.dialogsStorage.clear();\r\n this.filtersStorage && this.filtersStorage.clear();\r\n }\r\n\r\n public construct() {\r\n this.filtersStorage = new FiltersStorage(this, appPeersManager, appUsersManager, appNotificationsManager, appStateManager, apiUpdatesManager, /* apiManager, */ rootScope);\r\n this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager);\r\n }\r\n\r\n public getInputEntities(entities: MessageEntity[]) {\r\n const sendEntites = copy(entities);\r\n sendEntites.forEach((entity) => {\r\n if(entity._ === 'messageEntityMentionName') {\r\n (entity as any as MessageEntity.inputMessageEntityMentionName)._ = 'inputMessageEntityMentionName';\r\n (entity as any as MessageEntity.inputMessageEntityMentionName).user_id = appUsersManager.getUserInput(entity.user_id);\r\n }\r\n });\r\n return sendEntites;\r\n }\r\n\r\n public invokeAfterMessageIsSent(tempId: number, callbackName: string, callback: (message: any) => Promise<any>) {\r\n const finalize = this.tempFinalizeCallbacks[tempId] ?? (this.tempFinalizeCallbacks[tempId] = {});\r\n const obj = finalize[callbackName] ?? (finalize[callbackName] = {deferred: deferredPromise<void>()});\r\n\r\n obj.callback = callback;\r\n\r\n return obj.deferred;\r\n }\r\n\r\n public editMessage(message: any, text: string, options: Partial<{\r\n noWebPage: true,\r\n newMedia: any,\r\n scheduleDate: number,\r\n entities: MessageEntity[]\r\n }> = {}): Promise<void> {\r\n /* if(!this.canEditMessage(messageId)) {\r\n return Promise.reject({type: 'MESSAGE_EDIT_FORBIDDEN'});\r\n } */\r\n\r\n const {mid, peerId} = message;\r\n\r\n if(message.pFlags.is_outgoing) {\r\n return this.invokeAfterMessageIsSent(mid, 'edit', (message) => {\r\n //this.log('invoke editMessage callback', message);\r\n return this.editMessage(message, text, options);\r\n });\r\n }\r\n\r\n let entities = options.entities || [];\r\n if(text) {\r\n text = RichTextProcessor.parseMarkdown(text, entities);\r\n }\r\n\r\n const schedule_date = options.scheduleDate || (message.pFlags.is_scheduled ? message.date : undefined);\r\n return apiManager.invokeApi('messages.editMessage', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n id: message.id,\r\n message: text,\r\n media: options.newMedia,\r\n entities: entities.length ? this.getInputEntities(entities) : undefined,\r\n no_webpage: options.noWebPage,\r\n schedule_date\r\n }).then((updates) => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n }, (error) => {\r\n this.log.error('editMessage error:', error);\r\n \r\n if(error && error.type === 'MESSAGE_NOT_MODIFIED') {\r\n error.handled = true;\r\n return;\r\n }\r\n if(error && error.type === 'MESSAGE_EMPTY') {\r\n error.handled = true;\r\n }\r\n return Promise.reject(error);\r\n });\r\n }\r\n\r\n public sendText(peerId: number, text: string, options: Partial<{\r\n entities: any[],\r\n replyToMsgId: number,\r\n threadId: number,\r\n viaBotId: number,\r\n queryId: string,\r\n resultId: string,\r\n noWebPage: true,\r\n reply_markup: any,\r\n clearDraft: true,\r\n webPage: any,\r\n scheduleDate: number,\r\n silent: true\r\n }> = {}) {\r\n if(typeof(text) !== 'string' || !text.length) {\r\n return;\r\n }\r\n\r\n //this.checkSendOptions(options);\r\n\r\n if(options.threadId && !options.replyToMsgId) {\r\n options.replyToMsgId = options.threadId;\r\n }\r\n\r\n const MAX_LENGTH = rootScope.config.message_length_max;\r\n if(text.length > MAX_LENGTH) {\r\n const splitted = splitStringByLength(text, MAX_LENGTH);\r\n text = splitted[0];\r\n\r\n if(splitted.length > 1) {\r\n delete options.webPage;\r\n }\r\n\r\n for(let i = 1; i < splitted.length; ++i) {\r\n setTimeout(() => {\r\n this.sendText(peerId, splitted[i], options);\r\n }, i);\r\n }\r\n }\r\n\r\n peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;\r\n\r\n let entities = options.entities || [];\r\n if(!options.viaBotId) {\r\n text = RichTextProcessor.parseMarkdown(text, entities);\r\n //entities = RichTextProcessor.mergeEntities(entities, RichTextProcessor.parseEntities(text));\r\n }\r\n\r\n let sendEntites = this.getInputEntities(entities);\r\n if(!sendEntites.length) {\r\n sendEntites = undefined;\r\n }\r\n\r\n const message = this.generateOutgoingMessage(peerId, options);\r\n message.entities = entities;\r\n message.message = text;\r\n\r\n const replyToMsgId = options.replyToMsgId ? this.getServerMessageId(options.replyToMsgId) : undefined;\r\n const isChannel = appPeersManager.isChannel(peerId);\r\n\r\n if(options.webPage) {\r\n message.media = {\r\n _: 'messageMediaWebPage',\r\n webpage: options.webPage\r\n };\r\n }\r\n\r\n const toggleError = (on: any) => {\r\n if(on) {\r\n message.error = true;\r\n } else {\r\n delete message.error;\r\n }\r\n rootScope.dispatchEvent('messages_pending');\r\n };\r\n\r\n message.send = () => {\r\n toggleError(false);\r\n const sentRequestOptions: PendingAfterMsg = {};\r\n if(this.pendingAfterMsgs[peerId]) {\r\n sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;\r\n }\r\n\r\n let apiPromise: any;\r\n if(options.viaBotId) {\r\n apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n random_id: message.random_id,\r\n reply_to_msg_id: replyToMsgId || undefined,\r\n query_id: options.queryId,\r\n id: options.resultId,\r\n clear_draft: options.clearDraft\r\n }, sentRequestOptions);\r\n } else {\r\n apiPromise = apiManager.invokeApiAfter('messages.sendMessage', {\r\n no_webpage: options.noWebPage,\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n message: text,\r\n random_id: message.random_id,\r\n reply_to_msg_id: replyToMsgId || undefined,\r\n entities: sendEntites,\r\n clear_draft: options.clearDraft,\r\n schedule_date: options.scheduleDate || undefined,\r\n silent: options.silent\r\n }, sentRequestOptions);\r\n }\r\n\r\n /* function is<T>(value: any, condition: boolean): value is T {\r\n return condition;\r\n } */\r\n\r\n //this.log('sendText', message.mid);\r\n apiPromise.then((updates: Updates) => {\r\n //this.log('sendText sent', message.mid);\r\n //if(is<Updates.updateShortSentMessage>(updates, updates._ === 'updateShortSentMessage')) {\r\n if(updates._ === 'updateShortSentMessage') {\r\n //assumeType<Updates.updateShortSentMessage>(updates);\r\n message.date = updates.date;\r\n message.id = updates.id;\r\n message.media = updates.media;\r\n message.entities = updates.entities;\r\n this.wrapMessageEntities(message);\r\n if(updates.pFlags.out) {\r\n message.pFlags.out = true;\r\n }\r\n\r\n // * override with new updates\r\n updates = {\r\n _: 'updates',\r\n users: [],\r\n chats: [],\r\n seq: 0,\r\n updates: [{\r\n _: 'updateMessageID',\r\n random_id: message.random_id,\r\n id: updates.id\r\n }, {\r\n _: options.scheduleDate ? 'updateNewScheduledMessage' : (isChannel ? 'updateNewChannelMessage' : 'updateNewMessage'),\r\n message: message,\r\n pts: updates.pts,\r\n pts_count: updates.pts_count\r\n }]\r\n } as any;\r\n } else if((updates as Updates.updates).updates) {\r\n (updates as Updates.updates).updates.forEach((update) => {\r\n if(update._ === 'updateDraftMessage') {\r\n update.local = true;\r\n }\r\n });\r\n }\r\n // Testing bad situations\r\n // var upd = angular.copy(updates)\r\n // updates.updates.splice(0, 1)\r\n\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n\r\n // $timeout(function () {\r\n // ApiUpdatesManager.processUpdateMessage(upd)\r\n // }, 5000)\r\n }, (/* error: any */) => {\r\n toggleError(true);\r\n }).finally(() => {\r\n if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {\r\n delete this.pendingAfterMsgs[peerId];\r\n }\r\n });\r\n\r\n this.pendingAfterMsgs[peerId] = sentRequestOptions;\r\n }\r\n\r\n this.beforeMessageSending(message, {\r\n isScheduled: !!options.scheduleDate || undefined, \r\n threadId: options.threadId,\r\n clearDraft: options.clearDraft\r\n });\r\n }\r\n\r\n public sendFile(peerId: number, file: File | Blob | MyDocument, options: Partial<{\r\n isRoundMessage: true,\r\n isVoiceMessage: true,\r\n isGroupedItem: true,\r\n isMedia: true,\r\n\r\n replyToMsgId: number,\r\n threadId: number,\r\n groupId: string,\r\n caption: string,\r\n entities: MessageEntity[],\r\n width: number,\r\n height: number,\r\n objectURL: string,\r\n thumbBlob: Blob,\r\n thumbURL: string,\r\n duration: number,\r\n background: true,\r\n silent: true,\r\n clearDraft: true,\r\n scheduleDate: number,\r\n\r\n waveform: Uint8Array,\r\n }> = {}) {\r\n peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;\r\n\r\n //this.checkSendOptions(options);\r\n\r\n const message = this.generateOutgoingMessage(peerId, options);\r\n const replyToMsgId = options.replyToMsgId ? this.getServerMessageId(options.replyToMsgId) : undefined;\r\n\r\n let attachType: string, apiFileName: string;\r\n\r\n const fileType = 'mime_type' in file ? file.mime_type : file.type;\r\n const fileName = file instanceof File ? file.name : '';\r\n const isDocument = !(file instanceof File) && !(file instanceof Blob);\r\n let caption = options.caption || '';\r\n\r\n this.log('sendFile', file, fileType);\r\n\r\n const entities = options.entities || [];\r\n if(caption) {\r\n caption = RichTextProcessor.parseMarkdown(caption, entities);\r\n }\r\n\r\n const attributes: DocumentAttribute[] = [];\r\n\r\n const isPhoto = ['image/jpeg', 'image/png', 'image/bmp'].indexOf(fileType) >= 0;\r\n\r\n let photo: MyPhoto, document: MyDocument;\r\n\r\n let actionName: SendMessageAction['_'];\r\n if(isDocument) { // maybe it's a sticker or gif\r\n attachType = 'document';\r\n apiFileName = '';\r\n } else if(fileType.indexOf('audio/') === 0 || ['video/ogg'].indexOf(fileType) >= 0) {\r\n attachType = 'audio';\r\n apiFileName = 'audio.' + (fileType.split('/')[1] === 'ogg' ? 'ogg' : 'mp3');\r\n actionName = 'sendMessageUploadAudioAction';\r\n\r\n if(options.isVoiceMessage) {\r\n attachType = 'voice';\r\n message.pFlags.media_unread = true;\r\n }\r\n\r\n let attribute: DocumentAttribute.documentAttributeAudio = {\r\n _: 'documentAttributeAudio',\r\n pFlags: {\r\n voice: options.isVoiceMessage\r\n },\r\n waveform: options.waveform,\r\n duration: options.duration || 0\r\n };\r\n\r\n attributes.push(attribute);\r\n } else if(!options.isMedia) {\r\n attachType = 'document';\r\n apiFileName = 'document.' + fileType.split('/')[1];\r\n actionName = 'sendMessageUploadDocumentAction';\r\n } else if(isPhoto) {\r\n attachType = 'photo';\r\n apiFileName = 'photo.' + fileType.split('/')[1];\r\n actionName = 'sendMessageUploadPhotoAction';\r\n\r\n const photoSize = {\r\n _: 'photoSize',\r\n w: options.width,\r\n h: options.height,\r\n type: 'full',\r\n location: null,\r\n size: file.size\r\n } as PhotoSize.photoSize;\r\n\r\n photo = {\r\n _: 'photo',\r\n id: '' + message.id,\r\n sizes: [photoSize],\r\n w: options.width,\r\n h: options.height\r\n } as any;\r\n\r\n const cacheContext = appDownloadManager.getCacheContext(photo, photoSize.type);\r\n cacheContext.downloaded = file.size;\r\n cacheContext.url = options.objectURL || '';\r\n \r\n photo = appPhotosManager.savePhoto(photo);\r\n } else if(fileType.indexOf('video/') === 0) {\r\n attachType = 'video';\r\n apiFileName = 'video.mp4';\r\n actionName = 'sendMessageUploadVideoAction';\r\n\r\n let videoAttribute: DocumentAttribute.documentAttributeVideo = {\r\n _: 'documentAttributeVideo',\r\n pFlags: {\r\n round_message: options.isRoundMessage\r\n }, \r\n duration: options.duration,\r\n w: options.width,\r\n h: options.height\r\n };\r\n\r\n attributes.push(videoAttribute);\r\n } else {\r\n attachType = 'document';\r\n apiFileName = 'document.' + fileType.split('/')[1];\r\n actionName = 'sendMessageUploadDocumentAction';\r\n }\r\n\r\n attributes.push({_: 'documentAttributeFilename', file_name: fileName || apiFileName});\r\n\r\n if(['document', 'video', 'audio', 'voice'].indexOf(attachType) !== -1 && !isDocument) {\r\n const thumbs: PhotoSize[] = [];\r\n document = {\r\n _: 'document',\r\n id: '' + message.id,\r\n duration: options.duration,\r\n attributes,\r\n w: options.width,\r\n h: options.height,\r\n thumbs,\r\n mime_type: fileType,\r\n size: file.size\r\n } as any;\r\n\r\n const cacheContext = appDownloadManager.getCacheContext(document);\r\n cacheContext.downloaded = file.size;\r\n cacheContext.url = options.objectURL || '';\r\n\r\n let thumb: PhotoSize.photoSize;\r\n if(isPhoto) {\r\n attributes.push({\r\n _: 'documentAttributeImageSize',\r\n w: options.width,\r\n h: options.height\r\n });\r\n\r\n thumb = {\r\n _: 'photoSize',\r\n w: options.width,\r\n h: options.height,\r\n type: 'full',\r\n size: file.size\r\n };\r\n } else if(attachType === 'video') {\r\n if(options.thumbURL) {\r\n thumb = {\r\n _: 'photoSize',\r\n w: options.width,\r\n h: options.height,\r\n type: 'full',\r\n size: options.thumbBlob.size\r\n };\r\n\r\n const thumbCacheContext = appDownloadManager.getCacheContext(document, thumb.type);\r\n thumbCacheContext.downloaded = thumb.size;\r\n thumbCacheContext.url = options.thumbURL;\r\n }\r\n }\r\n\r\n if(thumb) {\r\n thumbs.push(thumb);\r\n }\r\n\r\n /* if(thumbs.length) {\r\n const thumb = thumbs[0] as PhotoSize.photoSize;\r\n const docThumb = appPhotosManager.getDocumentCachedThumb(document.id);\r\n docThumb.downloaded = thumb.size;\r\n docThumb.url = thumb.url;\r\n } */\r\n \r\n document = appDocsManager.saveDoc(document);\r\n }\r\n\r\n this.log('sendFile', attachType, apiFileName, file.type, options);\r\n\r\n const preloader = isDocument ? undefined : new ProgressivePreloader({\r\n attachMethod: 'prepend',\r\n tryAgainOnFail: false,\r\n isUpload: true\r\n });\r\n\r\n const sentDeferred = deferredPromise<InputMedia>();\r\n\r\n if(preloader) {\r\n preloader.attachPromise(sentDeferred);\r\n sentDeferred.cancel = () => {\r\n const error = new Error('Download canceled');\r\n error.name = 'AbortError';\r\n sentDeferred.reject(error);\r\n };\r\n\r\n sentDeferred.catch(err => {\r\n if(err.name === 'AbortError' && !uploaded) {\r\n this.log('cancelling upload', media);\r\n\r\n sentDeferred.reject(err);\r\n this.cancelPendingMessage(message.random_id);\r\n this.setTyping(peerId, {_: 'sendMessageCancelAction'});\r\n\r\n if(uploadPromise?.cancel) {\r\n uploadPromise.cancel();\r\n }\r\n }\r\n });\r\n }\r\n\r\n const media = isDocument ? undefined : {\r\n _: photo ? 'messageMediaPhoto' : 'messageMediaDocument',\r\n pFlags: {},\r\n preloader,\r\n photo,\r\n document,\r\n promise: sentDeferred\r\n };\r\n\r\n message.entities = entities;\r\n message.message = caption;\r\n message.media = isDocument ? {\r\n _: 'messageMediaDocument',\r\n pFlags: {},\r\n document: file \r\n } : media;\r\n\r\n const toggleError = (on: boolean) => {\r\n if(on) {\r\n message.error = true;\r\n } else {\r\n delete message.error;\r\n }\r\n\r\n rootScope.dispatchEvent('messages_pending');\r\n };\r\n\r\n let uploaded = false,\r\n uploadPromise: ReturnType<ApiFileManager['uploadFile']> = null;\r\n\r\n message.send = () => {\r\n if(isDocument) {\r\n const {id, access_hash, file_reference} = file as MyDocument;\r\n\r\n const inputMedia: InputMedia = {\r\n _: 'inputMediaDocument',\r\n id: {\r\n _: 'inputDocument',\r\n id,\r\n access_hash,\r\n file_reference\r\n }\r\n };\r\n \r\n sentDeferred.resolve(inputMedia);\r\n } else if(file instanceof File || file instanceof Blob) {\r\n const load = () => {\r\n if(!uploaded || message.error) {\r\n uploaded = false;\r\n uploadPromise = appDownloadManager.upload(file);\r\n sentDeferred.notifyAll({done: 0, total: file.size});\r\n }\r\n\r\n let thumbUploadPromise: typeof uploadPromise;\r\n if(attachType === 'video' && options.objectURL) {\r\n thumbUploadPromise = new Promise((resolve, reject) => {\r\n const blobPromise = options.thumbBlob ? Promise.resolve(options.thumbBlob) : createPosterForVideo(options.objectURL);\r\n blobPromise.then(blob => {\r\n if(!blob) {\r\n resolve(null);\r\n } else {\r\n appDownloadManager.upload(blob).then(resolve, reject);\r\n }\r\n }, reject);\r\n });\r\n }\r\n \r\n uploadPromise && uploadPromise.then(async(inputFile) => {\r\n /* if(DEBUG) {\r\n this.log('appMessagesManager: sendFile uploaded:', inputFile);\r\n } */\r\n\r\n delete message.media.preloader;\r\n\r\n inputFile.name = apiFileName;\r\n uploaded = true;\r\n let inputMedia: InputMedia;\r\n switch(attachType) {\r\n case 'photo':\r\n inputMedia = {\r\n _: 'inputMediaUploadedPhoto', \r\n file: inputFile\r\n };\r\n break;\r\n\r\n default:\r\n inputMedia = {\r\n _: 'inputMediaUploadedDocument', \r\n file: inputFile, \r\n mime_type: fileType, \r\n attributes\r\n };\r\n }\r\n\r\n if(thumbUploadPromise) {\r\n try {\r\n const inputFile = await thumbUploadPromise;\r\n (inputMedia as InputMedia.inputMediaUploadedDocument).thumb = inputFile;\r\n } catch(err) {\r\n this.log.error('sendFile thumb upload error:', err);\r\n }\r\n }\r\n \r\n sentDeferred.resolve(inputMedia);\r\n }, (/* error */) => {\r\n toggleError(true);\r\n });\r\n \r\n uploadPromise.addNotifyListener((progress: {done: number, total: number}) => {\r\n /* if(DEBUG) {\r\n this.log('upload progress', progress);\r\n } */\r\n\r\n const percents = Math.max(1, Math.floor(100 * progress.done / progress.total));\r\n if(actionName) {\r\n this.setTyping(peerId, {_: actionName, progress: percents | 0});\r\n }\r\n sentDeferred.notifyAll(progress);\r\n });\r\n\r\n return sentDeferred;\r\n };\r\n\r\n if(options.isGroupedItem) {\r\n load();\r\n } else {\r\n this.sendSmthLazyLoadQueue.push({\r\n load\r\n });\r\n }\r\n }\r\n\r\n return sentDeferred;\r\n };\r\n\r\n this.beforeMessageSending(message, {\r\n isGroupedItem: options.isGroupedItem, \r\n isScheduled: !!options.scheduleDate || undefined, \r\n threadId: options.threadId,\r\n clearDraft: options.clearDraft\r\n });\r\n\r\n if(!options.isGroupedItem) {\r\n sentDeferred.then(inputMedia => {\r\n this.setTyping(peerId, {_: 'sendMessageCancelAction'});\r\n\r\n return apiManager.invokeApi('messages.sendMedia', {\r\n background: options.background,\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n media: inputMedia,\r\n message: caption,\r\n random_id: message.random_id,\r\n reply_to_msg_id: replyToMsgId,\r\n schedule_date: options.scheduleDate,\r\n silent: options.silent,\r\n entities,\r\n clear_draft: options.clearDraft\r\n }).then((updates) => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n }, (error) => {\r\n if(attachType === 'photo' &&\r\n error.code === 400 &&\r\n (error.type === 'PHOTO_INVALID_DIMENSIONS' ||\r\n error.type === 'PHOTO_SAVE_FILE_INVALID')) {\r\n error.handled = true;\r\n attachType = 'document';\r\n message.send();\r\n return;\r\n }\r\n\r\n toggleError(true);\r\n });\r\n });\r\n }\r\n\r\n return {message, promise: sentDeferred};\r\n }\r\n\r\n public async sendAlbum(peerId: number, files: File[], options: Partial<{\r\n isMedia: true,\r\n entities: MessageEntity[],\r\n replyToMsgId: number,\r\n threadId: number,\r\n caption: string,\r\n sendFileDetails: Partial<{\r\n duration: number,\r\n width: number,\r\n height: number,\r\n objectURL: string,\r\n thumbBlob: Blob,\r\n thumbURL: string\r\n }>[],\r\n silent: true,\r\n clearDraft: true,\r\n scheduleDate: number\r\n }> = {}) {\r\n //this.checkSendOptions(options);\r\n\r\n if(options.threadId && !options.replyToMsgId) {\r\n options.replyToMsgId = options.threadId;\r\n }\r\n\r\n if(files.length === 1) {\r\n return this.sendFile(peerId, files[0], {...options, ...options.sendFileDetails[0]});\r\n }\r\n\r\n peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;\r\n const replyToMsgId = options.replyToMsgId ? this.getServerMessageId(options.replyToMsgId) : undefined;\r\n\r\n let caption = options.caption || '';\r\n let entities = options.entities || [];\r\n if(caption) {\r\n caption = RichTextProcessor.parseMarkdown(caption, entities);\r\n }\r\n\r\n this.log('sendAlbum', files, options);\r\n\r\n const groupId = '' + ++this.groupedTempId;\r\n\r\n const messages = files.map((file, idx) => {\r\n const details = options.sendFileDetails[idx];\r\n const o: any = {\r\n isGroupedItem: true,\r\n isMedia: options.isMedia,\r\n scheduleDate: options.scheduleDate,\r\n silent: options.silent,\r\n replyToMsgId,\r\n threadId: options.threadId,\r\n groupId,\r\n ...details\r\n };\r\n\r\n if(idx === 0) {\r\n o.caption = caption;\r\n o.entities = entities;\r\n //o.replyToMsgId = replyToMsgId;\r\n }\r\n\r\n return this.sendFile(peerId, file, o).message;\r\n });\r\n\r\n if(options.threadId) {\r\n appDraftsManager.syncDraft(peerId, options.threadId);\r\n } else {\r\n appDraftsManager.saveDraft(peerId, options.threadId, null, {notify: true}); \r\n }\r\n \r\n // * test pending\r\n //return;\r\n\r\n const toggleError = (message: any, on: boolean) => {\r\n if(on) {\r\n message.error = true;\r\n } else {\r\n delete message.error;\r\n }\r\n\r\n rootScope.dispatchEvent('messages_pending');\r\n };\r\n\r\n const inputPeer = appPeersManager.getInputPeerById(peerId);\r\n const invoke = (multiMedia: any[]) => {\r\n this.setTyping(peerId, {_: 'sendMessageCancelAction'});\r\n\r\n this.sendSmthLazyLoadQueue.push({\r\n load: () => {\r\n return apiManager.invokeApi('messages.sendMultiMedia', {\r\n peer: inputPeer,\r\n multi_media: multiMedia,\r\n reply_to_msg_id: replyToMsgId,\r\n schedule_date: options.scheduleDate,\r\n silent: options.silent,\r\n clear_draft: options.clearDraft\r\n }).then((updates) => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n }, (error) => {\r\n messages.forEach(message => toggleError(message, true));\r\n });\r\n }\r\n });\r\n };\r\n\r\n const promises: Promise<InputSingleMedia>[] = messages.map((message, idx) => {\r\n return (message.send() as Promise<InputMedia>).then((inputMedia: InputMedia) => {\r\n return apiManager.invokeApi('messages.uploadMedia', {\r\n peer: inputPeer,\r\n media: inputMedia\r\n });\r\n })\r\n .then(messageMedia => {\r\n let inputMedia: any;\r\n if(messageMedia._ === 'messageMediaPhoto') {\r\n const photo = appPhotosManager.savePhoto(messageMedia.photo);\r\n inputMedia = appPhotosManager.getInput(photo);\r\n } else if(messageMedia._ === 'messageMediaDocument') {\r\n const doc = appDocsManager.saveDoc(messageMedia.document);\r\n inputMedia = appDocsManager.getMediaInput(doc);\r\n }\r\n\r\n const inputSingleMedia: InputSingleMedia = {\r\n _: 'inputSingleMedia',\r\n media: inputMedia,\r\n random_id: message.random_id,\r\n message: caption,\r\n entities\r\n };\r\n\r\n // * only 1 caption for all inputs\r\n if(caption) {\r\n caption = '';\r\n entities = [];\r\n }\r\n\r\n return inputSingleMedia;\r\n }).catch((err: any) => {\r\n if(err.name === 'AbortError') {\r\n return null;\r\n }\r\n\r\n this.log.error('sendAlbum upload item error:', err, message);\r\n toggleError(message, true);\r\n throw err;\r\n });\r\n });\r\n\r\n Promise.all(promises).then(inputs => {\r\n invoke(inputs.filter(Boolean));\r\n });\r\n }\r\n\r\n public sendOther(peerId: number, inputMedia: any, options: Partial<{\r\n replyToMsgId: number,\r\n threadId: number,\r\n viaBotId: number,\r\n reply_markup: any,\r\n clearDraft: true,\r\n queryId: string\r\n resultId: string,\r\n scheduleDate: number,\r\n silent: true\r\n }> = {}) {\r\n peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;\r\n\r\n //this.checkSendOptions(options);\r\n const message = this.generateOutgoingMessage(peerId, options);\r\n const replyToMsgId = options.replyToMsgId ? this.getServerMessageId(options.replyToMsgId) : undefined;\r\n\r\n let media;\r\n switch(inputMedia._) {\r\n case 'inputMediaPoll': {\r\n inputMedia.poll.id = message.id;\r\n appPollsManager.savePoll(inputMedia.poll, {\r\n _: 'pollResults',\r\n flags: 4,\r\n total_voters: 0,\r\n pFlags: {},\r\n });\r\n\r\n const {poll, results} = appPollsManager.getPoll('' + message.id);\r\n media = {\r\n _: 'messageMediaPoll',\r\n poll,\r\n results\r\n };\r\n\r\n break;\r\n }\r\n /* case 'inputMediaPhoto':\r\n media = {\r\n _: 'messageMediaPhoto',\r\n photo: appPhotosManager.getPhoto(inputMedia.id.id),\r\n caption: inputMedia.caption || ''\r\n };\r\n break;\r\n\r\n case 'inputMediaDocument':\r\n var doc = appDocsManager.getDoc(inputMedia.id.id);\r\n if(doc.sticker && doc.stickerSetInput) {\r\n appStickersManager.pushPopularSticker(doc.id);\r\n }\r\n media = {\r\n _: 'messageMediaDocument',\r\n 'document': doc,\r\n caption: inputMedia.caption || ''\r\n };\r\n break;\r\n\r\n case 'inputMediaContact':\r\n media = {\r\n _: 'messageMediaContact',\r\n phone_number: inputMedia.phone_number,\r\n first_name: inputMedia.first_name,\r\n last_name: inputMedia.last_name,\r\n user_id: 0\r\n };\r\n break;\r\n\r\n case 'inputMediaGeoPoint':\r\n media = {\r\n _: 'messageMediaGeo',\r\n geo: {\r\n _: 'geoPoint',\r\n 'lat': inputMedia.geo_point['lat'],\r\n 'long': inputMedia.geo_point['long']\r\n }\r\n };\r\n break;\r\n\r\n case 'inputMediaVenue':\r\n media = {\r\n _: 'messageMediaVenue',\r\n geo: {\r\n _: 'geoPoint',\r\n 'lat': inputMedia.geo_point['lat'],\r\n 'long': inputMedia.geo_point['long']\r\n },\r\n title: inputMedia.title,\r\n address: inputMedia.address,\r\n provider: inputMedia.provider,\r\n venue_id: inputMedia.venue_id\r\n };\r\n break;\r\n\r\n case 'messageMediaPending':\r\n media = inputMedia;\r\n break; */\r\n }\r\n\r\n message.media = media;\r\n\r\n let toggleError = (on: boolean) => {\r\n /* const historyMessage = this.messagesForHistory[messageId];\r\n if (on) {\r\n message.error = true\r\n if (historyMessage) {\r\n historyMessage.error = true\r\n }\r\n } else {\r\n delete message.error\r\n if (historyMessage) {\r\n delete historyMessage.error\r\n }\r\n } */\r\n rootScope.dispatchEvent('messages_pending');\r\n };\r\n\r\n message.send = () => {\r\n const sentRequestOptions: PendingAfterMsg = {};\r\n if(this.pendingAfterMsgs[peerId]) {\r\n sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;\r\n }\r\n\r\n let apiPromise: Promise<any>;\r\n if(options.viaBotId) {\r\n apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n random_id: message.random_id,\r\n reply_to_msg_id: replyToMsgId || undefined,\r\n query_id: options.queryId,\r\n id: options.resultId,\r\n clear_draft: options.clearDraft\r\n }, sentRequestOptions);\r\n } else {\r\n apiPromise = apiManager.invokeApiAfter('messages.sendMedia', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n media: inputMedia,\r\n random_id: message.random_id,\r\n reply_to_msg_id: replyToMsgId || undefined,\r\n message: '',\r\n clear_draft: options.clearDraft,\r\n schedule_date: options.scheduleDate,\r\n silent: options.silent\r\n }, sentRequestOptions);\r\n }\r\n\r\n apiPromise.then((updates) => {\r\n if(updates.updates) {\r\n updates.updates.forEach((update: any) => {\r\n if(update._ === 'updateDraftMessage') {\r\n update.local = true\r\n }\r\n });\r\n }\r\n\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n }, (error) => {\r\n toggleError(true);\r\n }).finally(() => {\r\n if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {\r\n delete this.pendingAfterMsgs[peerId];\r\n }\r\n });\r\n this.pendingAfterMsgs[peerId] = sentRequestOptions;\r\n }\r\n\r\n this.beforeMessageSending(message, {\r\n isScheduled: !!options.scheduleDate || undefined, \r\n threadId: options.threadId,\r\n clearDraft: options.clearDraft\r\n });\r\n }\r\n\r\n /* private checkSendOptions(options: Partial<{\r\n scheduleDate: number\r\n }>) {\r\n if(options.scheduleDate) {\r\n const minTimestamp = (Date.now() / 1000 | 0) + 10;\r\n if(options.scheduleDate <= minTimestamp) {\r\n delete options.scheduleDate;\r\n }\r\n }\r\n } */\r\n\r\n private beforeMessageSending(message: any, options: Partial<{\r\n isGroupedItem: true, \r\n isScheduled: true, \r\n threadId: number, \r\n clearDraft: true\r\n }> = {}) {\r\n const messageId = message.id;\r\n const peerId = this.getMessagePeer(message);\r\n const storage = options.isScheduled ? this.getScheduledMessagesStorage(peerId) : this.getMessagesStorage(peerId);\r\n\r\n if(options.isScheduled) {\r\n //if(!options.isGroupedItem) {\r\n this.saveMessages([message], {storage, isScheduled: true, isOutgoing: true});\r\n setTimeout(() => {\r\n rootScope.dispatchEvent('scheduled_new', {peerId, mid: messageId});\r\n }, 0);\r\n } else {\r\n /* if(options.threadId && this.threadsStorage[peerId]) {\r\n delete this.threadsStorage[peerId][options.threadId];\r\n } */\r\n const storages: HistoryStorage[] = [\r\n this.getHistoryStorage(peerId),\r\n options.threadId ? this.getHistoryStorage(peerId, options.threadId) : undefined\r\n ];\r\n\r\n for(const storage of storages) {\r\n if(storage) {\r\n storage.history.unshift(messageId);\r\n }\r\n }\r\n\r\n //if(!options.isGroupedItem) {\r\n this.saveMessages([message], {storage, isOutgoing: true});\r\n setTimeout(() => {\r\n this.setDialogTopMessage(message);\r\n rootScope.dispatchEvent('history_append', {storage, peerId, mid: messageId});\r\n }, 0);\r\n }\r\n\r\n if(!options.isGroupedItem && options.clearDraft) {\r\n if(options.threadId) {\r\n appDraftsManager.syncDraft(peerId, options.threadId);\r\n } else {\r\n appDraftsManager.saveDraft(peerId, options.threadId, null, {notify: true}); \r\n }\r\n }\r\n \r\n this.pendingByRandomId[message.random_id] = {\r\n peerId, \r\n tempId: messageId, \r\n threadId: options.threadId, \r\n storage\r\n };\r\n\r\n if(!options.isGroupedItem && message.send) {\r\n setTimeout(message.send, 0);\r\n //setTimeout(message.send, 4000);\r\n //setTimeout(message.send, 7000);\r\n }\r\n }\r\n\r\n private generateOutgoingMessage(peerId: number, options: Partial<{\r\n scheduleDate: number,\r\n replyToMsgId: number,\r\n threadId: number,\r\n viaBotId: number,\r\n groupId: string,\r\n reply_markup: any,\r\n }>) {\r\n if(options.threadId && !options.replyToMsgId) {\r\n options.replyToMsgId = options.threadId;\r\n }\r\n\r\n const message: any = {\r\n _: 'message',\r\n id: this.generateTempMessageId(peerId),\r\n from_id: this.generateFromId(peerId),\r\n peer_id: appPeersManager.getOutputPeer(peerId),\r\n pFlags: this.generateFlags(peerId),\r\n date: options.scheduleDate || (tsNow(true) + serverTimeManager.serverTimeOffset),\r\n message: '',\r\n grouped_id: options.groupId,\r\n random_id: randomLong(),\r\n reply_to: this.generateReplyHeader(options.replyToMsgId, options.threadId),\r\n via_bot_id: options.viaBotId,\r\n reply_markup: options.reply_markup,\r\n replies: this.generateReplies(peerId),\r\n views: appPeersManager.isBroadcast(peerId) && 1,\r\n pending: true,\r\n };\r\n\r\n return message;\r\n }\r\n\r\n private generateReplyHeader(replyToMsgId: number, replyToTopId?: number) {\r\n const header = {\r\n _: 'messageReplyHeader',\r\n reply_to_msg_id: replyToMsgId || replyToTopId,\r\n } as MessageReplyHeader;\r\n\r\n if(replyToTopId && header.reply_to_msg_id !== replyToTopId) {\r\n header.reply_to_top_id = replyToTopId;\r\n }\r\n\r\n return header;\r\n }\r\n\r\n private generateReplies(peerId: number) {\r\n let replies: MessageReplies.messageReplies;\r\n if(appPeersManager.isBroadcast(peerId)) {\r\n const channelFull = appProfileManager.chatsFull[-peerId] as ChatFull.channelFull;\r\n if(channelFull?.linked_chat_id) {\r\n replies = {\r\n _: 'messageReplies',\r\n flags: 1,\r\n pFlags: {\r\n comments: true\r\n },\r\n channel_id: channelFull.linked_chat_id,\r\n replies: 0,\r\n replies_pts: 0\r\n };\r\n }\r\n }\r\n\r\n return replies;\r\n }\r\n\r\n /**\r\n * Generate correct from_id according to anonymous or broadcast\r\n */\r\n private generateFromId(peerId: number) {\r\n if(peerId < 0 && (appPeersManager.isBroadcast(peerId) || appPeersManager.getPeer(peerId).admin_rights?.pFlags?.anonymous)) {\r\n return undefined;\r\n } else {\r\n return appPeersManager.getOutputPeer(appUsersManager.getSelf().id);\r\n }\r\n }\r\n\r\n private generateFlags(peerId: number) {\r\n const pFlags: any = {};\r\n const fromId = appUsersManager.getSelf().id;\r\n if(peerId !== fromId) {\r\n pFlags.out = true;\r\n\r\n if(!appPeersManager.isChannel(peerId) && !appUsersManager.isBot(peerId)) {\r\n pFlags.unread = true;\r\n }\r\n }\r\n\r\n if(appPeersManager.isBroadcast(peerId)) {\r\n pFlags.post = true;\r\n }\r\n\r\n return pFlags;\r\n }\r\n\r\n private generateForwardHeader(peerId: number, originalMessage: Message.message) {\r\n const myId = appUsersManager.getSelf().id;\r\n if(originalMessage.fromId === myId && originalMessage.peerId === myId && !originalMessage.fwd_from) {\r\n return;\r\n }\r\n\r\n const fwdHeader: MessageFwdHeader.messageFwdHeader = {\r\n _: 'messageFwdHeader',\r\n flags: 0,\r\n date: originalMessage.date\r\n };\r\n\r\n if(originalMessage.fwd_from) {\r\n fwdHeader.from_id = originalMessage.fwd_from.from_id;\r\n fwdHeader.from_name = originalMessage.fwd_from.from_name;\r\n fwdHeader.post_author = originalMessage.fwd_from.post_author;\r\n } else {\r\n fwdHeader.from_id = appPeersManager.getOutputPeer(originalMessage.fromId);\r\n fwdHeader.post_author = originalMessage.post_author;\r\n }\r\n\r\n if(appPeersManager.isBroadcast(originalMessage.peerId)) {\r\n if(originalMessage.post_author) {\r\n fwdHeader.post_author = originalMessage.post_author;\r\n }\r\n\r\n fwdHeader.channel_post = originalMessage.id;\r\n }\r\n \r\n // * there is no way to detect whether user profile is hidden\r\n if(peerId === myId) {\r\n fwdHeader.saved_from_msg_id = originalMessage.id;\r\n fwdHeader.saved_from_peer = appPeersManager.getOutputPeer(originalMessage.peerId);\r\n }\r\n\r\n return fwdHeader;\r\n }\r\n\r\n public generateFakeAvatarMessage(peerId: number, photo: Photo) {\r\n const maxId = Number.MAX_SAFE_INTEGER;\r\n const message = {\r\n _: 'messageService',\r\n action: {\r\n _: 'messageActionChannelEditPhoto',\r\n photo\r\n },\r\n mid: maxId,\r\n peerId,\r\n date: (photo as Photo.photo).date,\r\n fromId: peerId\r\n } as Message.messageService;\r\n\r\n this.getMessagesStorage(peerId)[maxId] = message;\r\n return message;\r\n }\r\n\r\n public setDialogTopMessage(message: MyMessage, dialog: MTDialog.dialog = this.getDialogOnly(message.peerId)) {\r\n if(dialog) {\r\n dialog.top_message = message.mid;\r\n \r\n const historyStorage = this.getHistoryStorage(message.peerId);\r\n historyStorage.maxId = message.mid;\r\n\r\n this.dialogsStorage.generateIndexForDialog(dialog, false, message);\r\n\r\n this.scheduleHandleNewDialogs(message.peerId, dialog);\r\n }\r\n }\r\n\r\n public cancelPendingMessage(randomId: string) {\r\n const pendingData = this.pendingByRandomId[randomId];\r\n\r\n /* if(DEBUG) {\r\n this.log('cancelPendingMessage', randomId, pendingData);\r\n } */\r\n\r\n if(pendingData) {\r\n const {peerId, tempId, storage} = pendingData;\r\n const historyStorage = this.getHistoryStorage(peerId);\r\n\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateDeleteMessages',\r\n messages: [tempId],\r\n pts: undefined,\r\n pts_count: undefined\r\n });\r\n\r\n historyStorage.history.delete(tempId);\r\n\r\n delete this.pendingByRandomId[randomId];\r\n delete storage[tempId];\r\n\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n public async refreshConversations() {\r\n const limit = 200, outDialogs: Dialog[] = [];\r\n for(let folderId = 0; folderId < 2; ++folderId) {\r\n let offsetDate = 0;\r\n for(;;) {\r\n const {dialogs, isEnd} = await this.getTopMessages(limit, folderId, offsetDate);\r\n \r\n if(dialogs.length) {\r\n outDialogs.push(...dialogs as Dialog[]);\r\n const dialog = dialogs[dialogs.length - 1];\r\n\r\n // * get peerId and mid manually, because dialog can be migrated peer and it won't be saved\r\n const peerId = appPeersManager.getPeerId(dialog.peer);\r\n const mid = this.generateMessageId(dialog.top_message);\r\n offsetDate = this.getMessageByPeer(peerId, mid).date;\r\n\r\n if(!offsetDate) {\r\n console.error('refreshConversations: got no offsetDate', dialog);\r\n break;\r\n }\r\n }\r\n \r\n if(isEnd) {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n let obj: {[peerId: string]: Dialog} = {};\r\n outDialogs.forEach(dialog => {\r\n obj[dialog.peerId] = dialog;\r\n });\r\n rootScope.dispatchEvent('dialogs_multiupdate', obj);\r\n\r\n return outDialogs;\r\n }\r\n\r\n public async getConversationsAll(query = '', folderId = 0) {\r\n const limit = 200, outDialogs: Dialog[] = [];\r\n for(; folderId < 2; ++folderId) {\r\n let offsetIndex = 0;\r\n for(;;) {\r\n const {dialogs} = await appMessagesManager.getConversations(query, offsetIndex, limit, folderId);\r\n \r\n if(dialogs.length) {\r\n outDialogs.push(...dialogs);\r\n offsetIndex = dialogs[dialogs.length - 1].index || 0;\r\n } else {\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return outDialogs;\r\n }\r\n\r\n public getConversations(query = '', offsetIndex?: number, limit = 20, folderId = 0) {\r\n return this.dialogsStorage.getDialogs(query, offsetIndex, limit, folderId);\r\n }\r\n\r\n public getReadMaxIdIfUnread(peerId: number, threadId?: number) {\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n if(threadId) {\r\n const chatHistoryStorage = this.getHistoryStorage(peerId);\r\n const readMaxId = Math.max(chatHistoryStorage.readMaxId ?? 0, historyStorage.readMaxId);\r\n const message = this.getMessageByPeer(peerId, historyStorage.maxId); // usually message is missing, so pFlags.out won't be there anyway\r\n return !message.pFlags.out && readMaxId < historyStorage.maxId ? readMaxId : 0;\r\n } else {\r\n const message = this.getMessageByPeer(peerId, historyStorage.maxId);\r\n const readMaxId = peerId > 0 ? Math.max(historyStorage.readMaxId, historyStorage.readOutboxMaxId) : historyStorage.readMaxId;\r\n return !message.pFlags.out && readMaxId < historyStorage.maxId ? readMaxId : 0;\r\n }\r\n }\r\n\r\n // public lolSet = new Set();\r\n public getTopMessages(limit: number, folderId: number, offsetDate?: number) {\r\n //const dialogs = this.dialogsStorage.getFolder(folderId);\r\n let offsetId = 0;\r\n let offsetPeerId = 0;\r\n let offsetIndex = 0;\r\n\r\n if(offsetDate === undefined) {\r\n offsetDate = this.dialogsStorage.getOffsetDate(folderId);\r\n }\r\n\r\n if(offsetDate) {\r\n offsetIndex = offsetDate * 0x10000;\r\n offsetDate += serverTimeManager.serverTimeOffset;\r\n }\r\n\r\n const middleware = this.middleware.get();\r\n\r\n // ! ВНИМАНИЕ: ОЧЕНЬ СЛОЖНАЯ ЛОГИКА:\r\n // ! если делать запрос сначала по папке 0, потом по папке 1, по индексу 0 в массиве будет один и тот же диалог, с dialog.pFlags.pinned, ЛОЛ???\r\n // ! т.е., с запросом folder_id: 1, и exclude_pinned: 0, в результате будут ещё и закреплённые с папки 0\r\n return apiManager.invokeApiSingle('messages.getDialogs', {\r\n folder_id: folderId,\r\n offset_date: offsetDate,\r\n offset_id: offsetId,\r\n offset_peer: appPeersManager.getInputPeerById(offsetPeerId),\r\n limit,\r\n hash: 0\r\n }, {\r\n //timeout: APITIMEOUT,\r\n noErrorBox: true\r\n }).then((dialogsResult) => {\r\n if(!middleware() || dialogsResult._ === 'messages.dialogsNotModified') return null;\r\n\r\n if(DEBUG) {\r\n this.log('messages.getDialogs result:', dialogsResult.dialogs, {...dialogsResult.dialogs[0]});\r\n }\r\n\r\n /* if(!offsetDate) {\r\n telegramMeWebService.setAuthorized(true);\r\n } */\r\n\r\n // can reset pinned order here\r\n if(!offsetId && !offsetDate && !offsetPeerId) {\r\n this.dialogsStorage.resetPinnedOrder(folderId);\r\n }\r\n\r\n if(!offsetDate) {\r\n telegramMeWebManager.setAuthorized(true);\r\n }\r\n\r\n appUsersManager.saveApiUsers(dialogsResult.users);\r\n appChatsManager.saveApiChats(dialogsResult.chats);\r\n this.saveMessages(dialogsResult.messages);\r\n\r\n let maxSeenIdIncremented = offsetDate ? true : false;\r\n let hasPrepend = false;\r\n const noIdsDialogs: {[peerId: number]: Dialog} = {};\r\n forEachReverse((dialogsResult.dialogs as Dialog[]), dialog => {\r\n //const d = Object.assign({}, dialog);\r\n // ! нужно передавать folderId, так как по папке !== 0 нет свойства folder_id\r\n this.dialogsStorage.saveDialog(dialog, dialog.folder_id ?? folderId, true);\r\n\r\n if(!maxSeenIdIncremented &&\r\n !appPeersManager.isChannel(dialog.peerId || appPeersManager.getPeerId(dialog.peer))) {\r\n this.incrementMaxSeenId(dialog.top_message);\r\n maxSeenIdIncremented = true;\r\n }\r\n\r\n if(dialog.peerId === undefined) {\r\n return;\r\n }\r\n\r\n // if(!folderId && !dialog.folder_id) {\r\n // this.lolSet.add(dialog.peerId);\r\n // }\r\n\r\n /* if(dialog.peerId === -1213511294) {\r\n this.log.error('lun bot', folderId, d);\r\n } */\r\n\r\n if(offsetIndex && dialog.index > offsetIndex) {\r\n this.scheduleHandleNewDialogs(dialog.peerId, dialog);\r\n hasPrepend = true;\r\n }\r\n\r\n // ! это может случиться, если запрос идёт не по папке 0, а по 1. почему-то read'ов нет\r\n // ! в итоге, чтобы получить 1 диалог, делается первый запрос по папке 0, потом запрос для архивных по папке 1, и потом ещё перезагрузка архивного диалога\r\n if(!this.getServerMessageId(dialog.read_inbox_max_id) && !this.getServerMessageId(dialog.read_outbox_max_id)) {\r\n noIdsDialogs[dialog.peerId] = dialog;\r\n\r\n this.log.error('noIdsDialogs', dialog);\r\n\r\n /* if(dialog.peerId === -1213511294) {\r\n this.log.error('lun bot', folderId);\r\n } */\r\n }\r\n });\r\n\r\n if(Object.keys(noIdsDialogs).length) {\r\n //setTimeout(() => { // test bad situation\r\n this.reloadConversation(Object.keys(noIdsDialogs).map(id => +id)).then(() => {\r\n rootScope.dispatchEvent('dialogs_multiupdate', noIdsDialogs);\r\n \r\n for(let peerId in noIdsDialogs) {\r\n rootScope.dispatchEvent('dialog_unread', {peerId: +peerId});\r\n }\r\n });\r\n //}, 10e3);\r\n }\r\n\r\n const count = (dialogsResult as MessagesDialogs.messagesDialogsSlice).count;\r\n\r\n // exclude empty draft dialogs\r\n const dialogs = this.dialogsStorage.getFolder(folderId, false);\r\n let dialogsLength = 0;\r\n for(let i = 0, length = dialogs.length; i < length; ++i) {\r\n if(this.getServerMessageId(dialogs[i].top_message)) {\r\n ++dialogsLength;\r\n }\r\n }\r\n\r\n const isEnd = /* limit > dialogsResult.dialogs.length || */ \r\n !count || \r\n dialogsLength >= count ||\r\n !dialogsResult.dialogs.length;\r\n if(isEnd) {\r\n this.dialogsStorage.setDialogsLoaded(folderId, true);\r\n }\r\n\r\n if(hasPrepend) {\r\n this.scheduleHandleNewDialogs();\r\n } else {\r\n rootScope.dispatchEvent('dialogs_multiupdate', {});\r\n }\r\n\r\n return {\r\n isEnd, \r\n count, \r\n dialogs: (dialogsResult as MessagesDialogs.messagesDialogsSlice).dialogs\r\n };\r\n });\r\n }\r\n\r\n public forwardMessages(peerId: number, fromPeerId: number, mids: number[], options: Partial<{\r\n withMyScore: true,\r\n silent: true,\r\n scheduleDate: number\r\n }> = {}) {\r\n peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId;\r\n mids = mids.slice().sort((a, b) => a - b);\r\n\r\n const groups: {\r\n [groupId: string]: {\r\n tempId: string,\r\n messages: any[]\r\n }\r\n } = {};\r\n\r\n const newMessages = mids.map(mid => {\r\n const originalMessage: Message.message = this.getMessageByPeer(fromPeerId, mid);\r\n const message: Message.message = this.generateOutgoingMessage(peerId, options);\r\n message.fwd_from = this.generateForwardHeader(peerId, originalMessage);\r\n\r\n (['entities', 'forwards', 'message', 'media', 'reply_markup', 'views'] as any as Array<keyof MyMessage>).forEach(key => {\r\n // @ts-ignore\r\n message[key] = originalMessage[key];\r\n });\r\n\r\n const document = (message.media as MessageMedia.messageMediaDocument)?.document as MyDocument;\r\n if(document) {\r\n const types: MyDocument['type'][] = ['round', 'voice'];\r\n if(types.includes(document.type)) {\r\n (message as MyMessage).pFlags.media_unread = true;\r\n }\r\n }\r\n\r\n if(originalMessage.grouped_id) {\r\n const group = groups[originalMessage.grouped_id] ?? (groups[originalMessage.grouped_id] = {tempId: '' + ++this.groupedTempId, messages: []});\r\n group.messages.push(message);\r\n }\r\n\r\n return message;\r\n });\r\n\r\n for(const groupId in groups) {\r\n const group = groups[groupId];\r\n if(group.messages.length > 1) {\r\n group.messages.forEach(message => {\r\n message.grouped_id = group.tempId;\r\n });\r\n }\r\n }\r\n\r\n newMessages.forEach(message => {\r\n this.beforeMessageSending(message, {\r\n isScheduled: !!options.scheduleDate || undefined\r\n });\r\n });\r\n\r\n const sentRequestOptions: PendingAfterMsg = {};\r\n if(this.pendingAfterMsgs[peerId]) {\r\n sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId;\r\n }\r\n\r\n const promise = /* true ? Promise.resolve() : */apiManager.invokeApiAfter('messages.forwardMessages', {\r\n from_peer: appPeersManager.getInputPeerById(fromPeerId),\r\n id: mids.map(mid => this.getServerMessageId(mid)),\r\n random_id: newMessages.map(message => message.random_id),\r\n to_peer: appPeersManager.getInputPeerById(peerId),\r\n with_my_score: options.withMyScore,\r\n silent: options.silent,\r\n schedule_date: options.scheduleDate\r\n }, sentRequestOptions).then((updates) => {\r\n this.log('forwardMessages updates:', updates);\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n }).finally(() => {\r\n if(this.pendingAfterMsgs[peerId] === sentRequestOptions) {\r\n delete this.pendingAfterMsgs[peerId];\r\n }\r\n });\r\n\r\n this.pendingAfterMsgs[peerId] = sentRequestOptions;\r\n return promise;\r\n }\r\n\r\n public getMessageFromStorage(storage: MessagesStorage, messageId: number) {\r\n return storage && storage[messageId] || {\r\n _: 'messageEmpty',\r\n id: messageId,\r\n deleted: true,\r\n pFlags: {}\r\n };\r\n }\r\n\r\n private createMessageStorage() {\r\n const storage: MessagesStorage = {} as any;\r\n \r\n /* let num = 0;\r\n Object.defineProperty(storage, 'num', {\r\n get: () => ++num,\r\n set: (_num: number) => num = _num, \r\n enumerable: false\r\n });\r\n\r\n Object.defineProperty(storage, 'generateIndex', {\r\n value: (message: any) => {\r\n if(message.index === undefined) {\r\n message.index = (message.date * 0x10000) + (storage.num & 0xFFFF);\r\n }\r\n },\r\n enumerable: false\r\n }); */\r\n\r\n return storage;\r\n }\r\n\r\n public getMessagesStorage(peerId: number) {\r\n return this.messagesStorageByPeerId[peerId] ?? (this.messagesStorageByPeerId[peerId] = this.createMessageStorage());\r\n }\r\n\r\n public getMessageById(messageId: number) {\r\n for(const peerId in this.messagesStorageByPeerId) {\r\n if(appPeersManager.isChannel(+peerId)) {\r\n continue;\r\n }\r\n\r\n const message = this.messagesStorageByPeerId[peerId][messageId];\r\n if(message) {\r\n return message;\r\n }\r\n }\r\n\r\n return this.getMessageFromStorage(null, messageId);\r\n }\r\n\r\n public getMessageByPeer(peerId: number, messageId: number) {\r\n if(!peerId) {\r\n return this.getMessageById(messageId);\r\n }\r\n\r\n return this.getMessageFromStorage(this.getMessagesStorage(peerId), messageId);\r\n }\r\n\r\n public getMessagePeer(message: any): number {\r\n const toId = message.peer_id && appPeersManager.getPeerId(message.peer_id) || 0;\r\n\r\n return toId;\r\n }\r\n\r\n public getDialogByPeerId(peerId: number): [Dialog, number] | [] {\r\n return this.dialogsStorage.getDialog(peerId);\r\n }\r\n\r\n public getDialogOnly(peerId: number) {\r\n return this.dialogsStorage.getDialogOnly(peerId);\r\n }\r\n\r\n public reloadConversation(peerId?: number | number[]) {\r\n if(peerId !== undefined) {\r\n [].concat(peerId).forEach(peerId => {\r\n if(!this.reloadConversationsPeers.has(peerId)) {\r\n this.reloadConversationsPeers.add(peerId);\r\n //this.log('will reloadConversation', peerId);\r\n }\r\n });\r\n }\r\n\r\n if(this.reloadConversationsPromise) return this.reloadConversationsPromise;\r\n return this.reloadConversationsPromise = new Promise((resolve, reject) => {\r\n setTimeout(() => {\r\n const peers = Array.from(this.reloadConversationsPeers).map(peerId => appPeersManager.getInputDialogPeerById(peerId));\r\n this.reloadConversationsPeers.clear();\r\n\r\n apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => {\r\n this.dialogsStorage.applyDialogs(result);\r\n resolve();\r\n }, reject).finally(() => {\r\n this.reloadConversationsPromise = null;\r\n\r\n if(this.reloadConversationsPeers.size) {\r\n this.reloadConversation();\r\n }\r\n });\r\n }, 0);\r\n });\r\n }\r\n\r\n private doFlushHistory(peer: InputPeer, just_clear?: boolean, revoke?: boolean): Promise<true> {\r\n return apiManager.invokeApiSingle('messages.deleteHistory', {\r\n just_clear,\r\n revoke,\r\n peer,\r\n max_id: 0\r\n }).then((affectedHistory) => {\r\n apiUpdatesManager.processUpdateMessage({\r\n _: 'updateShort',\r\n update: {\r\n _: 'updatePts',\r\n pts: affectedHistory.pts,\r\n pts_count: affectedHistory.pts_count\r\n }\r\n });\r\n\r\n if(!affectedHistory.offset) {\r\n return true;\r\n }\r\n\r\n return this.doFlushHistory(peer, just_clear, revoke);\r\n });\r\n }\r\n\r\n public async flushHistory(peerId: number, justClear?: boolean, revoke?: boolean) {\r\n if(appPeersManager.isChannel(peerId)) {\r\n const promise = this.getHistory(peerId, 0, 1);\r\n\r\n const historyResult = promise instanceof Promise ? await promise : promise;\r\n\r\n const channelId = -peerId;\r\n const maxId = historyResult.history[0] || 0;\r\n return apiManager.invokeApiSingle('channels.deleteHistory', {\r\n channel: appChatsManager.getChannelInput(channelId),\r\n max_id: this.getServerMessageId(maxId)\r\n }).then(() => {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateChannelAvailableMessages',\r\n channel_id: channelId,\r\n available_min_id: maxId\r\n });\r\n\r\n return true;\r\n });\r\n }\r\n\r\n return this.doFlushHistory(appPeersManager.getInputPeerById(peerId), justClear, revoke).then(() => {\r\n delete this.historiesStorage[peerId];\r\n delete this.messagesStorageByPeerId[peerId];\r\n delete this.scheduledMessagesStorage[peerId];\r\n delete this.threadsStorage[peerId];\r\n delete this.searchesStorage[peerId];\r\n delete this.pinnedMessages[peerId];\r\n delete this.pendingAfterMsgs[peerId];\r\n delete this.pendingTopMsgs[peerId];\r\n delete this.needSingleMessages[peerId];\r\n \r\n if(justClear) {\r\n rootScope.dispatchEvent('dialog_flush', {peerId});\r\n } else {\r\n delete this.notificationsToHandle[peerId];\r\n delete this.typings[peerId];\r\n this.reloadConversationsPeers.delete(peerId);\r\n\r\n this.dialogsStorage.dropDialog(peerId);\r\n rootScope.dispatchEvent('dialog_drop', {peerId});\r\n }\r\n });\r\n }\r\n\r\n public hidePinnedMessages(peerId: number) {\r\n return Promise.all([\r\n appStateManager.getState(),\r\n this.getPinnedMessage(peerId)\r\n ])\r\n .then(([state, pinned]) => {\r\n state.hiddenPinnedMessages[peerId] = pinned.maxId;\r\n rootScope.dispatchEvent('peer_pinned_hidden', {peerId, maxId: pinned.maxId});\r\n });\r\n }\r\n\r\n public getPinnedMessage(peerId: number) {\r\n const p = this.pinnedMessages[peerId] ?? (this.pinnedMessages[peerId] = {});\r\n if(p.promise) return p.promise;\r\n else if(p.maxId) return Promise.resolve(p);\r\n\r\n return p.promise = this.getSearch({\r\n peerId, \r\n inputFilter: {_: 'inputMessagesFilterPinned'},\r\n maxId: 0,\r\n limit: 1\r\n }).then(result => {\r\n p.count = result.count;\r\n p.maxId = result.history[0]?.mid;\r\n return p;\r\n }).finally(() => {\r\n delete p.promise;\r\n });\r\n }\r\n\r\n public updatePinnedMessage(peerId: number, mid: number, unpin?: boolean, silent?: boolean, pm_oneside?: boolean) {\r\n return apiManager.invokeApi('messages.updatePinnedMessage', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n unpin,\r\n silent,\r\n pm_oneside,\r\n id: this.getServerMessageId(mid)\r\n }).then(updates => {\r\n //this.log('pinned updates:', updates);\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n }\r\n\r\n public unpinAllMessages(peerId: number): Promise<boolean> {\r\n return apiManager.invokeApiSingle('messages.unpinAllMessages', {\r\n peer: appPeersManager.getInputPeerById(peerId)\r\n }).then(affectedHistory => {\r\n apiUpdatesManager.processUpdateMessage({\r\n _: 'updateShort',\r\n update: {\r\n _: 'updatePts',\r\n pts: affectedHistory.pts,\r\n pts_count: affectedHistory.pts_count\r\n }\r\n });\r\n\r\n if(!affectedHistory.offset) {\r\n const storage = this.getMessagesStorage(peerId);\r\n for(const mid in storage) {\r\n const message = storage[mid];\r\n if(message.pFlags.pinned) {\r\n delete message.pFlags.pinned;\r\n }\r\n }\r\n\r\n rootScope.dispatchEvent('peer_pinned_messages', {peerId, unpinAll: true});\r\n delete this.pinnedMessages[peerId];\r\n\r\n return true;\r\n }\r\n\r\n return this.unpinAllMessages(peerId);\r\n });\r\n }\r\n\r\n public getAlbumText(grouped_id: string) {\r\n const group = this.groupedMessagesStorage[grouped_id];\r\n let foundMessages = 0, message: string, totalEntities: MessageEntity[], entities: MessageEntity[];\r\n for(const i in group) {\r\n const m = group[i];\r\n if(m.message) {\r\n if(++foundMessages > 1) break;\r\n message = m.message;\r\n totalEntities = m.totalEntities;\r\n entities = m.entities;\r\n } \r\n }\r\n\r\n if(foundMessages > 1) {\r\n message = undefined;\r\n totalEntities = undefined;\r\n entities = undefined;\r\n }\r\n\r\n return {message, entities, totalEntities};\r\n }\r\n\r\n public getMidsByAlbum(grouped_id: string) {\r\n return getObjectKeysAndSort(this.groupedMessagesStorage[grouped_id], 'asc');\r\n //return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b);\r\n }\r\n\r\n public getMidsByMessage(message: any) {\r\n if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id);\r\n else return [message.mid];\r\n }\r\n\r\n public filterMessages(message: any, verify: (message: MyMessage) => boolean) {\r\n const out: MyMessage[] = [];\r\n if(message.grouped_id) {\r\n const storage = this.groupedMessagesStorage[message.grouped_id];\r\n for(const mid in storage) {\r\n const message = storage[mid];\r\n if(verify(message)) {\r\n out.push(message);\r\n }\r\n }\r\n } else {\r\n if(verify(message)) {\r\n out.push(message);\r\n }\r\n }\r\n\r\n return out;\r\n }\r\n\r\n public generateTempMessageId(peerId: number) {\r\n const dialog = this.getDialogOnly(peerId);\r\n return this.generateMessageId(dialog?.top_message || 0, true);\r\n }\r\n\r\n public generateMessageId(messageId: number, temp = false) {\r\n const q = AppMessagesManager.MESSAGE_ID_OFFSET;\r\n const num = temp ? ++this.tempNum : 0;\r\n if(messageId >= q) {\r\n if(temp) {\r\n return messageId + (num & (AppMessagesManager.MESSAGE_ID_INCREMENT - 1));\r\n }\r\n\r\n return messageId;\r\n }\r\n\r\n return q + (messageId * AppMessagesManager.MESSAGE_ID_INCREMENT + (num & (AppMessagesManager.MESSAGE_ID_INCREMENT - 1)));\r\n }\r\n\r\n /**\r\n * * will ignore outgoing offset\r\n */\r\n public getServerMessageId(messageId: number) {\r\n const q = AppMessagesManager.MESSAGE_ID_OFFSET;\r\n if(messageId < q) { // id 0 -> mid 0xFFFFFFFF, so 0xFFFFFFFF must convert to 0\r\n return messageId;\r\n }\r\n\r\n const l = AppMessagesManager.MESSAGE_ID_INCREMENT - 1;\r\n const used = messageId & l;\r\n if(used !== l) {\r\n messageId -= used + 1;\r\n }\r\n\r\n return (messageId - q) / AppMessagesManager.MESSAGE_ID_INCREMENT;\r\n }\r\n\r\n public incrementMessageId(messageId: number, increment: number) {\r\n return this.generateMessageId(this.getServerMessageId(messageId) + increment);\r\n }\r\n\r\n public saveMessages(messages: any[], options: Partial<{\r\n storage: MessagesStorage,\r\n isScheduled: true,\r\n isOutgoing: true,\r\n //isNew: boolean, // * new - from update\r\n }> = {}) {\r\n //let groups: Set<string>;\r\n messages.forEach((message) => {\r\n if(message.pFlags === undefined) {\r\n message.pFlags = {};\r\n }\r\n\r\n if(message._ === 'messageEmpty') {\r\n return;\r\n }\r\n\r\n // * exclude from state\r\n // defineNotNumerableProperties(message, ['rReply', 'mid', 'savedFrom', 'fwdFromId', 'fromId', 'peerId', 'reply_to_mid', 'viaBotId']);\r\n\r\n const peerId = this.getMessagePeer(message);\r\n const storage = options.storage || this.getMessagesStorage(peerId);\r\n const isChannel = message.peer_id._ === 'peerChannel';\r\n const channelId = isChannel ? -peerId : 0;\r\n const isBroadcast = isChannel && appChatsManager.isBroadcast(channelId);\r\n\r\n if(options.isScheduled) {\r\n message.pFlags.is_scheduled = true;\r\n }\r\n\r\n if(options.isOutgoing) {\r\n message.pFlags.is_outgoing = true;\r\n }\r\n \r\n const mid = this.generateMessageId(message.id);\r\n message.mid = mid;\r\n\r\n if(message.grouped_id) {\r\n const storage = this.groupedMessagesStorage[message.grouped_id] ?? (this.groupedMessagesStorage[message.grouped_id] = {});\r\n storage[mid] = message;\r\n }\r\n\r\n const dialog = this.getDialogOnly(peerId);\r\n if(dialog && mid) {\r\n if(mid > dialog[message.pFlags.out\r\n ? 'read_outbox_max_id'\r\n : 'read_inbox_max_id']) {\r\n message.pFlags.unread = true;\r\n }\r\n }\r\n // this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id'])\r\n\r\n if(message.reply_to) {\r\n if(message.reply_to.reply_to_msg_id) {\r\n message.reply_to.reply_to_msg_id = message.reply_to_mid = this.generateMessageId(message.reply_to.reply_to_msg_id);\r\n } \r\n\r\n if(message.reply_to.reply_to_top_id) message.reply_to.reply_to_top_id = this.generateMessageId(message.reply_to.reply_to_top_id);\r\n }\r\n\r\n if(message.replies) {\r\n if(message.replies.max_id) message.replies.max_id = this.generateMessageId(message.replies.max_id);\r\n if(message.replies.read_max_id) message.replies.read_max_id = this.generateMessageId(message.replies.read_max_id);\r\n }\r\n\r\n const overwriting = !!peerId;\r\n if(!overwriting) {\r\n message.date -= serverTimeManager.serverTimeOffset;\r\n }\r\n \r\n //storage.generateIndex(message);\r\n const myId = appUsersManager.getSelf().id;\r\n\r\n message.peerId = peerId;\r\n if(peerId === myId/* && !message.from_id && !message.fwd_from */) {\r\n message.fromId = message.fwd_from ? (message.fwd_from.from_id ? appPeersManager.getPeerId(message.fwd_from.from_id) : 0) : myId;\r\n } else {\r\n //message.fromId = message.pFlags.post || (!message.pFlags.out && !message.from_id) ? peerId : appPeersManager.getPeerId(message.from_id);\r\n message.fromId = message.pFlags.post || !message.from_id ? peerId : appPeersManager.getPeerId(message.from_id);\r\n }\r\n\r\n const fwdHeader = message.fwd_from as MessageFwdHeader;\r\n if(fwdHeader) {\r\n //if(peerId === myID) {\r\n if(fwdHeader.saved_from_msg_id) fwdHeader.saved_from_msg_id = this.generateMessageId(fwdHeader.saved_from_msg_id);\r\n if(fwdHeader.channel_post) fwdHeader.channel_post = this.generateMessageId(fwdHeader.channel_post);\r\n\r\n const peer = fwdHeader.saved_from_peer || fwdHeader.from_id;\r\n const msgId = fwdHeader.saved_from_msg_id || fwdHeader.channel_post;\r\n if(peer && msgId) {\r\n const savedFromPeerId = appPeersManager.getPeerId(peer);\r\n const savedFromMid = this.generateMessageId(msgId);\r\n message.savedFrom = savedFromPeerId + '_' + savedFromMid;\r\n }\r\n\r\n /* if(peerId < 0 || peerId === myID) {\r\n message.fromId = appPeersManager.getPeerID(!message.from_id || deepEqual(message.from_id, fwdHeader.from_id) ? fwdHeader.from_id : message.from_id);\r\n } */\r\n /* } else {\r\n apiMessage.fwdPostID = fwdHeader.channel_post;\r\n } */\r\n\r\n message.fwdFromId = appPeersManager.getPeerId(fwdHeader.from_id);\r\n\r\n if(!overwriting) {\r\n fwdHeader.date -= serverTimeManager.serverTimeOffset;\r\n }\r\n }\r\n\r\n if(message.via_bot_id > 0) {\r\n message.viaBotId = message.via_bot_id;\r\n }\r\n\r\n const mediaContext: ReferenceContext = {\r\n type: 'message',\r\n peerId,\r\n messageId: mid\r\n };\r\n\r\n if(message.media) {\r\n switch(message.media._) {\r\n case 'messageMediaEmpty':\r\n delete message.media;\r\n break;\r\n case 'messageMediaPhoto':\r\n if(message.media.ttl_seconds) {\r\n message.media = {_: 'messageMediaUnsupportedWeb'};\r\n } else {\r\n message.media.photo = appPhotosManager.savePhoto(message.media.photo, mediaContext);\r\n }\r\n\r\n if(!message.media.photo) { // * found this bug on test DC\r\n delete message.media;\r\n }\r\n \r\n break;\r\n case 'messageMediaPoll':\r\n message.media.poll = appPollsManager.savePoll(message.media.poll, message.media.results);\r\n break;\r\n case 'messageMediaDocument':\r\n if(message.media.ttl_seconds) {\r\n message.media = {_: 'messageMediaUnsupportedWeb'};\r\n } else {\r\n message.media.document = appDocsManager.saveDoc(message.media.document, mediaContext); // 11.04.2020 warning\r\n }\r\n\r\n break;\r\n case 'messageMediaWebPage':\r\n message.media.webpage = appWebPagesManager.saveWebPage(message.media.webpage, message.mid, mediaContext);\r\n break;\r\n /*case 'messageMediaGame':\r\n AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext);\r\n apiMessage.media.handleMessage = true;\r\n break; */\r\n case 'messageMediaInvoice':\r\n message.media = {_: 'messageMediaUnsupportedWeb'};\r\n break;\r\n }\r\n }\r\n\r\n if(message.action) {\r\n const action = message.action;\r\n let migrateFrom: number;\r\n let migrateTo: number;\r\n const suffix = message.fromId === appUsersManager.getSelf().id ? 'You' : '';\r\n switch(action._) {\r\n //case 'messageActionChannelEditPhoto':\r\n case 'messageActionChatEditPhoto':\r\n action.photo = appPhotosManager.savePhoto(action.photo, mediaContext);\r\n if(action.photo.video_sizes) {\r\n action._ = isBroadcast ? 'messageActionChannelEditVideo' : 'messageActionChatEditVideo';\r\n } else {\r\n if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода.\r\n action._ = 'messageActionChannelEditPhoto';\r\n }\r\n }\r\n break;\r\n \r\n case 'messageActionGroupCall': {\r\n //assumeType<MessageAction.messageActionGroupCall>(action);\r\n\r\n let type: string;\r\n if(action.duration === undefined) {\r\n type = 'started';\r\n if(peerId !== message.fromId) {\r\n type += '_by' + suffix;\r\n }\r\n } else {\r\n type = 'ended_by' + suffix;\r\n }\r\n\r\n action.type = type;\r\n\r\n break;\r\n }\r\n\r\n case 'messageActionChatEditTitle':\r\n /* if(options.isNew) {\r\n const chat = appChatsManager.getChat(-peerId);\r\n chat.title = action.title;\r\n appChatsManager.saveApiChat(chat, true);\r\n } */\r\n \r\n if(isBroadcast) {\r\n action._ = 'messageActionChannelEditTitle';\r\n }\r\n break;\r\n\r\n case 'messageActionChatDeletePhoto':\r\n if(isBroadcast) {\r\n action._ = 'messageActionChannelDeletePhoto';\r\n }\r\n break;\r\n\r\n case 'messageActionChatAddUser':\r\n if(action.users.length === 1) {\r\n action.user_id = action.users[0];\r\n if(message.fromId === action.user_id) {\r\n if(isChannel) {\r\n action._ = 'messageActionChatJoined' + suffix;\r\n } else {\r\n action._ = 'messageActionChatReturn' + suffix;\r\n }\r\n }\r\n } else if(action.users.length > 1) {\r\n action._ = 'messageActionChatAddUsers';\r\n }\r\n break;\r\n\r\n case 'messageActionChatDeleteUser':\r\n if(message.fromId === action.user_id) {\r\n action._ = 'messageActionChatLeave' + suffix;\r\n }\r\n break;\r\n\r\n case 'messageActionChannelMigrateFrom':\r\n migrateFrom = -action.chat_id;\r\n migrateTo = -channelId;\r\n break\r\n\r\n case 'messageActionChatMigrateTo':\r\n migrateFrom = -channelId;\r\n migrateTo = -action.channel_id;\r\n break;\r\n\r\n case 'messageActionHistoryClear':\r\n //apiMessage.deleted = true;\r\n message.clear_history = true;\r\n delete message.pFlags.out;\r\n delete message.pFlags.unread;\r\n break;\r\n\r\n case 'messageActionPhoneCall':\r\n action.type = \r\n (message.pFlags.out ? 'out_' : 'in_') +\r\n (\r\n action.reason._ === 'phoneCallDiscardReasonMissed' ||\r\n action.reason._ === 'phoneCallDiscardReasonBusy'\r\n ? 'missed'\r\n : 'ok'\r\n );\r\n break;\r\n }\r\n \r\n if(migrateFrom &&\r\n migrateTo &&\r\n !this.migratedFromTo[migrateFrom] &&\r\n !this.migratedToFrom[migrateTo]) {\r\n this.migrateChecks(migrateFrom, migrateTo);\r\n }\r\n }\r\n\r\n /* if(message.grouped_id) {\r\n if(!groups) {\r\n groups = new Set();\r\n }\r\n\r\n groups.add(message.grouped_id);\r\n } else {\r\n message.rReply = this.getRichReplyText(message);\r\n } */\r\n\r\n if(message.message && message.message.length && !message.totalEntities) {\r\n this.wrapMessageEntities(message); \r\n }\r\n\r\n storage[mid] = message;\r\n });\r\n\r\n /* if(groups) {\r\n for(const groupId of groups) {\r\n const mids = this.groupedMessagesStorage[groupId];\r\n for(const mid in mids) {\r\n const message = this.groupedMessagesStorage[groupId][mid];\r\n message.rReply = this.getRichReplyText(message);\r\n }\r\n }\r\n } */\r\n }\r\n\r\n private wrapMessageEntities(message: any) {\r\n const apiEntities = message.entities ? message.entities.slice() : [];\r\n message.message = RichTextProcessor.fixEmoji(message.message, apiEntities);\r\n\r\n const myEntities = RichTextProcessor.parseEntities(message.message);\r\n message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities); // ! only in this order, otherwise bold and emoji formatting won't work\r\n }\r\n\r\n public wrapMessageForReply(message: any, text: string, usingMids: number[], plain: true, highlightWord?: string): string;\r\n public wrapMessageForReply(message: any, text?: string, usingMids?: number[], plain?: false, highlightWord?: string): DocumentFragment;\r\n public wrapMessageForReply(message: any, text: string = message.message, usingMids?: number[], plain?: boolean, highlightWord?: string): DocumentFragment | string {\r\n const parts: (HTMLElement | string)[] = [];\r\n\r\n const addPart = (langKey: LangPackKey, part?: string | HTMLElement, text?: string) => {\r\n if(langKey) {\r\n part = plain ? I18n.format(langKey, true) : i18n(langKey);\r\n }\r\n \r\n if(plain) {\r\n parts.push(part);\r\n } else {\r\n const el = document.createElement('i');\r\n if(typeof(part) === 'string') el.innerHTML = part;\r\n else el.append(part);\r\n parts.push(el);\r\n }\r\n\r\n if(text) {\r\n parts.push(', ');\r\n }\r\n };\r\n\r\n if(message.media) {\r\n let usingFullAlbum = true;\r\n if(message.grouped_id) {\r\n if(usingMids) {\r\n const mids = this.getMidsByMessage(message);\r\n if(usingMids.length === mids.length) {\r\n for(const mid of mids) {\r\n if(!usingMids.includes(mid)) {\r\n usingFullAlbum = false;\r\n break;\r\n }\r\n }\r\n } else {\r\n usingFullAlbum = false;\r\n }\r\n }\r\n\r\n if(usingFullAlbum) {\r\n text = this.getAlbumText(message.grouped_id).message;\r\n addPart('AttachAlbum', undefined, text);\r\n }\r\n } else {\r\n usingFullAlbum = false;\r\n }\r\n\r\n if(!usingFullAlbum) {\r\n const media = message.media;\r\n switch(media._) {\r\n case 'messageMediaPhoto':\r\n addPart('AttachPhoto', undefined, message.message);\r\n break;\r\n case 'messageMediaDice':\r\n addPart(undefined, plain ? media.emoticon : RichTextProcessor.wrapEmojiText(media.emoticon));\r\n break;\r\n case 'messageMediaVenue': {\r\n const text = plain ? media.title : RichTextProcessor.wrapEmojiText(media.title);\r\n addPart('AttachLocation', undefined, text);\r\n parts.push(htmlToDocumentFragment(text) as any);\r\n break;\r\n }\r\n case 'messageMediaGeo':\r\n addPart('AttachLocation');\r\n break;\r\n case 'messageMediaGeoLive':\r\n addPart('AttachLiveLocation');\r\n break;\r\n case 'messageMediaPoll':\r\n addPart(undefined, plain ? '📊' + ' ' + (media.poll.question || 'poll') : media.poll.rReply);\r\n break;\r\n case 'messageMediaContact':\r\n addPart('AttachContact');\r\n break;\r\n case 'messageMediaGame': {\r\n const prefix = '🎮' + ' ';\r\n addPart(undefined, plain ? prefix + media.game.title : RichTextProcessor.wrapEmojiText(prefix + media.game.title));\r\n break;\r\n }\r\n case 'messageMediaDocument':\r\n let document = media.document;\r\n \r\n if(document.type === 'video') {\r\n addPart('AttachVideo', undefined, message.message);\r\n } else if(document.type === 'voice') {\r\n addPart('AttachAudio', undefined, message.message);\r\n } else if(document.type === 'gif') {\r\n addPart('AttachGif', undefined, message.message);\r\n } else if(document.type === 'round') {\r\n addPart('AttachRound', undefined, message.message);\r\n } else if(document.type === 'sticker') {\r\n addPart(undefined, ((plain ? document.stickerEmojiRaw : document.stickerEmoji) || '') + 'Sticker');\r\n text = '';\r\n } else {\r\n addPart(document.file_name, undefined, message.message);\r\n }\r\n \r\n break;\r\n \r\n default:\r\n //messageText += media._;\r\n ///////this.log.warn('Got unknown media type!', message);\r\n break;\r\n }\r\n } \r\n }\r\n\r\n if(message.action) {\r\n const actionWrapped = this.wrapMessageActionTextNew(message, plain);\r\n if(actionWrapped) {\r\n addPart(undefined, actionWrapped);\r\n }\r\n }\r\n\r\n if(text) {\r\n text = limitSymbols(text, 100);\r\n\r\n if(plain) {\r\n parts.push(text);\r\n } else {\r\n let entities = RichTextProcessor.parseEntities(text.replace(/\\n/g, ' '));\r\n\r\n if(highlightWord) {\r\n highlightWord = highlightWord.trim();\r\n if(!entities) entities = [];\r\n let found = false;\r\n let match: any;\r\n let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');\r\n while((match = regExp.exec(text)) !== null) {\r\n entities.push({_: 'messageEntityHighlight', length: highlightWord.length, offset: match.index});\r\n found = true;\r\n }\r\n \r\n if(found) {\r\n entities.sort((a, b) => a.offset - b.offset);\r\n }\r\n }\r\n\r\n const messageWrapped = RichTextProcessor.wrapRichText(text, {\r\n noLinebreaks: true, \r\n entities, \r\n noLinks: true,\r\n noTextFormat: true\r\n });\r\n \r\n parts.push(htmlToDocumentFragment(messageWrapped) as any);\r\n }\r\n }\r\n\r\n if(plain) {\r\n return parts.join('');\r\n } else {\r\n const fragment = document.createDocumentFragment();\r\n fragment.append(...parts);\r\n return fragment;\r\n }\r\n }\r\n\r\n public getSenderToPeerText(message: MyMessage) {\r\n let senderTitle = '', peerTitle: string;\r\n \r\n senderTitle = message.pFlags.out ? 'You' : appPeersManager.getPeerTitle(message.fromId, false, false);\r\n peerTitle = appPeersManager.isAnyGroup(message.peerId) || (message.pFlags.out && message.peerId !== rootScope.myId) ? \r\n appPeersManager.getPeerTitle(message.peerId, false, false) : \r\n '';\r\n\r\n if(peerTitle) {\r\n senderTitle += ' ➝ ' + peerTitle;\r\n }\r\n\r\n return senderTitle;\r\n }\r\n\r\n public wrapMessageActionTextNew(message: any, plain: true): string;\r\n public wrapMessageActionTextNew(message: any, plain?: false): HTMLElement;\r\n public wrapMessageActionTextNew(message: any, plain: boolean): HTMLElement | string;\r\n public wrapMessageActionTextNew(message: any, plain?: boolean): HTMLElement | string {\r\n const element: HTMLElement = plain ? undefined : document.createElement('span');\r\n const action = message.action as MessageAction;\r\n\r\n // this.log('message action:', action);\r\n\r\n if((action as MessageAction.messageActionCustomAction).message) {\r\n if(plain) {\r\n return RichTextProcessor.wrapPlainText(message.message);\r\n } else {\r\n element.innerHTML = RichTextProcessor.wrapRichText((action as MessageAction.messageActionCustomAction).message, {noLinebreaks: true});\r\n return element;\r\n }\r\n } else {\r\n let _ = action._;\r\n //let suffix = '';\r\n let langPackKey: LangPackKey;\r\n let args: any[];\r\n\r\n const getNameDivHTML = (peerId: number, plain: boolean) => {\r\n return plain ? appPeersManager.getPeerTitle(peerId, plain) + ' ' : (new PeerTitle({peerId})).element;\r\n };\r\n\r\n switch(action._) {\r\n case 'messageActionPhoneCall': {\r\n _ += '.' + (action as any).type;\r\n\r\n args = [formatCallDuration(action.duration)];\r\n break;\r\n }\r\n\r\n case 'messageActionGroupCall': {\r\n _ += '.' + (action as any).type;\r\n\r\n args = [];\r\n if(!_.endsWith('You')) {\r\n args.push(getNameDivHTML(message.fromId, plain));\r\n }\r\n\r\n args.push(formatCallDuration(action.duration));\r\n break;\r\n }\r\n\r\n case 'messageActionInviteToGroupCall': {\r\n const peerIds = [message.fromId, action.users[0]];\r\n let a = 'ActionGroupCall';\r\n const myId = appUsersManager.getSelf().id;\r\n if(peerIds[0] === myId) a += 'You';\r\n a += 'Invited';\r\n if(peerIds[1] === myId) a += 'You';\r\n peerIds.findAndSplice(peerId => peerId === myId);\r\n\r\n langPackKey = a as LangPackKey;\r\n args = peerIds.map(peerId => getNameDivHTML(peerId, plain));\r\n break;\r\n }\r\n\r\n case 'messageActionGroupCallScheduled': {\r\n const today = new Date();\r\n const date = new Date(action.schedule_date * 1000);\r\n const daysToStart = (date.getTime() - today.getTime()) / 86400e3;\r\n const tomorrowDate = new Date(today);\r\n tomorrowDate.setDate(tomorrowDate.getDate() + 1);\r\n\r\n langPackKey = 'ChatList.Service.VoiceChatScheduled';\r\n const myId = appUsersManager.getSelf().id;\r\n if(message.fromId === myId) {\r\n langPackKey += 'You';\r\n }\r\n\r\n let k: LangPackKey, _args: any[] = [];\r\n if(daysToStart < 1 && date.getDate() === today.getDate()) {\r\n k = 'TodayAtFormattedWithToday';\r\n } else if(daysToStart < 2 && date.getDate() === tomorrowDate.getDate()) {\r\n k = 'Time.TomorrowAt';\r\n } else {\r\n k = 'formatDateAtTime';\r\n _args.push(new I18n.IntlDateElement({\r\n date, \r\n options: {\r\n day: '2-digit',\r\n month: '2-digit',\r\n year: '2-digit'\r\n }\r\n }).element);\r\n }\r\n\r\n _args.push(formatTime(date));\r\n const t = i18n(k, _args);\r\n args = [t];\r\n\r\n break;\r\n }\r\n\r\n case 'messageActionChatCreate': {\r\n const myId = appUsersManager.getSelf().id;\r\n if(message.fromId === myId) {\r\n _ += 'You';\r\n } else {\r\n args = [getNameDivHTML(message.fromId, plain)];\r\n }\r\n \r\n break;\r\n }\r\n\r\n case 'messageActionPinMessage':\r\n case 'messageActionContactSignUp':\r\n case 'messageActionChatReturn':\r\n case 'messageActionChatLeave':\r\n case 'messageActionChatJoined':\r\n case 'messageActionChatEditPhoto':\r\n case 'messageActionChatDeletePhoto':\r\n case 'messageActionChatEditVideo':\r\n case 'messageActionChatJoinedByLink':\r\n case 'messageActionChannelEditVideo':\r\n case 'messageActionChannelDeletePhoto': {\r\n args = [getNameDivHTML(message.fromId, plain)];\r\n break;\r\n }\r\n\r\n case 'messageActionChannelEditTitle':\r\n case 'messageActionChatEditTitle': {\r\n args = [];\r\n if(action._ === 'messageActionChatEditTitle') {\r\n args.push(getNameDivHTML(message.fromId, plain));\r\n }\r\n\r\n args.push(plain ? action.title : htmlToSpan(RichTextProcessor.wrapEmojiText(action.title)));\r\n break;\r\n }\r\n\r\n case 'messageActionChatDeleteUser':\r\n case 'messageActionChatAddUsers':\r\n case 'messageActionChatAddUser': {\r\n const users: number[] = (action as MessageAction.messageActionChatAddUser).users \r\n || [(action as MessageAction.messageActionChatDeleteUser).user_id];\r\n\r\n args = [getNameDivHTML(message.fromId, plain)];\r\n\r\n if(users.length > 1) {\r\n if(plain) {\r\n args.push(...users.map((userId: number) => (getNameDivHTML(userId, true) as string).trim()).join(', '));\r\n } else {\r\n const fragment = document.createElement('span');\r\n fragment.append(\r\n ...join(\r\n users.map((userId: number) => getNameDivHTML(userId, false)) as HTMLElement[],\r\n false\r\n )\r\n );\r\n args.push(fragment);\r\n }\r\n } else {\r\n args.push(getNameDivHTML(users[0], plain));\r\n }\r\n\r\n break;\r\n }\r\n\r\n case 'messageActionBotAllowed': {\r\n const anchorHTML = RichTextProcessor.wrapRichText(action.domain, {\r\n entities: [{\r\n _: 'messageEntityUrl',\r\n length: action.domain.length,\r\n offset: 0\r\n }]\r\n });\r\n\r\n const node = htmlToSpan(anchorHTML);\r\n\r\n args = [node];\r\n break;\r\n }\r\n\r\n default:\r\n langPackKey = (langPack[_] || `[${action._}]`) as any;\r\n break;\r\n }\r\n\r\n if(!langPackKey) {\r\n langPackKey = langPack[_];\r\n if(langPackKey === undefined) {\r\n langPackKey = '[' + _ + ']' as any;\r\n }\r\n }\r\n\r\n if(plain) {\r\n return I18n.format(langPackKey, true, args);\r\n } else {\r\n return _i18n(element, langPackKey, args);\r\n }\r\n\r\n //str = !langPackKey || langPackKey[0].toUpperCase() === langPackKey[0] ? langPackKey : getNameDivHTML(message.fromId) + langPackKey + (suffix ? ' ' : '');\r\n }\r\n }\r\n\r\n public editPeerFolders(peerIds: number[], folderId: number) {\r\n apiManager.invokeApi('folders.editPeerFolders', {\r\n folder_peers: peerIds.map(peerId => {\r\n return {\r\n _: 'inputFolderPeer',\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n folder_id: folderId\r\n };\r\n })\r\n }).then(updates => {\r\n //this.log('editPeerFolders updates:', updates);\r\n apiUpdatesManager.processUpdateMessage(updates); // WARNING! возможно тут нужно добавлять channelId, и вызывать апдейт для каждого канала отдельно\r\n });\r\n }\r\n\r\n public toggleDialogPin(peerId: number, filterId?: number) {\r\n if(filterId > 1) {\r\n return this.filtersStorage.toggleDialogPin(peerId, filterId);\r\n }\r\n\r\n const dialog = this.getDialogOnly(peerId);\r\n if(!dialog) return Promise.reject();\r\n\r\n const pinned = dialog.pFlags?.pinned ? undefined : true;\r\n\r\n if(pinned) {\r\n const max = filterId === 1 ? rootScope.config.pinned_infolder_count_max : rootScope.config.pinned_dialogs_count_max;\r\n if(this.dialogsStorage.getPinnedOrders(filterId).length >= max) {\r\n return Promise.reject({type: 'PINNED_DIALOGS_TOO_MUCH'});\r\n }\r\n }\r\n\r\n return apiManager.invokeApi('messages.toggleDialogPin', {\r\n peer: appPeersManager.getInputDialogPeerById(peerId),\r\n pinned\r\n }).then(bool => {\r\n if(bool) {\r\n const pFlags: Update.updateDialogPinned['pFlags'] = pinned ? {pinned} : {};\r\n apiUpdatesManager.saveUpdate({\r\n _: 'updateDialogPinned',\r\n peer: appPeersManager.getDialogPeer(peerId),\r\n folder_id: filterId,\r\n pFlags\r\n });\r\n }\r\n });\r\n }\r\n\r\n public markDialogUnread(peerId: number, read?: true) {\r\n const dialog = this.getDialogOnly(peerId);\r\n if(!dialog) return Promise.reject();\r\n\r\n const unread = read || dialog.pFlags?.unread_mark ? undefined : true;\r\n return apiManager.invokeApi('messages.markDialogUnread', {\r\n peer: appPeersManager.getInputDialogPeerById(peerId),\r\n unread\r\n }).then(bool => {\r\n if(bool) {\r\n const pFlags: Update.updateDialogUnreadMark['pFlags'] = unread ? {unread} : {};\r\n this.onUpdateDialogUnreadMark({\r\n _: 'updateDialogUnreadMark',\r\n peer: appPeersManager.getDialogPeer(peerId),\r\n pFlags\r\n });\r\n }\r\n });\r\n }\r\n\r\n public migrateChecks(migrateFrom: number, migrateTo: number) {\r\n if(!this.migratedFromTo[migrateFrom] &&\r\n !this.migratedToFrom[migrateTo] &&\r\n appChatsManager.hasChat(-migrateTo)) {\r\n const fromChat = appChatsManager.getChat(-migrateFrom);\r\n if(fromChat &&\r\n fromChat.migrated_to &&\r\n fromChat.migrated_to.channel_id === -migrateTo) {\r\n this.migratedFromTo[migrateFrom] = migrateTo;\r\n this.migratedToFrom[migrateTo] = migrateFrom;\r\n\r\n //setTimeout(() => {\r\n rootScope.dispatchEvent('dialog_migrate', {migrateFrom, migrateTo});\r\n\r\n const dropped = this.dialogsStorage.dropDialog(migrateFrom);\r\n if(dropped.length) {\r\n rootScope.dispatchEvent('dialog_drop', {peerId: migrateFrom, dialog: dropped[0]});\r\n }\r\n //}, 100);\r\n }\r\n }\r\n }\r\n\r\n private canMessageBeEdited(message: any, kind: 'text' | 'poll') {\r\n if(message.pFlags.is_outgoing) {\r\n return false;\r\n }\r\n\r\n const goodMedias = [\r\n 'messageMediaPhoto',\r\n 'messageMediaDocument',\r\n 'messageMediaWebPage'\r\n ];\r\n\r\n if(kind === 'poll') {\r\n goodMedias.push('messageMediaPoll');\r\n }\r\n\r\n if(message._ !== 'message' ||\r\n message.deleted ||\r\n message.fwd_from ||\r\n message.via_bot_id ||\r\n message.media && goodMedias.indexOf(message.media._) === -1 ||\r\n message.fromId && appUsersManager.isBot(message.fromId)) {\r\n return false;\r\n }\r\n \r\n if(message.media &&\r\n message.media._ === 'messageMediaDocument' &&\r\n (message.media.document.sticker || message.media.document.type === 'round')) {\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public canEditMessage(message: any, kind: 'text' | 'poll' = 'text') {\r\n if(!message || !this.canMessageBeEdited(message, kind)) {\r\n return false;\r\n }\r\n\r\n // * second rule for saved messages, because there is no 'out' flag\r\n if(/* message.pFlags.out || */this.getMessagePeer(message) === appUsersManager.getSelf().id) {\r\n return true;\r\n }\r\n\r\n if((message.date < (tsNow(true) - rootScope.config.edit_time_limit) && \r\n message.media?._ !== 'messageMediaPoll') || !message.pFlags.out) {\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n public canDeleteMessage(message: any) {\r\n return message && (\r\n message.peerId > 0 \r\n || message.fromId === rootScope.myId \r\n || appChatsManager.getChat(message.peerId)._ === 'chat' \r\n || appChatsManager.hasRights(message.peerId, 'delete_messages')\r\n ) && !message.pFlags.is_outgoing;\r\n }\r\n\r\n public getReplyKeyboard(peerId: number) {\r\n return this.getHistoryStorage(peerId).reply_markup;\r\n }\r\n\r\n public mergeReplyKeyboard(historyStorage: HistoryStorage, message: Message.messageService | Message.message) {\r\n // this.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup)\r\n let messageReplyMarkup = (message as Message.message).reply_markup;\r\n if(!messageReplyMarkup &&\r\n !message.pFlags?.out &&\r\n !(message as Message.messageService).action) {\r\n return false;\r\n }\r\n\r\n if(messageReplyMarkup?._ === 'replyInlineMarkup') {\r\n return false;\r\n }\r\n\r\n const lastReplyMarkup = historyStorage.reply_markup;\r\n if(messageReplyMarkup) {\r\n if(lastReplyMarkup && lastReplyMarkup.mid >= message.mid) {\r\n return false;\r\n }\r\n\r\n if(messageReplyMarkup.pFlags.selective) {\r\n return false;\r\n }\r\n\r\n if(historyStorage.maxOutId &&\r\n message.mid < historyStorage.maxOutId &&\r\n (messageReplyMarkup as ReplyMarkup.replyKeyboardMarkup | ReplyMarkup.replyKeyboardForceReply).pFlags.single_use) {\r\n (messageReplyMarkup as ReplyMarkup.replyKeyboardMarkup | ReplyMarkup.replyKeyboardForceReply).pFlags.hidden = true;\r\n }\r\n\r\n messageReplyMarkup.mid = message.mid;\r\n /* messageReplyMarkup = Object.assign({\r\n mid: message.mid\r\n }, messageReplyMarkup); */\r\n\r\n if(messageReplyMarkup._ !== 'replyKeyboardHide') {\r\n messageReplyMarkup.fromId = appPeersManager.getPeerId(message.from_id);\r\n }\r\n\r\n historyStorage.reply_markup = messageReplyMarkup;\r\n // this.log('set', historyStorage.reply_markup)\r\n return true;\r\n }\r\n\r\n if(message.pFlags.out) {\r\n if(lastReplyMarkup) {\r\n assumeType<ReplyMarkup.replyKeyboardMarkup>(lastReplyMarkup);\r\n if(lastReplyMarkup.pFlags.single_use &&\r\n !lastReplyMarkup.pFlags.hidden &&\r\n (message.mid > lastReplyMarkup.mid || message.pFlags.is_outgoing) &&\r\n (message as Message.message).message) {\r\n lastReplyMarkup.pFlags.hidden = true;\r\n // this.log('set', historyStorage.reply_markup)\r\n return true;\r\n }\r\n } else if(!historyStorage.maxOutId ||\r\n message.mid > historyStorage.maxOutId) {\r\n historyStorage.maxOutId = message.mid;\r\n }\r\n }\r\n\r\n assumeType<Message.messageService>(message);\r\n if(message.action?._ === 'messageActionChatDeleteUser' &&\r\n (lastReplyMarkup\r\n ? message.action.user_id === (lastReplyMarkup as ReplyMarkup.replyKeyboardMarkup).fromId\r\n : appUsersManager.isBot(message.action.user_id)\r\n )\r\n ) {\r\n historyStorage.reply_markup = {\r\n _: 'replyKeyboardHide',\r\n mid: message.mid,\r\n pFlags: {}\r\n };\r\n // this.log('set', historyStorage.reply_markup)\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n public getSearchStorage(peerId: number, inputFilter: MyInputMessagesFilter) {\r\n if(!this.searchesStorage[peerId]) this.searchesStorage[peerId] = {};\r\n if(!this.searchesStorage[peerId][inputFilter]) this.searchesStorage[peerId][inputFilter] = {history: []};\r\n return this.searchesStorage[peerId][inputFilter];\r\n }\r\n\r\n public getSearchCounters(peerId: number, filters: MessagesFilter[], canCache = true) {\r\n const func = (canCache ? apiManager.invokeApiCacheable : apiManager.invokeApi).bind(apiManager);\r\n return func('messages.getSearchCounters', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n filters\r\n });\r\n }\r\n\r\n public getSearch({peerId, query, inputFilter, maxId, limit, nextRate, backLimit, threadId, folderId, minDate, maxDate}: {\r\n peerId?: number,\r\n maxId?: number,\r\n limit?: number,\r\n nextRate?: number,\r\n backLimit?: number,\r\n threadId?: number,\r\n folderId?: number,\r\n query?: string,\r\n inputFilter?: {\r\n _: MyInputMessagesFilter\r\n },\r\n minDate?: number,\r\n maxDate?: number\r\n }): Promise<{\r\n count: number,\r\n next_rate: number,\r\n offset_id_offset: number,\r\n history: MyMessage[]\r\n }> {\r\n if(!peerId) peerId = 0;\r\n if(!query) query = '';\r\n if(!inputFilter) inputFilter = {_: 'inputMessagesFilterEmpty'};\r\n if(limit === undefined) limit = 20;\r\n if(!nextRate) nextRate = 0;\r\n if(!backLimit) backLimit = 0;\r\n\r\n minDate = minDate ? minDate / 1000 | 0 : 0;\r\n maxDate = maxDate ? maxDate / 1000 | 0 : 0;\r\n\r\n const foundMsgs: Message.message[] = [];\r\n\r\n //this.log('search', maxId);\r\n\r\n if(backLimit) {\r\n limit += backLimit;\r\n }\r\n\r\n //const beta = inputFilter._ === 'inputMessagesFilterPinned' && !backLimit;\r\n const beta = false;\r\n\r\n let storage: {\r\n count?: number;\r\n history: SlicedArray;\r\n };\r\n\r\n // * костыль для limit 1, если нужно и получить сообщение, и узнать количество сообщений\r\n if(peerId && !backLimit && !maxId && !query && limit !== 1 && !threadId/* && inputFilter._ !== 'inputMessagesFilterPinned' */) {\r\n storage = beta ? \r\n this.getSearchStorage(peerId, inputFilter._) as any : \r\n this.getHistoryStorage(peerId);\r\n let filtering = true;\r\n\r\n const history = /* maxId ? storage.history.slice(storage.history.indexOf(maxId) + 1) : */storage.history;\r\n\r\n if(storage !== undefined && history.length) {\r\n const neededContents: {\r\n [messageMediaType: string]: boolean\r\n } = {},\r\n neededDocTypes: string[] = [], \r\n excludeDocTypes: string[] = []/* ,\r\n neededFlags: string[] = [] */;\r\n\r\n switch(inputFilter._) {\r\n case 'inputMessagesFilterPhotos':\r\n neededContents['messageMediaPhoto'] = true;\r\n break;\r\n\r\n case 'inputMessagesFilterPhotoVideo':\r\n neededContents['messageMediaPhoto'] = true;\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('video');\r\n break;\r\n\r\n case 'inputMessagesFilterVideo':\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('video');\r\n break;\r\n\r\n case 'inputMessagesFilterDocument':\r\n neededContents['messageMediaDocument'] = true;\r\n excludeDocTypes.push('video');\r\n break;\r\n\r\n case 'inputMessagesFilterVoice':\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('voice');\r\n break;\r\n\r\n case 'inputMessagesFilterRoundVoice':\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('round', 'voice');\r\n break;\r\n\r\n case 'inputMessagesFilterRoundVideo':\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('round');\r\n break;\r\n\r\n case 'inputMessagesFilterMusic':\r\n neededContents['messageMediaDocument'] = true;\r\n neededDocTypes.push('audio');\r\n break;\r\n\r\n case 'inputMessagesFilterUrl':\r\n neededContents['url'] = true;\r\n break;\r\n\r\n case 'inputMessagesFilterChatPhotos':\r\n neededContents['avatar'] = true;\r\n break;\r\n\r\n /* case 'inputMessagesFilterPinned':\r\n neededFlags.push('pinned');\r\n break; */\r\n\r\n /* case 'inputMessagesFilterMyMentions':\r\n neededContents['mentioned'] = true;\r\n break; */\r\n\r\n default:\r\n filtering = false;\r\n break;\r\n /* return Promise.resolve({\r\n count: 0,\r\n next_rate: 0,\r\n history: [] as number[]\r\n }); */\r\n }\r\n\r\n if(filtering) {\r\n const storage = this.getMessagesStorage(peerId);\r\n for(let i = 0, length = history.length; i < length; i++) {\r\n const message = storage[history.slice[i]];\r\n\r\n if(!message) continue;\r\n \r\n //|| (neededContents['mentioned'] && message.totalEntities.find((e: any) => e._ === 'messageEntityMention'));\r\n \r\n let found = false;\r\n if(message.media && neededContents[message.media._] && !message.fwd_from) {\r\n if(message.media._ === 'messageMediaDocument') {\r\n if((neededDocTypes.length && !neededDocTypes.includes(message.media.document.type)) \r\n || excludeDocTypes.includes(message.media.document.type)) {\r\n continue;\r\n }\r\n }\r\n \r\n found = true;\r\n } else if(neededContents['url'] && message.message) {\r\n const goodEntities = ['messageEntityTextUrl', 'messageEntityUrl'];\r\n if((message.totalEntities as MessageEntity[]).find(e => goodEntities.includes(e._)) || RichTextProcessor.matchUrl(message.message)) {\r\n found = true;\r\n }\r\n } else if(neededContents['avatar'] && message.action && ['messageActionChannelEditPhoto', 'messageActionChatEditPhoto', 'messageActionChannelEditVideo', 'messageActionChatEditVideo'].includes(message.action._)) {\r\n found = true;\r\n }/* else if(neededFlags.find(flag => message.pFlags[flag])) {\r\n found = true;\r\n } */\r\n \r\n if(found) {\r\n foundMsgs.push(message);\r\n if(foundMsgs.length >= limit) {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n if(foundMsgs.length) {\r\n if(foundMsgs.length < limit && (beta ? storage.count !== storage.history.length : true)) {\r\n maxId = foundMsgs[foundMsgs.length - 1].mid;\r\n limit = limit - foundMsgs.length;\r\n } else {\r\n return Promise.resolve({\r\n count: beta ? storage.count : 0,\r\n next_rate: 0,\r\n offset_id_offset: 0,\r\n history: foundMsgs\r\n });\r\n }\r\n } else if(beta && storage?.count) {\r\n return Promise.resolve({\r\n count: storage.count,\r\n next_rate: 0,\r\n offset_id_offset: 0,\r\n history: []\r\n });\r\n }\r\n\r\n const canCache = false && (['inputMessagesFilterChatPhotos', 'inputMessagesFilterPinned'] as MyInputMessagesFilter[]).includes(inputFilter._);\r\n const method = (canCache ? apiManager.invokeApiCacheable : apiManager.invokeApi).bind(apiManager);\r\n\r\n let apiPromise: Promise<any>;\r\n if(peerId && !nextRate && folderId === undefined/* || !query */) {\r\n apiPromise = method('messages.search', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n q: query || '',\r\n filter: inputFilter as any as MessagesFilter,\r\n min_date: minDate,\r\n max_date: maxDate,\r\n limit,\r\n offset_id: this.getServerMessageId(maxId) || 0,\r\n add_offset: backLimit ? -backLimit : 0,\r\n max_id: 0,\r\n min_id: 0,\r\n hash: 0,\r\n top_msg_id: this.getServerMessageId(threadId) || 0\r\n }, {\r\n //timeout: APITIMEOUT,\r\n noErrorBox: true\r\n });\r\n } else {\r\n //var offsetDate = 0;\r\n let offsetPeerId = 0;\r\n let offsetId = 0;\r\n let offsetMessage = maxId && this.getMessageByPeer(peerId, maxId);\r\n\r\n if(offsetMessage && offsetMessage.date) {\r\n //offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset;\r\n offsetId = offsetMessage.id;\r\n offsetPeerId = this.getMessagePeer(offsetMessage);\r\n }\r\n\r\n apiPromise = method('messages.searchGlobal', {\r\n q: query,\r\n filter: inputFilter as any as MessagesFilter,\r\n min_date: minDate,\r\n max_date: maxDate,\r\n offset_rate: nextRate,\r\n offset_peer: appPeersManager.getInputPeerById(offsetPeerId),\r\n offset_id: offsetId,\r\n limit,\r\n folder_id: folderId\r\n }, {\r\n //timeout: APITIMEOUT,\r\n noErrorBox: true\r\n });\r\n }\r\n\r\n return apiPromise.then((searchResult: any) => {\r\n appUsersManager.saveApiUsers(searchResult.users);\r\n appChatsManager.saveApiChats(searchResult.chats);\r\n this.saveMessages(searchResult.messages);\r\n\r\n /* if(beta && storage && (!maxId || storage.history[storage.history.length - 1] === maxId)) {\r\n const storage = this.getSearchStorage(peerId, inputFilter._);\r\n const add = (searchResult.messages.map((m: any) => m.mid) as number[]).filter(mid => storage.history.indexOf(mid) === -1);\r\n storage.history.push(...add);\r\n storage.history.sort((a, b) => b - a);\r\n storage.count = searchResult.count;\r\n } */\r\n\r\n if(DEBUG) {\r\n this.log('getSearch result:', inputFilter, searchResult);\r\n }\r\n\r\n const foundCount: number = searchResult.count || (foundMsgs.length + searchResult.messages.length);\r\n\r\n searchResult.messages.forEach((message: any) => {\r\n const peerId = this.getMessagePeer(message);\r\n if(peerId < 0) {\r\n const chat = appChatsManager.getChat(-peerId);\r\n if(chat.migrated_to) {\r\n this.migrateChecks(peerId, -chat.migrated_to.channel_id);\r\n }\r\n }\r\n\r\n foundMsgs.push(message);\r\n });\r\n\r\n return {\r\n count: foundCount,\r\n offset_id_offset: searchResult.offset_id_offset || 0,\r\n next_rate: searchResult.next_rate,\r\n history: foundMsgs\r\n };\r\n });\r\n }\r\n\r\n public subscribeRepliesThread(peerId: number, mid: number) {\r\n const repliesKey = peerId + '_' + mid;\r\n for(const threadKey in this.threadsToReplies) {\r\n if(this.threadsToReplies[threadKey] === repliesKey) return;\r\n }\r\n\r\n this.getDiscussionMessage(peerId, mid);\r\n }\r\n\r\n public generateThreadServiceStartMessage(message: Message.message) {\r\n const threadKey = message.peerId + '_' + message.mid;\r\n if(this.threadsServiceMessagesIdsStorage[threadKey]) return;\r\n\r\n const maxMessageId = this.getServerMessageId(Math.max(...this.getMidsByMessage(message)));\r\n const serviceStartMessage: Message.messageService = {\r\n _: 'messageService',\r\n pFlags: {\r\n is_single: true\r\n },\r\n id: this.generateMessageId(maxMessageId, true),\r\n date: message.date,\r\n from_id: {_: 'peerUser', user_id: 0}/* message.from_id */,\r\n peer_id: message.peer_id,\r\n action: {\r\n _: 'messageActionCustomAction',\r\n message: 'Discussion started'\r\n },\r\n reply_to: this.generateReplyHeader(message.id)\r\n };\r\n\r\n this.saveMessages([serviceStartMessage], {isOutgoing: true});\r\n this.threadsServiceMessagesIdsStorage[threadKey] = serviceStartMessage.mid;\r\n } \r\n\r\n public getDiscussionMessage(peerId: number, mid: number) {\r\n return apiManager.invokeApiSingle('messages.getDiscussionMessage', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n msg_id: this.getServerMessageId(mid)\r\n }).then(result => {\r\n appChatsManager.saveApiChats(result.chats);\r\n appUsersManager.saveApiUsers(result.users);\r\n this.saveMessages(result.messages);\r\n\r\n const message = this.filterMessages(result.messages[0], message => !!(message as Message.message).replies)[0] as Message.message;\r\n const threadKey = message.peerId + '_' + message.mid;\r\n\r\n this.generateThreadServiceStartMessage(message);\r\n \r\n const historyStorage = this.getHistoryStorage(message.peerId, message.mid);\r\n result.max_id = historyStorage.maxId = this.generateMessageId(result.max_id) || 0;\r\n result.read_inbox_max_id = historyStorage.readMaxId = this.generateMessageId(result.read_inbox_max_id ?? message.mid);\r\n result.read_outbox_max_id = historyStorage.readOutboxMaxId = this.generateMessageId(result.read_outbox_max_id) || 0;\r\n\r\n this.threadsToReplies[threadKey] = peerId + '_' + mid;\r\n\r\n return message;\r\n });\r\n }\r\n\r\n private handleNewMessage(peerId: number, mid: number) {\r\n if(this.newMessagesToHandle[peerId] === undefined) {\r\n this.newMessagesToHandle[peerId] = new Set();\r\n }\r\n\r\n this.newMessagesToHandle[peerId].add(mid);\r\n if(!this.newMessagesHandleTimeout) {\r\n this.newMessagesHandleTimeout = window.setTimeout(this.handleNewMessages, 0);\r\n }\r\n }\r\n\r\n handleNewMessages = () => {\r\n clearTimeout(this.newMessagesHandleTimeout);\r\n this.newMessagesHandleTimeout = 0;\r\n\r\n rootScope.dispatchEvent('history_multiappend', this.newMessagesToHandle);\r\n this.newMessagesToHandle = {};\r\n };\r\n\r\n handleNewDialogs = () => {\r\n let newMaxSeenId = 0;\r\n const obj = this.newDialogsToHandle;\r\n for(const peerId in obj) {\r\n const dialog = obj[peerId];\r\n if(!dialog) {\r\n this.reloadConversation(+peerId);\r\n delete obj[peerId];\r\n } else {\r\n this.dialogsStorage.pushDialog(dialog);\r\n if(!appPeersManager.isChannel(+peerId)) {\r\n newMaxSeenId = Math.max(newMaxSeenId, dialog.top_message || 0);\r\n }\r\n }\r\n }\r\n\r\n //this.log('after order:', this.dialogsStorage[0].map(d => d.peerId));\r\n\r\n if(newMaxSeenId !== 0) {\r\n this.incrementMaxSeenId(newMaxSeenId);\r\n }\r\n\r\n rootScope.dispatchEvent('dialogs_multiupdate', obj);\r\n this.newDialogsToHandle = {};\r\n };\r\n\r\n public scheduleHandleNewDialogs(peerId?: number, dialog?: Dialog) {\r\n if(peerId !== undefined) {\r\n this.newDialogsToHandle[peerId] = dialog;\r\n }\r\n\r\n if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise;\r\n return this.newDialogsHandlePromise = new Promise((resolve) => {\r\n setTimeout(() => {\r\n this.newDialogsHandlePromise = undefined;\r\n this.handleNewDialogs();\r\n }, 0);\r\n });\r\n }\r\n\r\n public deleteMessages(peerId: number, mids: number[], revoke?: boolean) {\r\n let promise: Promise<any>;\r\n\r\n const localMessageIds = mids.map(mid => this.getServerMessageId(mid));\r\n\r\n if(peerId < 0 && appPeersManager.isChannel(peerId)) {\r\n const channelId = -peerId;\r\n const channel = appChatsManager.getChat(channelId);\r\n if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) {\r\n const goodMsgIds: number[] = [];\r\n if(channel.pFlags.editor || channel.pFlags.megagroup) {\r\n mids.forEach((msgId, i) => {\r\n const message = this.getMessageByPeer(peerId, mids[i]);\r\n if(message.pFlags.out) {\r\n goodMsgIds.push(msgId);\r\n }\r\n });\r\n }\r\n\r\n if(!goodMsgIds.length) {\r\n return;\r\n }\r\n\r\n mids = goodMsgIds;\r\n }\r\n\r\n promise = apiManager.invokeApi('channels.deleteMessages', {\r\n channel: appChatsManager.getChannelInput(channelId),\r\n id: localMessageIds\r\n }).then((affectedMessages) => {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateDeleteChannelMessages',\r\n channel_id: channelId,\r\n messages: mids,\r\n pts: affectedMessages.pts,\r\n pts_count: affectedMessages.pts_count\r\n });\r\n });\r\n } else {\r\n promise = apiManager.invokeApi('messages.deleteMessages', {\r\n revoke,\r\n id: localMessageIds\r\n }).then((affectedMessages) => {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateDeleteMessages',\r\n messages: mids,\r\n pts: affectedMessages.pts,\r\n pts_count: affectedMessages.pts_count\r\n });\r\n });\r\n }\r\n\r\n return promise;\r\n }\r\n\r\n // TODO: cancel notification by peer when this function is being called\r\n public readHistory(peerId: number, maxId = 0, threadId?: number, force = false) {\r\n // return Promise.resolve();\r\n // console.trace('start read')\r\n this.log('readHistory:', peerId, maxId, threadId);\r\n if(!this.getReadMaxIdIfUnread(peerId, threadId) && !force) {\r\n this.log('readHistory: isn\\'t unread');\r\n return Promise.resolve();\r\n }\r\n\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n\r\n if(historyStorage.triedToReadMaxId >= maxId) {\r\n return Promise.resolve();\r\n }\r\n\r\n let apiPromise: Promise<any>;\r\n if(threadId) {\r\n if(!historyStorage.readPromise) {\r\n apiPromise = apiManager.invokeApi('messages.readDiscussion', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n msg_id: this.getServerMessageId(threadId),\r\n read_max_id: this.getServerMessageId(maxId)\r\n });\r\n }\r\n\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateReadChannelDiscussionInbox',\r\n channel_id: -peerId,\r\n top_msg_id: threadId,\r\n read_max_id: maxId\r\n });\r\n } else if(appPeersManager.isChannel(peerId)) {\r\n if(!historyStorage.readPromise) {\r\n apiPromise = apiManager.invokeApi('channels.readHistory', {\r\n channel: appChatsManager.getChannelInput(-peerId),\r\n max_id: this.getServerMessageId(maxId)\r\n });\r\n }\r\n\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateReadChannelInbox',\r\n max_id: maxId,\r\n channel_id: -peerId,\r\n still_unread_count: undefined,\r\n pts: undefined\r\n });\r\n } else {\r\n if(!historyStorage.readPromise) {\r\n apiPromise = apiManager.invokeApi('messages.readHistory', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n max_id: this.getServerMessageId(maxId)\r\n }).then((affectedMessages) => {\r\n apiUpdatesManager.processUpdateMessage({\r\n _: 'updateShort',\r\n update: {\r\n _: 'updatePts',\r\n pts: affectedMessages.pts,\r\n pts_count: affectedMessages.pts_count\r\n }\r\n });\r\n });\r\n }\r\n\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateReadHistoryInbox',\r\n max_id: maxId,\r\n peer: appPeersManager.getOutputPeer(peerId),\r\n still_unread_count: undefined,\r\n pts: undefined,\r\n pts_count: undefined\r\n });\r\n }\r\n\r\n appNotificationsManager.soundReset(appPeersManager.getPeerString(peerId));\r\n\r\n if(historyStorage.readPromise) {\r\n return historyStorage.readPromise;\r\n }\r\n\r\n historyStorage.triedToReadMaxId = maxId;\r\n\r\n apiPromise.finally(() => {\r\n delete historyStorage.readPromise;\r\n\r\n this.log('readHistory: promise finally', maxId, historyStorage.readMaxId);\r\n\r\n if(historyStorage.readMaxId > maxId) {\r\n this.readHistory(peerId, historyStorage.readMaxId, threadId, true);\r\n }\r\n });\r\n\r\n return historyStorage.readPromise = apiPromise;\r\n }\r\n\r\n public readAllHistory(peerId: number, threadId?: number, force = false) {\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n if(historyStorage.maxId) {\r\n this.readHistory(peerId, historyStorage.maxId, threadId, force); // lol\r\n }\r\n }\r\n\r\n public readMessages(peerId: number, msgIds: number[]) {\r\n msgIds = msgIds.map(mid => this.getServerMessageId(mid));\r\n if(peerId < 0 && appPeersManager.isChannel(peerId)) {\r\n const channelId = -peerId;\r\n apiManager.invokeApi('channels.readMessageContents', {\r\n channel: appChatsManager.getChannelInput(channelId),\r\n id: msgIds\r\n }).then(() => {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateChannelReadMessagesContents',\r\n channel_id: channelId,\r\n messages: msgIds\r\n });\r\n });\r\n } else {\r\n apiManager.invokeApi('messages.readMessageContents', {\r\n id: msgIds\r\n }).then((affectedMessages) => {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateReadMessagesContents',\r\n messages: msgIds,\r\n pts: affectedMessages.pts,\r\n pts_count: affectedMessages.pts_count\r\n });\r\n });\r\n }\r\n }\r\n\r\n public getHistoryStorage(peerId: number, threadId?: number) {\r\n if(threadId) {\r\n //threadId = this.getLocalMessageId(threadId);\r\n if(!this.threadsStorage[peerId]) this.threadsStorage[peerId] = {};\r\n return this.threadsStorage[peerId][threadId] ?? (this.threadsStorage[peerId][threadId] = {count: null, history: new SlicedArray()});\r\n }\r\n\r\n return this.historiesStorage[peerId] ?? (this.historiesStorage[peerId] = {count: null, history: new SlicedArray()});\r\n }\r\n\r\n private handleNotifications = () => {\r\n window.clearTimeout(this.notificationsHandlePromise);\r\n this.notificationsHandlePromise = 0;\r\n\r\n //var timeout = $rootScope.idle.isIDLE && StatusManager.isOtherDeviceActive() ? 30000 : 1000;\r\n //const timeout = 1000;\r\n\r\n for(const _peerId in this.notificationsToHandle) {\r\n const peerId = +_peerId;\r\n\r\n if(rootScope.peerId === peerId && !rootScope.idle.isIDLE) {\r\n continue;\r\n }\r\n\r\n const notifyPeerToHandle = this.notificationsToHandle[peerId];\r\n\r\n Promise.all([\r\n appNotificationsManager.getNotifyPeerTypeSettings(),\r\n appNotificationsManager.getNotifySettings(appPeersManager.getInputNotifyPeerById(peerId, true))\r\n ]).then(([_, peerTypeNotifySettings]) => {\r\n const topMessage = notifyPeerToHandle.topMessage;\r\n if(appNotificationsManager.isPeerLocalMuted(peerId, true) || !topMessage.pFlags.unread) {\r\n return;\r\n }\r\n\r\n //setTimeout(() => {\r\n if(topMessage.pFlags.unread) {\r\n this.notifyAboutMessage(topMessage, {\r\n fwdCount: notifyPeerToHandle.fwdCount,\r\n peerTypeNotifySettings\r\n });\r\n }\r\n //}, timeout);\r\n });\r\n }\r\n\r\n this.notificationsToHandle = {};\r\n };\r\n\r\n private onUpdateMessageId = (update: Update.updateMessageID) => {\r\n const randomId = update.random_id;\r\n const pendingData = this.pendingByRandomId[randomId];\r\n //this.log('AMM updateMessageID:', update, pendingData);\r\n if(pendingData) {\r\n const {peerId, tempId, threadId, storage} = pendingData;\r\n //const mid = update.id;\r\n const mid = this.generateMessageId(update.id);\r\n const message = this.getMessageFromStorage(storage, mid);\r\n if(!message.deleted) {\r\n [this.getHistoryStorage(peerId), threadId ? this.getHistoryStorage(peerId, threadId) : undefined]\r\n .filter(Boolean)\r\n .forEach(storage => {\r\n storage.history.delete(tempId);\r\n });\r\n\r\n this.finalizePendingMessageCallbacks(storage, tempId, mid);\r\n } else {\r\n this.pendingByMessageId[mid] = randomId;\r\n }\r\n }\r\n };\r\n\r\n private onUpdateNewMessage = (update: Update.updateNewDiscussionMessage | Update.updateNewMessage | Update.updateNewChannelMessage) => {\r\n const message = update.message as MyMessage;\r\n const peerId = this.getMessagePeer(message);\r\n const storage = this.getMessagesStorage(peerId);\r\n const dialog = this.getDialogOnly(peerId);\r\n\r\n // * local update\r\n const isLocalThreadUpdate = update._ === 'updateNewDiscussionMessage';\r\n\r\n // * temporary save the message for info (peerId, reply mids...)\r\n this.saveMessages([message], {storage: {}});\r\n\r\n const threadKey = this.getThreadKey(message);\r\n const threadId = threadKey ? +threadKey.split('_')[1] : undefined;\r\n if(threadId && !isLocalThreadUpdate && this.threadsStorage[peerId] && this.threadsStorage[peerId][threadId]) {\r\n const update = {\r\n _: 'updateNewDiscussionMessage',\r\n message\r\n } as Update.updateNewDiscussionMessage;\r\n\r\n this.onUpdateNewMessage(update);\r\n }\r\n\r\n if(!dialog && !isLocalThreadUpdate) {\r\n let good = true;\r\n if(peerId < 0) {\r\n good = appChatsManager.isInChat(-peerId);\r\n }\r\n\r\n if(good) {\r\n const set = this.newUpdatesAfterReloadToHandle[peerId] ?? (this.newUpdatesAfterReloadToHandle[peerId] = new Set());\r\n if(set.has(update)) {\r\n this.log.error('here we go again', peerId);\r\n return;\r\n }\r\n\r\n this.scheduleHandleNewDialogs(peerId);\r\n set.add(update);\r\n }\r\n\r\n return;\r\n }\r\n\r\n /* if(update._ === 'updateNewChannelMessage') {\r\n const chat = appChatsManager.getChat(-peerId);\r\n if(chat.pFlags && (chat.pFlags.left || chat.pFlags.kicked)) {\r\n return;\r\n }\r\n } */\r\n\r\n this.saveMessages([message], {storage});\r\n // this.log.warn(dT(), 'message unread', message.mid, message.pFlags.unread)\r\n\r\n /* if((message as Message.message).grouped_id) {\r\n this.log('updateNewMessage', message);\r\n } */\r\n\r\n const pendingMessage = this.checkPendingMessage(message);\r\n const historyStorage = this.getHistoryStorage(peerId, isLocalThreadUpdate ? threadId : undefined);\r\n\r\n if(!isLocalThreadUpdate) {\r\n this.updateMessageRepliesIfNeeded(message);\r\n }\r\n\r\n if(historyStorage.history.findSlice(message.mid)) {\r\n return false;\r\n }\r\n\r\n // * catch situation with disconnect. if message's id is lower than we already have (in bottom end slice), will sort it\r\n const firstSlice = historyStorage.history.first;\r\n if(firstSlice.isEnd(SliceEnd.Bottom)) {\r\n let i = 0;\r\n for(const length = firstSlice.length; i < length; ++i) {\r\n if(message.mid > firstSlice[i]) {\r\n break;\r\n }\r\n }\r\n\r\n firstSlice.splice(i, 0, message.mid);\r\n } else {\r\n historyStorage.history.unshift(message.mid);\r\n }\r\n\r\n if(historyStorage.count !== null) {\r\n historyStorage.count++;\r\n }\r\n\r\n if(this.mergeReplyKeyboard(historyStorage, message)) {\r\n rootScope.dispatchEvent('history_reply_markup', {peerId});\r\n }\r\n\r\n const fromId = message.fromId;\r\n if(fromId > 0 && !message.pFlags.out && message.from_id) {\r\n appUsersManager.forceUserOnline(fromId, message.date);\r\n\r\n const action: SendMessageAction = {\r\n _: 'sendMessageCancelAction'\r\n };\r\n\r\n let update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping;\r\n if(peerId > 0) {\r\n update = {\r\n _: 'updateUserTyping',\r\n action,\r\n user_id: fromId\r\n };\r\n } else if(appPeersManager.isChannel(peerId)) {\r\n update = {\r\n _: 'updateChannelUserTyping',\r\n action,\r\n channel_id: -peerId,\r\n from_id: appPeersManager.getOutputPeer(fromId),\r\n top_msg_id: threadId ? this.getServerMessageId(threadId) : undefined\r\n };\r\n } else {\r\n update = {\r\n _: 'updateChatUserTyping',\r\n action,\r\n chat_id: -peerId,\r\n from_id: appPeersManager.getOutputPeer(fromId)\r\n };\r\n }\r\n\r\n apiUpdatesManager.processLocalUpdate(update);\r\n }\r\n\r\n if(!pendingMessage) {\r\n this.handleNewMessage(peerId, message.mid);\r\n }\r\n\r\n if(isLocalThreadUpdate) {\r\n return;\r\n }\r\n \r\n const inboxUnread = !message.pFlags.out && message.pFlags.unread;\r\n if(dialog) {\r\n this.setDialogTopMessage(message, dialog);\r\n if(inboxUnread) {\r\n dialog.unread_count++;\r\n }\r\n }\r\n\r\n if(inboxUnread/* && ($rootScope.selectedPeerID != peerID || $rootScope.idle.isIDLE) */) {\r\n const notifyPeer = peerId;\r\n let notifyPeerToHandle = this.notificationsToHandle[notifyPeer];\r\n if(notifyPeerToHandle === undefined) {\r\n notifyPeerToHandle = this.notificationsToHandle[notifyPeer] = {\r\n fwdCount: 0,\r\n fromId: 0\r\n };\r\n }\r\n\r\n if(notifyPeerToHandle.fromId !== fromId) {\r\n notifyPeerToHandle.fromId = fromId;\r\n notifyPeerToHandle.fwdCount = 0;\r\n }\r\n\r\n if((message as Message.message).fwd_from) {\r\n notifyPeerToHandle.fwdCount++;\r\n }\r\n\r\n notifyPeerToHandle.topMessage = message;\r\n\r\n if(!this.notificationsHandlePromise) {\r\n this.notificationsHandlePromise = window.setTimeout(this.handleNotifications, 0);\r\n }\r\n }\r\n };\r\n\r\n private onUpdateDialogUnreadMark = (update: Update.updateDialogUnreadMark) => {\r\n //this.log('updateDialogUnreadMark', update);\r\n const peerId = appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer);\r\n const dialog = this.getDialogOnly(peerId);\r\n\r\n if(!dialog) {\r\n this.scheduleHandleNewDialogs(peerId);\r\n } else {\r\n if(!update.pFlags.unread) {\r\n delete dialog.pFlags.unread_mark;\r\n } else {\r\n dialog.pFlags.unread_mark = true;\r\n }\r\n\r\n rootScope.dispatchEvent('dialogs_multiupdate', {[peerId]: dialog});\r\n this.dialogsStorage.setDialogToState(dialog);\r\n }\r\n };\r\n\r\n private onUpdateEditMessage = (update: Update.updateEditMessage | Update.updateEditChannelMessage) => {\r\n const message = update.message as MyMessage;\r\n const peerId = this.getMessagePeer(message);\r\n const mid = this.generateMessageId(message.id);\r\n const storage = this.getMessagesStorage(peerId);\r\n if(storage[mid] === undefined) {\r\n return;\r\n }\r\n\r\n // console.trace(dT(), 'edit message', message)\r\n \r\n const oldMessage = this.getMessageFromStorage(storage, mid);\r\n this.saveMessages([message], {storage});\r\n const newMessage = this.getMessageFromStorage(storage, mid);\r\n\r\n this.handleEditedMessage(oldMessage, newMessage);\r\n\r\n const dialog = this.getDialogOnly(peerId);\r\n const isTopMessage = dialog && dialog.top_message === mid;\r\n // @ts-ignore\r\n if(message.clear_history) { // that's will never happen\r\n if(isTopMessage) {\r\n rootScope.dispatchEvent('dialog_flush', {peerId});\r\n }\r\n } else {\r\n rootScope.dispatchEvent('message_edit', {\r\n storage,\r\n peerId,\r\n mid\r\n });\r\n\r\n if(isTopMessage || (message as Message.message).grouped_id) {\r\n const updatedDialogs: {[peerId: number]: Dialog} = {};\r\n updatedDialogs[peerId] = dialog;\r\n rootScope.dispatchEvent('dialogs_multiupdate', updatedDialogs);\r\n this.dialogsStorage.setDialogToState(dialog);\r\n }\r\n }\r\n };\r\n\r\n private onUpdateReadHistory = (update: Update.updateReadChannelDiscussionInbox | Update.updateReadChannelDiscussionOutbox \r\n | Update.updateReadHistoryInbox | Update.updateReadHistoryOutbox \r\n | Update.updateReadChannelInbox | Update.updateReadChannelOutbox) => {\r\n const channelId = (update as Update.updateReadChannelInbox).channel_id;\r\n const maxId = this.generateMessageId((update as Update.updateReadChannelInbox).max_id || (update as Update.updateReadChannelDiscussionInbox).read_max_id);\r\n const threadId = this.generateMessageId((update as Update.updateReadChannelDiscussionInbox).top_msg_id);\r\n const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updateReadHistoryInbox).peer);\r\n\r\n const isOut = update._ === 'updateReadHistoryOutbox' || update._ === 'updateReadChannelOutbox' || update._ === 'updateReadChannelDiscussionOutbox' ? true : undefined;\r\n\r\n const storage = this.getMessagesStorage(peerId);\r\n const history = getObjectKeysAndSort(storage, 'desc');\r\n const foundDialog = this.getDialogOnly(peerId);\r\n const stillUnreadCount = (update as Update.updateReadChannelInbox).still_unread_count;\r\n let newUnreadCount = 0;\r\n let foundAffected = false;\r\n\r\n //this.log.warn(dT(), 'read', peerId, isOut ? 'out' : 'in', maxId)\r\n\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n\r\n if(peerId > 0 && isOut) {\r\n appUsersManager.forceUserOnline(peerId);\r\n }\r\n\r\n if(threadId) {\r\n const repliesKey = this.threadsToReplies[peerId + '_' + threadId];\r\n if(repliesKey) {\r\n const [peerId, mid] = repliesKey.split('_').map(n => +n);\r\n this.updateMessage(peerId, mid, 'replies_updated');\r\n }\r\n }\r\n\r\n for(let i = 0, length = history.length; i < length; i++) {\r\n const messageId = history[i];\r\n if(messageId > maxId) {\r\n continue;\r\n }\r\n \r\n const message = storage[messageId];\r\n\r\n if(message.pFlags.out !== isOut) {\r\n continue;\r\n }\r\n\r\n if(!message.pFlags.unread) {\r\n break;\r\n }\r\n\r\n if(threadId) {\r\n const replyTo = message.reply_to as MessageReplyHeader;\r\n if(!replyTo || (replyTo.reply_to_top_id || replyTo.reply_to_msg_id) !== threadId) {\r\n continue;\r\n }\r\n }\r\n \r\n // this.log.warn('read', messageId, message.pFlags.unread, message)\r\n if(message.pFlags.unread) {\r\n delete message.pFlags.unread;\r\n if(!foundAffected) {\r\n foundAffected = true;\r\n }\r\n\r\n if(!message.pFlags.out && !threadId && foundDialog && stillUnreadCount === undefined) {\r\n newUnreadCount = --foundDialog.unread_count;\r\n }\r\n \r\n appNotificationsManager.cancel('msg' + messageId);\r\n }\r\n }\r\n\r\n if(isOut) historyStorage.readOutboxMaxId = maxId;\r\n else historyStorage.readMaxId = maxId;\r\n\r\n if(!threadId && foundDialog) {\r\n if(isOut) foundDialog.read_outbox_max_id = maxId;\r\n else foundDialog.read_inbox_max_id = maxId;\r\n\r\n if(!isOut) {\r\n if(newUnreadCount < 0 || !this.getReadMaxIdIfUnread(peerId)) {\r\n foundDialog.unread_count = 0;\r\n } else if(newUnreadCount && foundDialog.top_message > maxId) {\r\n foundDialog.unread_count = newUnreadCount;\r\n }\r\n }\r\n \r\n rootScope.dispatchEvent('dialog_unread', {peerId});\r\n this.dialogsStorage.setDialogToState(foundDialog);\r\n }\r\n\r\n if(foundAffected) {\r\n rootScope.dispatchEvent('messages_read');\r\n }\r\n\r\n if(!threadId && channelId) {\r\n const threadKeyPart = peerId + '_';\r\n for(const threadKey in this.threadsToReplies) {\r\n if(threadKey.indexOf(threadKeyPart) === 0) {\r\n const [peerId, mid] = this.threadsToReplies[threadKey].split('_').map(n => +n);\r\n rootScope.dispatchEvent('replies_updated', this.getMessageByPeer(peerId, mid));\r\n }\r\n }\r\n }\r\n };\r\n\r\n private onUpdateReadMessagesContents = (update: Update.updateChannelReadMessagesContents | Update.updateReadMessagesContents) => {\r\n const channelId = (update as Update.updateChannelReadMessagesContents).channel_id;\r\n const mids = (update as Update.updateReadMessagesContents).messages.map(id => this.generateMessageId(id));\r\n const peerId = channelId ? -channelId : this.getMessageById(mids[0]).peerId;\r\n for(const mid of mids) {\r\n const message = this.getMessageByPeer(peerId, mid);\r\n if(!message.deleted) {\r\n delete message.pFlags.media_unread;\r\n this.setDialogToStateIfMessageIsTop(message);\r\n }\r\n }\r\n\r\n rootScope.dispatchEvent('messages_media_read', {peerId, mids});\r\n };\r\n\r\n private onUpdateChannelAvailableMessages = (update: Update.updateChannelAvailableMessages) => {\r\n const channelId: number = update.channel_id;\r\n const messages: number[] = [];\r\n const peerId: number = -channelId;\r\n const history = this.getHistoryStorage(peerId).history.slice;\r\n if(history.length) {\r\n history.forEach((msgId: number) => {\r\n if(!update.available_min_id || msgId <= update.available_min_id) {\r\n messages.push(msgId);\r\n }\r\n });\r\n }\r\n\r\n (update as any as Update.updateDeleteChannelMessages).messages = messages;\r\n this.onUpdateDeleteMessages(update as any as Update.updateDeleteChannelMessages);\r\n };\r\n\r\n private onUpdateDeleteMessages = (update: Update.updateDeleteMessages | Update.updateDeleteChannelMessages) => {\r\n const channelId: number = (update as Update.updateDeleteChannelMessages).channel_id;\r\n //const messages = (update as any as Update.updateDeleteChannelMessages).messages;\r\n const messages = (update as any as Update.updateDeleteChannelMessages).messages.map(id => this.generateMessageId(id));\r\n const peerId: number = channelId ? -channelId : this.getMessageById(messages[0]).peerId;\r\n \r\n if(!peerId) {\r\n return;\r\n }\r\n\r\n apiManager.clearCache('messages.getSearchCounters', (params) => {\r\n return appPeersManager.getPeerId(params.peer) === peerId;\r\n });\r\n\r\n const threadKeys: Set<string> = new Set();\r\n for(const mid of messages) {\r\n const message = this.getMessageByPeer(peerId, mid);\r\n const threadKey = this.getThreadKey(message);\r\n if(threadKey && this.threadsStorage[peerId] && this.threadsStorage[peerId][+threadKey.split('_')[1]]) {\r\n threadKeys.add(threadKey);\r\n }\r\n }\r\n \r\n const historyUpdated = this.handleDeletedMessages(peerId, this.getMessagesStorage(peerId), messages);\r\n\r\n const threadsStorages = Array.from(threadKeys).map(threadKey => {\r\n const splitted = threadKey.split('_');\r\n return this.getHistoryStorage(+splitted[0], +splitted[1]);\r\n });\r\n\r\n [this.getHistoryStorage(peerId)].concat(threadsStorages).forEach(historyStorage => {\r\n for(const mid in historyUpdated.msgs) {\r\n historyStorage.history.delete(+mid);\r\n }\r\n if(historyUpdated.count &&\r\n historyStorage.count !== null &&\r\n historyStorage.count > 0) {\r\n historyStorage.count -= historyUpdated.count;\r\n if(historyStorage.count < 0) {\r\n historyStorage.count = 0;\r\n }\r\n }\r\n });\r\n\r\n rootScope.dispatchEvent('history_delete', {peerId, msgs: historyUpdated.msgs});\r\n\r\n const foundDialog = this.getDialogOnly(peerId);\r\n if(foundDialog) {\r\n if(historyUpdated.unread) {\r\n foundDialog.unread_count -= historyUpdated.unread;\r\n\r\n rootScope.dispatchEvent('dialog_unread', {peerId});\r\n }\r\n\r\n if(historyUpdated.msgs[foundDialog.top_message]) {\r\n this.reloadConversation(peerId);\r\n }\r\n }\r\n };\r\n\r\n private onUpdateChannel = (update: Update.updateChannel) => {\r\n const channelId: number = update.channel_id;\r\n const peerId = -channelId;\r\n const channel: Chat.channel = appChatsManager.getChat(channelId);\r\n\r\n const needDialog = appChatsManager.isInChat(channelId);\r\n \r\n const canViewHistory = !!channel.username || !channel.pFlags.left;\r\n const hasHistory = this.historiesStorage[peerId] !== undefined;\r\n \r\n if(canViewHistory !== hasHistory) {\r\n delete this.historiesStorage[peerId];\r\n rootScope.dispatchEvent('history_forbidden', peerId);\r\n }\r\n \r\n const dialog = this.getDialogOnly(peerId);\r\n if(!!dialog !== needDialog) {\r\n if(needDialog) {\r\n this.reloadConversation(-channelId);\r\n } else {\r\n if(dialog) {\r\n this.dialogsStorage.dropDialog(peerId);\r\n rootScope.dispatchEvent('dialog_drop', {peerId, dialog});\r\n }\r\n }\r\n }\r\n };\r\n\r\n private onUpdateChannelReload = (update: any) => {\r\n // @ts-ignore\r\n const channelId: number = update.channel_id;\r\n const peerId = -channelId;\r\n\r\n this.dialogsStorage.dropDialog(peerId);\r\n\r\n delete this.historiesStorage[peerId];\r\n this.reloadConversation(-channelId).then(() => {\r\n rootScope.dispatchEvent('history_reload', peerId);\r\n });\r\n };\r\n \r\n private onUpdateChannelMessageViews = (update: Update.updateChannelMessageViews) => {\r\n const views = update.views;\r\n //const mid = update.id;\r\n const mid = this.generateMessageId(update.id);\r\n const message = this.getMessageByPeer(-update.channel_id, mid);\r\n if(!message.deleted && message.views && message.views < views) {\r\n message.views = views;\r\n rootScope.dispatchEvent('message_views', {mid, views});\r\n }\r\n };\r\n\r\n private onUpdateServiceNotification = (update: Update.updateServiceNotification) => {\r\n //this.log('updateServiceNotification', update);\r\n const fromId = 777000;\r\n const peerId = fromId;\r\n const messageId = this.generateTempMessageId(peerId);\r\n const message: any = {\r\n _: 'message',\r\n id: messageId,\r\n from_id: appPeersManager.getOutputPeer(fromId),\r\n peer_id: appPeersManager.getOutputPeer(peerId),\r\n pFlags: {unread: true},\r\n date: (update.inbox_date || tsNow(true)) + serverTimeManager.serverTimeOffset,\r\n message: update.message,\r\n media: update.media,\r\n entities: update.entities\r\n };\r\n if(!appUsersManager.hasUser(fromId)) {\r\n appUsersManager.saveApiUsers([{\r\n _: 'user',\r\n id: fromId,\r\n pFlags: {verified: true},\r\n access_hash: 0,\r\n first_name: 'Telegram',\r\n phone: '42777'\r\n }]);\r\n }\r\n this.saveMessages([message], {isOutgoing: true});\r\n\r\n if(update.inbox_date) {\r\n this.pendingTopMsgs[peerId] = messageId;\r\n this.onUpdateNewMessage({\r\n _: 'updateNewMessage',\r\n message\r\n } as any);\r\n }\r\n };\r\n\r\n private onUpdatePinnedMessages = (update: Update.updatePinnedMessages | Update.updatePinnedChannelMessages) => {\r\n const channelId = update._ === 'updatePinnedChannelMessages' ? update.channel_id : undefined;\r\n const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updatePinnedMessages).peer);\r\n\r\n /* const storage = this.getSearchStorage(peerId, 'inputMessagesFilterPinned');\r\n if(storage.count !== storage.history.length) {\r\n if(storage.count !== undefined) {\r\n delete this.searchesStorage[peerId]['inputMessagesFilterPinned']; \r\n }\r\n\r\n rootScope.broadcast('peer_pinned_messages', peerId);\r\n break;\r\n } */\r\n\r\n const messages = update.messages.map(id => this.generateMessageId(id)); \r\n\r\n const storage = this.getMessagesStorage(peerId);\r\n const missingMessages = messages.filter(mid => !storage[mid]);\r\n const getMissingPromise = missingMessages.length ? Promise.all(missingMessages.map(mid => this.wrapSingleMessage(peerId, mid))) : Promise.resolve();\r\n getMissingPromise.finally(() => {\r\n const werePinned = update.pFlags?.pinned;\r\n if(werePinned) {\r\n for(const mid of messages) {\r\n //storage.history.push(mid);\r\n const message = storage[mid];\r\n message.pFlags.pinned = true;\r\n }\r\n\r\n /* if(this.pinnedMessages[peerId]?.maxId) {\r\n const maxMid = Math.max(...messages);\r\n this.pinnedMessages\r\n } */\r\n\r\n //storage.history.sort((a, b) => b - a);\r\n } else {\r\n for(const mid of messages) {\r\n //storage.history.findAndSplice(_mid => _mid === mid);\r\n const message = storage[mid];\r\n delete message.pFlags.pinned;\r\n }\r\n }\r\n\r\n /* const info = this.pinnedMessages[peerId];\r\n if(info) {\r\n info.count += messages.length * (werePinned ? 1 : -1);\r\n } */\r\n \r\n delete this.pinnedMessages[peerId];\r\n appStateManager.getState().then(state => {\r\n delete state.hiddenPinnedMessages[peerId];\r\n rootScope.dispatchEvent('peer_pinned_messages', {peerId, mids: messages, pinned: werePinned});\r\n });\r\n });\r\n };\r\n\r\n private onUpdateNotifySettings = (update: Update.updateNotifySettings) => {\r\n const {peer, notify_settings} = update;\r\n if(peer._ === 'notifyPeer') {\r\n const peerId = appPeersManager.getPeerId((peer as NotifyPeer.notifyPeer).peer);\r\n \r\n const dialog = this.getDialogOnly(peerId);\r\n if(dialog) {\r\n dialog.notify_settings = notify_settings;\r\n rootScope.dispatchEvent('dialog_notify_settings', dialog);\r\n this.dialogsStorage.setDialogToState(dialog);\r\n }\r\n }\r\n };\r\n\r\n private onUpdateNewScheduledMessage = (update: Update.updateNewScheduledMessage) => {\r\n const message = update.message as MyMessage;\r\n const peerId = this.getMessagePeer(message);\r\n\r\n const storage = this.scheduledMessagesStorage[peerId];\r\n if(storage) {\r\n const mid = this.generateMessageId(message.id);\r\n\r\n const oldMessage = this.getMessageFromStorage(storage, mid);\r\n this.saveMessages([message], {storage, isScheduled: true});\r\n const newMessage = this.getMessageFromStorage(storage, mid);\r\n\r\n if(!oldMessage.deleted) {\r\n this.handleEditedMessage(oldMessage, newMessage);\r\n rootScope.dispatchEvent('message_edit', {storage, peerId, mid: message.mid});\r\n } else {\r\n const pendingMessage = this.checkPendingMessage(message);\r\n if(!pendingMessage) {\r\n rootScope.dispatchEvent('scheduled_new', {peerId, mid: message.mid});\r\n }\r\n }\r\n }\r\n };\r\n\r\n private onUpdateDeleteScheduledMessages = (update: Update.updateDeleteScheduledMessages) => {\r\n const peerId = appPeersManager.getPeerId(update.peer);\r\n\r\n const storage = this.scheduledMessagesStorage[peerId];\r\n if(storage) {\r\n const mids = update.messages.map(id => this.generateMessageId(id));\r\n this.handleDeletedMessages(peerId, storage, mids);\r\n\r\n rootScope.dispatchEvent('scheduled_delete', {peerId, mids});\r\n }\r\n };\r\n\r\n public setDialogToStateIfMessageIsTop(message: any) {\r\n const dialog = this.getDialogOnly(message.peerId);\r\n if(dialog && dialog.top_message === message.mid) {\r\n this.dialogsStorage.setDialogToState(dialog);\r\n }\r\n }\r\n\r\n private updateMessageRepliesIfNeeded(threadMessage: MyMessage) {\r\n try { // * на всякий случай, скорее всего это не понадобится\r\n const threadKey = this.getThreadKey(threadMessage);\r\n if(threadKey) {\r\n const repliesKey = this.threadsToReplies[threadKey];\r\n if(repliesKey) {\r\n const [peerId, mid] = repliesKey.split('_').map(n => +n);\r\n\r\n this.updateMessage(peerId, mid, 'replies_updated');\r\n }\r\n }\r\n } catch(err) {\r\n this.log.error('incrementMessageReplies err', err, threadMessage);\r\n }\r\n }\r\n\r\n private getThreadKey(threadMessage: MyMessage) {\r\n let threadKey = '';\r\n if(threadMessage.peerId < 0 && threadMessage.reply_to) {\r\n const threadId = threadMessage.reply_to.reply_to_top_id || threadMessage.reply_to.reply_to_msg_id;\r\n threadKey = threadMessage.peerId + '_' + threadId;\r\n }\r\n\r\n return threadKey;\r\n }\r\n\r\n public updateMessage(peerId: number, mid: number, broadcastEventName?: 'replies_updated'): Promise<Message.message> {\r\n const promise: Promise<Message.message> = this.wrapSingleMessage(peerId, mid, true).then(() => {\r\n const message = this.getMessageByPeer(peerId, mid);\r\n\r\n if(broadcastEventName) {\r\n rootScope.dispatchEvent(broadcastEventName, message);\r\n }\r\n\r\n return message;\r\n });\r\n \r\n return promise;\r\n }\r\n\r\n private checkPendingMessage(message: any) {\r\n const randomId = this.pendingByMessageId[message.mid];\r\n let pendingMessage: any;\r\n if(randomId) {\r\n const pendingData = this.pendingByRandomId[randomId];\r\n if(pendingMessage = this.finalizePendingMessage(randomId, message)) {\r\n rootScope.dispatchEvent('history_update', {storage: pendingData.storage, peerId: message.peerId, mid: message.mid});\r\n }\r\n\r\n delete this.pendingByMessageId[message.mid];\r\n }\r\n\r\n return pendingMessage;\r\n }\r\n\r\n public mutePeer(peerId: number, mute?: boolean) {\r\n const settings: InputPeerNotifySettings = {\r\n _: 'inputPeerNotifySettings'\r\n };\r\n\r\n if(mute === undefined) {\r\n mute = !appNotificationsManager.isPeerLocalMuted(peerId, false);\r\n }\r\n \r\n settings.mute_until = mute ? 0x7FFFFFFF : 0;\r\n\r\n return appNotificationsManager.updateNotifySettings({\r\n _: 'inputNotifyPeer',\r\n peer: appPeersManager.getInputPeerById(peerId)\r\n }, settings);\r\n }\r\n\r\n public canWriteToPeer(peerId: number, threadId?: number) {\r\n if(peerId < 0) {\r\n //const isChannel = appPeersManager.isChannel(peerId);\r\n const hasRights = /* isChannel && */appChatsManager.hasRights(-peerId, 'send_messages', undefined, !!threadId); \r\n return /* !isChannel || */hasRights;\r\n } else {\r\n return appUsersManager.canSendToUser(peerId);\r\n }\r\n }\r\n\r\n public finalizePendingMessage(randomId: string, finalMessage: any) {\r\n const pendingData = this.pendingByRandomId[randomId];\r\n // this.log('pdata', randomID, pendingData)\r\n\r\n if(pendingData) {\r\n const {peerId, tempId, threadId, storage} = pendingData;\r\n\r\n [this.getHistoryStorage(peerId), threadId ? this.getHistoryStorage(peerId, threadId) : undefined]\r\n .filter(Boolean)\r\n .forEach(storage => {\r\n storage.history.delete(tempId);\r\n });\r\n\r\n // this.log('pending', randomID, historyStorage.pending)\r\n\r\n const message = this.getMessageFromStorage(storage, tempId);\r\n if(!message.deleted) {\r\n delete message.pFlags.is_outgoing;\r\n delete message.pending;\r\n delete message.error;\r\n delete message.random_id;\r\n delete message.send;\r\n\r\n rootScope.dispatchEvent('messages_pending');\r\n }\r\n \r\n delete this.pendingByRandomId[randomId];\r\n\r\n this.finalizePendingMessageCallbacks(storage, tempId, finalMessage.mid);\r\n\r\n return message;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n public finalizePendingMessageCallbacks(storage: MessagesStorage, tempId: number, mid: number) {\r\n const message = this.getMessageFromStorage(storage, mid);\r\n const callbacks = this.tempFinalizeCallbacks[tempId];\r\n //this.log.warn(callbacks, tempId);\r\n if(callbacks !== undefined) {\r\n for(const name in callbacks) {\r\n const {deferred, callback} = callbacks[name];\r\n //this.log(`finalizePendingMessageCallbacks: will invoke ${name} callback`);\r\n callback(message).then(deferred.resolve, deferred.reject);\r\n }\r\n\r\n delete this.tempFinalizeCallbacks[tempId];\r\n }\r\n\r\n // set cached url to media\r\n if(message.media) {\r\n if(message.media.photo) {\r\n const photo = appPhotosManager.getPhoto('' + tempId);\r\n if(/* photo._ !== 'photoEmpty' */photo) {\r\n const newPhoto = message.media.photo as MyPhoto;\r\n const newPhotoSize = newPhoto.sizes[newPhoto.sizes.length - 1];\r\n const cacheContext = appDownloadManager.getCacheContext(newPhoto, newPhotoSize.type);\r\n const oldCacheContext = appDownloadManager.getCacheContext(photo, 'full');\r\n Object.assign(cacheContext, oldCacheContext);\r\n\r\n const photoSize = newPhoto.sizes[newPhoto.sizes.length - 1] as PhotoSize.photoSize;\r\n\r\n const downloadOptions = appPhotosManager.getPhotoDownloadOptions(newPhoto, photoSize);\r\n const fileName = getFileNameByLocation(downloadOptions.location);\r\n appDownloadManager.fakeDownload(fileName, oldCacheContext.url);\r\n }\r\n } else if(message.media.document) {\r\n const doc = appDocsManager.getDoc('' + tempId);\r\n if(doc) {\r\n if(/* doc._ !== 'documentEmpty' && */doc.type && doc.type !== 'sticker') {\r\n const newDoc = message.media.document;\r\n const cacheContext = appDownloadManager.getCacheContext(newDoc);\r\n const oldCacheContext = appDownloadManager.getCacheContext(doc);\r\n Object.assign(cacheContext, oldCacheContext);\r\n\r\n const fileName = appDocsManager.getInputFileName(newDoc);\r\n appDownloadManager.fakeDownload(fileName, oldCacheContext.url);\r\n }\r\n }\r\n } else if(message.media.poll) {\r\n delete appPollsManager.polls[tempId];\r\n delete appPollsManager.results[tempId];\r\n }\r\n }\r\n\r\n const tempMessage = this.getMessageFromStorage(storage, tempId);\r\n delete storage[tempId];\r\n \r\n this.handleReleasingMessage(tempMessage);\r\n\r\n rootScope.dispatchEvent('message_sent', {storage, tempId, tempMessage, mid});\r\n }\r\n\r\n public incrementMaxSeenId(maxId: number) {\r\n if(!maxId || !(!this.maxSeenId || maxId > this.maxSeenId)) {\r\n return false;\r\n }\r\n\r\n this.maxSeenId = maxId;\r\n appStateManager.pushToState('maxSeenMsgId', maxId);\r\n\r\n apiManager.invokeApi('messages.receivedMessages', {\r\n max_id: this.getServerMessageId(maxId)\r\n });\r\n }\r\n\r\n private notifyAboutMessage(message: MyMessage, options: Partial<{\r\n fwdCount: number,\r\n peerTypeNotifySettings: PeerNotifySettings\r\n }> = {}) {\r\n const peerId = this.getMessagePeer(message);\r\n const notification: NotifyOptions = {};\r\n const peerString = appPeersManager.getPeerString(peerId);\r\n let notificationMessage: string;\r\n\r\n if(options.peerTypeNotifySettings.show_previews) {\r\n if(message._ === 'message' && message.fwd_from && options.fwdCount) {\r\n notificationMessage = I18n.format('Notifications.Forwarded', true, [options.fwdCount]);\r\n } else {\r\n notificationMessage = this.wrapMessageForReply(message, undefined, undefined, true);\r\n }\r\n } else {\r\n notificationMessage = I18n.format('Notifications.New', true);\r\n }\r\n\r\n notification.title = appPeersManager.getPeerTitle(peerId, true);\r\n if(peerId < 0 && message.fromId !== message.peerId) {\r\n notification.title = appPeersManager.getPeerTitle(message.fromId, true) +\r\n ' @ ' +\r\n notification.title;\r\n }\r\n\r\n notification.title = RichTextProcessor.wrapPlainText(notification.title);\r\n\r\n notification.onclick = () => {\r\n rootScope.dispatchEvent('history_focus', {peerId, mid: message.mid});\r\n };\r\n\r\n notification.message = notificationMessage;\r\n notification.key = 'msg' + message.mid;\r\n notification.tag = peerString;\r\n notification.silent = true;//message.pFlags.silent || false;\r\n\r\n const peerPhoto = appPeersManager.getPeerPhoto(peerId);\r\n if(peerPhoto) {\r\n appAvatarsManager.loadAvatar(peerId, peerPhoto, 'photo_small').loadPromise.then(url => {\r\n if(message.pFlags.unread) {\r\n notification.image = url;\r\n appNotificationsManager.notify(notification);\r\n }\r\n });\r\n } else {\r\n appNotificationsManager.notify(notification);\r\n }\r\n }\r\n\r\n public getScheduledMessagesStorage(peerId: number) {\r\n return this.scheduledMessagesStorage[peerId] ?? (this.scheduledMessagesStorage[peerId] = this.createMessageStorage());\r\n }\r\n\r\n public getScheduledMessages(peerId: number): Promise<number[]> {\r\n if(!this.canWriteToPeer(peerId)) return Promise.resolve([]);\r\n\r\n const storage = this.getScheduledMessagesStorage(peerId);\r\n if(Object.keys(storage).length) {\r\n return Promise.resolve(Object.keys(storage).map(id => +id));\r\n }\r\n\r\n return apiManager.invokeApiSingle('messages.getScheduledHistory', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n hash: 0\r\n }).then(historyResult => {\r\n if(historyResult._ !== 'messages.messagesNotModified') {\r\n appUsersManager.saveApiUsers(historyResult.users);\r\n appChatsManager.saveApiChats(historyResult.chats);\r\n \r\n const storage = this.getScheduledMessagesStorage(peerId);\r\n this.saveMessages(historyResult.messages, {storage, isScheduled: true});\r\n return Object.keys(storage).map(id => +id);\r\n }\r\n \r\n return [];\r\n });\r\n }\r\n\r\n public sendScheduledMessages(peerId: number, mids: number[]) {\r\n return apiManager.invokeApi('messages.sendScheduledMessages', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n id: mids.map(mid => this.getServerMessageId(mid))\r\n }).then(updates => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n }\r\n\r\n public deleteScheduledMessages(peerId: number, mids: number[]) {\r\n return apiManager.invokeApi('messages.deleteScheduledMessages', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n id: mids.map(mid => this.getServerMessageId(mid))\r\n }).then(updates => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n }\r\n\r\n public getMessageWithReplies(message: Message.message) {\r\n if(message.peerId !== REPLIES_PEER_ID) {\r\n message = this.filterMessages(message, message => !!(message as Message.message).replies)[0] as any;\r\n if(!(message && message.replies && message.replies.pFlags.comments && message.replies.channel_id !== 777)) {\r\n return;\r\n }\r\n }\r\n\r\n return message;\r\n }\r\n\r\n public isFetchIntervalNeeded(peerId: number) {\r\n return peerId < 0 && !appChatsManager.isInChat(peerId);\r\n }\r\n\r\n public async getNewHistory(peerId: number, threadId?: number) {\r\n if(!this.isFetchIntervalNeeded(peerId)) {\r\n return;\r\n }\r\n\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n const slice = historyStorage.history.slice;\r\n if(!slice.isEnd(SliceEnd.Bottom)) {\r\n return;\r\n }\r\n\r\n delete historyStorage.maxId;\r\n slice.unsetEnd(SliceEnd.Bottom);\r\n\r\n // if there is no id - then request by first id because cannot request by id 0 with backLimit\r\n let historyResult = this.getHistory(peerId, slice[0] ?? 1, 0, 50, threadId);\r\n if(historyResult instanceof Promise) {\r\n historyResult = await historyResult;\r\n }\r\n\r\n for(let i = 0, length = historyResult.history.length; i < length; ++i) {\r\n this.handleNewMessage(peerId, historyResult.history[i]);\r\n }\r\n\r\n return historyStorage;\r\n }\r\n\r\n /**\r\n * * https://core.telegram.org/api/offsets, offset_id is inclusive\r\n */\r\n public getHistory(peerId: number, maxId = 0, limit: number, backLimit?: number, threadId?: number): Promise<HistoryResult> | HistoryResult {\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n\r\n let offset = 0;\r\n /* \r\n let offsetFound = true;\r\n\r\n if(maxId) {\r\n offsetFound = false;\r\n for(; offset < historyStorage.history.length; offset++) {\r\n if(maxId > historyStorage.history.slice[offset]) {\r\n offsetFound = true;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if(offsetFound && (\r\n historyStorage.count !== null && historyStorage.history.length === historyStorage.count ||\r\n historyStorage.history.length >= offset + limit\r\n )) {\r\n if(backLimit) {\r\n backLimit = Math.min(offset, backLimit);\r\n offset = Math.max(0, offset - backLimit);\r\n limit += backLimit;\r\n } else {\r\n limit = limit;\r\n }\r\n\r\n const history = historyStorage.history.slice.slice(offset, offset + limit);\r\n return {\r\n count: historyStorage.count,\r\n history: history,\r\n offsetIdOffset: offset\r\n };\r\n }\r\n\r\n if(offsetFound) {\r\n offset = 0;\r\n } */\r\n\r\n if(backLimit) {\r\n offset = -backLimit;\r\n limit += backLimit;\r\n\r\n /* return this.requestHistory(reqPeerId, maxId, limit, offset, undefined, threadId).then((historyResult) => {\r\n historyStorage.count = (historyResult as MessagesMessages.messagesMessagesSlice).count || historyResult.messages.length;\r\n\r\n const history = (historyResult.messages as MyMessage[]).map(message => message.mid);\r\n return {\r\n count: historyStorage.count,\r\n history,\r\n offsetIdOffset: (historyResult as MessagesMessages.messagesMessagesSlice).offset_id_offset || 0\r\n };\r\n }); */\r\n }\r\n\r\n const haveSlice = historyStorage.history.sliceMe(maxId, offset, limit);\r\n if(haveSlice && (haveSlice.slice.length === limit || (haveSlice.fulfilled & SliceEnd.Both) === SliceEnd.Both)) {\r\n return {\r\n count: historyStorage.count,\r\n history: haveSlice.slice,\r\n offsetIdOffset: haveSlice.offsetIdOffset\r\n }; \r\n }\r\n\r\n return this.fillHistoryStorage(peerId, maxId, limit, offset, historyStorage, threadId).then(() => {\r\n const slice = historyStorage.history.sliceMe(maxId, offset, limit);\r\n return {\r\n count: historyStorage.count,\r\n history: slice?.slice || historyStorage.history.constructSlice(),\r\n offsetIdOffset: slice?.offsetIdOffset || historyStorage.count\r\n };\r\n });\r\n }\r\n\r\n public fillHistoryStorage(peerId: number, offset_id: number, limit: number, add_offset: number, historyStorage: HistoryStorage, threadId?: number): Promise<void> {\r\n return this.requestHistory(peerId, offset_id, limit, add_offset, undefined, threadId).then((historyResult) => {\r\n const {offset_id_offset, count, messages} = historyResult as MessagesMessages.messagesMessagesSlice;\r\n\r\n historyStorage.count = count || messages.length;\r\n const offsetIdOffset = offset_id_offset || 0;\r\n\r\n const topWasMeantToLoad = add_offset < 0 ? limit + add_offset : limit;\r\n\r\n const isTopEnd = offsetIdOffset >= (historyStorage.count - topWasMeantToLoad) || historyStorage.count < topWasMeantToLoad;\r\n const isBottomEnd = !offsetIdOffset || (add_offset < 0 && (offsetIdOffset + add_offset) <= 0);\r\n\r\n /* if(!maxId && historyResult.messages.length) {\r\n maxId = this.incrementMessageId((historyResult.messages[0] as MyMessage).mid, 1);\r\n }\r\n\r\n const wasTotalCount = historyStorage.history.length; */\r\n\r\n const mids = messages.map((message) => {\r\n if(this.mergeReplyKeyboard(historyStorage, message as MyMessage)) {\r\n rootScope.dispatchEvent('history_reply_markup', {peerId});\r\n }\r\n\r\n return (message as MyMessage).mid;\r\n });\r\n\r\n // * add bound manually. \r\n // * offset_id will be inclusive only if there is 'add_offset' <= -1 (-1 - will only include the 'offset_id')\r\n if(offset_id && !mids.includes(offset_id) && offsetIdOffset < historyStorage.count) {\r\n let i = 0;\r\n for(const length = mids.length; i < length; ++i) {\r\n if(offset_id > mids[i]) {\r\n break;\r\n }\r\n }\r\n\r\n mids.splice(i, 0, offset_id);\r\n }\r\n \r\n const slice = historyStorage.history.insertSlice(mids) || historyStorage.history.slice;\r\n if(isTopEnd) {\r\n slice.setEnd(SliceEnd.Top);\r\n }\r\n \r\n if(isBottomEnd) {\r\n slice.setEnd(SliceEnd.Bottom);\r\n historyStorage.maxId = slice[0]; // ! WARNING\r\n }\r\n \r\n /* const isBackLimit = offset < 0 && -offset !== fullLimit;\r\n if(isBackLimit) {\r\n return;\r\n }\r\n\r\n const totalCount = historyStorage.history.length;\r\n fullLimit -= (totalCount - wasTotalCount);\r\n\r\n const migratedNextPeer = this.migratedFromTo[peerId];\r\n const migratedPrevPeer = this.migratedToFrom[peerId]\r\n const isMigrated = migratedNextPeer !== undefined || migratedPrevPeer !== undefined;\r\n\r\n if(isMigrated) {\r\n historyStorage.count = Math.max(historyStorage.count, totalCount) + 1;\r\n }\r\n\r\n if(fullLimit > 0) {\r\n maxId = historyStorage.history.slice[totalCount - 1];\r\n if(isMigrated) {\r\n if(!historyResult.messages.length) {\r\n if(migratedPrevPeer) {\r\n maxId = 0;\r\n peerId = migratedPrevPeer;\r\n } else {\r\n historyStorage.count = totalCount;\r\n return true;\r\n }\r\n }\r\n\r\n return this.fillHistoryStorage(peerId, maxId, fullLimit, historyStorage, threadId);\r\n } else if(totalCount < historyStorage.count) {\r\n return this.fillHistoryStorage(peerId, maxId, fullLimit, offset, historyStorage, threadId);\r\n }\r\n } */\r\n });\r\n }\r\n\r\n public requestHistory(peerId: number, maxId: number, limit = 0, offset = 0, offsetDate = 0, threadId = 0): Promise<Exclude<MessagesMessages, MessagesMessages.messagesMessagesNotModified>> {\r\n //console.trace('requestHistory', peerId, maxId, limit, offset);\r\n\r\n //rootScope.broadcast('history_request');\r\n\r\n const options: any = {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n offset_id: this.getServerMessageId(maxId) || 0,\r\n offset_date: offsetDate,\r\n add_offset: offset,\r\n limit,\r\n max_id: 0,\r\n min_id: 0,\r\n hash: 0\r\n };\r\n\r\n if(threadId) {\r\n options.msg_id = this.getServerMessageId(threadId) || 0;\r\n }\r\n\r\n const promise: ReturnType<AppMessagesManager['requestHistory']> = apiManager.invokeApiSingle(threadId ? 'messages.getReplies' : 'messages.getHistory', options, {\r\n //timeout: APITIMEOUT,\r\n noErrorBox: true\r\n }) as any;\r\n\r\n return promise.then((historyResult) => {\r\n if(DEBUG) {\r\n this.log('requestHistory result:', peerId, historyResult, maxId, limit, offset);\r\n }\r\n\r\n appUsersManager.saveApiUsers(historyResult.users);\r\n appChatsManager.saveApiChats(historyResult.chats);\r\n this.saveMessages(historyResult.messages);\r\n\r\n if(appPeersManager.isChannel(peerId)) {\r\n apiUpdatesManager.addChannelState(-peerId, (historyResult as MessagesMessages.messagesChannelMessages).pts);\r\n }\r\n\r\n let length = historyResult.messages.length, count = (historyResult as MessagesMessages.messagesMessagesSlice).count;\r\n if(length && historyResult.messages[length - 1].deleted) {\r\n historyResult.messages.splice(length - 1, 1);\r\n length--;\r\n count--;\r\n }\r\n\r\n // will load more history if last message is album grouped (because it can be not last item)\r\n // historyResult.messages: desc sorted\r\n const historyStorage = this.getHistoryStorage(peerId, threadId);\r\n const oldestMessage: Message.message = historyResult.messages[length - 1] as any;\r\n if(length && oldestMessage.grouped_id) {\r\n const foundSlice = historyStorage.history.findSlice(oldestMessage.mid);\r\n if(foundSlice && (foundSlice.slice.length + historyResult.messages.length) < count) {\r\n return this.requestHistory(peerId, oldestMessage.mid, 10, 0, offsetDate, threadId).then((_historyResult) => {\r\n return historyResult;\r\n });\r\n }\r\n }\r\n\r\n return historyResult;\r\n }, (error) => {\r\n switch (error.type) {\r\n case 'CHANNEL_PRIVATE':\r\n let channel = appChatsManager.getChat(-peerId);\r\n channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};\r\n apiUpdatesManager.processUpdateMessage({\r\n _: 'updates',\r\n updates: [{\r\n _: 'updateChannel',\r\n channel_id: -peerId\r\n }],\r\n chats: [channel],\r\n users: []\r\n });\r\n break;\r\n }\r\n\r\n throw error;\r\n });\r\n }\r\n\r\n public fetchSingleMessages() {\r\n if(this.fetchSingleMessagesPromise) {\r\n return this.fetchSingleMessagesPromise;\r\n }\r\n\r\n return this.fetchSingleMessagesPromise = new Promise((resolve) => {\r\n setTimeout(() => {\r\n let promises: Promise<void>[] = [];\r\n \r\n for(const peerId in this.needSingleMessages) {\r\n const mids = this.needSingleMessages[peerId];\r\n delete this.needSingleMessages[peerId];\r\n \r\n const msgIds: InputMessage[] = mids.map((msgId: number) => {\r\n return {\r\n _: 'inputMessageID',\r\n id: this.getServerMessageId(msgId)\r\n };\r\n });\r\n \r\n let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;\r\n if(+peerId < 0 && appPeersManager.isChannel(+peerId)) {\r\n promise = apiManager.invokeApiSingle('channels.getMessages', {\r\n channel: appChatsManager.getChannelInput(-+peerId),\r\n id: msgIds\r\n });\r\n } else {\r\n promise = apiManager.invokeApiSingle('messages.getMessages', {\r\n id: msgIds\r\n });\r\n }\r\n \r\n promises.push(promise.then(getMessagesResult => {\r\n if(getMessagesResult._ !== 'messages.messagesNotModified') {\r\n appUsersManager.saveApiUsers(getMessagesResult.users);\r\n appChatsManager.saveApiChats(getMessagesResult.chats);\r\n this.saveMessages(getMessagesResult.messages);\r\n }\r\n \r\n rootScope.dispatchEvent('messages_downloaded', {peerId: +peerId, mids});\r\n }));\r\n }\r\n\r\n Promise.all(promises).finally(() => {\r\n this.fetchSingleMessagesPromise = null;\r\n if(Object.keys(this.needSingleMessages).length) this.fetchSingleMessages();\r\n resolve();\r\n });\r\n }, 0);\r\n });\r\n }\r\n\r\n public wrapSingleMessage(peerId: number, msgId: number, overwrite = false): Promise<void> {\r\n if(!this.getMessageByPeer(peerId, msgId).deleted && !overwrite) {\r\n rootScope.dispatchEvent('messages_downloaded', {peerId, mids: [msgId]});\r\n return Promise.resolve();\r\n } else if(!this.needSingleMessages[peerId] || this.needSingleMessages[peerId].indexOf(msgId) === -1) {\r\n (this.needSingleMessages[peerId] ?? (this.needSingleMessages[peerId] = [])).push(msgId);\r\n return this.fetchSingleMessages();\r\n } else if(this.fetchSingleMessagesPromise) {\r\n return this.fetchSingleMessagesPromise;\r\n }\r\n }\r\n\r\n public setTyping(peerId: number, action: SendMessageAction): Promise<boolean> {\r\n let typing = this.typings[peerId];\r\n if(!rootScope.myId || \r\n !peerId || \r\n !this.canWriteToPeer(peerId) || \r\n peerId === rootScope.myId ||\r\n typing?.type === action._\r\n ) {\r\n return Promise.resolve(false);\r\n }\r\n\r\n if(typing?.timeout) {\r\n clearTimeout(typing.timeout);\r\n }\r\n\r\n typing = this.typings[peerId] = {\r\n type: action._\r\n };\r\n\r\n return apiManager.invokeApi('messages.setTyping', {\r\n peer: appPeersManager.getInputPeerById(peerId),\r\n action\r\n }).finally(() => {\r\n if(typing === this.typings[peerId]) {\r\n typing.timeout = window.setTimeout(() => {\r\n delete this.typings[peerId];\r\n }, 6000);\r\n }\r\n });\r\n }\r\n\r\n private handleReleasingMessage(message: any) {\r\n if((message as Message.message).media) {\r\n // @ts-ignore\r\n const c = message.media.webpage || message.media;\r\n const smth = c.photo || c.document;\r\n\r\n if(smth?.file_reference) {\r\n referenceDatabase.deleteContext(smth.file_reference, {type: 'message', peerId: message.peerId, messageId: message.mid});\r\n }\r\n\r\n // @ts-ignore\r\n if(message.media.webpage) {\r\n // @ts-ignore\r\n appWebPagesManager.deleteWebPageFromPending(message.media.webpage, mid);\r\n }\r\n }\r\n }\r\n\r\n private handleDeletedMessages(peerId: number, storage: MessagesStorage, messages: number[]) {\r\n const history: {\r\n count: number, \r\n unread: number, \r\n msgs: {[mid: number]: true},\r\n albums?: {[groupId: string]: Set<number>},\r\n } = {count: 0, unread: 0, msgs: {}} as any;\r\n\r\n for(const mid of messages) {\r\n const message: MyMessage = this.getMessageFromStorage(storage, mid);\r\n if(message.deleted) continue;\r\n\r\n this.handleReleasingMessage(message);\r\n\r\n this.updateMessageRepliesIfNeeded(message);\r\n\r\n if(!message.pFlags.out && !message.pFlags.is_outgoing && message.pFlags.unread) {\r\n history.unread++;\r\n appNotificationsManager.cancel('msg' + mid);\r\n }\r\n history.count++;\r\n history.msgs[mid] = true;\r\n\r\n message.deleted = true;\r\n\r\n if(message._ !== 'messageService' && message.grouped_id) {\r\n const groupedStorage = this.groupedMessagesStorage[message.grouped_id];\r\n if(groupedStorage) {\r\n delete groupedStorage[mid];\r\n\r\n if(!history.albums) history.albums = {};\r\n (history.albums[message.grouped_id] || (history.albums[message.grouped_id] = new Set())).add(mid);\r\n\r\n if(!Object.keys(groupedStorage).length) {\r\n delete history.albums;\r\n delete this.groupedMessagesStorage[message.grouped_id];\r\n }\r\n }\r\n }\r\n\r\n delete storage[mid];\r\n\r\n const peerMessagesToHandle = this.newMessagesToHandle[peerId];\r\n if(peerMessagesToHandle && peerMessagesToHandle.has(mid)) {\r\n peerMessagesToHandle.delete(mid);\r\n }\r\n }\r\n\r\n if(history.albums) {\r\n for(const groupId in history.albums) {\r\n rootScope.dispatchEvent('album_edit', {peerId, groupId, deletedMids: [...history.albums[groupId]]});\r\n /* const mids = this.getMidsByAlbum(groupId);\r\n if(mids.length) {\r\n const mid = Math.max(...mids);\r\n rootScope.$broadcast('message_edit', {peerId, mid, justMedia: false});\r\n } */\r\n }\r\n }\r\n\r\n return history;\r\n }\r\n \r\n private handleEditedMessage(oldMessage: any, newMessage: any) {\r\n if(oldMessage.media?.webpage) {\r\n appWebPagesManager.deleteWebPageFromPending(oldMessage.media.webpage, oldMessage.mid);\r\n }\r\n }\r\n}\r\n\r\nconst appMessagesManager = new AppMessagesManager();\r\nMOUNT_CLASS_TO.appMessagesManager = appMessagesManager;\r\nexport default appMessagesManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport type { DownloadOptions } from \"../mtproto/apiFileManager\";\r\nimport { bytesFromHex } from \"../../helpers/bytes\";\r\nimport { CancellablePromise } from \"../../helpers/cancellablePromise\";\r\nimport { getFileNameByLocation } from \"../../helpers/fileName\";\r\nimport { safeReplaceArrayInObject, isObject } from \"../../helpers/object\";\r\nimport { isSafari } from \"../../helpers/userAgent\";\r\nimport { InputFileLocation, InputMedia, Photo, PhotoSize, PhotosPhotos } from \"../../layer\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport referenceDatabase, { ReferenceContext } from \"../mtproto/referenceDatabase\";\r\nimport { MyDocument } from \"./appDocsManager\";\r\nimport appDownloadManager, { ThumbCache } from \"./appDownloadManager\";\r\nimport appUsersManager from \"./appUsersManager\";\r\nimport blur from \"../../helpers/blur\";\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport { renderImageFromUrlPromise } from \"../../helpers/dom/renderImageFromUrl\";\r\nimport calcImageInBox from \"../../helpers/calcImageInBox\";\r\nimport { makeMediaSize, MediaSize } from \"../../helpers/mediaSizes\";\r\n\r\nexport type MyPhoto = Photo.photo;\r\n\r\n// TIMES = 2 DUE TO SIDEBAR AND CHAT\r\n//let TEST_FILE_REFERENCE = \"5440692274120994569\", TEST_FILE_REFERENCE_TIMES = 2;\r\n\r\nexport class AppPhotosManager {\r\n private photos: {\r\n [id: string]: MyPhoto\r\n } = {};\r\n\r\n public windowW = 0;\r\n public windowH = 0;\r\n \r\n private static jpegHeader = bytesFromHex('ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e19282321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c353c76414176f8a58ca5f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8ffc00011080000000003012200021101031101ffc4001f0000010501010101010100000000000000000102030405060708090a0bffc400b5100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9faffc4001f0100030101010101010101010000000000000102030405060708090a0bffc400b51100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00');\r\n private static jpegTail = bytesFromHex('ffd9');\r\n \r\n constructor() {\r\n // @ts-ignore\r\n const w: any = 'visualViewport' in window ? window.visualViewport : window;\r\n const set = () => {\r\n this.windowW = w.width || w.innerWidth;\r\n this.windowH = w.height || w.innerHeight;\r\n };\r\n w.addEventListener('resize', set);\r\n set();\r\n }\r\n \r\n public savePhoto(photo: Photo, context?: ReferenceContext) {\r\n if(photo._ === 'photoEmpty') return undefined;\r\n\r\n /* if(photo.id === TEST_FILE_REFERENCE) {\r\n console.warn('Testing FILE_REFERENCE_EXPIRED');\r\n const bytes = [2, 67, 175, 43, 190, 0, 0, 20, 62, 95, 111, 33, 45, 99, 220, 116, 218, 11, 167, 127, 213, 18, 127, 32, 243, 202, 117, 80, 30];\r\n //photo.file_reference = new Uint8Array(bytes);\r\n photo.file_reference = bytes;\r\n if(!--TEST_FILE_REFERENCE_TIMES) {\r\n TEST_FILE_REFERENCE = '';\r\n }\r\n } */\r\n\r\n const oldPhoto = this.photos[photo.id];\r\n if(photo.file_reference) { // * because we can have a new object w/o the file_reference while sending\r\n safeReplaceArrayInObject('file_reference', oldPhoto, photo);\r\n referenceDatabase.saveContext(photo.file_reference, context);\r\n }\r\n\r\n if(photo.sizes?.length) {\r\n const size = photo.sizes[photo.sizes.length - 1];\r\n if(size._ === 'photoSizeProgressive') {\r\n size.size = size.sizes[size.sizes.length - 1];\r\n }\r\n }\r\n\r\n if(oldPhoto) {\r\n return Object.assign(oldPhoto, photo);\r\n }\r\n\r\n return this.photos[photo.id] = photo;\r\n }\r\n \r\n public choosePhotoSize(photo: MyPhoto | MyDocument, boxWidth = 0, boxHeight = 0, useBytes = false) {\r\n if(window.devicePixelRatio > 1) {\r\n boxWidth *= 2;\r\n boxHeight *= 2;\r\n }\r\n \r\n /*\r\n s\tbox\t100x100\r\n m\tbox\t320x320\r\n x\tbox\t800x800\r\n y\tbox\t1280x1280\r\n w\tbox\t2560x2560\r\n a\tcrop\t160x160\r\n b\tcrop\t320x320\r\n c\tcrop\t640x640\r\n d\tcrop\t1280x1280 */\r\n\r\n let bestPhotoSize: PhotoSize = {_: 'photoSizeEmpty', type: ''};\r\n const sizes = ((photo as MyPhoto).sizes || (photo as MyDocument).thumbs) as PhotoSize[];\r\n if(sizes?.length) {\r\n for(let i = 0, length = sizes.length; i < length; ++i) {\r\n const photoSize = sizes[i];\r\n if(!('w' in photoSize) && !('h' in photoSize)) continue;\r\n \r\n bestPhotoSize = photoSize;\r\n \r\n const size = calcImageInBox(photoSize.w, photoSize.h, boxWidth, boxHeight);\r\n if(size.width >= boxWidth || size.height >= boxHeight) {\r\n break;\r\n }\r\n }\r\n\r\n if(useBytes && bestPhotoSize._ === 'photoSizeEmpty' && sizes[0]._ === 'photoStrippedSize') {\r\n bestPhotoSize = sizes[0];\r\n }\r\n }\r\n \r\n return bestPhotoSize;\r\n }\r\n \r\n public getUserPhotos(userId: number, maxId: string = '0', limit: number = 20) {\r\n const inputUser = appUsersManager.getUserInput(userId);\r\n return apiManager.invokeApiCacheable('photos.getUserPhotos', {\r\n user_id: inputUser,\r\n offset: 0,\r\n limit,\r\n max_id: maxId\r\n }, {cacheSeconds: 60}).then((photosResult) => {\r\n appUsersManager.saveApiUsers(photosResult.users);\r\n const photoIds: string[] = photosResult.photos.map((photo, idx) => {\r\n photosResult.photos[idx] = this.savePhoto(photo, {type: 'profilePhoto', peerId: userId});\r\n return photo.id;\r\n });\r\n \r\n return {\r\n count: (photosResult as PhotosPhotos.photosPhotosSlice).count || photosResult.photos.length,\r\n photos: photoIds\r\n };\r\n });\r\n }\r\n\r\n public getPreviewURLFromBytes(bytes: Uint8Array | number[], isSticker = false) {\r\n let arr: Uint8Array;\r\n if(!isSticker) {\r\n arr = new Uint8Array(AppPhotosManager.jpegHeader.concat(Array.from(bytes.slice(3)), AppPhotosManager.jpegTail));\r\n arr[164] = bytes[1];\r\n arr[166] = bytes[2];\r\n } else {\r\n arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\r\n }\r\n\r\n let mimeType: string;\r\n if(isSticker) {\r\n mimeType = isSafari ? 'image/png' : 'image/webp';\r\n } else {\r\n mimeType = 'image/jpeg';\r\n }\r\n\r\n const blob = new Blob([arr], {type: mimeType});\r\n return URL.createObjectURL(blob);\r\n }\r\n\r\n /**\r\n * https://core.telegram.org/api/files#vector-thumbnails\r\n */\r\n public getPathFromPhotoPathSize(size: PhotoSize.photoPathSize) {\r\n const bytes = size.bytes;\r\n const lookup = \"AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,\";\r\n\r\n let path = 'M';\r\n for(let i = 0, length = bytes.length; i < length; ++i) {\r\n const num = bytes[i];\r\n\r\n if(num >= (128 + 64)) {\r\n path += lookup[num - 128 - 64];\r\n } else {\r\n if(num >= 128) {\r\n path += ',';\r\n } else if(num >= 64) {\r\n path += '-'; \r\n }\r\n path += '' + (num & 63);\r\n }\r\n }\r\n path += 'z';\r\n\r\n return path;\r\n }\r\n\r\n public getPreviewURLFromThumb(photo: MyPhoto | MyDocument, thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, isSticker = false) {\r\n const cacheContext = appDownloadManager.getCacheContext(photo, thumb.type);\r\n return cacheContext.url || (cacheContext.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));\r\n }\r\n \r\n public getImageFromStrippedThumb(photo: MyPhoto | MyDocument, thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize, useBlur: boolean) {\r\n const url = this.getPreviewURLFromThumb(photo, thumb, false);\r\n\r\n const image = new Image();\r\n image.classList.add('thumbnail');\r\n\r\n const loadPromise = (useBlur ? blur(url) : Promise.resolve(url)).then(url => {\r\n return renderImageFromUrlPromise(image, url);\r\n });\r\n \r\n return {image, loadPromise};\r\n }\r\n \r\n public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, noZoom = true, message?: any) {\r\n const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);\r\n //console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);\r\n \r\n let size: MediaSize;\r\n if(photo._ === 'document') {\r\n size = makeMediaSize(photo.w || 512, photo.h || 512);\r\n } else {\r\n size = makeMediaSize('w' in photoSize ? photoSize.w : 100, 'h' in photoSize ? photoSize.h : 100);\r\n }\r\n\r\n let boxSize = makeMediaSize(boxWidth, boxHeight);\r\n\r\n boxSize = size = size.aspect(boxSize, noZoom);\r\n\r\n let isFit = true;\r\n\r\n if(photo._ === 'photo' || ['video', 'gif'].includes(photo.type)) {\r\n if(boxSize.width < 200 && boxSize.height < 200) { // make at least one side this big\r\n boxSize = size = size.aspectCovered(makeMediaSize(200, 200));\r\n }\r\n \r\n if(message && \r\n (message.message || \r\n message.reply_to_mid || \r\n message.media.webpage || \r\n (message.replies && message.replies.pFlags.comments && message.replies.channel_id !== 777)\r\n )\r\n ) { // make sure that bubble block is human-readable\r\n if(boxSize.width < 320) {\r\n boxSize = makeMediaSize(320, boxSize.height);\r\n isFit = false;\r\n }\r\n }\r\n \r\n if(isFit && boxSize.width < 120) { // if image is too narrow\r\n boxSize = makeMediaSize(120, boxSize.height);\r\n isFit = false;\r\n }\r\n }\r\n\r\n // if(element instanceof SVGForeignObjectElement) {\r\n // element.setAttributeNS(null, 'width', '' + w);\r\n // element.setAttributeNS(null, 'height', '' + h);\r\n\r\n // //console.log('set dimensions to svg element:', element, w, h);\r\n // } else {\r\n element.style.width = boxSize.width + 'px';\r\n element.style.height = boxSize.height + 'px';\r\n // }\r\n \r\n return {photoSize, size, isFit};\r\n }\r\n\r\n public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument, cacheContext: ThumbCache, useBlur: boolean, ignoreCache = false): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {\r\n if(!cacheContext.downloaded || (['video', 'gif'] as MyDocument['type'][]).includes((photo as MyDocument).type) || ignoreCache) {\r\n if(photo._ === 'document' && cacheContext.downloaded && !ignoreCache) {\r\n return null;\r\n }\r\n\r\n const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;\r\n const thumb = sizes?.length ? sizes.find(size => size._ === 'photoStrippedSize') : null;\r\n if(thumb && ('bytes' in thumb)) {\r\n return this.getImageFromStrippedThumb(photo, thumb as any, useBlur);\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n \r\n public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number, onlyCache?: boolean): DownloadOptions {\r\n const isDocument = photo._ === 'document';\r\n\r\n if(!photoSize || photoSize._ === 'photoSizeEmpty') {\r\n //console.error('no photoSize by photo:', photo);\r\n throw new Error('photoSizeEmpty!');\r\n }\r\n \r\n // maybe it's a thumb\r\n const isPhoto = (photoSize._ === 'photoSize' || photoSize._ === 'photoSizeProgressive') && photo.access_hash && photo.file_reference;\r\n const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation = {\r\n _: isDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',\r\n id: photo.id,\r\n access_hash: photo.access_hash,\r\n file_reference: photo.file_reference,\r\n thumb_size: photoSize.type\r\n };\r\n\r\n return {\r\n dcId: photo.dc_id, \r\n location, \r\n size: isPhoto ? (photoSize as PhotoSize.photoSize).size : undefined, \r\n queueId, \r\n onlyCache\r\n };\r\n }\r\n\r\n /* public getPhotoURL(photo: MTPhoto | MTMyDocument, photoSize: MTPhotoSize) {\r\n const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize);\r\n\r\n return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};\r\n } */\r\n\r\n /* public isDownloaded(media: any) {\r\n const isPhoto = media._ === 'photo';\r\n const photo = isPhoto ? this.getPhoto(media.id) : null;\r\n let isDownloaded: boolean;\r\n if(photo) {\r\n isDownloaded = photo.downloaded > 0;\r\n } else {\r\n const cachedThumb = this.getDocumentCachedThumb(media.id);\r\n isDownloaded = cachedThumb?.downloaded > 0;\r\n }\r\n\r\n return isDownloaded;\r\n } */\r\n \r\n public preloadPhoto(photoId: MyPhoto | MyDocument | string, photoSize?: PhotoSize, queueId?: number, onlyCache?: boolean): CancellablePromise<Blob> {\r\n const photo = this.getPhoto(photoId);\r\n\r\n // @ts-ignore\r\n if(!photo || photo._ === 'photoEmpty') {\r\n throw new Error('preloadPhoto photoEmpty!');\r\n }\r\n\r\n if(!photoSize) {\r\n const fullWidth = this.windowW;\r\n const fullHeight = this.windowH;\r\n \r\n photoSize = this.choosePhotoSize(photo, fullWidth, fullHeight);\r\n }\r\n\r\n const cacheContext = appDownloadManager.getCacheContext(photo, photoSize.type);\r\n if(cacheContext.downloaded >= ('size' in photoSize ? photoSize.size : 0) && cacheContext.url) {\r\n return Promise.resolve() as any;\r\n }\r\n \r\n const downloadOptions = this.getPhotoDownloadOptions(photo, photoSize, queueId, onlyCache);\r\n const fileName = getFileNameByLocation(downloadOptions.location);\r\n\r\n let download = appDownloadManager.getDownload(fileName);\r\n if(download) {\r\n return download;\r\n }\r\n\r\n download = appDownloadManager.download(downloadOptions);\r\n download.then(blob => {\r\n if(!cacheContext.downloaded || cacheContext.downloaded < blob.size) {\r\n const url = URL.createObjectURL(blob);\r\n cacheContext.downloaded = blob.size;\r\n cacheContext.url = url;\r\n\r\n //console.log('wrote photo:', photo, photoSize, cacheContext, blob);\r\n }\r\n\r\n return blob;\r\n }).catch(() => {});\r\n\r\n return download;\r\n }\r\n \r\n public getPhoto(photoId: any/* MyPhoto | string */): MyPhoto {\r\n return isObject(photoId) ? photoId as MyPhoto : this.photos[photoId as any as string];\r\n }\r\n\r\n public getInput(photo: MyPhoto): InputMedia.inputMediaPhoto {\r\n return {\r\n _: 'inputMediaPhoto',\r\n id: {\r\n _: 'inputPhoto',\r\n id: photo.id,\r\n access_hash: photo.access_hash,\r\n file_reference: photo.file_reference\r\n },\r\n ttl_seconds: 0\r\n };\r\n }\r\n\r\n public savePhotoFile(photo: MyPhoto | MyDocument, queueId?: number) {\r\n const fullPhotoSize = this.choosePhotoSize(photo, 0xFFFF, 0xFFFF);\r\n if(!(fullPhotoSize._ === 'photoSize' || fullPhotoSize._ === 'photoSizeProgressive')) {\r\n return;\r\n }\r\n\r\n const downloadOptions = this.getPhotoDownloadOptions(photo, fullPhotoSize, queueId);\r\n downloadOptions.fileName = 'photo' + photo.id + '.jpg';\r\n appDownloadManager.downloadToDisc(downloadOptions, downloadOptions.fileName);\r\n }\r\n}\r\n\r\nconst appPhotosManager = new AppPhotosManager();\r\nMOUNT_CLASS_TO && (MOUNT_CLASS_TO.appPhotosManager = appPhotosManager);\r\nexport default appPhotosManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { MOUNT_CLASS_TO } from \"../config/debug\";\r\nimport appPeersManager from \"../lib/appManagers/appPeersManager\";\r\nimport rootScope from \"../lib/rootScope\";\r\nimport { i18n } from \"../lib/langPack\";\r\nimport replaceContent from \"../helpers/dom/replaceContent\";\r\nimport appUsersManager from \"../lib/appManagers/appUsersManager\";\r\n\r\nexport type PeerTitleOptions = {\r\n peerId: number,\r\n plainText?: boolean,\r\n onlyFirstName?: boolean,\r\n dialog?: boolean\r\n};\r\n\r\nconst weakMap: WeakMap<HTMLElement, PeerTitle> = new WeakMap();\r\n\r\nMOUNT_CLASS_TO.peerTitleWeakMap = weakMap;\r\n\r\nrootScope.addEventListener('peer_title_edit', (peerId) => {\r\n const elements = Array.from(document.querySelectorAll(`.peer-title[data-peer-id=\"${peerId}\"]`)) as HTMLElement[];\r\n elements.forEach(element => {\r\n const peerTitle = weakMap.get(element);\r\n //console.log('in the summer silence i was doing nothing', peerTitle, peerId);\r\n\r\n if(peerTitle) {\r\n peerTitle.update();\r\n }\r\n });\r\n});\r\n\r\nexport default class PeerTitle {\r\n public element: HTMLElement;\r\n public peerId: number;\r\n public plainText = false;\r\n public onlyFirstName = false;\r\n public dialog = false;\r\n\r\n constructor(options: PeerTitleOptions) {\r\n this.element = document.createElement('span');\r\n this.element.classList.add('peer-title');\r\n this.element.setAttribute('dir', 'auto');\r\n \r\n this.update(options);\r\n weakMap.set(this.element, this);\r\n }\r\n\r\n public update(options?: PeerTitleOptions) {\r\n if(options) {\r\n for(let i in options) {\r\n // @ts-ignore\r\n this.element.dataset[i] = options[i] ? '' + (typeof(options[i]) === 'boolean' ? +options[i] : options[i]) : '0';\r\n // @ts-ignore\r\n this[i] = options[i];\r\n }\r\n }\r\n\r\n if(this.peerId !== rootScope.myId || !this.dialog) {\r\n if(this.peerId > 0 && appUsersManager.getUser(this.peerId).pFlags.deleted) {\r\n replaceContent(this.element, i18n(this.onlyFirstName ? 'Deleted' : 'HiddenName'));\r\n } else {\r\n this.element.innerHTML = appPeersManager.getPeerTitle(this.peerId, this.plainText, this.onlyFirstName);\r\n }\r\n } else {\r\n replaceContent(this.element, i18n(this.onlyFirstName ? 'Saved' : 'SavedMessages'));\r\n }\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport type { ApplyServerTimeOffsetTask } from './timeManager';\r\nimport { MOUNT_CLASS_TO } from '../../config/debug';\r\n// import { tsNow } from '../../helpers/date';\r\nimport sessionStorage from '../sessionStorage';\r\nimport apiManager from './mtprotoworker';\r\n\r\nexport class ServerTimeManager {\r\n /* private midnightNoOffset: number;\r\n private midnightOffseted: Date;\r\n\r\n private midnightOffset: number; */\r\n\r\n public serverTimeOffset: number; // in seconds\r\n /* private timeParams: {\r\n midnightOffset: number,\r\n serverTimeOffset: number\r\n }; */\r\n\r\n constructor() {\r\n /* const timestampNow = tsNow(true);\r\n this.midnightNoOffset = timestampNow - (timestampNow % 86400);\r\n this.midnightOffseted = new Date();\r\n this.midnightOffseted.setHours(0, 0, 0, 0);\r\n \r\n this.midnightOffset = this.midnightNoOffset - (Math.floor(+this.midnightOffseted / 1000)); */\r\n\r\n this.serverTimeOffset = 0;\r\n /* this.timeParams = {\r\n midnightOffset: this.midnightOffset,\r\n serverTimeOffset: this.serverTimeOffset\r\n }; */\r\n\r\n sessionStorage.get('server_time_offset').then((to) => {\r\n if(to) {\r\n this.serverTimeOffset = to;\r\n // this.timeParams.serverTimeOffset = to;\r\n }\r\n });\r\n\r\n apiManager.addTaskListener('applyServerTimeOffset', (task: ApplyServerTimeOffsetTask) => {\r\n this.serverTimeOffset = task.payload;\r\n });\r\n }\r\n}\r\n\r\nconst serverTimeManager = new ServerTimeManager();\r\nMOUNT_CLASS_TO && (MOUNT_CLASS_TO.serverTimeManager = serverTimeManager);\r\nexport default serverTimeManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName';\r\nimport { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object';\r\nimport { Document, InputFileLocation, PhotoSize } from '../../layer';\r\nimport referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase';\r\nimport opusDecodeController from '../opusDecodeController';\r\nimport { RichTextProcessor } from '../richtextprocessor';\r\nimport webpWorkerController from '../webp/webpWorkerController';\r\nimport appDownloadManager, { DownloadBlob } from './appDownloadManager';\r\nimport appPhotosManager from './appPhotosManager';\r\nimport blur from '../../helpers/blur';\r\nimport apiManager from '../mtproto/mtprotoworker';\r\nimport { MOUNT_CLASS_TO } from '../../config/debug';\r\nimport { getFullDate } from '../../helpers/date';\r\n\r\nexport type MyDocument = Document.document;\r\n\r\n// TODO: если залить картинку файлом, а потом перезайти в диалог - превьюшка заново скачается\r\n\r\nexport class AppDocsManager {\r\n private docs: {[docId: string]: MyDocument} = {};\r\n private savingLottiePreview: {[docId: string]: true} = {};\r\n\r\n constructor() {\r\n apiManager.onServiceWorkerFail = this.onServiceWorkerFail;\r\n }\r\n\r\n public onServiceWorkerFail = () => {\r\n for(const id in this.docs) {\r\n const doc = this.docs[id];\r\n\r\n if(doc.supportsStreaming) {\r\n delete doc.supportsStreaming;\r\n const cacheContext = appDownloadManager.getCacheContext(doc);\r\n delete cacheContext.url;\r\n }\r\n }\r\n };\r\n\r\n public saveDoc(doc: Document, context?: ReferenceContext): MyDocument {\r\n if(doc._ === 'documentEmpty') {\r\n return undefined;\r\n }\r\n\r\n const oldDoc = this.docs[doc.id];\r\n\r\n if(doc.file_reference) { // * because we can have a new object w/o the file_reference while sending\r\n safeReplaceArrayInObject('file_reference', oldDoc, doc);\r\n referenceDatabase.saveContext(doc.file_reference, context);\r\n }\r\n \r\n //console.log('saveDoc', apiDoc, this.docs[apiDoc.id]);\r\n // if(oldDoc) {\r\n // //if(doc._ !== 'documentEmpty' && doc._ === d._) {\r\n // if(doc.thumbs) {\r\n // if(!oldDoc.thumbs) oldDoc.thumbs = doc.thumbs;\r\n // /* else if(apiDoc.thumbs[0].bytes && !d.thumbs[0].bytes) {\r\n // d.thumbs.unshift(apiDoc.thumbs[0]);\r\n // } else if(d.thumbs[0].url) { // fix for converted thumb in safari\r\n // apiDoc.thumbs[0] = d.thumbs[0];\r\n // } */\r\n // }\r\n\r\n // //}\r\n\r\n // return oldDoc;\r\n\r\n // //return Object.assign(d, apiDoc, context);\r\n // //return context ? Object.assign(d, context) : d;\r\n // }\r\n\r\n if(!oldDoc) {\r\n this.docs[doc.id] = doc;\r\n }\r\n\r\n // * exclude from state\r\n // defineNotNumerableProperties(doc, [/* 'thumbs', */'type', 'h', 'w', 'file_name', \r\n // 'file', 'duration', 'downloaded', 'url', 'audioTitle', \r\n // 'audioPerformer', 'sticker', 'stickerEmoji', 'stickerEmojiRaw', \r\n // 'stickerSetInput', 'stickerThumbConverted', 'animated', 'supportsStreaming']);\r\n\r\n doc.attributes.forEach(attribute => {\r\n switch(attribute._) {\r\n case 'documentAttributeFilename':\r\n doc.file_name = RichTextProcessor.wrapPlainText(attribute.file_name);\r\n break;\r\n\r\n case 'documentAttributeAudio':\r\n doc.duration = attribute.duration;\r\n doc.audioTitle = attribute.title;\r\n doc.audioPerformer = attribute.performer;\r\n doc.type = attribute.pFlags.voice && doc.mime_type === 'audio/ogg' ? 'voice' : 'audio';\r\n /* if(apiDoc.type === 'audio') {\r\n apiDoc.supportsStreaming = true;\r\n } */\r\n break;\r\n\r\n case 'documentAttributeVideo':\r\n doc.duration = attribute.duration;\r\n doc.w = attribute.w;\r\n doc.h = attribute.h;\r\n //apiDoc.supportsStreaming = attribute.pFlags?.supports_streaming/* && apiDoc.size > 524288 */;\r\n if(/* apiDoc.thumbs && */attribute.pFlags.round_message) {\r\n doc.type = 'round';\r\n } else /* if(apiDoc.thumbs) */ {\r\n doc.type = 'video';\r\n }\r\n break;\r\n\r\n case 'documentAttributeSticker':\r\n if(attribute.alt !== undefined) {\r\n doc.stickerEmojiRaw = attribute.alt;\r\n doc.stickerEmoji = RichTextProcessor.wrapRichText(doc.stickerEmojiRaw, {noLinks: true, noLinebreaks: true});\r\n }\r\n\r\n if(attribute.stickerset) {\r\n if(attribute.stickerset._ === 'inputStickerSetEmpty') {\r\n delete attribute.stickerset;\r\n } else if(attribute.stickerset._ === 'inputStickerSetID') {\r\n doc.stickerSetInput = attribute.stickerset;\r\n }\r\n }\r\n\r\n // * there can be no thumbs, then it is a document\r\n if(/* apiDoc.thumbs && */doc.mime_type === 'image/webp' && (doc.thumbs || webpWorkerController.isWebpSupported())) {\r\n doc.type = 'sticker';\r\n doc.sticker = 1;\r\n }\r\n break;\r\n\r\n case 'documentAttributeImageSize':\r\n doc.type = 'photo';\r\n doc.w = attribute.w;\r\n doc.h = attribute.h;\r\n break;\r\n\r\n case 'documentAttributeAnimated':\r\n if((doc.mime_type === 'image/gif' || doc.mime_type === 'video/mp4')/* && apiDoc.thumbs */) {\r\n doc.type = 'gif';\r\n }\r\n\r\n doc.animated = true;\r\n break;\r\n }\r\n });\r\n \r\n if(!doc.mime_type) {\r\n switch(doc.type) {\r\n case 'gif':\r\n case 'video':\r\n case 'round':\r\n doc.mime_type = 'video/mp4';\r\n break;\r\n case 'sticker':\r\n doc.mime_type = 'image/webp';\r\n break;\r\n case 'audio':\r\n doc.mime_type = 'audio/mpeg';\r\n break;\r\n case 'voice':\r\n doc.mime_type = 'audio/ogg';\r\n break;\r\n default:\r\n doc.mime_type = 'application/octet-stream';\r\n break;\r\n }\r\n }\r\n\r\n if(doc.mime_type === 'application/pdf') {\r\n doc.type = 'pdf';\r\n }\r\n\r\n if(doc.type === 'voice' || doc.type === 'round') {\r\n // browser will identify extension\r\n doc.file_name = doc.type + '_' + getFullDate(new Date(doc.date * 1000), {monthAsNumber: true, leadingZero: true}).replace(/[:\\.]/g, '-').replace(', ', '_');\r\n }\r\n\r\n if(apiManager.isServiceWorkerOnline()) {\r\n if((doc.type === 'gif' && doc.size > 8e6) || doc.type === 'audio' || doc.type === 'video') {\r\n doc.supportsStreaming = true;\r\n \r\n const cacheContext = appDownloadManager.getCacheContext(doc);\r\n if(!cacheContext.url) {\r\n cacheContext.url = this.getFileURL(doc);\r\n }\r\n }\r\n }\r\n\r\n // for testing purposes\r\n // doc.supportsStreaming = false;\r\n // doc.url = ''; // * this will break upload urls\r\n \r\n if(!doc.file_name) {\r\n doc.file_name = '';\r\n }\r\n\r\n if(doc.mime_type === 'application/x-tgsticker' && doc.file_name === 'AnimatedSticker.tgs') {\r\n doc.type = 'sticker';\r\n doc.animated = true;\r\n doc.sticker = 2;\r\n }\r\n\r\n /* if(!doc.url) {\r\n doc.url = this.getFileURL(doc);\r\n } */\r\n\r\n if(oldDoc) {\r\n return Object.assign(oldDoc, doc);\r\n }\r\n\r\n return doc;\r\n }\r\n \r\n public getDoc(docId: string | MyDocument): MyDocument {\r\n return isObject(docId) && typeof(docId) !== 'string' ? docId as any : this.docs[docId as string] as any;\r\n }\r\n\r\n public getMediaInput(doc: MyDocument) {\r\n return {\r\n _: 'inputMediaDocument',\r\n id: {\r\n _: 'inputDocument',\r\n id: doc.id,\r\n access_hash: doc.access_hash,\r\n file_reference: doc.file_reference\r\n },\r\n ttl_seconds: 0\r\n };\r\n }\r\n\r\n public getInput(doc: MyDocument, thumbSize?: string): InputFileLocation.inputDocumentFileLocation {\r\n return {\r\n _: 'inputDocumentFileLocation',\r\n id: doc.id,\r\n access_hash: doc.access_hash,\r\n file_reference: doc.file_reference,\r\n thumb_size: thumbSize\r\n };\r\n }\r\n\r\n public getFileDownloadOptions(doc: MyDocument, thumb?: PhotoSize.photoSize, queueId?: number, onlyCache?: boolean) {\r\n const inputFileLocation = this.getInput(doc, thumb?.type);\r\n\r\n let mimeType: string;\r\n if(thumb) {\r\n mimeType = doc.sticker ? 'image/webp' : 'image/jpeg'/* doc.mime_type */;\r\n } else {\r\n mimeType = doc.mime_type || 'application/octet-stream';\r\n }\r\n\r\n return {\r\n dcId: doc.dc_id, \r\n location: inputFileLocation, \r\n size: thumb ? thumb.size : doc.size, \r\n mimeType,\r\n fileName: doc.file_name,\r\n queueId,\r\n onlyCache\r\n };\r\n }\r\n\r\n public getFileURL(doc: MyDocument, download = false, thumb?: PhotoSize.photoSize) {\r\n let type: FileURLType;\r\n if(download) {\r\n type = 'download';\r\n } else if(thumb) {\r\n type = 'thumb';\r\n } else if(doc.supportsStreaming) {\r\n type = 'stream';\r\n } else {\r\n type = 'document';\r\n }\r\n\r\n return getFileURL(type, this.getFileDownloadOptions(doc, thumb));\r\n }\r\n\r\n public getThumbURL(doc: MyDocument, thumb: PhotoSize.photoSize | PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {\r\n let promise: Promise<any> = Promise.resolve();\r\n\r\n const cacheContext = appDownloadManager.getCacheContext(doc, thumb.type);\r\n if(!cacheContext.url) {\r\n if('bytes' in thumb) {\r\n promise = blur(appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker)).then(url => {\r\n cacheContext.url = url;\r\n }) as any;\r\n } else {\r\n //return this.getFileURL(doc, false, thumb);\r\n promise = appPhotosManager.preloadPhoto(doc, thumb) as any;\r\n }\r\n }\r\n\r\n return {thumb, cacheContext, promise};\r\n }\r\n\r\n public getThumb(doc: MyDocument, tryNotToUseBytes = true) {\r\n const thumb = appPhotosManager.choosePhotoSize(doc, 0, 0, !tryNotToUseBytes);\r\n if(thumb._ === 'photoSizeEmpty') return null;\r\n return this.getThumbURL(doc, thumb as any);\r\n }\r\n\r\n public getInputFileName(doc: MyDocument, thumbSize?: string) {\r\n return getFileNameByLocation(this.getInput(doc, thumbSize), {fileName: doc.file_name});\r\n }\r\n\r\n public downloadDoc(doc: MyDocument, queueId?: number, onlyCache?: boolean): DownloadBlob {\r\n const fileName = this.getInputFileName(doc);\r\n\r\n let download: DownloadBlob = appDownloadManager.getDownload(fileName);\r\n if(download) {\r\n return download;\r\n }\r\n\r\n const downloadOptions = this.getFileDownloadOptions(doc, undefined, queueId, onlyCache);\r\n download = appDownloadManager.download(downloadOptions);\r\n\r\n const cacheContext = appDownloadManager.getCacheContext(doc);\r\n const originalPromise = download;\r\n originalPromise.then((blob) => {\r\n cacheContext.url = URL.createObjectURL(blob);\r\n cacheContext.downloaded = blob.size;\r\n }, () => {});\r\n \r\n if(doc.type === 'voice' && !opusDecodeController.isPlaySupported()) {\r\n download = originalPromise.then(async(blob) => {\r\n const reader = new FileReader();\r\n \r\n await new Promise<void>((resolve, reject) => {\r\n reader.onloadend = (e) => {\r\n const uint8 = new Uint8Array(e.target.result as ArrayBuffer);\r\n //console.log('sending uint8 to decoder:', uint8);\r\n opusDecodeController.decode(uint8).then(result => {\r\n cacheContext.url = result.url;\r\n resolve();\r\n }, (err) => {\r\n delete cacheContext.downloaded;\r\n reject(err);\r\n });\r\n };\r\n \r\n reader.readAsArrayBuffer(blob);\r\n });\r\n \r\n return blob;\r\n });\r\n }\r\n\r\n return download;\r\n }\r\n\r\n public saveLottiePreview(doc: MyDocument, canvas: HTMLCanvasElement, toneIndex: number) {\r\n const key = doc.id + '-' + toneIndex;\r\n if(this.savingLottiePreview[key]/* || true */) return;\r\n\r\n if(!doc.stickerCachedThumbs) {\r\n defineNotNumerableProperties(doc, ['stickerCachedThumbs']);\r\n doc.stickerCachedThumbs = {};\r\n }\r\n\r\n const thumb = doc.stickerCachedThumbs[toneIndex];\r\n if(thumb && thumb.w >= canvas.width && thumb.h >= canvas.height) {\r\n return;\r\n }\r\n\r\n /* if(doc.thumbs.find(t => t._ === 'photoStrippedSize') \r\n || (doc.stickerCachedThumb || (doc.stickerSavedThumbWidth >= canvas.width && doc.stickerSavedThumbHeight >= canvas.height))) {\r\n return;\r\n } */\r\n\r\n this.savingLottiePreview[key] = true;\r\n canvas.toBlob((blob) => {\r\n //console.log('got lottie preview', doc, blob, URL.createObjectURL(blob));\r\n\r\n const thumb = {\r\n url: URL.createObjectURL(blob),\r\n w: canvas.width,\r\n h: canvas.height\r\n };\r\n\r\n doc.stickerCachedThumbs[toneIndex] = thumb;\r\n\r\n delete this.savingLottiePreview[key];\r\n \r\n /* const reader = new FileReader();\r\n reader.onloadend = (e) => {\r\n const uint8 = new Uint8Array(e.target.result as ArrayBuffer);\r\n const thumb: PhotoSize.photoStrippedSize = {\r\n _: 'photoStrippedSize',\r\n bytes: uint8,\r\n type: 'i'\r\n };\r\n\r\n doc.stickerSavedThumbWidth = canvas.width;\r\n doc.stickerSavedThumbHeight = canvas.width;\r\n\r\n defineNotNumerableProperties(thumb, ['url']);\r\n thumb.url = URL.createObjectURL(blob);\r\n doc.thumbs.findAndSplice(t => t._ === thumb._);\r\n doc.thumbs.unshift(thumb);\r\n\r\n if(!webpWorkerController.isWebpSupported()) {\r\n doc.pFlags.stickerThumbConverted = true;\r\n }\r\n\r\n delete this.savingLottiePreview[doc.id];\r\n };\r\n reader.readAsArrayBuffer(blob); */\r\n });\r\n }\r\n\r\n public saveDocFile(doc: MyDocument, queueId?: number) {\r\n /* const options = this.getFileDownloadOptions(doc, undefined, queueId);\r\n return appDownloadManager.downloadToDisc(options, doc.file_name); */\r\n const promise = this.downloadDoc(doc, queueId);\r\n promise.then(() => {\r\n const cacheContext = appDownloadManager.getCacheContext(doc);\r\n appDownloadManager.createDownloadAnchor(cacheContext.url, doc.file_name);\r\n });\r\n return promise;\r\n }\r\n}\r\n\r\nconst appDocsManager = new AppDocsManager();\r\nMOUNT_CLASS_TO.appDocsManager = appDocsManager;\r\nexport default appDocsManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport { tsNow } from \"../../helpers/date\";\r\nimport { numberThousandSplitter } from \"../../helpers/number\";\r\nimport { ChannelParticipantsFilter, ChannelsChannelParticipants, Chat, ChatFull, ChatParticipants, ChatPhoto, ExportedChatInvite, InputChannel, InputFile, InputFileLocation, PhotoSize, SendMessageAction, Update, UserFull, UserProfilePhoto } from \"../../layer\";\r\nimport { LangPackKey, i18n } from \"../langPack\";\r\n//import apiManager from '../mtproto/apiManager';\r\nimport apiManager from '../mtproto/mtprotoworker';\r\nimport { RichTextProcessor } from \"../richtextprocessor\";\r\nimport rootScope from \"../rootScope\";\r\nimport SearchIndex from \"../searchIndex\";\r\nimport apiUpdatesManager from \"./apiUpdatesManager\";\r\nimport appChatsManager from \"./appChatsManager\";\r\nimport appNotificationsManager from \"./appNotificationsManager\";\r\nimport appPeersManager from \"./appPeersManager\";\r\nimport appPhotosManager from \"./appPhotosManager\";\r\nimport appUsersManager, { User } from \"./appUsersManager\";\r\n\r\nexport type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>;\r\n\r\nexport class AppProfileManager {\r\n //private botInfos: any = {};\r\n private usersFull: {[id: string]: UserFull.userFull} = {};\r\n public chatsFull: {[id: string]: ChatFull} = {};\r\n private fullPromises: {[peerId: string]: Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull>} = {};\r\n\r\n private megagroupOnlines: {[id: number]: {timestamp: number, onlines: number}};\r\n\r\n private typingsInPeer: {[peerId: number]: UserTyping[]};\r\n\r\n constructor() {\r\n rootScope.addMultipleEventsListeners({\r\n updateChatParticipants: (update) => {\r\n const participants = update.participants;\r\n if(participants._ === 'chatParticipants') {\r\n const chatId = participants.chat_id;\r\n const chatFull = this.chatsFull[chatId] as ChatFull.chatFull;\r\n if(chatFull !== undefined) {\r\n chatFull.participants = participants;\r\n rootScope.dispatchEvent('chat_full_update', chatId);\r\n }\r\n }\r\n },\r\n\r\n updateChatParticipantAdd: (update) => {\r\n const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;\r\n if(chatFull !== undefined) {\r\n const _participants = chatFull.participants as ChatParticipants.chatParticipants;\r\n const participants = _participants.participants || [];\r\n for(let i = 0, length = participants.length; i < length; i++) {\r\n if(participants[i].user_id === update.user_id) {\r\n return;\r\n }\r\n }\r\n\r\n participants.push({\r\n _: 'chatParticipant',\r\n user_id: update.user_id,\r\n inviter_id: update.inviter_id,\r\n date: tsNow(true)\r\n });\r\n\r\n _participants.version = update.version;\r\n rootScope.dispatchEvent('chat_full_update', update.chat_id);\r\n }\r\n },\r\n\r\n updateChatParticipantDelete: (update) => {\r\n const chatFull = this.chatsFull[update.chat_id] as ChatFull.chatFull;\r\n if(chatFull !== undefined) {\r\n const _participants = chatFull.participants as ChatParticipants.chatParticipants;\r\n const participants = _participants.participants || [];\r\n for(let i = 0, length = participants.length; i < length; i++) {\r\n if(participants[i].user_id === update.user_id) {\r\n participants.splice(i, 1);\r\n _participants.version = update.version;\r\n rootScope.dispatchEvent('chat_full_update', update.chat_id);\r\n return;\r\n }\r\n }\r\n }\r\n },\r\n\r\n updateUserTyping: this.onUpdateUserTyping,\r\n updateChatUserTyping: this.onUpdateUserTyping,\r\n updateChannelUserTyping: this.onUpdateUserTyping\r\n });\r\n\r\n rootScope.addEventListener('chat_update', (chatId) => {\r\n const fullChat = this.chatsFull[chatId];\r\n const chat: Chat.chat = appChatsManager.getChat(chatId);\r\n if(!chat.photo || !fullChat) {\r\n return;\r\n }\r\n\r\n const emptyPhoto = chat.photo._ === 'chatPhotoEmpty';\r\n //////console.log('chat_update:', fullChat);\r\n if(fullChat.chat_photo && emptyPhoto !== (fullChat.chat_photo._ === 'photoEmpty')) {\r\n delete this.chatsFull[chatId];\r\n rootScope.dispatchEvent('chat_full_update', chatId);\r\n return;\r\n }\r\n if(emptyPhoto) {\r\n return;\r\n }\r\n\r\n const photoId = (chat.photo as ChatPhoto.chatPhoto).photo_id;\r\n const chatFullPhotoId = fullChat.chat_photo?.id;\r\n if(chatFullPhotoId !== photoId) {\r\n delete this.chatsFull[chatId];\r\n rootScope.dispatchEvent('chat_full_update', chatId);\r\n }\r\n });\r\n\r\n rootScope.addEventListener('invalidate_participants', chatId => {\r\n this.invalidateChannelParticipants(chatId);\r\n });\r\n\r\n this.megagroupOnlines = {};\r\n this.typingsInPeer = {};\r\n }\r\n\r\n /* public saveBotInfo(botInfo: any) {\r\n const botId = botInfo && botInfo.user_id;\r\n if(!botId) {\r\n return null;\r\n }\r\n\r\n const commands: any = {};\r\n botInfo.commands.forEach((botCommand: any) => {\r\n commands[botCommand.command] = botCommand.description;\r\n });\r\n\r\n return this.botInfos[botId] = {\r\n id: botId,\r\n version: botInfo.version,\r\n shareText: botInfo.share_text,\r\n description: botInfo.description,\r\n commands: commands\r\n };\r\n } */\r\n\r\n public getProfile(id: number, override?: true): Promise<UserFull> {\r\n if(this.usersFull[id] && !override) {\r\n return Promise.resolve(this.usersFull[id]);\r\n }\r\n\r\n if(this.fullPromises[id]) {\r\n return this.fullPromises[id] as any;\r\n }\r\n\r\n return this.fullPromises[id] = apiManager.invokeApi('users.getFullUser', {\r\n id: appUsersManager.getUserInput(id)\r\n }).then((userFull) => {\r\n const user = userFull.user as User;\r\n appUsersManager.saveApiUser(user, true);\r\n\r\n if(userFull.profile_photo) {\r\n userFull.profile_photo = appPhotosManager.savePhoto(userFull.profile_photo, {type: 'profilePhoto', peerId: id});\r\n }\r\n\r\n if(userFull.about !== undefined) {\r\n userFull.rAbout = RichTextProcessor.wrapRichText(userFull.about, {noLinebreaks: true});\r\n }\r\n\r\n appNotificationsManager.savePeerSettings(id, userFull.notify_settings);\r\n\r\n /* if(userFull.bot_info) {\r\n userFull.bot_info = this.saveBotInfo(userFull.bot_info) as any;\r\n } */\r\n\r\n //appMessagesManager.savePinnedMessage(id, userFull.pinned_msg_id);\r\n\r\n delete this.fullPromises[id];\r\n\r\n return this.usersFull[id] = userFull;\r\n }) as any;\r\n }\r\n\r\n public getProfileByPeerId(peerId: number, override?: true): Promise<ChatFull.chatFull | ChatFull.channelFull | UserFull.userFull> {\r\n if(peerId < 0) return this.getChatFull(-peerId, override);\r\n else return this.getProfile(peerId, override);\r\n }\r\n\r\n public getFullPhoto(peerId: number) {\r\n return this.getProfileByPeerId(peerId).then(profile => {\r\n switch(profile._) {\r\n case 'userFull':\r\n return profile.profile_photo;\r\n case 'channelFull':\r\n case 'chatFull':\r\n return profile.chat_photo;\r\n }\r\n });\r\n }\r\n\r\n /* public getPeerBots(peerId: number) {\r\n var peerBots: any[] = [];\r\n if(peerId >= 0 && !appUsersManager.isBot(peerId) ||\r\n (appPeersManager.isChannel(peerId) && !appPeersManager.isMegagroup(peerId))) {\r\n return Promise.resolve(peerBots);\r\n }\r\n if(peerId >= 0) {\r\n return this.getProfile(peerId).then((userFull: any) => {\r\n var botInfo = userFull.bot_info;\r\n if(botInfo && botInfo._ !== 'botInfoEmpty') {\r\n peerBots.push(botInfo);\r\n }\r\n return peerBots;\r\n });\r\n }\r\n\r\n return this.getChatFull(-peerId).then((chatFull: any) => {\r\n chatFull.bot_info.forEach((botInfo: any) => {\r\n peerBots.push(this.saveBotInfo(botInfo))\r\n });\r\n return peerBots;\r\n });\r\n } */\r\n\r\n public getChatFull(id: number, override?: true): Promise<ChatFull.chatFull | ChatFull.channelFull> {\r\n if(appChatsManager.isChannel(id)) {\r\n return this.getChannelFull(id, override);\r\n }\r\n\r\n const fullChat = this.chatsFull[id] as ChatFull.chatFull;\r\n if(fullChat && !override) {\r\n const chat = appChatsManager.getChat(id);\r\n if(chat.version === (fullChat.participants as ChatParticipants.chatParticipants).version ||\r\n chat.pFlags.left) {\r\n return Promise.resolve(fullChat);\r\n }\r\n }\r\n\r\n const peerId = -id;\r\n if(this.fullPromises[peerId] !== undefined) {\r\n return this.fullPromises[peerId] as any;\r\n }\r\n\r\n // console.trace(dT(), 'Get chat full', id, appChatsManager.getChat(id))\r\n return this.fullPromises[peerId] = apiManager.invokeApi('messages.getFullChat', {\r\n chat_id: id\r\n }).then((result) => {\r\n appChatsManager.saveApiChats(result.chats, true);\r\n appUsersManager.saveApiUsers(result.users);\r\n const fullChat = result.full_chat as ChatFull.chatFull;\r\n if(fullChat && fullChat.chat_photo && fullChat.chat_photo.id) {\r\n fullChat.chat_photo = appPhotosManager.savePhoto(fullChat.chat_photo, {type: 'profilePhoto', peerId: peerId});\r\n }\r\n\r\n //appMessagesManager.savePinnedMessage(peerId, fullChat.pinned_msg_id);\r\n appNotificationsManager.savePeerSettings(peerId, fullChat.notify_settings);\r\n delete this.fullPromises[peerId];\r\n this.chatsFull[id] = fullChat;\r\n rootScope.dispatchEvent('chat_full_update', id);\r\n\r\n return fullChat;\r\n }) as any;\r\n }\r\n\r\n public getChatInviteLink(id: number, force?: boolean) {\r\n return this.getChatFull(id).then((chatFull) => {\r\n if(!force &&\r\n chatFull.exported_invite &&\r\n chatFull.exported_invite._ == 'chatInviteExported') {\r\n return chatFull.exported_invite.link;\r\n }\r\n \r\n return apiManager.invokeApi('messages.exportChatInvite', {\r\n peer: appPeersManager.getInputPeerById(-id)\r\n }).then((exportedInvite) => {\r\n if(this.chatsFull[id] !== undefined) {\r\n this.chatsFull[id].exported_invite = exportedInvite;\r\n }\r\n\r\n return (exportedInvite as ExportedChatInvite.chatInviteExported).link;\r\n });\r\n });\r\n }\r\n\r\n public getChannelParticipants(id: number, filter: ChannelParticipantsFilter = {_: 'channelParticipantsRecent'}, limit = 200, offset = 0) {\r\n if(filter._ === 'channelParticipantsRecent') {\r\n const chat = appChatsManager.getChat(id);\r\n if(chat &&\r\n chat.pFlags && (\r\n chat.pFlags.kicked ||\r\n chat.pFlags.broadcast && !chat.pFlags.creator && !chat.admin_rights\r\n )) {\r\n return Promise.reject();\r\n }\r\n }\r\n\r\n return apiManager.invokeApiCacheable('channels.getParticipants', {\r\n channel: appChatsManager.getChannelInput(id),\r\n filter,\r\n offset,\r\n limit,\r\n hash: 0\r\n }, {cacheSeconds: 60}).then(result => {\r\n appUsersManager.saveApiUsers((result as ChannelsChannelParticipants.channelsChannelParticipants).users);\r\n return result as ChannelsChannelParticipants.channelsChannelParticipants;\r\n });\r\n /* let maybeAddSelf = (participants: any[]) => {\r\n let chat = appChatsManager.getChat(id);\r\n let selfMustBeFirst = filter._ === 'channelParticipantsRecent' &&\r\n !offset &&\r\n !chat.pFlags.kicked &&\r\n !chat.pFlags.left;\r\n\r\n if(selfMustBeFirst) {\r\n participants = copy(participants);\r\n let myID = appUsersManager.getSelf().id;\r\n let myIndex = participants.findIndex(p => p.user_id === myID);\r\n let myParticipant;\r\n\r\n if(myIndex !== -1) {\r\n myParticipant = participants[myIndex];\r\n participants.splice(myIndex, 1);\r\n } else {\r\n myParticipant = {_: 'channelParticipantSelf', user_id: myID};\r\n }\r\n\r\n participants.unshift(myParticipant);\r\n }\r\n\r\n return participants;\r\n } */\r\n }\r\n\r\n public getChannelParticipant(id: number, peerId: number) {\r\n return apiManager.invokeApiSingle('channels.getParticipant', {\r\n channel: appChatsManager.getChannelInput(id),\r\n participant: appPeersManager.getInputPeerById(peerId),\r\n }).then(channelParticipant => {\r\n appUsersManager.saveApiUsers(channelParticipant.users);\r\n return channelParticipant.participant;\r\n });\r\n }\r\n\r\n public getChannelFull(id: number, override?: true): Promise<ChatFull.channelFull> {\r\n if(this.chatsFull[id] !== undefined && !override) {\r\n return Promise.resolve(this.chatsFull[id] as ChatFull.channelFull);\r\n }\r\n\r\n const peerId = -id;\r\n if(this.fullPromises[peerId] !== undefined) {\r\n return this.fullPromises[peerId] as any;\r\n }\r\n\r\n return this.fullPromises[peerId] = apiManager.invokeApi('channels.getFullChannel', {\r\n channel: appChatsManager.getChannelInput(id)\r\n }).then((result) => {\r\n appChatsManager.saveApiChats(result.chats, true);\r\n appUsersManager.saveApiUsers(result.users);\r\n const fullChannel = result.full_chat as ChatFull.channelFull;\r\n if(fullChannel && fullChannel.chat_photo.id) {\r\n fullChannel.chat_photo = appPhotosManager.savePhoto(fullChannel.chat_photo, {type: 'profilePhoto', peerId});\r\n //appPhotosManager.savePhoto(fullChannel.chat_photo);\r\n }\r\n appNotificationsManager.savePeerSettings(peerId, fullChannel.notify_settings);\r\n\r\n delete this.fullPromises[peerId];\r\n this.chatsFull[id] = fullChannel;\r\n rootScope.dispatchEvent('chat_full_update', id);\r\n\r\n return fullChannel;\r\n }, (error) => {\r\n switch (error.type) {\r\n case 'CHANNEL_PRIVATE':\r\n let channel = appChatsManager.getChat(id);\r\n channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};\r\n apiUpdatesManager.processUpdateMessage({\r\n _: 'updates',\r\n updates: [{\r\n _: 'updateChannel',\r\n channel_id: id\r\n } as Update.updateChannel],\r\n chats: [channel],\r\n users: []\r\n });\r\n break;\r\n }\r\n\r\n return Promise.reject(error);\r\n }) as any;\r\n }\r\n\r\n public getMentions(chatId: number, query: string, threadId?: number): Promise<number[]> {\r\n const processUserIds = (userIds: number[]) => {\r\n /* const startsWithAt = query.charAt(0) === '@';\r\n if(startsWithAt) query = query.slice(1);\r\n \r\n const index = new SearchIndex<number>(!startsWithAt, !startsWithAt); */\r\n const index = new SearchIndex<number>(true, true);\r\n userIds.forEach(userId => {\r\n index.indexObject(userId, appUsersManager.getUserSearchText(userId));\r\n });\r\n\r\n return Array.from(index.search(query));\r\n };\r\n\r\n if(appChatsManager.isChannel(chatId)) {\r\n return this.getChannelParticipants(chatId, {\r\n _: 'channelParticipantsMentions',\r\n q: query,\r\n top_msg_id: threadId\r\n }, 50, 0).then(cP => {\r\n return processUserIds(cP.participants.map(p => appChatsManager.getParticipantPeerId(p)));\r\n });\r\n } else {\r\n return (this.getChatFull(chatId) as Promise<ChatFull.chatFull>).then(chatFull => {\r\n return processUserIds((chatFull.participants as ChatParticipants.chatParticipants).participants.map(p => p.user_id));\r\n });\r\n }\r\n }\r\n\r\n public invalidateChannelParticipants(id: number) {\r\n delete this.chatsFull[id];\r\n delete this.fullPromises[-id];\r\n apiManager.clearCache('channels.getParticipants', (params) => (params.channel as InputChannel.inputChannel).channel_id === id);\r\n rootScope.dispatchEvent('chat_full_update', id);\r\n }\r\n\r\n public updateProfile(first_name: string, last_name: string, about: string) {\r\n return apiManager.invokeApi('account.updateProfile', {\r\n first_name,\r\n last_name,\r\n about\r\n }).then(user => {\r\n appUsersManager.saveApiUser(user);\r\n \r\n return this.getProfile(rootScope.myId, true);\r\n });\r\n }\r\n\r\n public uploadProfilePhoto(inputFile: InputFile) {\r\n return apiManager.invokeApi('photos.uploadProfilePhoto', {\r\n file: inputFile\r\n }).then((updateResult) => {\r\n appUsersManager.saveApiUsers(updateResult.users);\r\n\r\n const myId = rootScope.myId;\r\n appPhotosManager.savePhoto(updateResult.photo, {\r\n type: 'profilePhoto',\r\n peerId: myId\r\n });\r\n\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateUserPhoto',\r\n user_id: myId,\r\n date: tsNow(true),\r\n photo: appUsersManager.getUser(myId).photo,\r\n previous: true\r\n });\r\n });\r\n }\r\n\r\n public getChatMembersString(id: number) {\r\n const chat: Chat = appChatsManager.getChat(id);\r\n if(chat._ === 'chatForbidden') {\r\n return i18n('YouWereKicked');\r\n }\r\n\r\n const chatFull = this.chatsFull[id];\r\n let count: number;\r\n if(chatFull) {\r\n if(chatFull._ === 'channelFull') {\r\n count = chatFull.participants_count;\r\n } else {\r\n count = (chatFull.participants as ChatParticipants.chatParticipants).participants?.length;\r\n }\r\n } else {\r\n count = (chat as Chat.chat).participants_count || (chat as any).participants?.participants.length;\r\n }\r\n\r\n const isChannel = appChatsManager.isBroadcast(id);\r\n count = count || 1;\r\n\r\n let key: LangPackKey = isChannel ? 'Peer.Status.Subscribers' : 'Peer.Status.Member';\r\n return i18n(key, [numberThousandSplitter(count)]);\r\n }\r\n\r\n public async getOnlines(id: number): Promise<number> {\r\n if(appChatsManager.isMegagroup(id)) {\r\n const timestamp = Date.now() / 1000 | 0;\r\n const cached = this.megagroupOnlines[id] ?? (this.megagroupOnlines[id] = {timestamp: 0, onlines: 1});\r\n if((timestamp - cached.timestamp) < 60) {\r\n return cached.onlines;\r\n }\r\n\r\n const res = await apiManager.invokeApi('messages.getOnlines', {\r\n peer: appChatsManager.getChannelInputPeer(id)\r\n });\r\n\r\n const onlines = res.onlines ?? 1;\r\n cached.timestamp = timestamp;\r\n cached.onlines = onlines;\r\n\r\n return onlines;\r\n } else if(appChatsManager.isBroadcast(id)) {\r\n return 1;\r\n }\r\n\r\n const chatInfo = await this.getChatFull(id);\r\n const _participants = (chatInfo as ChatFull.chatFull).participants as ChatParticipants.chatParticipants;\r\n if(_participants && _participants.participants) {\r\n const participants = _participants.participants;\r\n\r\n return participants.reduce((acc: number, participant) => {\r\n const user = appUsersManager.getUser(participant.user_id);\r\n if(user && user.status && user.status._ === 'userStatusOnline') {\r\n return acc + 1;\r\n }\r\n\r\n return acc;\r\n }, 0);\r\n } else {\r\n return 1;\r\n }\r\n }\r\n\r\n private onUpdateUserTyping = (update: Update.updateUserTyping | Update.updateChatUserTyping | Update.updateChannelUserTyping) => {\r\n const fromId = (update as Update.updateUserTyping).user_id || appPeersManager.getPeerId((update as Update.updateChatUserTyping).from_id);\r\n if(rootScope.myId === fromId || update.action._ === 'speakingInGroupCallAction') {\r\n return;\r\n }\r\n \r\n const peerId = update._ === 'updateUserTyping' ? \r\n fromId : \r\n -((update as Update.updateChatUserTyping).chat_id || (update as Update.updateChannelUserTyping).channel_id);\r\n const typings = this.typingsInPeer[peerId] ?? (this.typingsInPeer[peerId] = []);\r\n let typing = typings.find(t => t.userId === fromId);\r\n\r\n const cancelAction = () => {\r\n delete typing.timeout;\r\n //typings.findAndSplice(t => t === typing);\r\n const idx = typings.indexOf(typing);\r\n if(idx !== -1) {\r\n typings.splice(idx, 1);\r\n }\r\n\r\n rootScope.dispatchEvent('peer_typings', {peerId, typings});\r\n\r\n if(!typings.length) {\r\n delete this.typingsInPeer[peerId];\r\n }\r\n };\r\n\r\n if(typing && typing.timeout !== undefined) {\r\n clearTimeout(typing.timeout);\r\n }\r\n\r\n if(update.action._ === 'sendMessageCancelAction') {\r\n if(!typing) {\r\n return;\r\n }\r\n\r\n cancelAction();\r\n return;\r\n }\r\n\r\n if(!typing) {\r\n typing = {\r\n userId: fromId\r\n };\r\n\r\n typings.push(typing);\r\n }\r\n\r\n //console.log('updateChatUserTyping', update, typings);\r\n \r\n typing.action = update.action;\r\n \r\n const hasUser = appUsersManager.hasUser(fromId);\r\n if(!hasUser) {\r\n // let's load user here\r\n if(update._ === 'updateChatUserTyping') {\r\n if(update.chat_id && appChatsManager.hasChat(update.chat_id) && !appChatsManager.isChannel(update.chat_id)) {\r\n appProfileManager.getChatFull(update.chat_id).then(() => {\r\n if(typing.timeout !== undefined && appUsersManager.hasUser(fromId)) {\r\n rootScope.dispatchEvent('peer_typings', {peerId, typings});\r\n }\r\n });\r\n }\r\n }\r\n \r\n //return;\r\n } else {\r\n appUsersManager.forceUserOnline(fromId);\r\n }\r\n\r\n typing.timeout = window.setTimeout(cancelAction, 6000);\r\n if(hasUser) {\r\n rootScope.dispatchEvent('peer_typings', {peerId, typings});\r\n }\r\n };\r\n\r\n public getPeerTypings(peerId: number) {\r\n return this.typingsInPeer[peerId];\r\n }\r\n}\r\n\r\nconst appProfileManager = new AppProfileManager();\r\nMOUNT_CLASS_TO.appProfileManager = appProfileManager;\r\nexport default appProfileManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nexport function bytesToHex(bytes: ArrayLike<number>) {\r\n const arr: string[] = new Array(bytes.length);\r\n for(let i = 0; i < bytes.length; ++i) {\r\n arr[i] = (bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16);\r\n }\r\n return arr.join('');\r\n}\r\n\r\nexport function bytesFromHex(hexString: string) {\r\n const len = hexString.length;\r\n const bytes = new Uint8Array(Math.ceil(len / 2));\r\n let start = 0;\r\n\r\n if(len % 2) { // read 0x581 as 0x0581\r\n bytes[start++] = parseInt(hexString.charAt(0), 16);\r\n }\r\n\r\n for(let i = start; i < len; i += 2) {\r\n bytes[start++] = parseInt(hexString.substr(i, 2), 16);\r\n }\r\n\r\n return bytes;\r\n}\r\n\r\nexport function bytesToBase64(bytes: number[] | Uint8Array) {\r\n let mod3: number;\r\n let result = '';\r\n\r\n for(let nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; ++nIdx) {\r\n mod3 = nIdx % 3;\r\n nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);\r\n if(mod3 === 2 || nLen - nIdx === 1) {\r\n result += String.fromCharCode(\r\n uint6ToBase64(nUint24 >>> 18 & 63),\r\n uint6ToBase64(nUint24 >>> 12 & 63),\r\n uint6ToBase64(nUint24 >>> 6 & 63),\r\n uint6ToBase64(nUint24 & 63)\r\n );\r\n nUint24 = 0;\r\n }\r\n }\r\n\r\n return result.replace(/A(?=A$|$)/g, '=');\r\n}\r\n\r\nexport function uint6ToBase64(nUint6: number) {\r\n return nUint6 < 26\r\n ? nUint6 + 65\r\n : nUint6 < 52\r\n ? nUint6 + 71\r\n : nUint6 < 62\r\n ? nUint6 - 4\r\n : nUint6 === 62\r\n ? 43\r\n : nUint6 === 63\r\n ? 47\r\n : 65;\r\n}\r\n\r\nexport function bytesCmp(bytes1: number[] | Uint8Array, bytes2: number[] | Uint8Array) {\r\n const len = bytes1.length;\r\n if(len !== bytes2.length) {\r\n return false;\r\n }\r\n\r\n for(let i = 0; i < len; ++i) {\r\n if(bytes1[i] !== bytes2[i]) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\nexport function bytesXor(bytes1: Uint8Array, bytes2: Uint8Array) {\r\n const len = bytes1.length;\r\n const bytes = new Uint8Array(len);\r\n\r\n for(let i = 0; i < len; ++i) {\r\n bytes[i] = bytes1[i] ^ bytes2[i];\r\n }\r\n\r\n return bytes;\r\n}\r\n\r\n/* export function bytesToArrayBuffer(b: number[]) {\r\n return (new Uint8Array(b)).buffer;\r\n}\r\n\r\nexport function convertToArrayBuffer(bytes: any | ArrayBuffer | Uint8Array) {\r\n // Be careful with converting subarrays!!\r\n if(bytes instanceof ArrayBuffer) {\r\n return bytes;\r\n }\r\n if(bytes.buffer !== undefined &&\r\n bytes.buffer.byteLength === bytes.length * bytes.BYTES_PER_ELEMENT) {\r\n return bytes.buffer;\r\n }\r\n return bytesToArrayBuffer(bytes);\r\n} */\r\n\r\nexport function convertToUint8Array(bytes: Uint8Array | ArrayBuffer | number[] | string): Uint8Array {\r\n if(bytes instanceof Uint8Array) {\r\n return bytes;\r\n } else if(typeof(bytes) === 'string') {\r\n return new TextEncoder().encode(bytes);\r\n }\r\n\r\n return new Uint8Array(bytes);\r\n}\r\n\r\n/* export function bytesFromArrayBuffer(buffer: ArrayBuffer) {\r\n const len = buffer.byteLength;\r\n const byteView = new Uint8Array(buffer);\r\n const bytes: number[] = [];\r\n\r\n for(let i = 0; i < len; ++i) {\r\n bytes[i] = byteView[i];\r\n }\r\n\r\n return bytes;\r\n}\r\n\r\nexport function bufferConcat(buffer1: any, buffer2: any) {\r\n const l1 = buffer1.byteLength || buffer1.length;\r\n const l2 = buffer2.byteLength || buffer2.length;\r\n const tmp = new Uint8Array(l1 + l2);\r\n tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0);\r\n tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1);\r\n\r\n return tmp.buffer;\r\n} */\r\n\r\nexport function bufferConcats(...args: (ArrayBuffer | Uint8Array | number[])[]) {\r\n const length = args.reduce((acc, v) => acc + ((v as ArrayBuffer).byteLength || (v as Uint8Array).length), 0);\r\n\r\n const tmp = new Uint8Array(length);\r\n \r\n let lastLength = 0;\r\n args.forEach(b => {\r\n tmp.set(b instanceof ArrayBuffer ? new Uint8Array(b) : b, lastLength);\r\n lastLength += (b as ArrayBuffer).byteLength || (b as Uint8Array).length;\r\n });\r\n\r\n return tmp/* .buffer */;\r\n}\r\n\r\nexport function bytesFromWordss(input: Uint32Array) {\r\n const o = new Uint8Array(input.byteLength);\r\n for(let i = 0, length = input.length * 4; i < length; ++i) {\r\n o[i] = ((input[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);\r\n }\r\n\r\n return o;\r\n}\r\n\r\nexport function bytesToWordss(input: Parameters<typeof convertToUint8Array>[0]) {\r\n const bytes = convertToUint8Array(input);\r\n\r\n const words: number[] = [];\r\n for(let i = 0, len = bytes.length; i < len; ++i) {\r\n words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);\r\n }\r\n\r\n return new Uint32Array(words);\r\n}\r\n\r\n// * https://stackoverflow.com/a/52827031\r\n/* export const isBigEndian = (() => {\r\n const array = new Uint8Array(4);\r\n const view = new Uint32Array(array.buffer);\r\n return !((view[0] = 1) & array[0]);\r\n})(); */\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\ntype TargetType = HTMLElement;\r\nexport type OnVisibilityChange = (target: TargetType, visible: boolean) => void;\r\n\r\nexport default class VisibilityIntersector {\r\n private observer: IntersectionObserver;\r\n private items: Map<TargetType, boolean> = new Map();\r\n private locked = false;\r\n\r\n constructor(onVisibilityChange: OnVisibilityChange) {\r\n this.observer = new IntersectionObserver((entries) => {\r\n if(this.locked) {\r\n return;\r\n }\r\n\r\n const changed: {target: TargetType, visible: boolean}[] = [];\r\n\r\n entries.forEach(entry => {\r\n const target = entry.target as TargetType;\r\n\r\n if(this.items.get(target) === entry.isIntersecting) {\r\n return;\r\n } else {\r\n this.items.set(target, entry.isIntersecting);\r\n }\r\n\r\n /* if(entry.isIntersecting) {\r\n console.log('ooo', entry);\r\n } */\r\n\r\n /* if(this.locked) {\r\n return;\r\n } */\r\n\r\n changed[entry.isIntersecting ? 'unshift' : 'push']({target, visible: entry.isIntersecting});\r\n\r\n //onVisibilityChange(target, entry.isIntersecting);\r\n });\r\n\r\n changed.forEach(smth => {\r\n onVisibilityChange(smth.target, smth.visible);\r\n });\r\n });\r\n }\r\n\r\n public getVisible() {\r\n const items: TargetType[] = [];\r\n this.items.forEach((value, key) => {\r\n if(value) {\r\n items.push(key);\r\n }\r\n });\r\n\r\n return items;\r\n }\r\n\r\n public clearVisible() {\r\n const visible = this.getVisible();\r\n for(const target of visible) {\r\n this.items.set(target, false);\r\n }\r\n }\r\n\r\n public isVisible(target: TargetType) {\r\n return this.items.get(target);\r\n }\r\n\r\n public disconnect() {\r\n this.observer.disconnect();\r\n this.items.clear();\r\n }\r\n\r\n public refresh() {\r\n this.observer.disconnect();\r\n\r\n //window.requestAnimationFrame(() => {\r\n const targets = [...this.items.keys()];\r\n for(const target of targets) {\r\n //this.items.set(target, false);\r\n this.observer.observe(target);\r\n }\r\n //});\r\n }\r\n\r\n public refreshVisible() {\r\n const visible = this.getVisible();\r\n for(const target of visible) {\r\n this.observer.unobserve(target);\r\n }\r\n\r\n for(const target of visible) {\r\n this.observer.observe(target);\r\n }\r\n }\r\n\r\n public observe(target: TargetType) {\r\n this.items.set(target, false);\r\n this.observer.observe(target);\r\n }\r\n\r\n public unobserve(target: TargetType) {\r\n this.observer.unobserve(target);\r\n this.items.delete(target);\r\n }\r\n\r\n public unlock() {\r\n this.locked = false;\r\n }\r\n\r\n public unlockAndRefresh() {\r\n this.unlock();\r\n this.refresh();\r\n }\r\n\r\n public lock() {\r\n this.locked = true;\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { throttle } from \"../helpers/schedulers\";\r\nimport { logger, LogTypes } from \"../lib/logger\";\r\nimport VisibilityIntersector, { OnVisibilityChange } from \"./visibilityIntersector\";\r\nimport { findAndSpliceAll } from \"../helpers/array\";\r\n\r\ntype LazyLoadElementBase = {\r\n load: () => Promise<any>\r\n};\r\n\r\ntype LazyLoadElement = Omit<LazyLoadElementBase, 'load'> & {\r\n load: (target?: HTMLElement) => Promise<any>,\r\n div: HTMLElement\r\n wasSeen?: boolean,\r\n};\r\n\r\nconst PARALLEL_LIMIT = 8;\r\n\r\nexport class LazyLoadQueueBase {\r\n public queueId = 0;\r\n protected queue: Array<LazyLoadElementBase> = [];\r\n protected inProcess: Set<LazyLoadElementBase> = new Set();\r\n\r\n protected lockPromise: Promise<void> = null;\r\n protected unlockResolve: () => void = null;\r\n\r\n protected log = logger('LL', LogTypes.Error);\r\n protected processQueue: () => void;\r\n\r\n constructor(protected parallelLimit = PARALLEL_LIMIT) {\r\n this.processQueue = throttle(() => this._processQueue(), 20, false);\r\n }\r\n\r\n public clear() {\r\n this.inProcess.clear(); // ацтеки забьются, будет плохо\r\n\r\n this.queue.length = 0;\r\n // unreachable code\r\n /* for(let item of this.inProcess) { \r\n this.lazyLoadMedia.push(item);\r\n } */\r\n }\r\n\r\n public lock() {\r\n if(this.lockPromise) return;\r\n\r\n //const perf = performance.now();\r\n this.lockPromise = new Promise((resolve, reject) => {\r\n this.unlockResolve = resolve;\r\n });\r\n\r\n /* if(DEBUG) {\r\n this.lockPromise.then(() => {\r\n this.log('was locked for:', performance.now() - perf);\r\n });\r\n } */\r\n }\r\n\r\n public unlock() {\r\n if(!this.unlockResolve) return;\r\n\r\n this.unlockResolve();\r\n this.unlockResolve = this.lockPromise = null;\r\n\r\n this.processQueue();\r\n }\r\n\r\n protected async processItem(item: LazyLoadElementBase) {\r\n if(this.lockPromise) {\r\n return;\r\n }\r\n\r\n this.inProcess.add(item);\r\n\r\n /* if(DEBUG) {\r\n this.log('will load media', this.lockPromise, item);\r\n } */\r\n\r\n try {\r\n //await new Promise((resolve) => setTimeout(resolve, 2e3));\r\n //await new Promise((resolve, reject) => window.requestAnimationFrame(() => window.requestAnimationFrame(resolve)));\r\n //await item.load(item.div);\r\n await this.loadItem(item);\r\n } catch(err) {\r\n if(!['NO_ENTRY_FOUND', 'STORAGE_OFFLINE'].includes(err)) {\r\n this.log.error('loadMediaQueue error:', err/* , item */);\r\n }\r\n }\r\n\r\n this.inProcess.delete(item);\r\n\r\n /* if(DEBUG) {\r\n this.log('loaded media', item);\r\n } */\r\n\r\n this.processQueue();\r\n }\r\n\r\n protected loadItem(item: LazyLoadElementBase) {\r\n return item.load();\r\n }\r\n\r\n protected getItem() {\r\n return this.queue.shift();\r\n }\r\n\r\n protected addElement(method: 'push' | 'unshift', el: LazyLoadElementBase) {\r\n this.queue[method](el);\r\n this.processQueue();\r\n }\r\n\r\n protected _processQueue(item?: LazyLoadElementBase) {\r\n if(!this.queue.length || this.lockPromise || (this.parallelLimit > 0 && this.inProcess.size >= this.parallelLimit)) return;\r\n\r\n //console.log('_processQueue start');\r\n let added = 0;\r\n do {\r\n if(item) {\r\n this.queue.findAndSplice(i => i === item);\r\n } else {\r\n item = this.getItem();\r\n }\r\n \r\n if(item) {\r\n this.processItem(item);\r\n } else {\r\n break;\r\n }\r\n\r\n item = null;\r\n ++added;\r\n } while(this.inProcess.size < this.parallelLimit && this.queue.length);\r\n //console.log('_processQueue end, added', added, this.queue.length);\r\n }\r\n\r\n public push(el: LazyLoadElementBase) {\r\n this.addElement('push', el);\r\n }\r\n\r\n public unshift(el: LazyLoadElementBase) {\r\n this.addElement('unshift', el);\r\n }\r\n}\r\n\r\nexport class LazyLoadQueueIntersector extends LazyLoadQueueBase {\r\n protected queue: Array<LazyLoadElement> = [];\r\n protected inProcess: Set<LazyLoadElement> = new Set();\r\n\r\n public intersector: VisibilityIntersector;\r\n protected intersectorTimeout: number;\r\n\r\n constructor(protected parallelLimit = PARALLEL_LIMIT) {\r\n super(parallelLimit);\r\n }\r\n\r\n public lock() {\r\n super.lock();\r\n this.intersector.lock();\r\n }\r\n\r\n public unlock() {\r\n super.unlock();\r\n this.intersector.unlock();\r\n }\r\n\r\n public unlockAndRefresh() {\r\n super.unlock();\r\n this.intersector.unlockAndRefresh();\r\n }\r\n\r\n public clear() {\r\n super.clear();\r\n this.intersector.disconnect();\r\n }\r\n\r\n public refresh() {\r\n this.intersector.refresh();\r\n }\r\n\r\n protected loadItem(item: LazyLoadElement) {\r\n return item.load(item.div);\r\n }\r\n\r\n protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {\r\n const item = this.queue.find(i => i.div === el.div && i.load === el.load);\r\n if(item) {\r\n return false;\r\n } else {\r\n for(const item of this.inProcess) {\r\n if(item.div === el.div && item.load === el.load) {\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n this.queue[method](el);\r\n return true;\r\n }\r\n\r\n protected setProcessQueueTimeout() {\r\n if(!this.intersectorTimeout) {\r\n this.intersectorTimeout = window.setTimeout(() => {\r\n this.intersectorTimeout = 0;\r\n this.processQueue();\r\n }, 0);\r\n }\r\n }\r\n\r\n public push(el: LazyLoadElement) {\r\n super.push(el);\r\n }\r\n\r\n public unshift(el: LazyLoadElement) {\r\n super.unshift(el);\r\n }\r\n\r\n public unobserve(el: HTMLElement) {\r\n findAndSpliceAll(this.queue, (i) => i.div === el);\r\n\r\n this.intersector.unobserve(el);\r\n }\r\n}\r\n\r\nexport default class LazyLoadQueue extends LazyLoadQueueIntersector {\r\n constructor(protected parallelLimit = PARALLEL_LIMIT) {\r\n super(parallelLimit);\r\n\r\n this.intersector = new VisibilityIntersector(this.onVisibilityChange);\r\n }\r\n\r\n private onVisibilityChange = (target: HTMLElement, visible: boolean) => {\r\n if(visible) {\r\n /* if(DEBUG) {\r\n this.log('isIntersecting', target);\r\n } */\r\n\r\n // need for set element first if scrolled\r\n findAndSpliceAll(this.queue, (i) => i.div === target).forEach(item => {\r\n item.wasSeen = true;\r\n this.queue.unshift(item);\r\n //this.processQueue(item);\r\n });\r\n\r\n this.setProcessQueueTimeout();\r\n }\r\n };\r\n\r\n protected getItem() {\r\n return this.queue.findAndSplice(item => item.wasSeen);\r\n }\r\n\r\n public async processItem(item: LazyLoadElement) {\r\n await super.processItem(item);\r\n this.intersector.unobserve(item.div);\r\n }\r\n\r\n protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {\r\n const inserted = super.addElement(method, el);\r\n\r\n if(!inserted) return false;\r\n\r\n this.intersector.observe(el.div);\r\n /* if(el.wasSeen) {\r\n this.processQueue(el);\r\n } else */if(!el.hasOwnProperty('wasSeen')) {\r\n el.wasSeen = false;\r\n }\r\n \r\n return true;\r\n }\r\n}\r\n\r\nexport class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {\r\n private _queue: Map<HTMLElement, LazyLoadElement> = new Map();\r\n\r\n constructor(protected parallelLimit = PARALLEL_LIMIT, protected onVisibilityChange?: OnVisibilityChange) {\r\n super(parallelLimit);\r\n\r\n this.intersector = new VisibilityIntersector((target, visible) => {\r\n const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);\r\n if(visible) {\r\n const items = spliced.length ? spliced : [this._queue.get(target)];\r\n items.forEach(item => {\r\n this.queue.unshift(item || this._queue.get(target));\r\n });\r\n }\r\n \r\n this.onVisibilityChange && this.onVisibilityChange(target, visible);\r\n this.setProcessQueueTimeout();\r\n });\r\n }\r\n\r\n public clear() {\r\n super.clear();\r\n this._queue.clear();\r\n }\r\n\r\n /* public async processItem(item: LazyLoadElement) {\r\n //await super.processItem(item);\r\n await LazyLoadQueueBase.prototype.processItem.call(this, item);\r\n\r\n if(this.lazyLoadMedia.length) {\r\n this.processQueue();\r\n }\r\n } */\r\n\r\n public observe(el: LazyLoadElement) {\r\n this._queue.set(el.div, el);\r\n this.intersector.observe(el.div);\r\n }\r\n}\r\n\r\nexport class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {\r\n constructor(protected parallelLimit = PARALLEL_LIMIT, protected onVisibilityChange?: OnVisibilityChange) {\r\n super(parallelLimit);\r\n\r\n this.intersector = new VisibilityIntersector((target, visible) => {\r\n const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);\r\n if(visible && spliced.length) {\r\n spliced.forEach(item => {\r\n this.queue.unshift(item);\r\n });\r\n }\r\n \r\n this.onVisibilityChange && this.onVisibilityChange(target, visible);\r\n this.setProcessQueueTimeout();\r\n });\r\n }\r\n\r\n public observe(el: HTMLElement) {\r\n this.intersector.observe(el);\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nexport const loadedURLs: {[url: string]: boolean} = {};\r\nconst set = (elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, url: string) => {\r\n if(elem instanceof HTMLImageElement || elem instanceof HTMLVideoElement) elem.src = url;\r\n else if(elem instanceof SVGImageElement) elem.setAttributeNS(null, 'href', url);\r\n else elem.style.backgroundImage = 'url(' + url + ')';\r\n};\r\n\r\n// проблема функции в том, что она не подходит для ссылок, пригодна только для blob'ов, потому что обычным ссылкам нужен 'load' каждый раз.\r\nexport default function renderImageFromUrl(elem: HTMLElement | HTMLImageElement | SVGImageElement | HTMLVideoElement, \r\n url: string, callback?: (err?: Event) => void, useCache = true) {\r\n if(!url) {\r\n console.error('renderImageFromUrl: no url?', elem, url);\r\n callback && callback();\r\n return;\r\n }\r\n\r\n if(((loadedURLs[url]/* && false */) && useCache) || elem instanceof HTMLVideoElement) {\r\n if(elem) {\r\n set(elem, url);\r\n }\r\n \r\n callback && callback();\r\n } else {\r\n const isImage = elem instanceof HTMLImageElement;\r\n const loader = isImage ? elem as HTMLImageElement : new Image();\r\n //const loader = new Image();\r\n loader.src = url;\r\n //let perf = performance.now();\r\n loader.addEventListener('load', () => {\r\n if(!isImage && elem) {\r\n set(elem, url);\r\n }\r\n\r\n loadedURLs[url] = true;\r\n //console.log('onload:', url, performance.now() - perf);\r\n if(callback) {\r\n // TODO: переделать прогрузки аватаров до начала анимации, иначе с этим ожиданием они неприятно появляются\r\n /* getHeavyAnimationPromise().then(() => {\r\n callback();\r\n }); */\r\n callback();\r\n }\r\n\r\n //callback && callback();\r\n });\r\n\r\n if(callback) {\r\n loader.addEventListener('error', callback);\r\n }\r\n }\r\n}\r\n\r\nexport function renderImageFromUrlPromise(elem: Parameters<typeof renderImageFromUrl>[0], url: string, useCache?: boolean) {\r\n return new Promise((resolve) => {\r\n renderImageFromUrl(elem, url, resolve, useCache);\r\n });\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n * \r\n * Originally from:\r\n * https://github.com/zhukov/webogram\r\n * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>\r\n * https://github.com/zhukov/webogram/blob/master/LICENSE\r\n */\r\n\r\nimport { fontFamily } from \"../../components/middleEllipsis\";\r\nimport { MOUNT_CLASS_TO } from \"../../config/debug\";\r\nimport { CancellablePromise, deferredPromise } from \"../../helpers/cancellablePromise\";\r\nimport { tsNow } from \"../../helpers/date\";\r\nimport { deepEqual } from \"../../helpers/object\";\r\nimport { convertInputKeyToKey } from \"../../helpers/string\";\r\nimport { isMobile } from \"../../helpers/userAgent\";\r\nimport { InputNotifyPeer, InputPeerNotifySettings, NotifyPeer, PeerNotifySettings, Update } from \"../../layer\";\r\nimport I18n from \"../langPack\";\r\nimport apiManager from \"../mtproto/mtprotoworker\";\r\nimport webPushApiManager, { PushSubscriptionNotify } from \"../mtproto/webPushApiManager\";\r\nimport rootScope from \"../rootScope\";\r\nimport stateStorage from \"../stateStorage\";\r\nimport apiUpdatesManager from \"./apiUpdatesManager\";\r\nimport appChatsManager from \"./appChatsManager\";\r\nimport appPeersManager from \"./appPeersManager\";\r\nimport appRuntimeManager from \"./appRuntimeManager\";\r\nimport appStateManager from \"./appStateManager\";\r\nimport appUsersManager from \"./appUsersManager\";\r\n\r\ntype MyNotification = Notification & {\r\n hidden?: boolean,\r\n show?: () => void,\r\n};\r\n\r\nexport type NotifyOptions = Partial<{\r\n tag: string;\r\n image: string;\r\n key: string;\r\n title: string;\r\n message: string;\r\n silent: boolean;\r\n onclick: () => void;\r\n}>;\r\n\r\nexport type NotificationSettings = {\r\n nodesktop: boolean,\r\n volume: number,\r\n novibrate: boolean,\r\n nopreview: boolean,\r\n nopush: boolean,\r\n nosound: boolean\r\n};\r\n\r\ntype ImSadAboutIt = Promise<PeerNotifySettings> | PeerNotifySettings;\r\nexport class AppNotificationsManager {\r\n private notificationsUiSupport: boolean;\r\n private notificationsShown: {[key: string]: MyNotification} = {};\r\n private notificationIndex = 0;\r\n private notificationsCount = 0;\r\n private soundsPlayed: {[tag: string]: number} = {};\r\n private vibrateSupport = !!navigator.vibrate;\r\n private nextSoundAt: number;\r\n private prevSoundVolume: number;\r\n private peerSettings = {\r\n notifyPeer: {} as {[peerId: number]: ImSadAboutIt},\r\n notifyUsers: null as ImSadAboutIt,\r\n notifyChats: null as ImSadAboutIt,\r\n notifyBroadcasts: null as ImSadAboutIt\r\n };\r\n //private exceptions: {[peerId: string]: PeerNotifySettings} = {};\r\n private notifyContactsSignUp: Promise<boolean>;\r\n private faviconEl: HTMLLinkElement = document.head.querySelector('link[rel=\"icon\"]');\r\n\r\n private titleBackup = document.title;\r\n private titleChanged = false;\r\n private titleInterval: number;\r\n private prevFavicon: string;\r\n private stopped = false;\r\n\r\n private settings: NotificationSettings = {} as any;\r\n\r\n private registeredDevice: any;\r\n private pushInited = false;\r\n\r\n private topMessagesDeferred: CancellablePromise<void>;\r\n\r\n private notifySoundEl: HTMLElement;\r\n\r\n private getNotifyPeerTypePromise: Promise<any>;\r\n\r\n constructor() {\r\n // @ts-ignore\r\n navigator.vibrate = navigator.vibrate || navigator.mozVibrate || navigator.webkitVibrate;\r\n\r\n this.notificationsUiSupport = ('Notification' in window) || ('mozNotification' in navigator);\r\n\r\n this.topMessagesDeferred = deferredPromise<void>();\r\n\r\n this.notifySoundEl = document.createElement('div');\r\n this.notifySoundEl.id = 'notify-sound';\r\n document.body.append(this.notifySoundEl);\r\n\r\n rootScope.addEventListener('instance_deactivated', () => {\r\n this.stop();\r\n });\r\n\r\n rootScope.addEventListener('instance_activated', () => {\r\n if(this.stopped) {\r\n this.start();\r\n }\r\n });\r\n\r\n rootScope.addEventListener('idle', (newVal) => {\r\n if(this.stopped) {\r\n return;\r\n }\r\n\r\n if(!newVal) {\r\n this.clear();\r\n }\r\n\r\n this.toggleToggler();\r\n });\r\n\r\n rootScope.addMultipleEventsListeners({\r\n updateNotifySettings: (update) => {\r\n this.savePeerSettings(update.peer._ === 'notifyPeer' ? appPeersManager.getPeerId(update.peer.peer) : update.peer._, update.notify_settings);\r\n rootScope.dispatchEvent('notify_settings', update);\r\n }\r\n });\r\n\r\n rootScope.addEventListener('push_init', (tokenData) => {\r\n this.pushInited = true;\r\n if(!this.settings.nodesktop && !this.settings.nopush) {\r\n if(tokenData) {\r\n this.registerDevice(tokenData);\r\n } else {\r\n webPushApiManager.subscribe();\r\n }\r\n } else {\r\n this.unregisterDevice(tokenData);\r\n }\r\n });\r\n rootScope.addEventListener('push_subscribe', (tokenData) => {\r\n this.registerDevice(tokenData);\r\n });\r\n rootScope.addEventListener('push_unsubscribe', (tokenData) => {\r\n this.unregisterDevice(tokenData);\r\n });\r\n\r\n rootScope.addEventListener('dialogs_multiupdate', () => {\r\n //unregisterTopMsgs()\r\n this.topMessagesDeferred.resolve();\r\n }, {once: true});\r\n\r\n rootScope.addEventListener('push_notification_click', (notificationData) => {\r\n if(notificationData.action === 'push_settings') {\r\n /* this.topMessagesDeferred.then(() => {\r\n $modal.open({\r\n templateUrl: templateUrl('settings_modal'),\r\n controller: 'SettingsModalController',\r\n windowClass: 'settings_modal_window mobile_modal',\r\n backdrop: 'single'\r\n })\r\n }); */\r\n return;\r\n }\r\n\r\n if(notificationData.action === 'mute1d') {\r\n apiManager.invokeApi('account.updateDeviceLocked', {\r\n period: 86400\r\n }).then(() => {\r\n // var toastData = toaster.pop({\r\n // type: 'info',\r\n // body: _('push_action_mute1d_success'),\r\n // bodyOutputType: 'trustedHtml',\r\n // clickHandler: () => {\r\n // toaster.clear(toastData)\r\n // },\r\n // showCloseButton: false\r\n // })\r\n });\r\n\r\n return;\r\n }\r\n\r\n const peerId = notificationData.custom && +notificationData.custom.peerId;\r\n console.log('click', notificationData, peerId);\r\n if(peerId) {\r\n this.topMessagesDeferred.then(() => {\r\n if(notificationData.custom.channel_id &&\r\n !appChatsManager.hasChat(+notificationData.custom.channel_id)) {\r\n return;\r\n }\r\n\r\n if(peerId > 0 && !appUsersManager.hasUser(peerId)) {\r\n return;\r\n }\r\n\r\n rootScope.dispatchEvent('history_focus', {\r\n peerId,\r\n mid: +notificationData.custom.msg_id\r\n });\r\n });\r\n }\r\n });\r\n }\r\n\r\n private toggleToggler(enable = rootScope.idle.isIDLE) {\r\n if(isMobile) return;\r\n\r\n const resetTitle = () => {\r\n this.titleChanged = false;\r\n document.title = this.titleBackup;\r\n this.setFavicon();\r\n };\r\n\r\n window.clearInterval(this.titleInterval);\r\n this.titleInterval = 0;\r\n\r\n if(!enable) {\r\n resetTitle();\r\n } else {\r\n this.titleInterval = window.setInterval(() => {\r\n if(!this.notificationsCount) {\r\n this.toggleToggler(false);\r\n } else if(this.titleChanged) {\r\n resetTitle();\r\n } else {\r\n this.titleChanged = true;\r\n document.title = I18n.format('Notifications.Count', true, [this.notificationsCount]);\r\n //this.setFavicon('assets/img/favicon_unread.ico');\r\n\r\n // fetch('assets/img/favicon.ico')\r\n // .then(res => res.blob())\r\n // .then(blob => {\r\n // const img = document.createElement('img');\r\n // img.src = URL.createObjectURL(blob);\r\n\r\n const canvas = document.createElement('canvas');\r\n canvas.width = 32 * window.devicePixelRatio;\r\n canvas.height = canvas.width;\r\n \r\n const ctx = canvas.getContext('2d');\r\n ctx.beginPath();\r\n ctx.arc(canvas.width / 2, canvas.height / 2, canvas.width / 2, 0, 2 * Math.PI, false);\r\n ctx.fillStyle = '#3390ec';\r\n ctx.fill();\r\n\r\n let fontSize = 24;\r\n let str = '' + this.notificationsCount;\r\n if(this.notificationsCount < 10) {\r\n fontSize = 22;\r\n } else if(this.notificationsCount < 100) {\r\n fontSize = 20;\r\n } else {\r\n str = '99+';\r\n fontSize = 16;\r\n }\r\n\r\n fontSize *= window.devicePixelRatio;\r\n \r\n ctx.font = `700 ${fontSize}px ${fontFamily}`;\r\n ctx.textBaseline = 'middle';\r\n ctx.textAlign = 'center';\r\n ctx.fillStyle = 'white';\r\n ctx.fillText(str, canvas.width / 2, canvas.height * .5625);\r\n\r\n /* const ctx = canvas.getContext('2d');\r\n ctx.drawImage(img, 0, 0, canvas.width, canvas.height); */\r\n \r\n this.setFavicon(canvas.toDataURL());\r\n // });\r\n }\r\n }, 1000);\r\n }\r\n }\r\n\r\n public updateLocalSettings = () => {\r\n Promise.all(['notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview', 'notify_nopush'].map(k => stateStorage.get(k as any)))\r\n .then((updSettings) => {\r\n this.settings.nodesktop = updSettings[0];\r\n this.settings.volume = updSettings[1] === undefined ? 0.5 : updSettings[1];\r\n this.settings.novibrate = updSettings[2];\r\n this.settings.nopreview = updSettings[3];\r\n this.settings.nopush = updSettings[4];\r\n\r\n if(this.pushInited) {\r\n const needPush = !this.settings.nopush && !this.settings.nodesktop && webPushApiManager.isAvailable || false;\r\n const hasPush = this.registeredDevice !== false;\r\n if(needPush !== hasPush) {\r\n if(needPush) {\r\n webPushApiManager.subscribe();\r\n } else {\r\n webPushApiManager.unsubscribe();\r\n }\r\n }\r\n }\r\n\r\n webPushApiManager.setSettings(this.settings);\r\n });\r\n\r\n appStateManager.getState().then(state => {\r\n this.settings.nosound = !state.settings.notifications.sound;\r\n });\r\n }\r\n\r\n public getLocalSettings() {\r\n return this.settings;\r\n }\r\n\r\n public getNotifySettings(peer: InputNotifyPeer): ImSadAboutIt {\r\n let key: any = convertInputKeyToKey(peer._);\r\n let obj: any = this.peerSettings[key as NotifyPeer['_']];\r\n\r\n if(peer._ === 'inputNotifyPeer') {\r\n key = appPeersManager.getPeerId(peer.peer);\r\n obj = obj[key];\r\n }\r\n\r\n if(obj) {\r\n return obj;\r\n }\r\n\r\n return (obj || this.peerSettings)[key] = apiManager.invokeApi('account.getNotifySettings', {peer})\r\n .then(settings => {\r\n this.savePeerSettings(key, settings);\r\n return settings;\r\n });\r\n }\r\n\r\n public getNotifyPeerTypeSettings() {\r\n if(this.getNotifyPeerTypePromise) return this.getNotifyPeerTypePromise;\r\n\r\n const promises = (['inputNotifyBroadcasts', 'inputNotifyUsers', 'inputNotifyChats'] as Exclude<InputNotifyPeer['_'], 'inputNotifyPeer'>[])\r\n .map((inputKey) => {\r\n return this.getNotifySettings({_: inputKey});\r\n });\r\n\r\n return this.getNotifyPeerTypePromise = Promise.all(promises);\r\n }\r\n\r\n public updateNotifySettings(peer: InputNotifyPeer, settings: InputPeerNotifySettings) {\r\n //this.savePeerSettings(peerId, settings);\r\n\r\n /* const inputSettings: InputPeerNotifySettings = copy(settings) as any;\r\n inputSettings._ = 'inputPeerNotifySettings'; */\r\n\r\n return apiManager.invokeApi('account.updateNotifySettings', {\r\n peer,\r\n settings\r\n }).then(value => {\r\n if(value) {\r\n apiUpdatesManager.processLocalUpdate({\r\n _: 'updateNotifySettings', \r\n peer: {\r\n ...peer as any,\r\n _: convertInputKeyToKey(peer._)\r\n }, \r\n notify_settings: { // ! WOW, IT WORKS !\r\n ...settings,\r\n _: 'peerNotifySettings',\r\n }\r\n });\r\n }\r\n });\r\n }\r\n\r\n public getNotifyExceptions() {\r\n apiManager.invokeApi('account.getNotifyExceptions', {compare_sound: true})\r\n .then((updates) => {\r\n apiUpdatesManager.processUpdateMessage(updates);\r\n });\r\n }\r\n\r\n public getContactSignUpNotification() {\r\n if(this.notifyContactsSignUp) return this.notifyContactsSignUp;\r\n return this.notifyContactsSignUp = apiManager.invokeApi('account.getContactSignUpNotification');\r\n }\r\n\r\n public setContactSignUpNotification(silent: boolean) {\r\n apiManager.invokeApi('account.setContactSignUpNotification', {silent})\r\n .then(value => {\r\n this.notifyContactsSignUp = Promise.resolve(!silent);\r\n });\r\n }\r\n\r\n private setFavicon(href: string = 'assets/img/favicon.ico') {\r\n if(this.prevFavicon === href) {\r\n return;\r\n }\r\n\r\n const link = this.faviconEl.cloneNode() as HTMLLinkElement;\r\n link.href = href;\r\n this.faviconEl.parentNode.replaceChild(link, this.faviconEl);\r\n this.faviconEl = link;\r\n\r\n this.prevFavicon = href;\r\n }\r\n\r\n public savePeerSettings(key: number | Exclude<NotifyPeer['_'], 'notifyPeer'>, settings: PeerNotifySettings) {\r\n let obj: any;\r\n if(typeof(key) === 'number') {\r\n obj = this.peerSettings['notifyPeer'];\r\n }\r\n \r\n (obj || this.peerSettings)[key] = settings;\r\n\r\n if(typeof(key) !== 'number') {\r\n rootScope.dispatchEvent('notify_peer_type_settings', {key, settings});\r\n }\r\n\r\n //rootScope.broadcast('notify_settings', {peerId: peerId});\r\n }\r\n\r\n public isMuted(peerNotifySettings: PeerNotifySettings) {\r\n return peerNotifySettings._ === 'peerNotifySettings' &&\r\n ((peerNotifySettings.mute_until * 1000) > tsNow() || peerNotifySettings.silent);\r\n }\r\n\r\n public getPeerMuted(peerId: number) {\r\n const ret = this.getNotifySettings({_: 'inputNotifyPeer', peer: appPeersManager.getInputPeerById(peerId)});\r\n return (ret instanceof Promise ? ret : Promise.resolve(ret))\r\n .then((peerNotifySettings) => this.isMuted(peerNotifySettings));\r\n }\r\n\r\n public getPeerLocalSettings(peerId: number, respectType = true): PeerNotifySettings {\r\n const n: PeerNotifySettings = {\r\n _: 'peerNotifySettings'\r\n };\r\n\r\n const notifySettings = this.peerSettings['notifyPeer'][peerId];\r\n //if(!notifySettings || (notifySettings instanceof Promise)) return false;\r\n if(notifySettings && !(notifySettings instanceof Promise)) {\r\n Object.assign(n, notifySettings);\r\n }\r\n\r\n if(respectType) {\r\n const inputNotify = appPeersManager.getInputNotifyPeerById(peerId, true);\r\n const key = convertInputKeyToKey(inputNotify._);\r\n const typeNotifySettings = this.peerSettings[key as NotifyPeer['_']];\r\n if(typeNotifySettings && !(typeNotifySettings instanceof Promise)) {\r\n for(let i in typeNotifySettings) {\r\n // @ts-ignore\r\n if(n[i] === undefined) {\r\n // @ts-ignore\r\n n[i] = typeNotifySettings[i];\r\n }\r\n }\r\n }\r\n }\r\n\r\n return n;\r\n }\r\n\r\n public isPeerLocalMuted(peerId: number, respectType = true) {\r\n if(peerId === rootScope.myId) return false;\r\n\r\n const notifySettings = this.getPeerLocalSettings(peerId, respectType);\r\n return this.isMuted(notifySettings);\r\n }\r\n\r\n public start() {\r\n this.updateLocalSettings();\r\n rootScope.addEventListener('settings_updated', this.updateLocalSettings);\r\n webPushApiManager.start();\r\n\r\n if(!this.notificationsUiSupport) {\r\n return false;\r\n }\r\n\r\n if('Notification' in window && Notification.permission !== 'granted' && Notification.permission !== 'denied') {\r\n window.addEventListener('click', this.requestPermission);\r\n }\r\n\r\n try {\r\n if('onbeforeunload' in window) {\r\n window.addEventListener('beforeunload', this.clear);\r\n }\r\n } catch (e) {}\r\n }\r\n\r\n private stop() {\r\n this.clear();\r\n window.clearInterval(this.titleInterval);\r\n this.titleInterval = 0;\r\n this.setFavicon();\r\n this.stopped = true;\r\n }\r\n\r\n private requestPermission = () => {\r\n Notification.requestPermission();\r\n window.removeEventListener('click', this.requestPermission);\r\n };\r\n\r\n public notify(data: NotifyOptions) {\r\n //console.log('notify', data, rootScope.idle.isIDLE, this.notificationsUiSupport, this.stopped);\r\n \r\n if(this.stopped) {\r\n return;\r\n }\r\n\r\n // FFOS Notification blob src bug workaround\r\n /* if(Config.Navigator.ffos && !Config.Navigator.ffos2p) {\r\n data.image = 'https://telegram.org/img/t_logo.png'\r\n }\r\n else if (data.image && !angular.isString(data.image)) {\r\n if (Config.Navigator.ffos2p) {\r\n FileManager.getDataUrl(data.image, 'image/jpeg').then(function (url) {\r\n data.image = url\r\n notify(data)\r\n })\r\n return false\r\n } else {\r\n data.image = FileManager.getUrl(data.image, 'image/jpeg')\r\n }\r\n }\r\n else */ if(!data.image) {\r\n data.image = 'assets/img/logo_filled_rounded.png';\r\n }\r\n // console.log('notify image', data.image)\r\n\r\n this.notificationsCount++;\r\n if(!this.titleInterval) {\r\n this.toggleToggler();\r\n }\r\n\r\n const now = tsNow();\r\n if(this.settings.volume > 0 && !this.settings.nosound/* &&\r\n (\r\n !data.tag ||\r\n !this.soundsPlayed[data.tag] ||\r\n now > this.soundsPlayed[data.tag] + 60000\r\n ) */\r\n ) {\r\n this.testSound(this.settings.volume);\r\n this.soundsPlayed[data.tag] = now;\r\n }\r\n\r\n if(!this.notificationsUiSupport ||\r\n 'Notification' in window && Notification.permission !== 'granted') {\r\n return false;\r\n }\r\n\r\n if(this.settings.nodesktop) {\r\n if(this.vibrateSupport && !this.settings.novibrate) {\r\n navigator.vibrate([200, 100, 200]);\r\n return;\r\n }\r\n\r\n return;\r\n }\r\n\r\n const idx = ++this.notificationIndex;\r\n const key = data.key || 'k' + idx;\r\n let notification: MyNotification;\r\n\r\n if('Notification' in window) {\r\n try {\r\n if(data.tag) {\r\n for(let i in this.notificationsShown) {\r\n const notification = this.notificationsShown[i];\r\n if(notification &&\r\n notification.tag === data.tag) {\r\n notification.hidden = true;\r\n }\r\n }\r\n }\r\n\r\n notification = new Notification(data.title, {\r\n icon: data.image || '',\r\n body: data.message || '',\r\n tag: data.tag || '',\r\n silent: data.silent || false\r\n });\r\n\r\n //console.log('notify constructed notification');\r\n } catch(e) {\r\n this.notificationsUiSupport = false;\r\n webPushApiManager.setLocalNotificationsDisabled();\r\n return;\r\n }\r\n } /* else if('mozNotification' in navigator) {\r\n notification = navigator.mozNotification.createNotification(data.title, data.message || '', data.image || '')\r\n } else if(notificationsMsSiteMode) {\r\n window.external.msSiteModeClearIconOverlay()\r\n window.external.msSiteModeSetIconOverlay('img/icons/icon16.png', data.title)\r\n window.external.msSiteModeActivate()\r\n notification = {\r\n index: idx\r\n }\r\n } */ else {\r\n return;\r\n }\r\n\r\n notification.onclick = () => {\r\n notification.close();\r\n appRuntimeManager.focus();\r\n this.clear();\r\n if(data.onclick) {\r\n data.onclick();\r\n }\r\n };\r\n\r\n notification.onclose = () => {\r\n if(!notification.hidden) {\r\n delete this.notificationsShown[key];\r\n this.clear();\r\n }\r\n };\r\n\r\n if(notification.show) {\r\n notification.show();\r\n }\r\n this.notificationsShown[key] = notification;\r\n\r\n if(!isMobile) {\r\n setTimeout(() => {\r\n this.hide(key);\r\n }, 8000);\r\n }\r\n }\r\n\r\n public testSound(volume: number) {\r\n const now = tsNow();\r\n if(this.nextSoundAt && now < this.nextSoundAt && this.prevSoundVolume === volume) {\r\n return;\r\n }\r\n\r\n this.nextSoundAt = now + 1000;\r\n this.prevSoundVolume = volume;\r\n const filename = 'assets/audio/notification.mp3';\r\n const audio = document.createElement('audio');\r\n audio.autoplay = true;\r\n audio.setAttribute('mozaudiochannel', 'notification');\r\n audio.volume = volume;\r\n audio.innerHTML = `\r\n <source src=\"${filename}\" type=\"audio/mpeg\" />\r\n <embed hidden=\"true\" autostart=\"true\" loop=\"false\" volume=\"${volume * 100}\" src=\"${filename}\" />\r\n `;\r\n this.notifySoundEl.append(audio);\r\n\r\n audio.addEventListener('ended', () => {\r\n audio.remove();\r\n }, {once: true});\r\n }\r\n\r\n public cancel(key: string) {\r\n const notification = this.notificationsShown[key];\r\n if(notification) {\r\n if(this.notificationsCount > 0) {\r\n this.notificationsCount--;\r\n }\r\n\r\n try {\r\n if(notification.close) {\r\n notification.hidden = true;\r\n notification.close();\r\n }/* else if(notificationsMsSiteMode &&\r\n notification.index === notificationIndex) {\r\n window.external.msSiteModeClearIconOverlay()\r\n } */\r\n } catch (e) {}\r\n\r\n delete this.notificationsShown[key];\r\n }\r\n }\r\n\r\n private hide(key: string) {\r\n const notification = this.notificationsShown[key];\r\n if(notification) {\r\n try {\r\n if(notification.close) {\r\n notification.hidden = true;\r\n notification.close();\r\n }\r\n } catch (e) {}\r\n }\r\n }\r\n\r\n public soundReset(tag: string) {\r\n delete this.soundsPlayed[tag];\r\n }\r\n\r\n public clear() {\r\n /* if(notificationsMsSiteMode) {\r\n window.external.msSiteModeClearIconOverlay()\r\n } else { */\r\n for(let i in this.notificationsShown) {\r\n const notification = this.notificationsShown[i];\r\n try {\r\n if(notification.close) {\r\n notification.close();\r\n }\r\n } catch (e) {}\r\n }\r\n /* } */\r\n this.notificationsShown = {};\r\n this.notificationsCount = 0;\r\n\r\n webPushApiManager.hidePushNotifications();\r\n }\r\n\r\n private registerDevice(tokenData: PushSubscriptionNotify) {\r\n if(this.registeredDevice && deepEqual(this.registeredDevice, tokenData)) {\r\n return false;\r\n }\r\n\r\n apiManager.invokeApi('account.registerDevice', {\r\n token_type: tokenData.tokenType,\r\n token: tokenData.tokenValue,\r\n other_uids: [],\r\n app_sandbox: false,\r\n secret: new Uint8Array()\r\n }).then(() => {\r\n this.registeredDevice = tokenData;\r\n }, (error) => {\r\n error.handled = true;\r\n });\r\n }\r\n\r\n private unregisterDevice(tokenData: PushSubscriptionNotify) {\r\n if(!this.registeredDevice) {\r\n return false;\r\n }\r\n\r\n apiManager.invokeApi('account.unregisterDevice', {\r\n token_type: tokenData.tokenType,\r\n token: tokenData.tokenValue,\r\n other_uids: []\r\n }).then(() => {\r\n this.registeredDevice = false;\r\n }, (error) => {\r\n error.handled = true;\r\n });\r\n }\r\n\r\n public getVibrateSupport() {\r\n return this.vibrateSupport\r\n }\r\n}\r\n\r\nconst appNotificationsManager = new AppNotificationsManager();\r\nMOUNT_CLASS_TO.appNotificationsManager = appNotificationsManager;\r\nexport default appNotificationsManager;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport rootScope from \"../lib/rootScope\";\r\n\r\nconst SetTransition = (element: HTMLElement, className: string, forwards: boolean, duration: number, onTransitionEnd?: () => void) => {\r\n const timeout = element.dataset.timeout;\r\n if(timeout !== undefined) {\r\n clearTimeout(+timeout);\r\n }\r\n\r\n if(forwards && className) {\r\n element.classList.add(className);\r\n }\r\n\r\n const afterTimeout = () => {\r\n delete element.dataset.timeout;\r\n if(!forwards && className) {\r\n element.classList.remove('backwards', className);\r\n }\r\n\r\n element.classList.remove('animating');\r\n \r\n onTransitionEnd && onTransitionEnd();\r\n };\r\n\r\n if(!rootScope.settings.animationsEnabled) {\r\n element.classList.remove('animating', 'backwards');\r\n afterTimeout();\r\n return;\r\n }\r\n\r\n element.classList.add('animating');\r\n\r\n element.classList.toggle('backwards', !forwards);\r\n element.dataset.timeout = '' + setTimeout(afterTimeout, duration);\r\n};\r\n\r\nexport default SetTransition;\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { CancellablePromise } from \"../helpers/cancellablePromise\";\r\nimport SetTransition from \"./singleTransition\";\r\nimport { fastRaf } from \"../helpers/schedulers\";\r\nimport { safeAssign } from \"../helpers/object\";\r\nimport { cancelEvent } from \"../helpers/dom/cancelEvent\";\r\nimport { attachClickEvent } from \"../helpers/dom/clickEvent\";\r\nimport isInDOM from \"../helpers/dom/isInDOM\";\r\n\r\nconst TRANSITION_TIME = 200;\r\n\r\nexport default class ProgressivePreloader {\r\n public preloader: HTMLDivElement;\r\n private circle: SVGCircleElement;\r\n private cancelSvg: SVGSVGElement;\r\n private downloadSvg: HTMLElement;\r\n \r\n private tempId = 0;\r\n private detached = true;\r\n\r\n public promise: CancellablePromise<any> = null;\r\n\r\n public isUpload = false;\r\n private cancelable = true;\r\n private streamable = false;\r\n private tryAgainOnFail = true;\r\n private attachMethod: 'append' | 'prepend' = 'append';\r\n\r\n public loadFunc: () => {download: CancellablePromise<any>};\r\n\r\n private totalLength: number;\r\n\r\n constructor(options?: Partial<{\r\n isUpload: ProgressivePreloader['isUpload'],\r\n cancelable: ProgressivePreloader['cancelable'], \r\n streamable: ProgressivePreloader['streamable'], \r\n tryAgainOnFail: ProgressivePreloader['tryAgainOnFail'],\r\n attachMethod: ProgressivePreloader['attachMethod']\r\n }>) {\r\n if(options) {\r\n safeAssign(this, options);\r\n }\r\n }\r\n\r\n public constructContainer(options: Partial<{\r\n color: 'transparent',\r\n bold: boolean\r\n }> = {}) {\r\n if(!this.preloader) {\r\n this.preloader = document.createElement('div');\r\n this.preloader.classList.add('preloader-container');\r\n\r\n if(options.color) {\r\n this.preloader.classList.add('preloader-' + options.color);\r\n }\r\n\r\n if(options.bold) {\r\n this.preloader.classList.add('preloader-bold');\r\n }\r\n \r\n if(this.streamable) {\r\n this.preloader.classList.add('preloader-streamable');\r\n }\r\n }\r\n }\r\n\r\n public constructDownloadIcon() {\r\n this.constructContainer();\r\n }\r\n\r\n public construct() {\r\n this.construct = null;\r\n\r\n this.constructContainer();\r\n \r\n this.preloader.innerHTML = `\r\n <div class=\"you-spin-me-round\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"preloader-circular\" viewBox=\"${this.streamable ? '25 25 50 50' : '27 27 54 54'}\">\r\n <circle class=\"preloader-path-new\" cx=\"${this.streamable ? '50' : '54'}\" cy=\"${this.streamable ? '50' : '54'}\" r=\"${this.streamable ? 19 : 24}\" fill=\"none\" stroke-miterlimit=\"10\"/>\r\n </svg>\r\n </div>`;\r\n\r\n if(this.cancelable) {\r\n this.preloader.innerHTML += `\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"preloader-close\" viewBox=\"0 0 24 24\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <polygon points=\"0 0 24 0 24 24 0 24\"/>\r\n <path fill=\"#000\" fill-rule=\"nonzero\" d=\"M5.20970461,5.38710056 L5.29289322,5.29289322 C5.65337718,4.93240926 6.22060824,4.90467972 6.61289944,5.20970461 L6.70710678,5.29289322 L12,10.585 L17.2928932,5.29289322 C17.6834175,4.90236893 18.3165825,4.90236893 18.7071068,5.29289322 C19.0976311,5.68341751 19.0976311,6.31658249 18.7071068,6.70710678 L13.415,12 L18.7071068,17.2928932 C19.0675907,17.6533772 19.0953203,18.2206082 18.7902954,18.6128994 L18.7071068,18.7071068 C18.3466228,19.0675907 17.7793918,19.0953203 17.3871006,18.7902954 L17.2928932,18.7071068 L12,13.415 L6.70710678,18.7071068 C6.31658249,19.0976311 5.68341751,19.0976311 5.29289322,18.7071068 C4.90236893,18.3165825 4.90236893,17.6834175 5.29289322,17.2928932 L10.585,12 L5.29289322,6.70710678 C4.93240926,6.34662282 4.90467972,5.77939176 5.20970461,5.38710056 L5.29289322,5.29289322 L5.20970461,5.38710056 Z\"/>\r\n </g>\r\n </svg>\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"preloader-download\" viewBox=\"0 0 24 24\">\r\n <g fill=\"none\" fill-rule=\"evenodd\">\r\n <polygon points=\"0 0 24 0 24 24 0 24\"/>\r\n <path fill=\"#000\" fill-rule=\"nonzero\" d=\"M5,19 L19,19 C19.5522847,19 20,19.4477153 20,20 C20,20.5128358 19.6139598,20.9355072 19.1166211,20.9932723 L19,21 L5,21 C4.44771525,21 4,20.5522847 4,20 C4,19.4871642 4.38604019,19.0644928 4.88337887,19.0067277 L5,19 L19,19 L5,19 Z M11.8833789,3.00672773 L12,3 C12.5128358,3 12.9355072,3.38604019 12.9932723,3.88337887 L13,4 L13,13.585 L16.2928932,10.2928932 C16.6533772,9.93240926 17.2206082,9.90467972 17.6128994,10.2097046 L17.7071068,10.2928932 C18.0675907,10.6533772 18.0953203,11.2206082 17.7902954,11.6128994 L17.7071068,11.7071068 L12.7071068,16.7071068 C12.3466228,17.0675907 11.7793918,17.0953203 11.3871006,16.7902954 L11.2928932,16.7071068 L6.29289322,11.7071068 C5.90236893,11.3165825 5.90236893,10.6834175 6.29289322,10.2928932 C6.65337718,9.93240926 7.22060824,9.90467972 7.61289944,10.2097046 L7.70710678,10.2928932 L11,13.585 L11,4 C11,3.48716416 11.3860402,3.06449284 11.8833789,3.00672773 L12,3 L11.8833789,3.00672773 Z\"/>\r\n </g>\r\n </svg>`;\r\n\r\n this.downloadSvg = this.preloader.lastElementChild as HTMLElement;\r\n this.cancelSvg = this.downloadSvg.previousElementSibling as any;\r\n } else {\r\n this.preloader.classList.add('preloader-swing');\r\n }\r\n \r\n this.circle = this.preloader.firstElementChild.firstElementChild.firstElementChild as SVGCircleElement;\r\n\r\n if(this.cancelable) {\r\n attachClickEvent(this.preloader, this.onClick);\r\n }\r\n }\r\n\r\n public onClick = (e?: Event) => {\r\n if(e) {\r\n cancelEvent(e);\r\n }\r\n\r\n if(this.preloader.classList.contains('manual')) {\r\n if(this.loadFunc) {\r\n this.loadFunc();\r\n }\r\n } else {\r\n if(this.promise && this.promise.cancel) {\r\n this.promise.cancel();\r\n }\r\n }\r\n };\r\n\r\n public setDownloadFunction(func: ProgressivePreloader['loadFunc']) {\r\n this.loadFunc = func;\r\n }\r\n\r\n public setManual() {\r\n this.preloader.classList.add('manual');\r\n this.setProgress(0);\r\n }\r\n\r\n public attachPromise(promise: CancellablePromise<any>) {\r\n if(this.isUpload && this.promise) return;\r\n\r\n this.promise = promise;\r\n\r\n const tempId = --this.tempId;\r\n const startTime = Date.now();\r\n\r\n const onEnd = (err: Error) => {\r\n promise.notify = null;\r\n\r\n if(tempId !== this.tempId) {\r\n return;\r\n }\r\n\r\n const elapsedTime = Date.now() - startTime;\r\n\r\n //console.log('[PP]: end', this.detached, performance.now());\r\n\r\n if(!err && this.cancelable) {\r\n this.setProgress(100);\r\n\r\n const delay = TRANSITION_TIME * 0.75;\r\n\r\n if(elapsedTime < delay) {\r\n this.detach();\r\n } else {\r\n setTimeout(() => { // * wait for transition complete\r\n if(tempId === this.tempId) {\r\n this.detach();\r\n }\r\n }, delay);\r\n }\r\n } else {\r\n if(this.tryAgainOnFail) {\r\n SetTransition(this.preloader, '', true, TRANSITION_TIME);\r\n fastRaf(() => {\r\n this.setManual();\r\n });\r\n } else {\r\n this.detach();\r\n }\r\n }\r\n \r\n this.promise = promise = null;\r\n };\r\n \r\n promise\r\n .then(() => onEnd(null))\r\n .catch((err) => onEnd(err));\r\n\r\n if(promise.addNotifyListener) {\r\n promise.addNotifyListener((details: {done: number, total: number}) => {\r\n /* if(details.done >= details.total) {\r\n onEnd();\r\n } */\r\n\r\n if(tempId !== this.tempId) return;\r\n\r\n //console.log('preloader download', promise, details);\r\n const percents = details.done / details.total * 100;\r\n this.setProgress(percents);\r\n });\r\n }\r\n }\r\n\r\n public attach(elem: Element, reset = false, promise?: CancellablePromise<any>) {\r\n if(promise/* && false */) {\r\n this.attachPromise(promise);\r\n }\r\n\r\n //return;\r\n\r\n this.detached = false;\r\n /* fastRaf(() => {\r\n if(this.detached) return;\r\n this.detached = false; */\r\n\r\n if(this.construct) {\r\n this.construct();\r\n }\r\n\r\n if(this.preloader.parentElement) {\r\n this.preloader.classList.remove('manual');\r\n }\r\n\r\n if(this.preloader.parentElement !== elem) {\r\n elem[this.attachMethod](this.preloader);\r\n }\r\n\r\n fastRaf(() => {\r\n //console.log('[PP]: attach after rAF', this.detached, performance.now());\r\n\r\n if(this.detached) {\r\n return;\r\n }\r\n\r\n SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME);\r\n });\r\n\r\n if(this.cancelable && reset) {\r\n this.setProgress(0);\r\n }\r\n //});\r\n }\r\n \r\n public detach() {\r\n //return;\r\n\r\n this.detached = true;\r\n\r\n //return;\r\n \r\n if(this.preloader && this.preloader.parentElement) {\r\n /* setTimeout(() => *///fastRaf(() => {\r\n /* if(!this.detached) return;\r\n this.detached = true; */\r\n\r\n fastRaf(() => {\r\n //console.log('[PP]: detach after rAF', this.detached, performance.now());\r\n\r\n if(!this.detached || !this.preloader.parentElement) {\r\n return;\r\n }\r\n\r\n SetTransition(this.preloader, 'is-visible', false, TRANSITION_TIME, () => {\r\n this.preloader.remove();\r\n });\r\n });\r\n //})/* , 5e3) */;\r\n }\r\n }\r\n \r\n public setProgress(percents: number) {\r\n if(!isInDOM(this.circle)) {\r\n return;\r\n }\r\n \r\n if(percents === 0) {\r\n this.circle.style.strokeDasharray = '';\r\n return;\r\n }\r\n \r\n try {\r\n if(!this.totalLength) {\r\n this.totalLength = this.circle.getTotalLength();\r\n }\r\n\r\n //console.log('setProgress', (percents / 100 * totalLength));\r\n this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * this.totalLength) + ', ' + this.totalLength;\r\n } catch(err) {}\r\n }\r\n}\r\n","/*\r\n * https://github.com/morethanwords/tweb\r\n * Copyright (C) 2019-2021 Eduard Kuzmenko\r\n * https://github.com/morethanwords/tweb/blob/master/LICENSE\r\n */\r\n\r\nimport { pause } from \"./schedulers\";\r\nimport { isAppleMobile } from \"./userAgent\";\r\n\r\nexport function preloadVideo(url: string): Promise<HTMLVideoElement> {\r\n return new Promise((resolve, reject) => {\r\n const video = document.createElement('video');\r\n video.volume = 0;\r\n video.onloadedmetadata = () => resolve(video);\r\n video.onerror = reject;\r\n video.src = url;\r\n });\r\n}\r\n\r\nexport function createPosterFromVideo(video: HTMLVideoElement): Promise<Blob> {\r\n return new Promise((resolve, reject) => {\r\n video.onseeked = () => {\r\n const canvas = document.createElement('canvas');\r\n canvas.width = Math.min(1280, video.videoWidth);\r\n canvas.height = Math.min(720, video.videoHeight);\r\n const ctx = canvas.getContext('2d')!;\r\n ctx.drawImage(video, 0, 0);\r\n canvas.toBlob(blob => {\r\n resolve(blob);\r\n }, 'image/jpeg', 1);\r\n };\r\n\r\n video.onerror = reject;\r\n video.currentTime = Math.min(video.duration, 1);\r\n });\r\n}\r\n\r\nexport async function createPosterForVideo(url: string): Promise<Blob | undefined> {\r\n const video = await preloadVideo(url);\r\n\r\n return Promise.race([\r\n pause(2000) as Promise<undefined>,\r\n createPosterFromVideo(video),\r\n ]);\r\n}\r\n\r\nexport function onVideoLoad(video: HTMLVideoElement) {\r\n return new Promise<void>((resolve) => {\r\n if(video.readyState >= video.HAVE_METADATA) {\r\n resolve();\r\n return;\r\n }\r\n\r\n video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => resolve(), {once: true});\r\n });\r\n}\r\n\r\nexport async function getFilesFromEvent(e: ClipboardEvent | DragEvent, onlyTypes = false): Promise<any[]> {\r\n const files: any[] = [];\r\n\r\n const scanFiles = async(entry: any, item: DataTransferItem) => {\r\n if(entry.isDirectory) {\r\n const directoryReader = entry.createReader();\r\n await new Promise<void>((resolve, reject) => {\r\n directoryReader.readEntries(async(entries: any) => {\r\n for(const entry of entries) {\r\n await scanFiles(entry, item);\r\n }\r\n\r\n resolve();\r\n });\r\n });\r\n } else if(entry) {\r\n if(onlyTypes) {\r\n files.push(entry.type);\r\n } else {\r\n const itemFile = item.getAsFile(); // * Safari can't handle entry.file with pasting\r\n const file = entry instanceof File ? \r\n entry : \r\n (\r\n entry instanceof DataTransferItem ? \r\n entry.getAsFile() : \r\n await new Promise((resolve, reject) => entry.file(resolve, (err: any) => resolve(itemFile)))\r\n );\r\n\r\n /* if(!onlyTypes) {\r\n console.log('getFilesFromEvent: got file', item, file);\r\n } */\r\n\r\n if(!file) return;\r\n files.push(file);\r\n }\r\n }\r\n };\r\n\r\n if(e instanceof DragEvent && e.dataTransfer.files && !e.dataTransfer.items) {\r\n for(let i = 0; i < e.dataTransfer.files.length; i++) {\r\n const file = e.dataTransfer.files[i];\r\n files.push(onlyTypes ? file.type : file);\r\n }\r\n } else {\r\n // @ts-ignore\r\n const items = (e.dataTransfer || e.clipboardData || e.originalEvent.clipboardData).items;\r\n\r\n const promises: Promise<any>[] = [];\r\n for(let i = 0; i < items.length; ++i) {\r\n const item: DataTransferItem = items[i];\r\n if(item.kind === 'file') {\r\n const entry = (onlyTypes ? item : item.webkitGetAsEntry()) || item.getAsFile();\r\n promises.push(scanFiles(entry, item));\r\n }\r\n }\r\n \r\n await Promise.all(promises);\r\n }\r\n\r\n /* if(!onlyTypes) {\r\n console.log('getFilesFromEvent: got files:', e, files);\r\n } */\r\n \r\n return files;\r\n}\r\n\r\nexport function requestFile(accept?: string) {\r\n const input = document.createElement('input');\r\n input.type = 'file';\r\n input.style.display = 'none';\r\n\r\n if(accept) {\r\n input.accept = accept;\r\n }\r\n\r\n document.body.append(input);\r\n\r\n const promise = new Promise<File>((resolve, reject) => {\r\n input.addEventListener('change', (e: any) => {\r\n const file: File = e.target.files[0];\r\n if(!file) {\r\n reject('NO_FILE_SELECTED');\r\n return;\r\n }\r\n \r\n resolve(file);\r\n }, {once: true});\r\n }).finally(() => {\r\n input.remove();\r\n });\r\n\r\n input.click();\r\n\r\n return promise;\r\n}\r\n"],"sourceRoot":""}