From 77cc01f0d9aed98cd03d3a3bfd74eef0a62e9efd Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 26 Mar 2026 13:58:50 +0100 Subject: [PATCH] Various fixes --- .../Sources/ChatListSearchListPaneNode.swift | 3 +- .../Sources/Items/ChatImageGalleryItem.swift | 2 + .../Sources/TGMediaAssetsController.m | 30 ++----- .../TGMediaPickerGalleryInterfaceView.m | 8 ++ .../Sources/TGPhotoCaptionInputMixin.m | 8 +- .../Sources/LegacyMediaPickerGallery.swift | 39 ++++++-- .../Sources/MediaPickerGridItem.swift | 2 +- .../Sources/MediaPickerScreen.swift | 88 +++++++++++++------ .../Sources/MessageContentKind.swift | 2 +- .../ChatHistorySearchContainerNode.swift | 3 +- .../ChatMessagePollBubbleContentNode/BUILD | 1 + .../ChatMessagePollBubbleContentNode.swift | 49 ++++++----- .../ListComposePollOptionComponent.swift | 10 ++- .../Sources/ChatHistoryListNode.swift | 3 +- 14 files changed, 157 insertions(+), 91 deletions(-) diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index c9b263ff10..9a293cb39f 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -3580,8 +3580,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } interaction.messageContextAction(EngineMessage(message), node, rect, gesture, key, fetchResourceId) - }, toggleMediaPlayback: { _ in - }, toggleMessagesSelection: { messageId, selected in + }, toggleMediaPlayback: nil, toggleMessagesSelection: { messageId, selected in if let messageId = messageId.first { interaction.toggleMessageSelection(messageId, selected) } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 67e0fba7b9..b42741087d 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -155,6 +155,8 @@ class ChatImageGalleryItem: GalleryItem { for media in self.message.media { if let poll = media as? TelegramMediaPoll, let image = poll.attachedMedia as? TelegramMediaImage { node.setImage(userLocation: .peer(self.message.id.peerId), imageReference: .message(message: MessageReference(self.message), media: image)) + } else if let poll = media as? TelegramMediaPoll, let file = poll.attachedMedia as? TelegramMediaFile, file.mimeType.hasPrefix("image/") { + node.setFile(context: self.context, userLocation: .peer(self.message.id.peerId), fileReference: .message(message: MessageReference(self.message), media: file)) } else if let paidContent = media as? TelegramMediaPaidContent { let mediaIndex = self.mediaIndex ?? 0 if case let .full(fullMedia) = paidContent.extendedMedia[Int(mediaIndex)], let image = fullMedia as? TelegramMediaImage { diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index cb8298c2fc..2069ab98c4 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -976,9 +976,11 @@ if (adjustments.paintingData.hasAnimation) { grouping = false; } -// if ([editingContext livePhotoModeForItem:asset] != TGMediaLivePhotoModeOff) { -// grouping = false; -// } + + TGMediaLivePhotoMode livePhotoMode = [editingContext livePhotoModeForItem:asset]; + if (livePhotoMode == TGMediaLivePhotoModeLoop || livePhotoMode == TGMediaLivePhotoModeBounce) { + grouping = false; + } } } @@ -1544,9 +1546,10 @@ if (adjustments.paintingData.hasAnimation) { grouping = false; } -// if ([editingContext livePhotoModeForItem:asset] != TGMediaLivePhotoModeOff) { -// grouping = false; -// } + TGMediaLivePhotoMode livePhotoMode = [editingContext livePhotoModeForItem:asset]; + if (livePhotoMode == TGMediaLivePhotoModeLoop || livePhotoMode == TGMediaLivePhotoModeBounce) { + grouping = false; + } } } @@ -1771,21 +1774,6 @@ - (UIBarButtonItem *)rightBarButtonItem { return nil; -// if (_intent == TGMediaAssetsControllerSendFileIntent) -// return nil; -// if (self.requestSearchController == nil) { -// return nil; -// } -// -// if (iosMajorVersion() < 7) -// { -// TGModernBarButton *searchButton = [[TGModernBarButton alloc] initWithImage:TGComponentsImageNamed(@"NavigationSearchIcon.png")]; -// searchButton.portraitAdjustment = CGPointMake(-7, -5); -// [searchButton addTarget:self action:@selector(searchButtonPressed) forControlEvents:UIControlEventTouchUpInside]; -// return [[UIBarButtonItem alloc] initWithCustomView:searchButton]; -// } -// -// return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchButtonPressed)]; } - (void)cancelButtonPressed diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m index 3d46c2d8ca..8114f92e61 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryInterfaceView.m @@ -754,6 +754,14 @@ hasLivePhotoButton = true; } } + } else if ([item isKindOfClass:[TGMediaPickerGalleryPhotoItem class]]) { + TGMediaPickerGalleryPhotoItem *photoGalleryItem = (TGMediaPickerGalleryPhotoItem *)item; + if ([photoGalleryItem.asset isKindOfClass:[TGMediaAsset class]]) { + TGMediaAsset *asset = (TGMediaAsset *)photoGalleryItem.asset; + if (asset.subtypes & TGMediaAssetSubtypePhotoLive) { + hasLivePhotoButton = true; + } + } } [_muteButton setImage:muteIcon forState:UIControlStateNormal]; [_muteButton setImage:muteActiveIcon forState:UIControlStateSelected]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m index fa14d8490e..7c5c7528f8 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m +++ b/submodules/LegacyComponents/Sources/TGPhotoCaptionInputMixin.m @@ -338,7 +338,13 @@ backgroundHeight += _keyboardHeight - edgeInsets.bottom; } - CGRect livePhotoButtonFrame = CGRectMake(edgeInsets.left + 16.0, edgeInsets.top + 145.0 - _keyboardHeight, 160.0, 18.0); + CGFloat livePhotoY = 0.0; + if (frame.size.width > frame.size.height && !TGIsPad()) { + livePhotoY = 48.0; + } else { + livePhotoY = edgeInsets.top + 145.0 - _keyboardHeight; + } + CGRect livePhotoButtonFrame = CGRectMake(_safeAreaInset.left + edgeInsets.left + 16.0, livePhotoY, 160.0, 18.0); _livePhotoButtonView.frame = livePhotoButtonFrame; CGRect panelFrame = CGRectMake(edgeInsets.left, panelY, frame.size.width, panelHeight); diff --git a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift index cd635f88cc..fc256f60f9 100644 --- a/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift +++ b/submodules/MediaPickerUI/Sources/LegacyMediaPickerGallery.swift @@ -13,7 +13,16 @@ import LegacyMediaPickerUI import Photos import MediaAssetsContext -private func galleryFetchResultItems(fetchResult: PHFetchResult, index: Int, reversed: Bool, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, stickersContext: TGPhotoPaintStickersContext, immediateThumbnail: UIImage?) -> ([TGModernGalleryItem], TGModernGalleryItem?) { +private func galleryFetchResultItems( + fetchResult: PHFetchResult, + index: Int, + reversed: Bool, + selectionContext: TGMediaSelectionContext?, + editingContext: TGMediaEditingContext, + stickersContext: TGPhotoPaintStickersContext, + immediateThumbnail: UIImage?, + asFile: Bool +) -> ([TGModernGalleryItem], TGModernGalleryItem?) { var focusItem: TGModernGalleryItem? var galleryItems: [TGModernGalleryItem] = [] @@ -24,6 +33,7 @@ private func galleryFetchResultItems(fetchResult: PHFetchResult, index: galleryItem.selectionContext = selectionContext galleryItem.editingContext = editingContext galleryItem.stickersContext = stickersContext + galleryItem.asFile = asFile galleryItems.append(galleryItem) if i == index { @@ -112,6 +122,7 @@ func presentLegacyMediaPickerGallery( immediateThumbnail: UIImage?, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, + asFile: Bool, hasSilentPosting: Bool, hasSchedule: Bool, hasTimer: Bool, @@ -166,10 +177,10 @@ func presentLegacyMediaPickerGallery( let (items, focusItem): ([TGModernGalleryItem], TGModernGalleryItem?) switch source { - case let .fetchResult(fetchResult, index, reversed): - (items, focusItem) = galleryFetchResultItems(fetchResult: fetchResult, index: index, reversed: reversed, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail) - case let .selection(item): - (items, focusItem) = gallerySelectionItems(item: item, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail) + case let .fetchResult(fetchResult, index, reversed): + (items, focusItem) = galleryFetchResultItems(fetchResult: fetchResult, index: index, reversed: reversed, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail, asFile: asFile) + case let .selection(item): + (items, focusItem) = gallerySelectionItems(item: item, selectionContext: selectionContext, editingContext: editingContext, stickersContext: paintStickersContext, immediateThumbnail: immediateThumbnail) } let recipientName: String? @@ -183,7 +194,23 @@ func presentLegacyMediaPickerGallery( } } - let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: true, allowCaptionEntities: true, hasTimer: hasTimer, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: true, hasCamera: false, recipientName: recipientName, isScheduledMessages: isScheduledMessages, hasCoverButton: hasCoverButton)! + let model = TGMediaPickerGalleryModel( + context: legacyController.context, + items: items, + focus: focusItem, + selectionContext: selectionContext, + editingContext: editingContext, + hasCaptions: true, + allowCaptionEntities: true, + hasTimer: hasTimer, + onlyCrop: false, + inhibitDocumentCaptions: false, + hasSelectionPanel: true, + hasCamera: false, + recipientName: recipientName, + isScheduledMessages: isScheduledMessages, + hasCoverButton: hasCoverButton + )! model.stickersContext = paintStickersContext controller.model = model model.controller = controller diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 176ebbc0f0..c33738cf21 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -603,7 +603,7 @@ final class MediaPickerGridItemNode: GridItemNode { } duration = stringForDuration(Int32(asset.duration)) } else { - if asset.mediaSubtypes.contains(.photoLive) { + if asset.mediaSubtypes.contains(.photoLive) && interaction.displayLivePhotoIcon { typeIcon = UIImage(bundleImageName: "Chat/Message/LivePhoto") } } diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index fd120adc0d..c3d4c09c01 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -47,8 +47,20 @@ final class MediaPickerInteraction { let selectionState: TGMediaSelectionContext? let editingState: TGMediaEditingContext var hiddenMediaId: String? + var displayLivePhotoIcon: Bool = false - init(downloadManager: AssetDownloadManager, openMedia: @escaping (PHFetchResult, Int, UIImage?) -> Void, openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.SendParameters?, @escaping () -> Void) -> Void, schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void, dismissInput: @escaping () -> Void, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext) { + init( + downloadManager: AssetDownloadManager, + openMedia: @escaping (PHFetchResult, Int, UIImage?) -> Void, + openSelectedMedia: @escaping (TGMediaSelectableItem, UIImage?) -> Void, + openDraft: @escaping (MediaEditorDraft, UIImage?) -> Void, + toggleSelection: @escaping (TGMediaSelectableItem, Bool, Bool) -> Bool, + sendSelected: @escaping (TGMediaSelectableItem?, Bool, Int32?, Bool, ChatSendMessageActionSheetController.SendParameters?, @escaping () -> Void) -> Void, + schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void, + dismissInput: @escaping () -> Void, + selectionState: TGMediaSelectionContext?, + editingState: TGMediaEditingContext + ) { self.downloadManager = downloadManager self.openMedia = openMedia self.openSelectedMedia = openSelectedMedia @@ -185,6 +197,18 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att case assets(PHAssetCollection?, AssetsMode) case media([Media]) + + var asFile: Bool { + switch self { + case let .assets(_, mode): + if case let .poll(_, asFile) = mode { + return asFile + } + return false + default: + return false + } + } } private let context: AccountContext @@ -607,11 +631,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.gridNode.scrollView.alwaysBounceVertical = true self.gridNode.scrollView.showsVerticalScrollIndicator = false - if case let .assets(_, mode) = controller.subject, [.wallpaper, .story, .addImage, .cover, .createSticker, .createAvatar].contains(mode) { - - } else { - self.setupSelectionGesture() - } + self.setupSelectionGesture() if let controller = self.controller, case let .assets(collection, _) = controller.subject, collection != nil { self.gridNode.view.interactiveTransitionGestureRecognizerTest = { point -> Bool in @@ -808,10 +828,20 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } } - func setupSelectionGesture() { - guard self.selectionGesture == nil else { + func setupSelectionGesture(force: Bool = false) { + guard self.selectionGesture == nil, let controller = self.controller else { return } + + if !force, case let .assets(_, mode) = controller.subject { + switch mode { + case .default: + break + default: + return + } + } + let selectionGesture = MediaPickerGridSelectionGesture() selectionGesture.delegate = self.wrappedGestureRecognizerDelegate selectionGesture.began = { [weak self] in @@ -1296,7 +1326,6 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.openingMedia = true let asset = fetchResult[index] - let _ = (checkIfAssetIsLocal(asset) |> deliverOnMainQueue).start(next: { [weak self] isLocallyAvailable in guard let self else { @@ -1333,7 +1362,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.openingMedia = true - self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .fetchResult(fetchResult: fetchResult, index: index, reversed: reversed), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState?.selectionLimit == 1 ? nil : interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: hasSchedule, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in + self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .fetchResult(fetchResult: fetchResult, index: index, reversed: reversed), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState?.selectionLimit == 1 ? nil : interaction.selectionState, editingContext: interaction.editingState, asFile: controller.subject.asFile, hasSilentPosting: true, hasSchedule: hasSchedule, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in self?.hiddenMediaId.set(.single(id)) }, initialLayout: layout, transitionHostView: { [weak self] in return self?.gridNode.view @@ -1346,7 +1375,11 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att }, presentSchedulePicker: controller.presentSchedulePicker, presentTimerPicker: controller.presentTimerPicker, getCaptionPanelView: controller.getCaptionPanelView, present: { [weak self] c, a in self?.currentGalleryParentController = c c.navigationPresentation = .flatModal - self?.controller?.parentController()?.push(c) + if let parentController = self?.controller?.parentController() { + parentController.push(c) + } else { + self?.controller?.push(c) + } //self?.controller?.present(c, in: .window(.root), with: a) }, finishedTransitionIn: { [weak self] in self?.openingMedia = false @@ -1376,7 +1409,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } self.openingMedia = true - self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .selection(item: item), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, hasSilentPosting: true, hasSchedule: true, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in + self.currentGalleryController = presentLegacyMediaPickerGallery(context: controller.context, peer: controller.peer, threadTitle: controller.threadTitle, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, presentationData: self.presentationData, source: .selection(item: item), immediateThumbnail: immediateThumbnail, selectionContext: interaction.selectionState, editingContext: interaction.editingState, asFile: controller.subject.asFile, hasSilentPosting: true, hasSchedule: true, hasTimer: hasTimer, updateHiddenMedia: { [weak self] id in self?.hiddenMediaId.set(.single(id)) }, initialLayout: layout, transitionHostView: { [weak self] in return self?.selectionNode?.view @@ -2021,6 +2054,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.secondaryButtonAction = secondaryButtonAction let selectionContext = selectionContext ?? TGMediaSelectionContext(groupingAllowed: false, selectionLimit: enableMultiselection ? 100 : 1)! + let editingContext = editingContext ?? (subject.asFile ? TGMediaEditingContext.forCaptionsOnly() : TGMediaEditingContext())! self.titleView = MediaPickerTitleView(theme: self.presentationData.theme, glass: style == .glass, segments: [self.presentationData.strings.Attachment_AllMedia, self.presentationData.strings.Attachment_SelectedMedia(1)], selectedIndex: 0) @@ -2260,7 +2294,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if let currentItem = currentItem { selectionState.setItem(currentItem, selected: true) } - strongSelf.controllerNode.send(fromGallery: currentItem != nil, silently: silently, scheduleTime: scheduleTime, animated: animated, parameters: parameters, completion: completion) + strongSelf.controllerNode.send(fromGallery: currentItem != nil, asFile: strongSelf.subject.asFile, silently: silently, scheduleTime: scheduleTime, animated: animated, parameters: parameters, completion: completion) } }, schedule: { [weak self] parameters in if let strongSelf = self { @@ -2272,7 +2306,16 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att if let strongSelf = self { strongSelf.controllerNode.dismissInput() } - }, selectionState: selectionContext, editingState: editingContext ?? TGMediaEditingContext()) + }, selectionState: selectionContext, editingState: editingContext) + + var displayLivePhotoIcon = false + if case let .assets(_, mode) = subject, case .default = mode { + if let peer = self.peer, case .secretChat = peer { + } else { + displayLivePhotoIcon = true + } + } + self.interaction?.displayLivePhotoIcon = displayLivePhotoIcon let highQualityPhoto = UserDefaults.standard.bool(forKey: "TG_photoHighQuality_v0") if highQualityPhoto { @@ -2575,13 +2618,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att let selectedSize = self.selectedButtonNode.update(count: count) - var safeInset: CGFloat = 0.0 - if layout.safeInsets.right > 0.0 { - safeInset += layout.safeInsets.right + 16.0 - } - let selectedButtonInset: CGFloat = self._hasGlassStyle ? 68.0 : 54.0 let navigationHeight = navigationLayout(layout: layout).navigationFrame.height - self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - selectedButtonInset - selectedSize.width - safeInset, y: self._hasGlassStyle ? 16.0 : floorToScreenPixels((navigationHeight - selectedSize.height) / 2.0) + 1.0), size: selectedSize) + self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: 16.0 + 44.0 + 12.0, y: self._hasGlassStyle ? 16.0 : floorToScreenPixels((navigationHeight - selectedSize.height) / 2.0) + 1.0), size: selectedSize) let isSelectionButtonVisible = count > 0 && self.controllerNode.currentDisplayMode == .all transition.updateAlpha(node: self.selectedButtonNode, alpha: isSelectionButtonVisible ? 1.0 : 0.0) @@ -2973,7 +3011,7 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att } self.explicitMultipleSelection = true - self.controllerNode.setupSelectionGesture() + self.controllerNode.setupSelectionGesture(force: true) self.requestAttachmentMenuExpansion() if let state = self.controllerNode.state { @@ -3202,14 +3240,8 @@ public final class MediaPickerScreenImpl: ViewController, MediaPickerScreen, Att self.validLayout = layout self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) - var safeInset: CGFloat = 0.0 - if layout.safeInsets.right > 0.0 { - safeInset += layout.safeInsets.right + 16.0 - } let navigationHeight = navigationLayout(layout: layout).navigationFrame.height - - let selectedButtonInset: CGFloat = self._hasGlassStyle ? 68.0 : 54.0 - self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: self.view.bounds.width - selectedButtonInset - self.selectedButtonNode.frame.width - safeInset, y: self._hasGlassStyle ? 16.0 : floorToScreenPixels((navigationHeight - self.selectedButtonNode.frame.height) / 2.0) + 1.0), size: self.selectedButtonNode.frame.size) + self.selectedButtonNode.frame = CGRect(origin: CGPoint(x: 16.0 + 44.0 + 12.0, y: self._hasGlassStyle ? 16.0 : floorToScreenPixels((navigationHeight - self.selectedButtonNode.frame.height) / 2.0) + 1.0), size: self.selectedButtonNode.frame.size) } public func dismissAnimated() { diff --git a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift index ec8b0a686f..deff712d0d 100644 --- a/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift +++ b/submodules/TelegramStringFormatting/Sources/MessageContentKind.swift @@ -501,7 +501,7 @@ public func stringForMediaKind(_ kind: MessageContentKind, strings: Presentation public func descriptionStringForMessage(contentSettings: ContentSettings, message: EngineMessage, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, accountPeerId: EnginePeer.Id) -> (NSAttributedString, Bool, Bool) { let contentKind = messageContentKind(contentSettings: contentSettings, message: message, strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, accountPeerId: accountPeerId) - if !message.text.isEmpty && ![.expiredImage, .expiredVideo].contains(contentKind.key) { + if !message.text.isEmpty && ![.expiredImage, .expiredVideo, .poll].contains(contentKind.key) { return (foldLineBreaks(messageTextWithAttributes(message: message)), false, true) } let result = stringForMediaKind(contentKind, strings: strings) diff --git a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift index 1768f1bae7..c87f824712 100644 --- a/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatHistorySearchContainerNode/Sources/ChatHistorySearchContainerNode.swift @@ -20,8 +20,7 @@ private extension ListMessageItemInteraction { return controllerInteraction.openMessage(message, OpenMessageParams(mode: mode)) }, openMessageContextMenu: { message, bool, node, rect, gesture in controllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture, nil) - }, toggleMediaPlayback: { _ in - }, toggleMessagesSelection: { messageId, selected in + }, toggleMediaPlayback: nil, toggleMessagesSelection: { messageId, selected in controllerInteraction.toggleMessagesSelection(messageId, selected) }, openUrl: { url, param1, param2, message in controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: param1, external: param2, message: message)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/BUILD index 2199a250f0..491db8f060 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/BUILD @@ -11,6 +11,7 @@ swift_library( ], deps = [ "//submodules/AsyncDisplayKit", + "//submodules/Crc32", "//submodules/Display", "//submodules/TelegramCore", "//submodules/Postbox", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index ef2a6b4368..9a04ffb1a1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1,6 +1,7 @@ import Foundation import UIKit import AsyncDisplayKit +import Crc32 import Display import TelegramCore import Postbox @@ -3654,11 +3655,6 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } } -private struct ArbitraryRandomNumberGenerator : RandomNumberGenerator { - init(seed: Int) { srand48(seed) } - func next() -> UInt64 { return UInt64(drand48() * Double(UInt64.max)) } -} - private func stringForRemainingTime(_ duration: Int32, strings: PresentationStrings, results: Bool) -> String { let days = duration / (3600 * 24) let hours = duration / 3600 @@ -3739,26 +3735,31 @@ private func resolvedOptionOrder(for item: ChatMessageBubbleContentItem) -> [(In return defaultOrderedOptions } - let currentOpaqueIdentifiers = poll.options.map(\.opaqueIdentifier) - let messageSeed = UInt64(bitPattern: Int64(item.message.stableId)) - let peerSeed = UInt64(bitPattern: item.context.account.peerId.toInt64()) - let seed = Int(truncatingIfNeeded: (messageSeed &* 6364136223846793005) ^ peerSeed) + let userId = item.context.account.peerId.id._internalGetInt64Value() + let pollId = poll.pollId.id - var resolvedOpaqueIdentifiers = currentOpaqueIdentifiers - var randomNumberGenerator = ArbitraryRandomNumberGenerator(seed: seed) - resolvedOpaqueIdentifiers.shuffle(using: &randomNumberGenerator) + return defaultOrderedOptions + .map { index, option in + var hashValue = Data() + hashValue.append(contentsOf: String(userId).utf8) + hashValue.append(option.opaqueIdentifier) + hashValue.append(contentsOf: String(pollId).utf8) - let indicesByOpaqueIdentifier = Dictionary(uniqueKeysWithValues: poll.options.enumerated().map { ($0.element.opaqueIdentifier, $0.offset) }) - let orderedOptions = resolvedOpaqueIdentifiers.compactMap { opaqueIdentifier -> (Int, TelegramMediaPollOption)? in - guard let index = indicesByOpaqueIdentifier[opaqueIdentifier] else { - return nil + let sortValue: UInt32 = hashValue.withUnsafeBytes { bytes in + guard let baseAddress = bytes.baseAddress else { + return 0 + } + return Crc32(baseAddress, Int32(bytes.count)) + } + + return (index, option, sortValue) } - return (index, poll.options[index]) - } - - if orderedOptions.count == poll.options.count { - return orderedOptions - } else { - return defaultOrderedOptions - } + .sorted(by: { lhs, rhs in + if lhs.2 != rhs.2 { + return lhs.2 < rhs.2 + } else { + return lhs.0 < rhs.0 + } + }) + .map { ($0.0, $0.1) } } diff --git a/submodules/TelegramUI/Components/ListComposePollOptionComponent/Sources/ListComposePollOptionComponent.swift b/submodules/TelegramUI/Components/ListComposePollOptionComponent/Sources/ListComposePollOptionComponent.swift index 6eb9b9cf08..8ca1510769 100644 --- a/submodules/TelegramUI/Components/ListComposePollOptionComponent/Sources/ListComposePollOptionComponent.swift +++ b/submodules/TelegramUI/Components/ListComposePollOptionComponent/Sources/ListComposePollOptionComponent.swift @@ -1103,9 +1103,13 @@ public final class ListComposePollOptionComponent: Component { let cornerRadius: CGFloat = 10.0 let makeLayout = imageNode.asyncLayout() - let apply = makeLayout(TransformImageArguments(corners: ImageCorners(radius: cornerRadius), imageSize: imageSize, boundingSize: imageNodeSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor)) - apply() - + Queue.concurrentDefaultQueue().async { + let apply = makeLayout(TransformImageArguments(corners: ImageCorners(radius: cornerRadius), imageSize: imageSize, boundingSize: imageNodeSize, intrinsicInsets: UIEdgeInsets(), emptyColor: component.theme.list.mediaPlaceholderColor)) + Queue.mainQueue().async { + apply() + } + } + if self.imageButton.superview == nil { self.imageButton.addTarget(self, action: #selector(self.imageButtonPressed), for: .touchUpInside) self.addSubview(self.imageButton) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 1b69c37a7e..72cde46ac7 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -202,8 +202,7 @@ extension ListMessageItemInteraction { return controllerInteraction.openMessage(message, OpenMessageParams(mode: mode)) }, openMessageContextMenu: { message, bool, node, rect, gesture in controllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture, nil) - }, toggleMediaPlayback: { _ in - }, toggleMessagesSelection: { messageId, selected in + }, toggleMediaPlayback: nil, toggleMessagesSelection: { messageId, selected in controllerInteraction.toggleMessagesSelection(messageId, selected) }, openUrl: { url, param1, param2, message in controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: param1, external: param2, message: message, progress: Promise()))