Home telegram - message context menu
Post
Cancel

telegram - message context menu

contextMenuForChatPresentationIntefaceState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: ChatPresentationInterfaceState, context: AccountContext, messages: [Message], controllerInteraction: ChatControllerInteraction?, selectAll: Bool, interfaceInteraction: ChatPanelInterfaceInteraction?) -> Signal<[ContextMenuItem], NoError> {
    guard let interfaceInteraction = interfaceInteraction, let controllerInteraction = controllerInteraction else {
        return .single([])
    }
    
    let dataSignal: Signal<MessageContextMenuData, NoError>
    
    var loadStickerSaveStatus: MediaId?
    var loadCopyMediaResource: MediaResource?
    var isAction = false
    
    //gxg--> 互动 只显示删除
    var onlyShowDelete:Bool = false
    
    if messages.count == 1 {
        for media in messages[0].media {
            if let file = media as? UChatMediaFile {
                for attribute in file.attributes {
                    if case let .Sticker(_, packInfo, _) = attribute, packInfo != nil {
                        loadStickerSaveStatus = file.fileId
                    }
                }
            } else if media is UChatMediaAction || media is UChatMediaExpiredContent {
                isAction = true
            } else if let image = media as? UChatMediaImage {
                if !messages[0].containsSecretMedia {
                    loadCopyMediaResource = largestImageRepresentation(image.representations)?.resource
                }
            }else if let media = media as? UChatMediaSystemNotify {
                if media.isInteraction() {
                    //gxg--> TODO: 只显示删除
                    onlyShowDelete = true
                }
            }
        }
    }
    
    var canReply = canReplyInChat(chatPresentationInterfaceState)
    var canPin = false
    var canDelete = true
    let canSelect = !isAction
    
    let message = messages[0]
    
// hcl redenvelopes(禁止回复)
    var isRedEnvelopesOrTransfer = false
    var isRedEnvelopeAction = false
    for media in message.media {
        if media is UChatMediaRedEnvelopes {
            isRedEnvelopesOrTransfer = true
        } else if let action = media as? UChatMediaAction, case .receivedRedEnvelopes = action.action {
            isRedEnvelopeAction = true
        }
    }
    
    if isRedEnvelopesOrTransfer || isRedEnvelopeAction {
        canReply = false
    } else if Namespaces.Message.allScheduled.contains(message.id.namespace) {
        canReply = false
        canPin = false
    }
    else if messages[0].flags.intersection([.Failed, .Unsent]).isEmpty {
        switch chatPresentationInterfaceState.chatLocation {
            case .peer:
                if let channel = messages[0].peers[messages[0].id.peerId] as? UChatChannel {
                    if !isAction {
                        canPin = channel.hasPermission(.pinMessages)
                    }
                } else if let group = messages[0].peers[messages[0].id.peerId] as? UChatGroup {
                    if !isAction {
                        switch group.role {
                            case .creator, .admin:
                                canPin = true
                            default:
                                if let defaultBannedRights = group.defaultBannedRights {
                                    canPin = !defaultBannedRights.flags.contains(.banPinMessages)
                                } else {
                                    canPin = true
                                }
                        }
                    }
                } else if let _ = messages[0].peers[messages[0].id.peerId] as? UChatUser, chatPresentationInterfaceState.explicitelyCanPinMessages {
                    if !isAction {
                        canPin = true
                    }
                }
            /*case .group:
                break*/
        }
    } else {
        canReply = false
        canPin = false
    }
    
    //nicoo 系统号不显示回复
    if systemPeerIds.contains(chatPresentationInterfaceState.chatLocation.peerId) {
        canReply = false
    }
    
    var loadStickerSaveStatusSignal: Signal<Bool?, NoError> = .single(nil)
    if loadStickerSaveStatus != nil {
        loadStickerSaveStatusSignal = context.account.postbox.transaction { transaction -> Bool? in
            var starStatus: Bool?
            if let loadStickerSaveStatus = loadStickerSaveStatus {
                if getIsStickerSaved(transaction: transaction, fileId: loadStickerSaveStatus) {
                    starStatus = true
                } else {
                    starStatus = false
                }
            }
            
            return starStatus
        }
    }
    
    var loadResourceStatusSignal: Signal<MediaResourceStatus?, NoError> = .single(nil)
    if let loadCopyMediaResource = loadCopyMediaResource {
        loadResourceStatusSignal = context.account.postbox.mediaBox.resourceStatus(loadCopyMediaResource)
        |> take(1)
        |> map(Optional.init)
    }
    
    let loadLimits = context.account.postbox.transaction { transaction -> LimitsConfiguration in
        return transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration) as? LimitsConfiguration ?? LimitsConfiguration.defaultValue
    }
    
    dataSignal = combineLatest(loadLimits, loadStickerSaveStatusSignal, loadResourceStatusSignal, context.sharedContext.chatAvailableMessageActions(postbox: context.account.postbox, accountPeerId: context.account.peerId, messageIds: Set(messages.map { $0.id })))
    |> map { limitsConfiguration, stickerSaveStatus, resourceStatus, messageActions -> MessageContextMenuData in
        var canEdit = false
        if !isAction {
            let message = messages[0]
            canEdit = canEditMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
            canDelete = canDeleteMessage(context: context, limitsConfiguration: limitsConfiguration, message: message)
        }
        
        return MessageContextMenuData(starStatus: stickerSaveStatus, canReply: canReply, canPin: canPin, canEdit: canEdit, canSelect: canSelect, canDelete: canDelete, resourceStatus: resourceStatus, messageActions: messageActions)
    }
    
    return dataSignal
    |> deliverOnMainQueue
    |> map { data -> [ContextMenuItem] in
        var actions: [ContextMenuItem] = []
        
        //gxg--> 互动 只显示删除
        if onlyShowDelete {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
            }, action: { controller, f in
                interfaceInteraction.deleteMessages(messages, controller, f)
            })))
            return actions
        }
        //<--end
        
        if let starStatus = data.starStatus {
            actions.append(.action(ContextMenuActionItem(text: starStatus ? chatPresentationInterfaceState.strings.Stickers_RemoveFromFavorites : chatPresentationInterfaceState.strings.Stickers_AddToFavorites, icon: { theme in
                return generateTintedImage(image: starStatus ? UIImage(bundleImageName: "Chat/Context Menu/Unstar") : UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                interfaceInteraction.toggleMessageStickerStarred(messages[0].id)
                f(.default)
            })))
        }
        
        if data.canReply {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                interfaceInteraction.setupReplyMessage(messages[0].id, { transition in
                    f(.custom(transition))
                })
            })))
        }
        
        if data.messageActions.options.contains(.sendScheduledNow) {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_SendNow, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Resend"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                controllerInteraction.sendScheduledMessagesNow(selectAll ? messages.map { $0.id } : [message.id])
                f(.dismissWithoutContent)
            })))
        }
        
        if data.messageActions.options.contains(.editScheduledTime) {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.ScheduledMessages_EditTime, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Schedule"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                controllerInteraction.editScheduledMessagesTime(selectAll ? messages.map { $0.id } : [message.id])
                f(.dismissWithoutContent)
            })))
        }
        
        let resourceAvailable: Bool
        if let resourceStatus = data.resourceStatus, case .Local = resourceStatus {
            resourceAvailable = true
        } else {
            resourceAvailable = false
        }
        
        if !messages[0].text.isEmpty || resourceAvailable {
            let message = messages[0]
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopy, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                if resourceAvailable {
                    for media in message.media {
                        if let image = media as? UChatMediaImage, let largest = largestImageRepresentation(image.representations) {
                            let _ = (context.account.postbox.mediaBox.resourceData(largest.resource, option: .incremental(waitUntilFetchStatus: false))
                                |> take(1)
                                |> deliverOnMainQueue).start(next: { data in
                                    if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
                                        if let image = UIImage(data: imageData) {
                                            if !message.text.isEmpty {
                                                UIPasteboard.general.string = message.text
                                                /*UIPasteboard.general.items = [
                                                 [kUTTypeUTF8PlainText as String: message.text],
                                                 [kUTTypePNG as String: image]
                                                 ]*/
                                            } else {
                                                UIPasteboard.general.image = image
                                            }
                                        } else {
                                            UIPasteboard.general.string = message.text
                                        }
                                    } else {
                                        UIPasteboard.general.string = message.text
                                    }
                                })
                        }
                    }
                } else {
                    var messageEntities: [MessageTextEntity]?
                    for attribute in message.attributes {
                        
                        if let attribute = attribute as? TextEntitiesMessageAttribute {
                            //gxg--> @拷贝消息不附带mention
                            var containMention = false
                            for entity in attribute.entities {
                                switch entity.type {
                                case .Mention,.TextMention(peerId: _):
                                    containMention = true
                                default:
                                    break
                                }
                            }
                            if !containMention {
                                messageEntities = attribute.entities
                            }
                            //<--end
                            
                            
//                            messageEntities = attribute.entities
                            
                            break
                        }
                    }
                    
                    storeMessageTextInPasteboard(message.text, entities: messageEntities)
                }
                f(.default)
            })))
        }
        
        if data.canEdit {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_MessageDialogEdit, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Edit"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                interfaceInteraction.setupEditMessage(messages[0].id, { transition in
                    f(.custom(transition))
                })
            })))
        }
        
        var activePoll: UChatMediaPoll?
        for media in message.media {
            if let poll = media as? UChatMediaPoll, !poll.isClosed, message.id.namespace == Namespaces.Message.Cloud, poll.pollId.namespace == Namespaces.Media.CloudPoll {
                activePoll = poll
            }
        }
        
        if let activePoll = activePoll, let voters = activePoll.results.voters {
            var hasSelected = false
            for result in voters {
                if result.selected {
                    hasSelected = true
                }
            }
            if hasSelected {
                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_UnvotePoll, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unvote"), color: theme.actionSheet.primaryTextColor)
                }, action: { _, f in
                    interfaceInteraction.requestUnvoteInMessage(messages[0].id)
                    f(.dismissWithoutContent)
                })))
            }
        }
        
        if data.canPin {
            if chatPresentationInterfaceState.pinnedMessage?.id != messages[0].id {
                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
                }, action: { _, f in
                    interfaceInteraction.pinMessage(messages[0].id)
                    f(.dismissWithoutContent)
                })))
            } else {
                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor)
                }, action: { _, f in
                    interfaceInteraction.unpinMessage()
                    f(.default)
                })))
            }
        }
        
        if let _ = activePoll, messages[0].forwardInfo == nil {
            var canStopPoll = false
            if !messages[0].flags.contains(.Incoming) {
                canStopPoll = true
            } else {
                var hasEditRights = false
                if messages[0].id.namespace == Namespaces.Message.Cloud {
                    if message.id.peerId.namespace == Namespaces.Peer.SecretChat {
                        hasEditRights = false
                    } else if let author = message.author, author.id == context.account.peerId {
                        hasEditRights = true
                    } else if message.author?.id == message.id.peerId, let peer = message.peers[message.id.peerId] {
                        if let peer = peer as? UChatChannel, case .broadcast = peer.info {
                            if peer.hasPermission(.editAllMessages) {
                                hasEditRights = true
                            }
                        }
                    }
                }
                
                if hasEditRights {
                    canStopPoll = true
                }
            }
            
            if canStopPoll {
                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_StopPoll, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/StopPoll"), color: theme.actionSheet.primaryTextColor)
                }, action: { _, f in
                    interfaceInteraction.requestStopPollInMessage(messages[0].id)
                    f(.dismissWithoutContent)
                })))
            }
        }
        
        if let message = messages.first, message.id.namespace == Namespaces.Message.Cloud, let channel = message.peers[message.id.peerId] as? UChatChannel, !(message.media.first is UChatMediaAction) {
// hcl 667 【正式环境-复制消息】三端屏蔽掉聊天消息中复制链接按钮
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuCopyLink, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                let _ = (exportMessageLink(account: context.account, peerId: message.id.peerId, messageId: message.id)
                |> map { result -> String? in
                    return result
                }
                |> deliverOnMainQueue).start(next: { link in
                    if let link = link {
                        UIPasteboard.general.string = link
                        
                        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
                        if channel.addressName == nil {
                            controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.Conversation_PrivateMessageLinkCopied, true)), nil)
                        } else {
                            controllerInteraction.presentGlobalOverlayController(OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .genericSuccess(presentationData.strings.GroupInfo_InviteLink_CopyAlert_Success, false)), nil)
                        }
                    }
                })
                f(.default)
            })))
        }
        
        if messages.count == 1 {
            let message = messages[0]
            
            var hasAutoremove = false
            for attribute in message.attributes {
                if let _ = attribute as? AutoremoveTimeoutMessageAttribute {
                    hasAutoremove = true
                    break
                }
            }
            
            if !hasAutoremove {
                for media in message.media {
                    if let file = media as? UChatMediaFile {
                        if file.isVideo {
                            if file.isAnimated {
// hcl mark 判断是否可以保存媒体
                                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_LinkDialogSave, icon: { theme in
                                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
                                }, action: { _, f in
                                    let _ = addSavedGif(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file)).start()
                                    f(.default)
                                })))
                            }
                            break
                        }
                    }
                }
            }
        }
        if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && isAction {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete, textColor: .destructive, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
            }, action: { controller, f in
                interfaceInteraction.deleteMessages(messages, controller, f)
            })))
        }
        
        if data.messageActions.options.contains(.viewStickerPack) {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.StickerPack_ViewPack, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Sticker"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                let _ = controllerInteraction.openMessage(message, .default)
                f(.dismissWithoutContent)
            })))
        }
        
        if data.messageActions.options.contains(.rateCall) {
            var callId: CallId?
            for media in message.media {
                if let action = media as? UChatMediaAction, case let .phoneCall(_ , id, discardReason, _) = action.action {
                    if discardReason != .busy && discardReason != .missed {
                        if let logName = callLogNameForId(id: id, account: context.account) {
                            let start = logName.index(logName.startIndex, offsetBy: "\(id)".count + 1)
                            let end = logName.index(logName.endIndex, offsetBy: -4)
                            let accessHash = logName[start..<end]
                            if let accessHash = Int64(accessHash) {
                                callId = CallId(id: id, accessHash: accessHash)
                            }
                        }
                    }
                    break
                }
            }
            if let callId = callId {
                actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Call_RateCall, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Rate"), color: theme.actionSheet.primaryTextColor)
                }, action: { _, f in
                    let _ = controllerInteraction.rateCall(message, callId)
                    f(.dismissWithoutContent)
                })))
            }
        }
        
        if data.messageActions.options.contains(.forward) {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuForward, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                interfaceInteraction.forwardMessages(selectAll ? messages : [message])
                f(.dismissWithoutContent)
            })))
        }
        // lfq 翻译 文本翻译
        if !messages[0].text.isEmpty || resourceAvailable {
            let message = messages[0]
            var isDisplay = true
            var isLoading = false
            if !message.text.isEmpty && message.id.peerId.namespace != Namespaces.Peer.SecretChat { // lfq 翻译
                let medias = message.media
                for media in medias {
                    if let media = media as? UChatMediaTranslation, media.isDisplay == true {
                        isDisplay = false
                        isLoading = media.isLoading
                    }
                }
                if isDisplay { // 显示
                    if isLoading == false {
                        let presentationData = context.sharedContext.currentPresentationData.with { $0 }
                        let languageCode = presentationData.strings.primaryComponent.languageCode
                        actions.append(.action(ContextMenuActionItem(text: uchatLanguage("UChat.Translation.ActionMenu", languageCode), icon: { theme  in
                            return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor)
                        }, action: { _, f in
                            interfaceInteraction.translationMessage(message.text, message.id, true)
                            f(.dismissWithoutContent)
                        })))
                    }
                } else { // 隐藏
                    let presentationData = context.sharedContext.currentPresentationData.with { $0 }
                    let languageCode = presentationData.strings.primaryComponent.languageCode
                    actions.append(.action(ContextMenuActionItem(text: uchatLanguage("UChat.Translation.CancelDisplay", languageCode), icon: { theme  in
                        return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/CancelEn"), color: theme.actionSheet.primaryTextColor)
                    }, action: { _, f in
                        interfaceInteraction.translationMessage(message.text, message.id, false)
                        f(.dismissWithoutContent)
                    })))
                }
            }
        }
        
        // lfq 语音转文字
        for media in message.media {
            if let file = media as? UChatMediaFile {
                for attribute in file.attributes {
                    switch attribute {
                    case let .Audio(isVoice, duration, _, _, _):
                        if duration <= 60 && isVoice && message.id.peerId.namespace != Namespaces.Peer.SecretChat {
                            
                            let presentationData = context.sharedContext.currentPresentationData.with { $0 }
                            let languageCode = presentationData.strings.primaryComponent.languageCode
                            var isDisplay = true
                            var isLoading = false
                            for voice in message.media {
                                if let voice = voice as? UChatMediaTranslation, voice.isDisplay == true {
                                    isDisplay = false
                                    isLoading = voice.isLoading
                                }
                            }
                            if isDisplay {
                                if isLoading == false {
                                    actions.append(.action(ContextMenuActionItem(text: uchatLanguage("UChat.Translation.VoiceToText", languageCode), icon: { theme  in
                                        return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor)
                                    }, action: { _, f in
                                        interfaceInteraction.voiceTransitionToText(message.id, true)
                                        f(.dismissWithoutContent)
                                    })))
                                }
                            } else {
                                let presentationData = context.sharedContext.currentPresentationData.with { $0 }
                                let languageCode = presentationData.strings.primaryComponent.languageCode
                                actions.append(.action(ContextMenuActionItem(text: uchatLanguage("UChat.Translation.CancelDisplayWithVoice", languageCode), icon: { theme  in
                                    return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/CancelEn"), color: theme.actionSheet.primaryTextColor)
                                }, action: { _, f in
                                    interfaceInteraction.translationMessage(message.text, message.id, false)
                                    f(.dismissWithoutContent)
                                })))
                            }
                            
                        }
                    default:break
                    }
                }
            }
        }
        
        if data.messageActions.options.contains(.report) {
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReport, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.actionSheet.primaryTextColor)
            }, action: { controller, f in
                interfaceInteraction.reportMessages(selectAll ? messages : [message], controller)
            })))
        }
        
// hcl 增加识别二维码
        if resourceAvailable {
            let presentationData = context.sharedContext.currentPresentationData.with { $0 }
            let languageCode = presentationData.strings.primaryComponent.languageCode
            actions.append(.action(ContextMenuActionItem(text: uchatLanguage("UChat.Scan.IdentifyQRCode", languageCode), icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/IdentifyQRCode"), color: theme.actionSheet.primaryTextColor)
            }, action: { (_, f) in
                f(.default)
                for media in message.media {
                    if let image = media as? UChatMediaImage, let largest = largestImageRepresentation(image.representations) {
                        let _ = (context.account.postbox.mediaBox.resourceData(largest.resource, option: .incremental(waitUntilFetchStatus: false))
                            |> take(1)
                            |> deliverOnMainQueue).start(next: { data in
                                if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: imageData), let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]), let cgImage = image.cgImage {
                                    let features = detector.features(in: CIImage(cgImage: cgImage))
                                    if let scannedResult = features.first as? CIQRCodeFeature, let messageString = scannedResult.messageString {
                                        interfaceInteraction.identifyQRcode(context, messageString)
                                    }
                                    else
                                    {
                                        interfaceInteraction.identifyQRcode(context, "")
                                    }
                                }
                                else
                                {
                                    interfaceInteraction.identifyQRcode(context, "")
                                }
                            })
                    }
                }
            })))
        }
        
// hcl 消息增加可删除时间限制
        if data.canDelete {
            if !data.messageActions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty && !isAction {
                let title = message.flags.isSending ? chatPresentationInterfaceState.strings.Conversation_ContextMenuCancelSending : chatPresentationInterfaceState.strings.Conversation_ContextMenuDelete
                actions.append(.action(ContextMenuActionItem(text: title, textColor: .destructive, icon: { theme in
                    return generateTintedImage(image: UIImage(bundleImageName: message.flags.isSending ? "Chat/Context Menu/Clear" : "Chat/Context Menu/Delete"), color: theme.actionSheet.destructiveActionTextColor)
                }, action: { controller, f in
                    interfaceInteraction.deleteMessages(selectAll ? messages : [message], controller, f)
                })))
            }
        }
        
        if data.canSelect {
            if !actions.isEmpty {
                actions.append(.separator)
            }
            actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuMore, icon: { theme in
                return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/More"), color: theme.actionSheet.primaryTextColor)
            }, action: { _, f in
                interfaceInteraction.beginMessageSelection(selectAll ? messages.map { $0.id } : [message.id], { transition in
                    f(.custom(transition))
                })
            })))
        }
        
        return actions
    }
}
This post is licensed under CC BY 4.0 by the author.