diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 4b54f5fb5d..bf5a16c21d 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15982,6 +15982,9 @@ Error: %8$@"; "Notification.PollAddedOption" = "%1$@ added the option \"%2$@\" to the poll"; "Notification.PollAddedOptionYou" = "You added the option \"%1$@\" to the poll"; +"Notification.PollDeletedOption" = "%1$@ deleted the option \"%2$@\" from the poll"; +"Notification.PollDeletedOptionYou" = "You deleted the option \"%1$@\" to the poll"; + "Notification.ManagedBotCreated" = "%@ bot created"; "Chat.PolOptionAddedTimestamp.Date" = "Added by %1$@ %2$@"; @@ -15999,3 +16002,6 @@ Error: %8$@"; "MessagePoll.ResultsIn" = "results in %@"; "MessagePoll.ResultsInDays_1" = "results in %@ day"; "MessagePoll.ResultsInDays_any" = "results in %@ days"; + +"Premium.AiTools" = "AI Tools"; +"Premium.AiToolsInfo" = "Transform your messages and entire chats in your preferred style and language."; diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index 3dfd66de17..cfce7d1d14 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -44,6 +44,7 @@ public enum PremiumIntroSource { case messageEffects case todo case copyProtection + case aiTools case auth(String) case premiumGift(TelegramMediaFile) } @@ -85,6 +86,7 @@ public enum PremiumDemoSubject { case messageEffects case todo case copyProtection + case aiTools case businessLocation case businessHours 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 aaad318d8f..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 + 80.0 + 32.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/PremiumUI/Sources/PremiumDemoScreen.swift b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift index ddd15e4f70..def7028de2 100644 --- a/submodules/PremiumUI/Sources/PremiumDemoScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumDemoScreen.swift @@ -1129,6 +1129,26 @@ private final class DemoSheetContent: CombinedComponent { ) ) + availableItems[.aiTools] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.aiTools, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: component.context, + position: .top, + model: .island, + videoFile: configuration.videos["ai_compose"], + decoration: .badgeStars + )), + title: strings.Premium_AiTools, + text: strings.Premium_AiToolsInfo, + textColor: textColor + ) + ) + ) + ) + let index: Int = 0 var items: [DemoPagerComponent.Item] = [] if let item = availableItems.first(where: { $0.value.content.id == component.subject as AnyHashable }) { @@ -1221,6 +1241,8 @@ private final class DemoSheetContent: CombinedComponent { text = strings.Premium_TodoInfo case .copyProtection: text = strings.Premium_CopyProtectionInfo + case .aiTools: + text = "Transform your messages and entire chats in your preferred style and language." default: text = "" } @@ -1309,6 +1331,8 @@ private final class DemoSheetContent: CombinedComponent { buttonText = strings.Premium_PaidMessages_Proceed case .copyProtection: buttonText = strings.Premium_PaidMessages_Proceed + case .aiTools: + buttonText = strings.Premium_PaidMessages_Proceed default: buttonText = strings.Common_OK } @@ -1498,6 +1522,7 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { case messageEffects case todo case copyProtection + case aiTools case businessLocation case businessHours @@ -1560,6 +1585,8 @@ public class PremiumDemoScreen: ViewControllerComponentContainer { return .todo case .copyProtection: return .copyProtection + case .aiTools: + return .aiTools case .businessLocation: return .businessLocation case .businessHours: diff --git a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift index 85d095b7d7..c11551e0ef 100644 --- a/submodules/PremiumUI/Sources/PremiumGiftScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumGiftScreen.swift @@ -443,6 +443,7 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent { UIColor(rgb: 0x676bff), UIColor(rgb: 0x6172ff), UIColor(rgb: 0x5b79ff), + UIColor(rgb: 0x5b79ff), UIColor(rgb: 0x4492ff), UIColor(rgb: 0x429bd5), UIColor(rgb: 0x41a6a5), diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 6f5fd2847a..f25801b513 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -323,6 +323,12 @@ public enum PremiumSource: Equatable { } else { return false } + case .aiTools: + if case .aiTools = rhs { + return true + } else { + return false + } case let .auth(lhsPrice): if case let .auth(rhsPrice) = rhs, lhsPrice == rhsPrice { return true @@ -384,6 +390,7 @@ public enum PremiumSource: Equatable { case messageEffects case todo case copyProtection + case aiTools case auth(String) case premiumGift(TelegramMediaFile) @@ -483,6 +490,8 @@ public enum PremiumSource: Equatable { return "todo" case .copyProtection: return "pm_noforwards" + case .aiTools: + return "ai_compose" case .auth: return "auth" case .premiumGift: @@ -517,6 +526,7 @@ public enum PremiumPerk: CaseIterable { case messageEffects case todo case copyProtection + case aiTools case businessLocation case businessHours @@ -553,7 +563,8 @@ public enum PremiumPerk: CaseIterable { .business, .messageEffects, .todo, - .copyProtection + .copyProtection, + .aiTools ] } @@ -631,6 +642,8 @@ public enum PremiumPerk: CaseIterable { return "todo" case .copyProtection: return "pm_noforwards" + case .aiTools: + return "ai_compose" case .business: return "business" case .businessLocation: @@ -704,6 +717,8 @@ public enum PremiumPerk: CaseIterable { return strings.Premium_Todo case .copyProtection: return strings.Premium_CopyProtection + case .aiTools: + return strings.Premium_AiTools case .businessLocation: return strings.Business_Location case .businessHours: @@ -775,6 +790,8 @@ public enum PremiumPerk: CaseIterable { return strings.Premium_TodoInfo case .copyProtection: return strings.Premium_CopyProtectionInfo + case .aiTools: + return strings.Premium_AiToolsInfo case .businessLocation: return strings.Business_LocationInfo case .businessHours: @@ -846,6 +863,8 @@ public enum PremiumPerk: CaseIterable { return "Item List/Icons/Checkbox" case .copyProtection: return "Item List/Icons/NoForward" + case .aiTools: + return "Item List/Icons/AITools" case .businessLocation: return "Item List/Icons/Location" case .businessHours: @@ -870,6 +889,7 @@ struct PremiumIntroConfiguration { static var defaultValue: PremiumIntroConfiguration { return PremiumIntroConfiguration(perks: [ .stories, + .aiTools, .moreUpload, .doubleLimits, .lastSeen, @@ -932,6 +952,12 @@ struct PremiumIntroConfiguration { if perks.count < 4 { perks = PremiumIntroConfiguration.defaultValue.perks } + + #if DEBUG + if !perks.contains(.aiTools) { + perks.insert(.aiTools, at: 1) + } + #endif var businessPerks: [PremiumPerk] = [] if let values = data["business_promo_order"] as? [String] { @@ -1635,7 +1661,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { super.init() - self.newPerks = [PremiumPerk.copyProtection.identifier] + self.newPerks = [PremiumPerk.aiTools.identifier, PremiumPerk.copyProtection.identifier] let premiumIntroConfiguration: Signal let accountPeer: Signal @@ -1970,6 +1996,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { UIColor(rgb: 0x676bff), UIColor(rgb: 0x6172ff), UIColor(rgb: 0x5b79ff), + UIColor(rgb: 0x5b79ff), UIColor(rgb: 0x4492ff), UIColor(rgb: 0x429bd5), UIColor(rgb: 0x41a6a5), @@ -2220,6 +2247,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { demoSubject = .todo case .copyProtection: demoSubject = .copyProtection + case .aiTools: + demoSubject = .aiTools case .business: demoSubject = .business default: diff --git a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift index cf87b35f4d..f5c6e6af7b 100644 --- a/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumLimitsListScreen.swift @@ -884,6 +884,26 @@ public class PremiumLimitsListScreen: ViewController { ) ) + availableItems[.aiTools] = DemoPagerComponent.Item( + AnyComponentWithIdentity( + id: PremiumDemoScreen.Subject.aiTools, + component: AnyComponent( + PageComponent( + content: AnyComponent(PhoneDemoComponent( + context: context, + position: .top, + model: .island, + videoFile: videos["ai_compose"], + decoration: .badgeStars + )), + title: strings.Premium_AiTools, + text: strings.Premium_AiToolsInfo, + textColor: textColor + ) + ) + ) + ) + availableItems[.business] = DemoPagerComponent.Item( AnyComponentWithIdentity( id: PremiumDemoScreen.Subject.business, diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index b4a2b67461..5122764c32 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -296,7 +296,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-207944868] = { return Api.FileHash.parse_fileHash($0) } dict[-11252123] = { return Api.Folder.parse_folder($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } - dict[-838922550] = { return Api.ForumTopic.parse_forumTopic($0) } + dict[-52766699] = { return Api.ForumTopic.parse_forumTopic($0) } dict[37687451] = { return Api.ForumTopic.parse_forumTopicDeleted($0) } dict[-394605632] = { return Api.FoundStory.parse_foundStory($0) } dict[-1107729093] = { return Api.Game.parse_game($0) } @@ -623,6 +623,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-2132731265] = { return Api.MessageAction.parse_messageActionPhoneCall($0) } dict[-1799538451] = { return Api.MessageAction.parse_messageActionPinMessage($0) } dict[-1650340500] = { return Api.MessageAction.parse_messageActionPollAppendAnswer($0) } + dict[966161628] = { return Api.MessageAction.parse_messageActionPollDeleteAnswer($0) } dict[-1341372510] = { return Api.MessageAction.parse_messageActionPrizeStars($0) } dict[827428507] = { return Api.MessageAction.parse_messageActionRequestedPeer($0) } dict[-1816979384] = { return Api.MessageAction.parse_messageActionRequestedPeerSentMe($0) } @@ -1174,7 +1175,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-710666460] = { return Api.Update.parse_updateMessageExtendedMedia($0) } dict[1318109142] = { return Api.Update.parse_updateMessageID($0) } dict[-699641301] = { return Api.Update.parse_updateMessagePoll($0) } - dict[619974263] = { return Api.Update.parse_updateMessagePollVote($0) } + dict[1989799956] = { return Api.Update.parse_updateMessagePollVote($0) } dict[506035194] = { return Api.Update.parse_updateMessageReactions($0) } dict[-1618924792] = { return Api.Update.parse_updateMonoForumNoPaidException($0) } dict[-2030252155] = { return Api.Update.parse_updateMoveStickerSetToTop($0) } @@ -1258,7 +1259,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-484987010] = { return Api.Updates.parse_updatesTooLong($0) } dict[1648005024] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) } dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) } - dict[-117904610] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } + dict[1020666860] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } dict[829899656] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } dict[114026053] = { return Api.UserFull.parse_userFull($0) } diff --git a/submodules/TelegramApi/Sources/Api15.swift b/submodules/TelegramApi/Sources/Api15.swift index 826437b76f..88516bfd09 100644 --- a/submodules/TelegramApi/Sources/Api15.swift +++ b/submodules/TelegramApi/Sources/Api15.swift @@ -2080,6 +2080,15 @@ public extension Api { return ("messageActionPollAppendAnswer", [("answer", self.answer as Any)]) } } + public class Cons_messageActionPollDeleteAnswer: TypeConstructorDescription { + public var answer: Api.PollAnswer + public init(answer: Api.PollAnswer) { + self.answer = answer + } + public func descriptionFields() -> (String, [(String, Any)]) { + return ("messageActionPollDeleteAnswer", [("answer", self.answer as Any)]) + } + } public class Cons_messageActionPrizeStars: TypeConstructorDescription { public var flags: Int32 public var stars: Int64 @@ -2429,6 +2438,7 @@ public extension Api { case messageActionPhoneCall(Cons_messageActionPhoneCall) case messageActionPinMessage case messageActionPollAppendAnswer(Cons_messageActionPollAppendAnswer) + case messageActionPollDeleteAnswer(Cons_messageActionPollDeleteAnswer) case messageActionPrizeStars(Cons_messageActionPrizeStars) case messageActionRequestedPeer(Cons_messageActionRequestedPeer) case messageActionRequestedPeerSentMe(Cons_messageActionRequestedPeerSentMe) @@ -2838,6 +2848,12 @@ public extension Api { } _data.answer.serialize(buffer, true) break + case .messageActionPollDeleteAnswer(let _data): + if boxed { + buffer.appendInt32(966161628) + } + _data.answer.serialize(buffer, true) + break case .messageActionPrizeStars(let _data): if boxed { buffer.appendInt32(-1341372510) @@ -3208,6 +3224,8 @@ public extension Api { return ("messageActionPinMessage", []) case .messageActionPollAppendAnswer(let _data): return ("messageActionPollAppendAnswer", [("answer", _data.answer as Any)]) + case .messageActionPollDeleteAnswer(let _data): + return ("messageActionPollDeleteAnswer", [("answer", _data.answer as Any)]) case .messageActionPrizeStars(let _data): return ("messageActionPrizeStars", [("flags", _data.flags as Any), ("stars", _data.stars as Any), ("transactionId", _data.transactionId as Any), ("boostPeer", _data.boostPeer as Any), ("giveawayMsgId", _data.giveawayMsgId as Any)]) case .messageActionRequestedPeer(let _data): @@ -3973,6 +3991,19 @@ public extension Api { return nil } } + public static func parse_messageActionPollDeleteAnswer(_ reader: BufferReader) -> MessageAction? { + var _1: Api.PollAnswer? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PollAnswer + } + let _c1 = _1 != nil + if _c1 { + return Api.MessageAction.messageActionPollDeleteAnswer(Cons_messageActionPollDeleteAnswer(answer: _1!)) + } + else { + return nil + } + } public static func parse_messageActionPrizeStars(_ reader: BufferReader) -> MessageAction? { var _1: Int32? _1 = reader.readInt32() diff --git a/submodules/TelegramApi/Sources/Api28.swift b/submodules/TelegramApi/Sources/Api28.swift index 685da34e1b..acde42a931 100644 --- a/submodules/TelegramApi/Sources/Api28.swift +++ b/submodules/TelegramApi/Sources/Api28.swift @@ -1690,15 +1690,17 @@ public extension Api { public var pollId: Int64 public var peer: Api.Peer public var options: [Buffer] + public var positions: [Int32] public var qts: Int32 - public init(pollId: Int64, peer: Api.Peer, options: [Buffer], qts: Int32) { + public init(pollId: Int64, peer: Api.Peer, options: [Buffer], positions: [Int32], qts: Int32) { self.pollId = pollId self.peer = peer self.options = options + self.positions = positions self.qts = qts } public func descriptionFields() -> (String, [(String, Any)]) { - return ("updateMessagePollVote", [("pollId", self.pollId as Any), ("peer", self.peer as Any), ("options", self.options as Any), ("qts", self.qts as Any)]) + return ("updateMessagePollVote", [("pollId", self.pollId as Any), ("peer", self.peer as Any), ("options", self.options as Any), ("positions", self.positions as Any), ("qts", self.qts as Any)]) } } public class Cons_updateMessageReactions: TypeConstructorDescription { @@ -3440,7 +3442,7 @@ public extension Api { break case .updateMessagePollVote(let _data): if boxed { - buffer.appendInt32(619974263) + buffer.appendInt32(1989799956) } serializeInt64(_data.pollId, buffer: buffer, boxed: false) _data.peer.serialize(buffer, true) @@ -3449,6 +3451,11 @@ public extension Api { for item in _data.options { serializeBytes(item, buffer: buffer, boxed: false) } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(_data.positions.count)) + for item in _data.positions { + serializeInt32(item, buffer: buffer, boxed: false) + } serializeInt32(_data.qts, buffer: buffer, boxed: false) break case .updateMessageReactions(let _data): @@ -4245,7 +4252,7 @@ public extension Api { case .updateMessagePoll(let _data): return ("updateMessagePoll", [("flags", _data.flags as Any), ("peer", _data.peer as Any), ("msgId", _data.msgId as Any), ("topMsgId", _data.topMsgId as Any), ("pollId", _data.pollId as Any), ("poll", _data.poll as Any), ("results", _data.results as Any)]) case .updateMessagePollVote(let _data): - return ("updateMessagePollVote", [("pollId", _data.pollId as Any), ("peer", _data.peer as Any), ("options", _data.options as Any), ("qts", _data.qts as Any)]) + return ("updateMessagePollVote", [("pollId", _data.pollId as Any), ("peer", _data.peer as Any), ("options", _data.options as Any), ("positions", _data.positions as Any), ("qts", _data.qts as Any)]) case .updateMessageReactions(let _data): return ("updateMessageReactions", [("flags", _data.flags as Any), ("peer", _data.peer as Any), ("msgId", _data.msgId as Any), ("topMsgId", _data.topMsgId as Any), ("savedPeerId", _data.savedPeerId as Any), ("reactions", _data.reactions as Any)]) case .updateMonoForumNoPaidException(let _data): @@ -6022,14 +6029,19 @@ public extension Api { if let _ = reader.readInt32() { _3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: Buffer.self) } - var _4: Int32? - _4 = reader.readInt32() + var _4: [Int32]? + if let _ = reader.readInt32() { + _4 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _5: Int32? + _5 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil let _c4 = _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.Update.updateMessagePollVote(Cons_updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, qts: _4!)) + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updateMessagePollVote(Cons_updateMessagePollVote(pollId: _1!, peer: _2!, options: _3!, positions: _4!, qts: _5!)) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 52472fe9fc..4aa4b68c4a 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -579,7 +579,8 @@ public extension Api { public var region: String? public var matchCodes: [String]? public var userIdHint: Int64? - public init(flags: Int32, bot: Api.User, domain: String, browser: String?, platform: String?, ip: String?, region: String?, matchCodes: [String]?, userIdHint: Int64?) { + public var verifiedAppName: String? + public init(flags: Int32, bot: Api.User, domain: String, browser: String?, platform: String?, ip: String?, region: String?, matchCodes: [String]?, userIdHint: Int64?, verifiedAppName: String?) { self.flags = flags self.bot = bot self.domain = domain @@ -589,9 +590,10 @@ public extension Api { self.region = region self.matchCodes = matchCodes self.userIdHint = userIdHint + self.verifiedAppName = verifiedAppName } public func descriptionFields() -> (String, [(String, Any)]) { - return ("urlAuthResultRequest", [("flags", self.flags as Any), ("bot", self.bot as Any), ("domain", self.domain as Any), ("browser", self.browser as Any), ("platform", self.platform as Any), ("ip", self.ip as Any), ("region", self.region as Any), ("matchCodes", self.matchCodes as Any), ("userIdHint", self.userIdHint as Any)]) + return ("urlAuthResultRequest", [("flags", self.flags as Any), ("bot", self.bot as Any), ("domain", self.domain as Any), ("browser", self.browser as Any), ("platform", self.platform as Any), ("ip", self.ip as Any), ("region", self.region as Any), ("matchCodes", self.matchCodes as Any), ("userIdHint", self.userIdHint as Any), ("verifiedAppName", self.verifiedAppName as Any)]) } } case urlAuthResultAccepted(Cons_urlAuthResultAccepted) @@ -616,7 +618,7 @@ public extension Api { break case .urlAuthResultRequest(let _data): if boxed { - buffer.appendInt32(-117904610) + buffer.appendInt32(1020666860) } serializeInt32(_data.flags, buffer: buffer, boxed: false) _data.bot.serialize(buffer, true) @@ -643,6 +645,9 @@ public extension Api { if Int(_data.flags) & Int(1 << 4) != 0 { serializeInt64(_data.userIdHint!, buffer: buffer, boxed: false) } + if Int(_data.flags) & Int(1 << 7) != 0 { + serializeString(_data.verifiedAppName!, buffer: buffer, boxed: false) + } break } } @@ -654,7 +659,7 @@ public extension Api { case .urlAuthResultDefault: return ("urlAuthResultDefault", []) case .urlAuthResultRequest(let _data): - return ("urlAuthResultRequest", [("flags", _data.flags as Any), ("bot", _data.bot as Any), ("domain", _data.domain as Any), ("browser", _data.browser as Any), ("platform", _data.platform as Any), ("ip", _data.ip as Any), ("region", _data.region as Any), ("matchCodes", _data.matchCodes as Any), ("userIdHint", _data.userIdHint as Any)]) + return ("urlAuthResultRequest", [("flags", _data.flags as Any), ("bot", _data.bot as Any), ("domain", _data.domain as Any), ("browser", _data.browser as Any), ("platform", _data.platform as Any), ("ip", _data.ip as Any), ("region", _data.region as Any), ("matchCodes", _data.matchCodes as Any), ("userIdHint", _data.userIdHint as Any), ("verifiedAppName", _data.verifiedAppName as Any)]) } } @@ -712,6 +717,10 @@ public extension Api { if Int(_1!) & Int(1 << 4) != 0 { _9 = reader.readInt64() } + var _10: String? + if Int(_1!) & Int(1 << 7) != 0 { + _10 = parseString(reader) + } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -721,8 +730,9 @@ public extension Api { let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.UrlAuthResult.urlAuthResultRequest(Cons_urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!, browser: _4, platform: _5, ip: _6, region: _7, matchCodes: _8, userIdHint: _9)) + let _c10 = (Int(_1!) & Int(1 << 7) == 0) || _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.UrlAuthResult.urlAuthResultRequest(Cons_urlAuthResultRequest(flags: _1!, bot: _2!, domain: _3!, browser: _4, platform: _5, ip: _6, region: _7, matchCodes: _8, userIdHint: _9, verifiedAppName: _10)) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api7.swift b/submodules/TelegramApi/Sources/Api7.swift index 56b7a4c9ff..2dd2a6c5bb 100644 --- a/submodules/TelegramApi/Sources/Api7.swift +++ b/submodules/TelegramApi/Sources/Api7.swift @@ -1141,10 +1141,11 @@ public extension Api { public var unreadCount: Int32 public var unreadMentionsCount: Int32 public var unreadReactionsCount: Int32 + public var unreadPollVotesCount: Int32 public var fromId: Api.Peer public var notifySettings: Api.PeerNotifySettings public var draft: Api.DraftMessage? - public init(flags: Int32, id: Int32, date: Int32, peer: Api.Peer, title: String, iconColor: Int32, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, fromId: Api.Peer, notifySettings: Api.PeerNotifySettings, draft: Api.DraftMessage?) { + public init(flags: Int32, id: Int32, date: Int32, peer: Api.Peer, title: String, iconColor: Int32, iconEmojiId: Int64?, topMessage: Int32, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, unreadMentionsCount: Int32, unreadReactionsCount: Int32, unreadPollVotesCount: Int32, fromId: Api.Peer, notifySettings: Api.PeerNotifySettings, draft: Api.DraftMessage?) { self.flags = flags self.id = id self.date = date @@ -1158,12 +1159,13 @@ public extension Api { self.unreadCount = unreadCount self.unreadMentionsCount = unreadMentionsCount self.unreadReactionsCount = unreadReactionsCount + self.unreadPollVotesCount = unreadPollVotesCount self.fromId = fromId self.notifySettings = notifySettings self.draft = draft } public func descriptionFields() -> (String, [(String, Any)]) { - return ("forumTopic", [("flags", self.flags as Any), ("id", self.id as Any), ("date", self.date as Any), ("peer", self.peer as Any), ("title", self.title as Any), ("iconColor", self.iconColor as Any), ("iconEmojiId", self.iconEmojiId as Any), ("topMessage", self.topMessage as Any), ("readInboxMaxId", self.readInboxMaxId as Any), ("readOutboxMaxId", self.readOutboxMaxId as Any), ("unreadCount", self.unreadCount as Any), ("unreadMentionsCount", self.unreadMentionsCount as Any), ("unreadReactionsCount", self.unreadReactionsCount as Any), ("fromId", self.fromId as Any), ("notifySettings", self.notifySettings as Any), ("draft", self.draft as Any)]) + return ("forumTopic", [("flags", self.flags as Any), ("id", self.id as Any), ("date", self.date as Any), ("peer", self.peer as Any), ("title", self.title as Any), ("iconColor", self.iconColor as Any), ("iconEmojiId", self.iconEmojiId as Any), ("topMessage", self.topMessage as Any), ("readInboxMaxId", self.readInboxMaxId as Any), ("readOutboxMaxId", self.readOutboxMaxId as Any), ("unreadCount", self.unreadCount as Any), ("unreadMentionsCount", self.unreadMentionsCount as Any), ("unreadReactionsCount", self.unreadReactionsCount as Any), ("unreadPollVotesCount", self.unreadPollVotesCount as Any), ("fromId", self.fromId as Any), ("notifySettings", self.notifySettings as Any), ("draft", self.draft as Any)]) } } public class Cons_forumTopicDeleted: TypeConstructorDescription { @@ -1182,7 +1184,7 @@ public extension Api { switch self { case .forumTopic(let _data): if boxed { - buffer.appendInt32(-838922550) + buffer.appendInt32(-52766699) } serializeInt32(_data.flags, buffer: buffer, boxed: false) serializeInt32(_data.id, buffer: buffer, boxed: false) @@ -1199,6 +1201,7 @@ public extension Api { serializeInt32(_data.unreadCount, buffer: buffer, boxed: false) serializeInt32(_data.unreadMentionsCount, buffer: buffer, boxed: false) serializeInt32(_data.unreadReactionsCount, buffer: buffer, boxed: false) + serializeInt32(_data.unreadPollVotesCount, buffer: buffer, boxed: false) _data.fromId.serialize(buffer, true) _data.notifySettings.serialize(buffer, true) if Int(_data.flags) & Int(1 << 4) != 0 { @@ -1217,7 +1220,7 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { case .forumTopic(let _data): - return ("forumTopic", [("flags", _data.flags as Any), ("id", _data.id as Any), ("date", _data.date as Any), ("peer", _data.peer as Any), ("title", _data.title as Any), ("iconColor", _data.iconColor as Any), ("iconEmojiId", _data.iconEmojiId as Any), ("topMessage", _data.topMessage as Any), ("readInboxMaxId", _data.readInboxMaxId as Any), ("readOutboxMaxId", _data.readOutboxMaxId as Any), ("unreadCount", _data.unreadCount as Any), ("unreadMentionsCount", _data.unreadMentionsCount as Any), ("unreadReactionsCount", _data.unreadReactionsCount as Any), ("fromId", _data.fromId as Any), ("notifySettings", _data.notifySettings as Any), ("draft", _data.draft as Any)]) + return ("forumTopic", [("flags", _data.flags as Any), ("id", _data.id as Any), ("date", _data.date as Any), ("peer", _data.peer as Any), ("title", _data.title as Any), ("iconColor", _data.iconColor as Any), ("iconEmojiId", _data.iconEmojiId as Any), ("topMessage", _data.topMessage as Any), ("readInboxMaxId", _data.readInboxMaxId as Any), ("readOutboxMaxId", _data.readOutboxMaxId as Any), ("unreadCount", _data.unreadCount as Any), ("unreadMentionsCount", _data.unreadMentionsCount as Any), ("unreadReactionsCount", _data.unreadReactionsCount as Any), ("unreadPollVotesCount", _data.unreadPollVotesCount as Any), ("fromId", _data.fromId as Any), ("notifySettings", _data.notifySettings as Any), ("draft", _data.draft as Any)]) case .forumTopicDeleted(let _data): return ("forumTopicDeleted", [("id", _data.id as Any)]) } @@ -1254,18 +1257,20 @@ public extension Api { _12 = reader.readInt32() var _13: Int32? _13 = reader.readInt32() - var _14: Api.Peer? + var _14: Int32? + _14 = reader.readInt32() + var _15: Api.Peer? if let signature = reader.readInt32() { - _14 = Api.parse(reader, signature: signature) as? Api.Peer + _15 = Api.parse(reader, signature: signature) as? Api.Peer } - var _15: Api.PeerNotifySettings? + var _16: Api.PeerNotifySettings? if let signature = reader.readInt32() { - _15 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings + _16 = Api.parse(reader, signature: signature) as? Api.PeerNotifySettings } - var _16: Api.DraftMessage? + var _17: Api.DraftMessage? if Int(_1!) & Int(1 << 4) != 0 { if let signature = reader.readInt32() { - _16 = Api.parse(reader, signature: signature) as? Api.DraftMessage + _17 = Api.parse(reader, signature: signature) as? Api.DraftMessage } } let _c1 = _1 != nil @@ -1283,9 +1288,10 @@ public extension Api { let _c13 = _13 != nil let _c14 = _14 != nil let _c15 = _15 != nil - let _c16 = (Int(_1!) & Int(1 << 4) == 0) || _16 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 { - return Api.ForumTopic.forumTopic(Cons_forumTopic(flags: _1!, id: _2!, date: _3!, peer: _4!, title: _5!, iconColor: _6!, iconEmojiId: _7, topMessage: _8!, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, unreadMentionsCount: _12!, unreadReactionsCount: _13!, fromId: _14!, notifySettings: _15!, draft: _16)) + let _c16 = _16 != nil + let _c17 = (Int(_1!) & Int(1 << 4) == 0) || _17 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.ForumTopic.forumTopic(Cons_forumTopic(flags: _1!, id: _2!, date: _3!, peer: _4!, title: _5!, iconColor: _6!, iconEmojiId: _7, topMessage: _8!, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, unreadMentionsCount: _12!, unreadReactionsCount: _13!, unreadPollVotesCount: _14!, fromId: _15!, notifySettings: _16!, draft: _17)) } else { return nil diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 4f4f825cdd..f76522b5b3 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -242,7 +242,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] { } switch action { - case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionGiftStars, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionGiveawayLaunch, .messageActionGiveawayResults, .messageActionBoostApply, .messageActionRequestedPeerSentMe, .messageActionStarGift, .messageActionStarGiftUnique, .messageActionPaidMessagesRefunded, .messageActionPaidMessagesPrice, .messageActionTodoCompletions, .messageActionTodoAppendTasks, .messageActionSuggestedPostApproval, .messageActionGiftTon, .messageActionSuggestedPostSuccess, .messageActionSuggestedPostRefund, .messageActionSuggestBirthday, .messageActionStarGiftPurchaseOffer, .messageActionStarGiftPurchaseOfferDeclined, .messageActionNoForwardsToggle, .messageActionNoForwardsRequest, .messageActionPollAppendAnswer: + case .messageActionChannelCreate, .messageActionChatDeletePhoto, .messageActionChatEditPhoto, .messageActionChatEditTitle, .messageActionEmpty, .messageActionPinMessage, .messageActionHistoryClear, .messageActionGameScore, .messageActionPaymentSent, .messageActionPaymentSentMe, .messageActionPhoneCall, .messageActionScreenshotTaken, .messageActionCustomAction, .messageActionBotAllowed, .messageActionSecureValuesSent, .messageActionSecureValuesSentMe, .messageActionContactSignUp, .messageActionGroupCall, .messageActionSetMessagesTTL, .messageActionGroupCallScheduled, .messageActionSetChatTheme, .messageActionChatJoinedByRequest, .messageActionWebViewDataSent, .messageActionWebViewDataSentMe, .messageActionGiftPremium, .messageActionGiftStars, .messageActionTopicCreate, .messageActionTopicEdit, .messageActionSuggestProfilePhoto, .messageActionSetChatWallPaper, .messageActionGiveawayLaunch, .messageActionGiveawayResults, .messageActionBoostApply, .messageActionRequestedPeerSentMe, .messageActionStarGift, .messageActionStarGiftUnique, .messageActionPaidMessagesRefunded, .messageActionPaidMessagesPrice, .messageActionTodoCompletions, .messageActionTodoAppendTasks, .messageActionSuggestedPostApproval, .messageActionGiftTon, .messageActionSuggestedPostSuccess, .messageActionSuggestedPostRefund, .messageActionSuggestBirthday, .messageActionStarGiftPurchaseOffer, .messageActionStarGiftPurchaseOfferDeclined, .messageActionNoForwardsToggle, .messageActionNoForwardsRequest, .messageActionPollAppendAnswer, .messageActionPollDeleteAnswer: break case let .messageActionManagedBotCreated(messageActionManagedBotCreated): let botId = messageActionManagedBotCreated.botId diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift index 955418d430..f7d713139f 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramMediaAction.swift @@ -367,6 +367,8 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe return TelegramMediaAction(action: .managedBotCreated(botId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(messageActionManagedBotCreatedData.botId)))) case let .messageActionPollAppendAnswer(messageActionPollAppendAnswerData): return TelegramMediaAction(action: .pollOptionAppended(TelegramMediaPollOption(apiOption: messageActionPollAppendAnswerData.answer))) + case let .messageActionPollDeleteAnswer(messageActionPollDeleteAnswerData): + return TelegramMediaAction(action: .pollOptionDeleted(TelegramMediaPollOption(apiOption: messageActionPollDeleteAnswerData.answer))) } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift index 2e66217687..c8f0b8365f 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaAction.swift @@ -306,6 +306,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case copyProtectionRequest(hasExpired: Bool, previousValue: Bool, newValue: Bool) case managedBotCreated(botId: PeerId) case pollOptionAppended(TelegramMediaPollOption) + case pollOptionDeleted(TelegramMediaPollOption) public init(decoder: PostboxDecoder) { let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) @@ -487,6 +488,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { self = .managedBotCreated(botId: PeerId(decoder.decodeInt64ForKey("botId", orElse: 0))) case 63: self = .pollOptionAppended(decoder.decodeObjectForKey("option", decoder: { TelegramMediaPollOption(decoder: $0) }) as! TelegramMediaPollOption) + case 64: + self = .pollOptionDeleted(decoder.decodeObjectForKey("option", decoder: { TelegramMediaPollOption(decoder: $0) }) as! TelegramMediaPollOption) default: self = .unknown } @@ -996,6 +999,9 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case let .pollOptionAppended(option): encoder.encodeInt32(63, forKey: "_rawValue") encoder.encodeObject(option, forKey: "option") + case let .pollOptionDeleted(option): + encoder.encodeInt32(64, forKey: "_rawValue") + encoder.encodeObject(option, forKey: "option") } } 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/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 60ebfa9acc..15cc9e0b4b 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1822,19 +1822,6 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, let attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: peerIds) attributedString = addAttributesToStringWithRanges(strings.Notification_ManagedBotCreated(peerName)._tuple, body: bodyAttributes, argumentAttributes: attributes) case let .pollOptionAppended(option): - var optionTitle = "DELETED" - for attribute in message.attributes { - if let attribute = attribute as? ReplyMessageAttribute, let message = message.associatedMessages[attribute.messageId] { - for media in message.media { - if let todo = media as? TelegramMediaTodo { - optionTitle = todo.text - } - } - } - } - if optionTitle.count > 20 { - optionTitle = optionTitle.prefix(20) + "…" - } let optionEntities = option.entities.filter { entity in switch entity.type { case .Spoiler, .CustomEmoji: @@ -1872,6 +1859,44 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } attributedString = resultAttributedString } + case let .pollOptionDeleted(option): + let optionEntities = option.entities.filter { entity in + switch entity.type { + case .Spoiler, .CustomEmoji: + return true + default: + return false + } + } + if message.author?.id == accountPeerId { + var optionText = option.text + if optionText.count > 20 { + optionText = optionText.prefix(20) + "…" + } + let resultString = strings.Notification_PollDeletedOptionYou(optionText) + let stringWithRanges = resultString._tuple + let resultAttributedString = NSMutableAttributedString(attributedString: addAttributesToStringWithRanges(stringWithRanges, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes])) + if let optionRange = serviceMessageArgumentRange(index: 0, value: optionText, in: stringWithRanges) { + addServiceMessageTextEntities(optionEntities, to: resultAttributedString, text: optionText, range: optionRange, associatedMedia: message.associatedMedia) + } + attributedString = resultAttributedString + } else { + let peerName = message.author?.compactDisplayTitle ?? "" + var attributes = peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id)]) + attributes[1] = boldAttributes + + var optionText = option.text + if optionText.count > 20 { + optionText = optionText.prefix(20) + "…" + } + let resultString = strings.Notification_PollDeletedOption(peerName, optionText) + let stringWithRanges = resultString._tuple + let resultAttributedString = NSMutableAttributedString(attributedString: addAttributesToStringWithRanges(stringWithRanges, body: bodyAttributes, argumentAttributes: attributes)) + if let optionRange = serviceMessageArgumentRange(index: 1, value: optionText, in: stringWithRanges) { + addServiceMessageTextEntities(optionEntities, to: resultAttributedString, text: optionText, range: optionRange, associatedMedia: message.associatedMedia) + } + attributedString = resultAttributedString + } case .unknown: attributedString = nil } diff --git a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift index a179d739f1..008f97b418 100644 --- a/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift +++ b/submodules/TelegramUI/Components/AttachmentFileController/Sources/AttachmentFileSearchItem.swift @@ -631,7 +631,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon var index: Int32 = 0 if let savedMusic, !savedMusic.isEmpty { - entries.append(.header(title: "SAVED MUSIC", section: section)) + entries.append(.header(title: "PROFILE MUSIC", section: section)) var savedMusic = savedMusic var hasShowMore = false @@ -652,7 +652,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon section += 1 if !messages.isEmpty { - entries.append(.header(title: "SHARED AUDIO", section: section)) + entries.append(.header(title: "YOUR CHATS", section: section)) var messages = messages var hasShowMore = false if messages.count > 4 && !expandedSections.contains(section) { @@ -672,7 +672,7 @@ public final class AttachmentFileSearchContainerNode: SearchDisplayControllerCon index = 0 section += 1 - entries.append(.header(title: "GLOBAL SEARCH", section: section)) + entries.append(.header(title: "PUBLIC CHATS", section: section)) for message in globalMusic { entries.append(.file(index: index, message: message, section: section)) index += 1 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/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index d869bf841a..0939675b93 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -3239,7 +3239,7 @@ private final class GiftViewSheetContent: CombinedComponent { var hasDescriptionButton = false if let uniqueGift { titleString = uniqueGift.title + " **#\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))**" - descriptionText = "\(strings.Gift_Unique_Collectible) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: environment.dateTimeFormat))" + descriptionText = "\(strings.Gift_Unique_Collectible)" for attribute in uniqueGift.attributes { if case let .model(name, _, _, _) = attribute { descriptionText = name @@ -3520,7 +3520,7 @@ private final class GiftViewSheetContent: CombinedComponent { } var descriptionSize = CGSize() - if state.justUpgraded { + if !"".isEmpty, state.justUpgraded { var items: [AnyComponentWithIdentity] = [ AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: "\(strings.Gift_Unique_Collectible) #", font: textFont, color: .white, tintColor: textColor))) ] 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/Images.xcassets/Item List/Icons/AITools.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/Icons/AITools.imageset/Contents.json new file mode 100644 index 0000000000..ab14f3ed29 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/Icons/AITools.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ai.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/Icons/AITools.imageset/ai.pdf b/submodules/TelegramUI/Images.xcassets/Item List/Icons/AITools.imageset/ai.pdf new file mode 100644 index 0000000000..f98924d394 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Item List/Icons/AITools.imageset/ai.pdf differ 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())) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index f3acbcf411..303493f6a9 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2970,6 +2970,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSource = .todo case .copyProtection: mappedSource = .copyProtection + case .aiTools: + mappedSource = .aiTools case let .auth(price): mappedSource = .auth(price) case let .premiumGift(file): @@ -3052,6 +3054,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSubject = .todo case .copyProtection: mappedSubject = .copyProtection + case .aiTools: + mappedSubject = .aiTools case .business: mappedSubject = .business buttonText = presentationData.strings.Chat_EmptyStateIntroFooterPremiumActionButton