From c92a85508359001bb497998f6da007328c913894 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 24 Mar 2023 16:50:15 +0400 Subject: [PATCH 1/7] Cherry-pick some fixes --- .../AuthorizationSequenceController.swift | 3 +- ...heckoutNativeCardEntryControllerNode.swift | 2 +- .../Sources/BotPaymentFieldItemNode.swift | 4 + .../ChatListUI/Sources/ChatContextMenus.swift | 48 +++++++++-- .../Sources/ChatListSearchListPaneNode.swift | 81 +++++++++++++------ .../TabBarChatListFilterController.swift | 33 +++++++- .../ChatItemGalleryFooterContentNode.swift | 32 ++++---- .../Sources/ListMessageFileItemNode.swift | 2 +- .../Sources/MediaGroupsScreen.swift | 2 +- .../Sources/SemanticStatusNode.swift | 15 ++-- .../Reactions/ItemListReactionItem.swift | 12 +-- .../Sources/ThemeCarouselItem.swift | 15 ++-- .../Themes/ThemeSettingsController.swift | 10 +-- .../Data/TelegramEngineData.swift | 55 +++++++++++++ .../ChatTitleView/Sources/ChatTitleView.swift | 5 +- .../Sources/EmojiPagerContentComponent.swift | 63 +-------------- .../TelegramUI/Sources/ChatController.swift | 25 +++--- .../ChatInterfaceStateContextMenus.swift | 4 +- .../Sources/ChatMessageBubbleItemNode.swift | 30 ++++++- .../ChatTextInputAudioRecordingTimeNode.swift | 9 ++- .../CommandMenuChatInputPanelItem.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 26 +++--- .../UrlHandling/Sources/UrlHandling.swift | 3 + .../Sources/WallpaperBackgroundNode.swift | 17 +--- .../Sources/WebSearchController.swift | 23 +++++- .../WebUI/Sources/WebAppController.swift | 7 +- 26 files changed, 341 insertions(+), 187 deletions(-) diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index be8ba1b554..6b4edc58d2 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -320,7 +320,8 @@ public final class AuthorizationSequenceController: NavigationController, MFMail let bold = MarkdownAttributeSet(font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize * 13.0 / 17.0), textColor: self.presentationData.theme.actionSheet.primaryTextColor) if let _ = resetPendingDate { self.actionDisposable.set( - resetLoginEmail(account: self.account, phoneNumber: number, phoneCodeHash: phoneCodeHash).start(error: { [weak self] error in + (resetLoginEmail(account: self.account, phoneNumber: number, phoneCodeHash: phoneCodeHash) + |> deliverOnMainQueue).start(error: { [weak self] error in if let self, case .alreadyInProgress = error { let formattedNumber = formatPhoneNumber(number) let title = NSAttributedString(string: self.presentationData.strings.Login_Email_PremiumRequiredTitle, font: Font.semibold(self.presentationData.listsFontSize.baseDisplaySize), textColor: self.presentationData.theme.actionSheet.primaryTextColor) diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift index 752e4bd0b7..0602b878ef 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryControllerNode.swift @@ -115,7 +115,7 @@ final class BotCheckoutNativeCardEntryControllerNode: ViewControllerTracingNode, sectionItems.append(BotPaymentHeaderItemNode(text: strings.Checkout_NewCard_CardholderNameTitle)) - let cardholderItem = BotPaymentFieldItemNode(title: "", placeholder: strings.Checkout_NewCard_CardholderNamePlaceholder, contentType: .name) + let cardholderItem = BotPaymentFieldItemNode(title: "", placeholder: strings.Checkout_NewCard_CardholderNamePlaceholder, contentType: .asciiName) self.cardholderItem = cardholderItem sectionItems.append(cardholderItem) diff --git a/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift b/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift index ad28dc16e7..41150436ee 100644 --- a/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift +++ b/submodules/BotPaymentsUI/Sources/BotPaymentFieldItemNode.swift @@ -9,6 +9,7 @@ private let titleFont = Font.regular(17.0) enum BotPaymentFieldContentType { case generic case name + case asciiName case phoneNumber case email case address @@ -51,6 +52,9 @@ final class BotPaymentFieldItemNode: BotPaymentItemNode, UITextFieldDelegate { case .generic: break case .name: + self.textField.textField.autocorrectionType = .no + self.textField.textField.keyboardType = .default + case .asciiName: self.textField.textField.autocorrectionType = .no self.textField.textField.keyboardType = .asciiCapable case .address: diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index ecc992b3ad..5af6a04022 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -103,9 +103,10 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch return context.engine.data.get( TelegramEngine.EngineData.Item.Peer.IsContact(id: peer.id), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peer.id), + TelegramEngine.EngineData.Item.NotificationSettings.Global(), TelegramEngine.EngineData.Item.Messages.PeerReadCounters(id: peer.id) ) - |> map { [weak chatListController] isContact, notificationSettings, readCounters -> [ContextMenuItem] in + |> map { [weak chatListController] isContact, notificationSettings, globalNotificationSettings, readCounters -> [ContextMenuItem] in if promoInfo != nil { return [] } @@ -164,8 +165,21 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch } var isMuted = false - if case .muted = notificationSettings.muteState { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { isMuted = true + } else if case .default = notificationSettings.muteState { + if case .user = peer { + isMuted = !globalNotificationSettings.privateChats.enabled + } else if case .legacyGroup = peer { + isMuted = !globalNotificationSettings.groupChats.enabled + } else if case let .channel(channel) = peer { + switch channel.info { + case .group: + isMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + isMuted = !globalNotificationSettings.channels.enabled + } + } } var isUnread = false @@ -406,8 +420,21 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch if !isSavedMessages { var isMuted = false - if case .muted = notificationSettings.muteState { + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { isMuted = true + } else if case .default = notificationSettings.muteState { + if case .user = peer { + isMuted = !globalNotificationSettings.privateChats.enabled + } else if case .legacyGroup = peer { + isMuted = !globalNotificationSettings.groupChats.enabled + } else if case let .channel(channel) = peer { + switch channel.info { + case .group: + isMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + isMuted = !globalNotificationSettings.channels.enabled + } + } } items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { _, f in let _ = (context.engine.peers.togglePeerMuted(peerId: peerId, threadId: nil) @@ -506,9 +533,10 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: return context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), - TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId) + TelegramEngine.EngineData.Item.Peer.ThreadData(id: peerId, threadId: threadId), + TelegramEngine.EngineData.Item.NotificationSettings.Global() ) - |> mapToSignal { peer, peerNotificationSettings, threadData -> Signal<[ContextMenuItem], NoError> in + |> mapToSignal { peer, peerNotificationSettings, threadData, globalNotificationSettings -> Signal<[ContextMenuItem], NoError> in guard case let .channel(channel) = peer else { return .single([]) } @@ -560,9 +588,15 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId: case .unmuted: isMuted = false case .default: - if case .muted = peerNotificationSettings.muteState { - isMuted = true + var peerIsMuted = false + if case let .muted(until) = peerNotificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + peerIsMuted = true + } else if case .default = peerNotificationSettings.muteState { + if case let .channel(channel) = peer, case .group = channel.info { + peerIsMuted = !globalNotificationSettings.groupChats.enabled + } } + isMuted = peerIsMuted } items.append(.action(ContextMenuActionItem(text: isMuted ? strings.ChatList_Context_Unmute : strings.ChatList_Context_Mute, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isMuted ? "Chat/Context Menu/Unmute" : "Chat/Context Menu/Muted"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] c, f in if isMuted { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index a08f64a149..ca4c846ae7 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -38,13 +38,13 @@ private enum ChatListRecentEntryStableId: Hashable { private enum ChatListRecentEntry: Comparable, Identifiable { case topPeers([EnginePeer], PresentationTheme, PresentationStrings) - case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder) + case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, EngineGlobalNotificationSettings) var stableId: ChatListRecentEntryStableId { switch self { case .topPeers: return .topPeers - case let .peer(_, peer, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _): return .peerId(peer.peer.peerId) } } @@ -66,8 +66,8 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } else { return false } - case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder): - if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder { + case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsGlobalNotificationsSettings): + if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsGlobalNotificationsSettings) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsGlobalNotificationsSettings == rhsGlobalNotificationsSettings { return true } else { return false @@ -79,11 +79,11 @@ private enum ChatListRecentEntry: Comparable, Identifiable { switch lhs { case .topPeers: return true - case let .peer(lhsIndex, _, _, _, _, _, _): + case let .peer(lhsIndex, _, _, _, _, _, _, _): switch rhs { case .topPeers: return false - case let .peer(rhsIndex, _, _, _, _, _, _): + case let .peer(rhsIndex, _, _, _, _, _, _, _): return lhsIndex <= rhsIndex } } @@ -101,7 +101,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { gesture?.cancel() } }) - case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder): + case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder, globalNotificationSettings): let primaryPeer: EnginePeer var chatPeer: EnginePeer? let maybeChatPeer = EnginePeer(peer.peer.peers[peer.peer.peerId]!) @@ -185,11 +185,27 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } else { status = .none } - + var isMuted = false if let notificationSettings = peer.notificationSettings { - isMuted = notificationSettings.isRemovedFromTotalUnreadCount(default: false) + if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { + isMuted = true + } else if case .default = notificationSettings.muteState { + if case .user = primaryPeer { + isMuted = !globalNotificationSettings.privateChats.enabled + } else if case .legacyGroup = primaryPeer { + isMuted = !globalNotificationSettings.groupChats.enabled + } else if case let .channel(channel) = primaryPeer { + switch channel.info { + case .group: + isMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + isMuted = !globalNotificationSettings.channels.enabled + } + } + } } + var badge: ContactsPeerItemBadge? if peer.unreadCount > 0 { badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active) @@ -1374,7 +1390,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { context.engine.contacts.searchLocalPeers(query: query.lowercased()), fixedOrRemovedRecentlySearchedPeers ) - |> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional], [EnginePeer.Id: Int], [EngineRenderedPeer], Set), NoError> in + |> mapToSignal { local, allRecentlySearched -> Signal<([EnginePeer.Id: Optional], [EnginePeer.Id: Int], [EngineRenderedPeer], Set, EngineGlobalNotificationSettings), NoError> in let recentlySearched = allRecentlySearched.filter { peer in guard let peer = peer.peer.peer else { return false @@ -1408,25 +1424,37 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { peerIds.map { peerId -> TelegramEngine.EngineData.Item.Messages.PeerUnreadCount in return TelegramEngine.EngineData.Item.Messages.PeerUnreadCount(id: peerId) } - ) + ), + TelegramEngine.EngineData.Item.NotificationSettings.Global() ) - |> map { notificationSettings, unreadCounts in - return (notificationSettings, unreadCounts, peers, Set(recentlySearched.map(\.peer.peerId))) + |> map { notificationSettings, unreadCounts, globalNotificationSettings in + return (notificationSettings, unreadCounts, peers, Set(recentlySearched.map(\.peer.peerId)), globalNotificationSettings) } } - |> map { notificationSettings, unreadCounts, peers, recentlySearchedPeerIds -> (peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set) in + |> map { notificationSettings, unreadCounts, peers, recentlySearchedPeerIds, globalNotificationSettings -> (peers: [EngineRenderedPeer], unread: [EnginePeer.Id: (Int32, Bool)], recentlySearchedPeerIds: Set) in var unread: [EnginePeer.Id: (Int32, Bool)] = [:] for peer in peers { - var isMuted: Bool = false - if let nofiticationSettings = notificationSettings[peer.peerId] { - switch nofiticationSettings?.muteState { - case .muted: + var isMuted = false + if let peerNotificationSettings = notificationSettings[peer.peerId], let peerNotificationSettings { + if case let .muted(until) = peerNotificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { isMuted = true - default: - break + } else if case .default = peerNotificationSettings.muteState { + if let peer = peer.peer { + if case .user = peer { + isMuted = !globalNotificationSettings.privateChats.enabled + } else if case .legacyGroup = peer { + isMuted = !globalNotificationSettings.groupChats.enabled + } else if case let .channel(channel) = peer { + switch channel.info { + case .group: + isMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + isMuted = !globalNotificationSettings.channels.enabled + } + } + } } } - let unreadCount = unreadCounts[peer.peerId] if let unreadCount = unreadCount, unreadCount > 0 { unread[peer.peerId] = (Int32(unreadCount), isMuted) @@ -2399,8 +2427,13 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } |> distinctUntilChanged - var recentItems = combineLatest(hasRecentPeers, fixedRecentlySearchedPeers, presentationDataPromise.get()) - |> mapToSignal { hasRecentPeers, peers, presentationData -> Signal<[ChatListRecentEntry], NoError> in + var recentItems = combineLatest( + hasRecentPeers, + fixedRecentlySearchedPeers, + presentationDataPromise.get(), + context.engine.data.subscribe(TelegramEngine.EngineData.Item.NotificationSettings.Global()) + ) + |> mapToSignal { hasRecentPeers, peers, presentationData, globalNotificationSettings -> Signal<[ChatListRecentEntry], NoError> in var entries: [ChatListRecentEntry] = [] if !peersFilter.contains(.onlyGroups) { if hasRecentPeers { @@ -2419,7 +2452,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { } peerIds.insert(peer.id) - entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder)) + entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, globalNotificationSettings)) index += 1 } } diff --git a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift index a8e6776991..6120890cf6 100644 --- a/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift +++ b/submodules/ChatListUI/Sources/TabBarChatListFilterController.swift @@ -32,8 +32,11 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL for groupId in additionalGroupIds { unreadCountItems.append(.totalInGroup(groupId)) } + + let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.globalNotifications])) let unreadKey: PostboxViewKey = .unreadCounts(items: unreadCountItems) var keys: [PostboxViewKey] = [] + keys.append(globalNotificationsKey) keys.append(unreadKey) for peerId in additionalPeerIds { keys.append(.basicPeer(peerId)) @@ -45,6 +48,13 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL return (0, []) } + var globalNotificationSettings: GlobalNotificationSettingsSet + if let settingsView = view.views[globalNotificationsKey] as? PreferencesView, let settings = settingsView.values[PreferencesKeys.globalNotifications]?.get(GlobalNotificationSettings.self) { + globalNotificationSettings = settings.effective + } else { + globalNotificationSettings = GlobalNotificationSettings.defaultSettings.effective + } + var result: [(ChatListFilter, Int, Bool)] = [] var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool, PeerGroupId?, Bool)] = [:] @@ -66,7 +76,28 @@ public func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatL peerCount = max(1, peerCount) } - if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState { + var isMuted = false + if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { + if case .muted = notificationSettings.muteState { + isMuted = true + } else if case .default = notificationSettings.muteState { + if let peer = peerView.peer { + if peer is TelegramUser { + isMuted = !globalNotificationSettings.privateChats.enabled + } else if peer is TelegramGroup { + isMuted = !globalNotificationSettings.groupChats.enabled + } else if let channel = peer as? TelegramChannel { + switch channel.info { + case .group: + isMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + isMuted = !globalNotificationSettings.channels.enabled + } + } + } + } + } + if isMuted { peerTagAndCount[peerId] = (tag, peerCount, false, peerView.groupId, true) } else { peerTagAndCount[peerId] = (tag, peerCount, true, peerView.groupId, false) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 7bb59da58d..892d0d3bb2 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -1328,16 +1328,19 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } else if let file = m as? TelegramMediaFile { subject = .media(.message(message: MessageReference(messages[0]._asMessage()), media: file)) if file.isAnimated { - preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in - if let strongSelf = self { - let message = messages[0] - - let context = strongSelf.context - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controllerInteraction = strongSelf.controllerInteraction - let _ = (toggleGifSaved(account: context.account, fileReference: .message(message: MessageReference(message._asMessage()), media: file), saved: true) - |> deliverOnMainQueue).start(next: { result in - switch result { + if messages[0].id.peerId.namespace == Namespaces.Peer.SecretChat { + preferredAction = .default + } else { + preferredAction = .custom(action: ShareControllerAction(title: presentationData.strings.Preview_SaveGif, action: { [weak self] in + if let strongSelf = self { + let message = messages[0] + + let context = strongSelf.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controllerInteraction = strongSelf.controllerInteraction + let _ = (toggleGifSaved(account: context.account, fileReference: .message(message: MessageReference(message._asMessage()), media: file), saved: true) + |> deliverOnMainQueue).start(next: { result in + switch result { case .generic: controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_gif", scale: 0.075, colors: [:], title: nil, text: presentationData.strings.Gallery_GifSaved, customUndoText: nil, timeout: nil), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), nil) case let .limitExceeded(limit, premiumLimit): @@ -1356,10 +1359,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } return false }), nil) - } - }) - } - })) + } + }) + } + })) + } } else if file.mimeType.hasPrefix("image/") { preferredAction = .saveToCameraRoll actionCompletionText = strongSelf.presentationData.strings.Gallery_ImageSaved diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 37bd422bfa..a2c1817353 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -1195,7 +1195,7 @@ public final class ListMessageFileItemNode: ListMessageNode { strongSelf.currentIconImage = iconImage if let updateIconImageSignal, let iconImage, case .albumArt = iconImage { - strongSelf.iconStatusNode.setBackgroundImage(updateIconImageSignal) + strongSelf.iconStatusNode.setBackgroundImage(updateIconImageSignal, size: CGSize(width: 40.0, height: 40.0)) } if let iconImageApply = iconImageApply { diff --git a/submodules/MediaPickerUI/Sources/MediaGroupsScreen.swift b/submodules/MediaPickerUI/Sources/MediaGroupsScreen.swift index bf94a0446c..2d845df038 100644 --- a/submodules/MediaPickerUI/Sources/MediaGroupsScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaGroupsScreen.swift @@ -234,7 +234,7 @@ public final class MediaGroupsScreen: ViewController { albums.append(collection) } } - state.albums.enumerateObjects { collection, _, _ in + state.albums.enumerateObjects(options: [.reverse]) { collection, _, _ in albums.append(collection) } entries.append(.albums(self.presentationData.theme, albums)) diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index 440082d668..37fd617d91 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -864,16 +864,19 @@ public final class SemanticStatusNode: ASControlNode { } } - public func setBackgroundImage(_ image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>) { + public func setBackgroundImage(_ image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>, size: CGSize) { let start = CACurrentMediaTime() - self.disposable = combineLatest(queue: Queue.mainQueue(), image, self.hasLayoutPromise.get()).start(next: { [weak self] transform, ready in + let imageSignal: Signal = image + |> map { transform -> UIImage? in + let context = transform(TransformImageArguments(corners: ImageCorners(radius: size.width / 2.0), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets())) + return context?.generateImage() + } + self.disposable = combineLatest(queue: Queue.mainQueue(), imageSignal, self.hasLayoutPromise.get()).start(next: { [weak self] image, ready in guard let strongSelf = self, ready else { return } - let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets())) - let previousAppearanceContext = strongSelf.appearanceContext - strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage()) + strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(image) if CACurrentMediaTime() - start > 0.3 { strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {}) @@ -915,7 +918,7 @@ public final class SemanticStatusNode: ASControlNode { self.displaysAsynchronously = true if let image { - self.setBackgroundImage(image) + self.setBackgroundImage(image, size: CGSize(width: 44.0, height: 44.0)) } } diff --git a/submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift b/submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift index d833e77578..4e244e9371 100644 --- a/submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ItemListReactionItem.swift @@ -45,13 +45,13 @@ public class ItemListReactionItem: ListViewItem, ItemListItem { public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { - let node = ItemListReactionItemNode() - let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) - - node.contentSize = layout.contentSize - node.insets = layout.insets - Queue.mainQueue().async { + let node = ItemListReactionItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + completion(node, { return (nil, { _ in apply() }) }) diff --git a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift b/submodules/SettingsUI/Sources/ThemeCarouselItem.swift index 80fb839bf8..7061318417 100644 --- a/submodules/SettingsUI/Sources/ThemeCarouselItem.swift +++ b/submodules/SettingsUI/Sources/ThemeCarouselItem.swift @@ -711,11 +711,10 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { strongSelf.item = item strongSelf.layoutParams = params - strongSelf.listNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor - + if strongSelf.backgroundNode.supernode == nil { strongSelf.containerNode.insertSubnode(strongSelf.backgroundNode, at: 0) } @@ -828,10 +827,11 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { self.snapshotView = snapshotView } - self.listNode.forEachVisibleItemNode { node in + self.listNode.enumerateItemNodes { node in if let node = node as? ThemeCarouselThemeItemIconNode { node.prepareCrossfadeTransition() } + return true } } @@ -839,22 +839,23 @@ class ThemeCarouselThemeItemNode: ListViewItemNode, ItemListItemNode { guard self.snapshotView?.layer.animationKeys()?.isEmpty ?? true else { return } - + var views: [UIView] = [] if let snapshotView = self.snapshotView { views.append(snapshotView) self.snapshotView = nil } - - self.listNode.forEachVisibleItemNode { node in + + self.listNode.enumerateItemNodes { node in if let node = node as? ThemeCarouselThemeItemIconNode { if let snapshotView = node.snapshotView { views.append(snapshotView) node.snapshotView = nil } } + return true } - + UIView.animate(withDuration: 0.3, animations: { for view in views { view.alpha = 0.0 diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index 1009b43d5b..93eab9cfd5 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -1292,9 +1292,7 @@ public final class ThemeSettingsCrossfadeController: ViewController { public init(view: UIView? = nil, topOffset: CGFloat? = nil, bottomOffset: CGFloat? = nil, leftOffset: CGFloat? = nil, sideInset: CGFloat = 0.0) { if let view = view { - if var leftOffset = leftOffset { - leftOffset += UIScreenPixel - + if let leftOffset = leftOffset { if let view = view.snapshotView(afterScreenUpdates: false) { let clipView = UIView() clipView.clipsToBounds = true @@ -1306,13 +1304,13 @@ public final class ThemeSettingsCrossfadeController: ViewController { if let topOffset = topOffset, let bottomOffset = bottomOffset { var frame = view.frame frame.origin.y = topOffset - frame.size.width = leftOffset + frame.size.width = leftOffset + sideInset frame.size.height = bottomOffset - topOffset clipView.frame = frame frame = view.frame frame.origin.y = -topOffset - frame.size.width = leftOffset + frame.size.width = leftOffset + sideInset frame.size.height = bottomOffset view.frame = frame } @@ -1322,7 +1320,7 @@ public final class ThemeSettingsCrossfadeController: ViewController { } if sideInset > 0.0 { - if let view = view.snapshotView(afterScreenUpdates: false) { + if let view = view.snapshotView(afterScreenUpdates: false), leftOffset == nil { let clipView = UIView() clipView.clipsToBounds = true clipView.addSubview(view) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift index 79a6fca323..61d44d999b 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Data/TelegramEngineData.swift @@ -220,6 +220,40 @@ public extension TelegramEngine { } } + public func subscribe< + T0: TelegramEngineDataItem, + T1: TelegramEngineDataItem, + T2: TelegramEngineDataItem, + T3: TelegramEngineDataItem + >( + _ t0: T0, + _ t1: T1, + _ t2: T2, + _ t3: T3 + ) -> Signal< + ( + T0.Result, + T1.Result, + T2.Result, + T3.Result + ), + NoError> { + return self._subscribe(items: [ + t0 as! AnyPostboxViewDataItem, + t1 as! AnyPostboxViewDataItem, + t2 as! AnyPostboxViewDataItem, + t3 as! AnyPostboxViewDataItem + ]) + |> map { results -> (T0.Result, T1.Result, T2.Result, T3.Result) in + return ( + results[0] as! T0.Result, + results[1] as! T1.Result, + results[2] as! T2.Result, + results[3] as! T3.Result + ) + } + } + public func get< T0: TelegramEngineDataItem, @@ -253,5 +287,26 @@ public extension TelegramEngine { NoError> { return self.subscribe(t0, t1, t2) |> take(1) } + + public func get< + T0: TelegramEngineDataItem, + T1: TelegramEngineDataItem, + T2: TelegramEngineDataItem, + T3: TelegramEngineDataItem + >( + _ t0: T0, + _ t1: T1, + _ t2: T2, + _ t3: T3 + ) -> Signal< + ( + T0.Result, + T1.Result, + T2.Result, + T3.Result + ), + NoError> { + return self.subscribe(t0, t1, t2, t3) |> take(1) + } } } diff --git a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift index d8eff66339..73684b09d5 100644 --- a/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Components/ChatTitleView/Sources/ChatTitleView.swift @@ -368,15 +368,12 @@ public final class ChatTitleView: UIView, NavigationBarTitleView { var inputActivitiesAllowed = true if let titleContent = self.titleContent { switch titleContent { - case let .peer(peerView, _, _, isScheduledMessages, _, customMessageCount, _): + case let .peer(peerView, _, _, isScheduledMessages, _, _, _): if let peer = peerViewMainPeer(peerView) { if peer.id == self.context.account.peerId || isScheduledMessages || peer.id.isReplies { inputActivitiesAllowed = false } } - if customMessageCount != nil { - inputActivitiesAllowed = false - } case .replyThread: inputActivitiesAllowed = true default: diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 0b62234a2f..6bb3cfc864 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -8051,7 +8051,6 @@ public final class EmojiPagerContentComponent: Component { searchCategories ) |> map { view, hasPremium, featuredStickerPacks, featuredStickersConfiguration, dismissedTrendingStickerPacks, peerSpecificPack, searchCategories -> EmojiPagerContentComponent in - let actuallyHasPremium = hasPremium let hasPremium = forceHasPremium || hasPremium struct ItemGroup { var supergroupId: AnyHashable @@ -8070,14 +8069,11 @@ public final class EmojiPagerContentComponent: Component { var savedStickers: OrderedItemListView? var recentStickers: OrderedItemListView? - var cloudPremiumStickers: OrderedItemListView? for orderedView in view.orderedItemListsViews { if orderedView.collectionId == Namespaces.OrderedItemList.CloudRecentStickers { recentStickers = orderedView } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudSavedStickers { savedStickers = orderedView - } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudAllPremiumStickers { - cloudPremiumStickers = orderedView } } @@ -8225,64 +8221,7 @@ public final class EmojiPagerContentComponent: Component { } } } - - var premiumStickers: [StickerPackItem] = [] - if hasPremium { - for entry in view.entries { - guard let item = entry.item as? StickerPackItem else { - continue - } - - if item.file.isPremiumSticker { - premiumStickers.append(item) - } - } - - if let cloudPremiumStickers = cloudPremiumStickers, !cloudPremiumStickers.items.isEmpty, actuallyHasPremium { - premiumStickers.append(contentsOf: cloudPremiumStickers.items.compactMap { item -> StickerPackItem? in guard let item = item.contents.get(RecentMediaItem.self) else { - return nil - } - return StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: item.media, indexKeys: []) - }) - } - } - - if !premiumStickers.isEmpty { - var processedIds = Set() - for item in premiumStickers { - if isPremiumDisabled && item.file.isPremiumSticker { - continue - } - if processedIds.contains(item.file.fileId) { - continue - } - processedIds.insert(item.file.fileId) - - var tintMode: Item.TintMode = .none - if item.file.isCustomTemplateEmoji { - tintMode = .primary - } - - let animationData = EntityKeyboardAnimationData(file: item.file) - let resultItem = EmojiPagerContentComponent.Item( - animationData: animationData, - content: .animation(animationData), - itemFile: item.file, - subgroupId: nil, - icon: .none, - tintMode: tintMode - ) - - let groupId = "premium" - if let groupIndex = itemGroupIndexById[groupId] { - itemGroups[groupIndex].items.append(resultItem) - } else { - itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: strings.EmojiInput_SectionTitlePremiumStickers, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) - } - } - } - + var avatarPeer: EnginePeer? if let peerSpecificPack = peerSpecificPack { avatarPeer = peerSpecificPack.peer diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 0e3ca261b1..c218c1d6f8 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -361,7 +361,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var recentlyUsedInlineBotsDisposable: Disposable? private var unpinMessageDisposable: MetaDisposable? - + private let typingActivityPromise = Promise(false) private var inputActivityDisposable: Disposable? private var recordingActivityValue: ChatRecordingActivity = .none @@ -3875,16 +3875,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = strongSelf.presentVoiceMessageDiscardAlert(action: { let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) |> mapToSignal { message -> Signal<(EngineMessage.Id, Int32?)?, NoError> in - if let message = message, let sourceMessageId = message.forwardInfo?.sourceMessageId { - return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: sourceMessageId.peerId)) + if let message { + return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: message.id.peerId)) |> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in - return (sourceMessageId, statsDatacenterId) + return (message.id, statsDatacenterId) } } else { - return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: id.peerId)) - |> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in - return (id, statsDatacenterId) - } + return .complete() } } |> deliverOnMainQueue).start(next: { [weak self] messageIdAndStatsDatacenterId in @@ -4106,7 +4103,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true) } - let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, isInline: false, isSimple: false) + let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: true, fromAttachMenu: false, isInline: false, isSimple: false) let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in self?.openUrl(url, concealed: true, forceExternal: true) }, getInputContainerNode: { [weak self] in @@ -4161,7 +4158,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - let params = WebAppParameters(peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, isInline: isInline, isSimple: true) + let params = WebAppParameters(peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, fromMenu: false, fromAttachMenu: false, isInline: isInline, isSimple: true) let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in self?.openUrl(url, concealed: true, forceExternal: true) }, requestSwitchInline: { [weak self] query, chatTypes, completion in @@ -4201,7 +4198,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false, isInline: false, isSimple: false) + let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, fromMenu: false, fromAttachMenu: false, isInline: false, isSimple: false) let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in self?.openUrl(url, concealed: true, forceExternal: true) }, completion: { [weak self] in @@ -12704,7 +12701,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let strongSelf = self else { return } - let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false) + let params = WebAppParameters(peerId: peerId, botId: peerId, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, fromMenu: false, fromAttachMenu: false, isInline: false, isSimple: false) let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url in self?.openUrl(url, concealed: true, forceExternal: true) }, completion: { [weak self] in @@ -13224,10 +13221,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } case let .app(bot, botName, _): var payload: String? + var fromAttachMenu = true if case let .bot(_, botPayload, _) = subject { payload = botPayload + fromAttachMenu = false } - let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, isInline: false, isSimple: false) + let params = WebAppParameters(peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, fromMenu: false, fromAttachMenu: fromAttachMenu, isInline: false, isSimple: false) let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageId, threadId: strongSelf.chatLocation.threadId) controller.openUrl = { [weak self] url in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 36378e61d9..56421f8581 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1514,7 +1514,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState break } } - if let file = media as? TelegramMediaFile, !isCopyProtected { + if message.id.peerId.namespace == Namespaces.Peer.SecretChat { + + } else if let file = media as? TelegramMediaFile, !isCopyProtected { if file.isVideo { if file.isAnimated && !file.isVideoSticker { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveGif, icon: { theme in diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index fa3012d9bc..731862597a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -3540,9 +3540,33 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode var mediaMessage: Message? var forceOpen = false if let item = self.item { - for media in item.message.media { - if let file = media as? TelegramMediaFile, file.duration != nil { - mediaMessage = item.message + if case .group = item.content { + var message: Message? = item.content.firstMessage + loop: for contentNode in self.contentNodes { + if !(contentNode is ChatMessageTextBubbleContentNode) { + continue loop + } + let convertedNodeFrame = contentNode.view.convert(contentNode.bounds, to: self.view).insetBy(dx: 0.0, dy: -10.0) + if !convertedNodeFrame.contains(location) { + continue loop + } + if contentNode is ChatMessageEventLogPreviousMessageContentNode { + } else { + message = contentNode.item?.message + } + } + if let message { + for media in message.media { + if let file = media as? TelegramMediaFile, file.duration != nil { + mediaMessage = message + } + } + } + } else { + for media in item.message.media { + if let file = media as? TelegramMediaFile, file.duration != nil { + mediaMessage = item.message + } } } if mediaMessage == nil { diff --git a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift index ba8dde0a1d..7905048a82 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputAudioRecordingTimeNode.swift @@ -113,7 +113,7 @@ final class ChatTextInputAudioRecordingTimeNode: ASDisplayNode { override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { let makeLayout = TextNode.asyncLayout(self.textNode) - let (size, apply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "00:00,00", font: Font.regular(15.0), textColor: theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 200.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (size, apply) = makeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "0:00:00,00", font: Font.regular(15.0), textColor: theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: 200.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let _ = apply() self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 1.0 + UIScreenPixel), size: size.size) return size.size @@ -135,7 +135,12 @@ final class ChatTextInputAudioRecordingTimeNode: ASDisplayNode { if let parameters = parameters as? ChatTextInputAudioRecordingTimeNodeParameters { let currentAudioDurationSeconds = Int(parameters.timestamp) let currentAudioDurationMilliseconds = Int(parameters.timestamp * 100.0) % 100 - let text = String(format: "%d:%02d,%02d", currentAudioDurationSeconds / 60, currentAudioDurationSeconds % 60, currentAudioDurationMilliseconds) + let text: String + if currentAudioDurationSeconds >= 60 * 60 { + text = String(format: "%d:%02d:%02d,%02d", currentAudioDurationSeconds / 3600, currentAudioDurationSeconds / 60 % 60, currentAudioDurationSeconds % 60, currentAudioDurationMilliseconds) + } else { + text = String(format: "%d:%02d,%02d", currentAudioDurationSeconds / 60, currentAudioDurationSeconds % 60, currentAudioDurationMilliseconds) + } let string = NSAttributedString(string: text, font: textFont, textColor: parameters.theme.chat.inputPanel.primaryTextColor) string.draw(at: CGPoint()) } diff --git a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift index 49855b427a..5e03f5aa11 100644 --- a/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift +++ b/submodules/TelegramUI/Sources/CommandMenuChatInputPanelItem.swift @@ -210,7 +210,7 @@ final class CommandMenuChatInputPanelItemNode: ListViewItemNode { let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: textString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - rightInset - 130.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (commandLayout, commandApply) = makeCommandLayout(TextNodeLayoutArguments(attributedString: commandString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: 120.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let (commandLayout, commandApply) = makeCommandLayout(TextNodeLayoutArguments(attributedString: commandString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - rightInset - textLayout.size.width - 16.0, height: 100.0), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(CommandMenuChatInputPanelItemNode.itemHeight, textLayout.size.height + 14.0)), insets: UIEdgeInsets()) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 8bc6db0b8f..6e48b0f56f 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -4908,15 +4908,13 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }))) } - let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: user, cachedData: strongSelf.data?.cachedData) + let clearPeerHistory = ClearPeerHistory(context: strongSelf.context, peer: user, chatPeer: chatPeer, cachedData: strongSelf.data?.cachedData) if clearPeerHistory.canClearForMyself != nil || clearPeerHistory.canClearForEveryone != nil { - if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser { - items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in - generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) - }, action: { c, _ in - self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) - }))) - } + items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.PeerInfo_ClearMessages, icon: { theme in + generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ClearMessages"), color: theme.contextMenu.primaryColor) + }, action: { c, _ in + self?.openClearHistory(contextController: c, clearPeerHistory: clearPeerHistory, peer: user, chatPeer: user) + }))) } if strongSelf.peerId.namespace == Namespaces.Peer.CloudUser && user.botInfo == nil && !user.flags.contains(.isSupport) { @@ -5560,8 +5558,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }))) } - if let _ = clearPeerHistory.canClearForMyself { - let text: String = self.presentationData.strings.Conversation_DeleteMessagesForMe + if let canClearForMyself = clearPeerHistory.canClearForMyself { + let text: String + switch canClearForMyself { + case .secretChat: + text = self.presentationData.strings.Conversation_DeleteMessagesFor(EnginePeer(chatPeer).compactDisplayTitle).string + default: + text = self.presentationData.strings.Conversation_DeleteMessagesForMe + } subItems.append(.action(ContextMenuActionItem(text: text, textColor: .destructive, icon: { _ in return nil @@ -6012,7 +6016,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } private func openChatWithClearedHistory(type: InteractiveHistoryClearingType) { - guard let peer = self.data?.peer, let navigationController = self.controller?.navigationController as? NavigationController else { + guard let peer = self.data?.chatPeer, let navigationController = self.controller?.navigationController as? NavigationController else { return } diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 532ea2fe32..b4dd071d1e 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -117,6 +117,9 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { if !pathComponents.isEmpty { pathComponents.removeFirst() } + if let lastComponent = pathComponents.last, lastComponent.isEmpty { + pathComponents.removeLast() + } if !pathComponents.isEmpty && !pathComponents[0].isEmpty { let peerName: String = pathComponents[0] if pathComponents.count == 1 { diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index cdd22ea5f5..0095b1c454 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -154,6 +154,7 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver } private var isUsingSoftlight: Bool = false + private var useFilter: Bool = false var suspendCompositionUpdates: Bool = false private var needsCompositionUpdate: Bool = false @@ -172,10 +173,11 @@ private final class EffectImageLayer: SimpleLayer, GradientBackgroundPatternOver useSoftlight = true useFilter = false } - if self.isUsingSoftlight != useSoftlight { + if self.isUsingSoftlight != useSoftlight || self.useFilter != useFilter { self.isUsingSoftlight = useSoftlight + self.useFilter = useFilter - if self.isUsingSoftlight && useFilter { + if self.isUsingSoftlight && self.useFilter { self.compositingFilter = "softLightBlendMode" } else { self.compositingFilter = nil @@ -842,10 +844,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } private static var cachedSharedPattern: (PatternKey, UIImage)? - //private var inlineAnimationNodes: [(AnimatedStickerNode, CGPoint)] = [] - //private let hierarchyTrackingLayer = HierarchyTrackingLayer() - //private var activateInlineAnimationTimer: SwiftSignalKit.Timer? - private let _isReady = ValuePromise(false, ignoreRepeated: true) var isReady: Signal { return self._isReady.get() @@ -1308,13 +1306,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: transition) - - /*for (animationNode, relativePosition) in self.inlineAnimationNodes { - let sizeNorm = CGSize(width: 1440, height: 2960) - let animationSize = CGSize(width: 512.0 / sizeNorm.width * size.width, height: 512.0 / sizeNorm.height * size.height) - animationNode.frame = CGRect(origin: CGPoint(x: relativePosition.x / sizeNorm.width * size.width, y: relativePosition.y / sizeNorm.height * size.height), size: animationSize) - animationNode.updateLayout(size: animationNode.frame.size) - }*/ if isFirstLayout && !self.frame.isEmpty { self.updateScale() diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 7d8bfe60a7..062f6272c9 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -122,6 +122,9 @@ public final class WebSearchController: ViewController { public var attemptItemSelection: (ChatContextResult) -> Bool = { _ in return true } + private var searchQueryPromise = ValuePromise() + private var searchQueryDisposable: Disposable? + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: EngineConfiguration.SearchBots, mode: WebSearchControllerMode, activateOnDisplay: Bool = true) { self.context = context self.mode = mode @@ -195,7 +198,7 @@ public final class WebSearchController: ViewController { self.navigationContentNode = navigationContentNode navigationContentNode.setQueryUpdated { [weak self] query in if let strongSelf = self, strongSelf.isNodeLoaded { - strongSelf.updateSearchQuery(query) + strongSelf.searchQueryPromise.set(query) strongSelf.searchingUpdated(!query.isEmpty) } } @@ -288,6 +291,23 @@ public final class WebSearchController: ViewController { } }) } + + let throttledSearchQuery = self.searchQueryPromise.get() + |> mapToSignal { query -> Signal in + if !query.isEmpty { + return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + |> then(.single(query)) + } else { + return .single(query) + } + } + + self.searchQueryDisposable = (throttledSearchQuery + |> deliverOnMainQueue).start(next: { [weak self] query in + if let self { + self.updateSearchQuery(query) + } + }) } required public init(coder aDecoder: NSCoder) { @@ -298,6 +318,7 @@ public final class WebSearchController: ViewController { self.disposable?.dispose() self.resultsDisposable.dispose() self.selectionDisposable?.dispose() + self.searchQueryDisposable?.dispose() } public func cancel() { diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 1b29b48576..7797e366b3 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -134,6 +134,7 @@ public struct WebAppParameters { let buttonText: String? let keepAliveSignal: Signal? let fromMenu: Bool + let fromAttachMenu: Bool let isInline: Bool let isSimple: Bool @@ -147,6 +148,7 @@ public struct WebAppParameters { buttonText: String?, keepAliveSignal: Signal?, fromMenu: Bool, + fromAttachMenu: Bool, isInline: Bool, isSimple: Bool ) { @@ -159,6 +161,7 @@ public struct WebAppParameters { self.buttonText = buttonText self.keepAliveSignal = keepAliveSignal self.fromMenu = fromMenu + self.fromAttachMenu = fromAttachMenu self.isInline = isInline self.isSimple = isSimple } @@ -656,7 +659,7 @@ public final class WebAppController: ViewController, AttachmentContainable { self.handleSendData(data: eventData) } case "web_app_setup_main_button": - if let webView = self.webView, !webView.didTouchOnce && controller.url == nil { + if let webView = self.webView, !webView.didTouchOnce && controller.url == nil && controller.fromAttachMenu { self.delayedScriptMessage = message } else if let json = json { if var isVisible = json["is_visible"] as? Bool { @@ -1058,6 +1061,7 @@ public final class WebAppController: ViewController, AttachmentContainable { private let payload: String? private let buttonText: String? private let fromMenu: Bool + private let fromAttachMenu: Bool private let isInline: Bool private let isSimple: Bool private let keepAliveSignal: Signal? @@ -1083,6 +1087,7 @@ public final class WebAppController: ViewController, AttachmentContainable { self.payload = params.payload self.buttonText = params.buttonText self.fromMenu = params.fromMenu + self.fromAttachMenu = params.fromAttachMenu self.isInline = params.isInline self.isSimple = params.isSimple self.keepAliveSignal = params.keepAliveSignal From 158f92cf4e27682addc89ae91ac11c8757738c74 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 24 Mar 2023 17:01:39 +0400 Subject: [PATCH 2/7] Cherry-pick more fixes --- .../AvatarNode/Sources/PeerAvatar.swift | 2 +- .../BrowserUI/Sources/BrowserWebContent.swift | 5 +- .../Sources/CallListControllerNode.swift | 9 +- .../Sources/CallListNodeEntries.swift | 2 +- .../Sources/ChatListController.swift | 8 +- .../Sources/Node/ChatListItemStrings.swift | 8 +- ...SendMessageActionSheetControllerNode.swift | 33 +--- .../Sources/CreatePollController.swift | 6 +- .../ContextControllerActionsStackNode.swift | 2 +- .../ContainedViewLayoutTransition.swift | 8 +- .../Sources/ItemListPeerItem.swift | 7 +- .../Sources/LocationSearchContainerNode.swift | 2 +- .../MtProtoKit/Sources/MTApiEnvironment.m | 4 + .../ChannelBannedMemberController.swift | 41 +++- .../ChannelMembersSearchContainerNode.swift | 8 + .../ChannelPermissionsController.swift | 4 + .../Sources/TelegramBaseController.swift | 8 +- .../GroupCallNavigationAccessoryPanel.swift | 12 ++ .../Sources/PresentationGroupCall.swift | 12 +- .../Sources/State/AccountStateManager.swift | 5 +- .../State/ManagedConfigurationUpdates.swift | 4 +- .../State/UserLimitsConfiguration.swift | 5 + .../SyncCore_LimitsConfiguration.swift | 24 +-- .../Data/ConfigurationData.swift | 29 +-- .../TelegramEngine/Messages/ChatList.swift | 9 +- .../Peers/TogglePeerChatPinned.swift | 3 +- .../Sources/ForumCreateTopicScreen.swift | 59 ++++-- .../TelegramUI/Sources/AppDelegate.swift | 8 +- .../TelegramUI/Sources/ChatController.swift | 162 ++++++++-------- .../ChatMediaInputStickerGridItem.swift | 2 +- ...MessageInstantVideoBubbleContentNode.swift | 6 - ...atMessageInteractiveInstantVideoNode.swift | 6 +- .../ChatMessageInteractiveMediaNode.swift | 18 +- .../Sources/ChatMessageNotificationItem.swift | 17 +- .../Sources/PeerInfo/PeerInfoData.swift | 71 +++++-- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 26 +-- .../Sources/PeerInfo/PeerInfoScreen.swift | 176 ++++++++---------- .../Sources/PeerMessagesMediaPlaylist.swift | 58 +++--- .../TelegramUI/Sources/PrefetchManager.swift | 8 +- .../Sources/WebSearchController.swift | 2 +- submodules/rlottie/LottieInstance.mm | 2 +- 41 files changed, 468 insertions(+), 413 deletions(-) diff --git a/submodules/AvatarNode/Sources/PeerAvatar.swift b/submodules/AvatarNode/Sources/PeerAvatar.swift index 7d63152342..b022dcd08d 100644 --- a/submodules/AvatarNode/Sources/PeerAvatar.swift +++ b/submodules/AvatarNode/Sources/PeerAvatar.swift @@ -89,7 +89,7 @@ public func peerAvatarCompleteImage(account: Account, peer: EnginePeer, forcePro let clipStyle: AvatarNodeClipStyle if round { - if case let .channel(channel) = peer, channel.flags.contains(.isForum) { + if case let .channel(channel) = peer, channel.isForum { clipStyle = .roundedRect } else { clipStyle = .round diff --git a/submodules/BrowserUI/Sources/BrowserWebContent.swift b/submodules/BrowserUI/Sources/BrowserWebContent.swift index 0d4d13012d..bd4f801433 100644 --- a/submodules/BrowserUI/Sources/BrowserWebContent.swift +++ b/submodules/BrowserUI/Sources/BrowserWebContent.swift @@ -26,9 +26,8 @@ final class BrowserWebContent: UIView, BrowserContent, UIScrollViewDelegate { let configuration = WKWebViewConfiguration() self.webView = WKWebView(frame: CGRect(), configuration: configuration) - if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - self.webView.allowsLinkPreview = false - } + self.webView.allowsLinkPreview = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { self.webView.scrollView.contentInsetAdjustmentBehavior = .never } diff --git a/submodules/CallListUI/Sources/CallListControllerNode.swift b/submodules/CallListUI/Sources/CallListControllerNode.swift index 9579a547a3..da0a55f504 100644 --- a/submodules/CallListUI/Sources/CallListControllerNode.swift +++ b/submodules/CallListUI/Sources/CallListControllerNode.swift @@ -690,13 +690,13 @@ final class CallListControllerNode: ASDisplayNode { let alpha: CGFloat = isHidden ? 0.0 : 1.0 let previousAlpha = self.emptyTextNode.alpha self.emptyTextNode.alpha = alpha - self.emptyTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) if previousAlpha.isZero && !alpha.isZero { self.emptyAnimationNode.visibility = true } self.emptyAnimationNode.alpha = alpha - self.emptyAnimationNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2, completion: { [weak self] _ in + self.emptyAnimationNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25, completion: { [weak self] _ in if let strongSelf = self { if !previousAlpha.isZero && strongSelf.emptyAnimationNode.alpha.isZero { strongSelf.emptyAnimationNode.visibility = false @@ -705,9 +705,9 @@ final class CallListControllerNode: ASDisplayNode { }) self.emptyButtonIconNode.alpha = alpha - self.emptyButtonIconNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyButtonIconNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) self.emptyButtonTextNode.alpha = alpha - self.emptyButtonTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.2) + self.emptyButtonTextNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: 0.25) self.emptyButtonNode.isUserInteractionEnabled = !isHidden if !isHidden { @@ -733,7 +733,6 @@ final class CallListControllerNode: ASDisplayNode { } self.emptyTextNode.attributedText = NSAttributedString(string: emptyText, font: textFont, textColor: color, paragraphAlignment: .center) - self.emptyButtonTextNode.attributedText = NSAttributedString(string: buttonText, font: buttonFont, textColor: theme.list.itemAccentColor, paragraphAlignment: .center) if let layout = self.containerLayout { diff --git a/submodules/CallListUI/Sources/CallListNodeEntries.swift b/submodules/CallListUI/Sources/CallListNodeEntries.swift index 47f83e21e0..d4a917251e 100644 --- a/submodules/CallListUI/Sources/CallListNodeEntries.swift +++ b/submodules/CallListUI/Sources/CallListNodeEntries.swift @@ -230,7 +230,7 @@ func countMeaningfulCallListEntries(_ entries: [CallListNodeEntry]) -> Int { var count: Int = 0 for entry in entries { switch entry.stableId { - case .setting, .groupCall: + case .setting: break default: count += 1 diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 31209f9e0d..76db6b0dd5 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1205,10 +1205,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId, _ in + controller.completion = { [weak controller] title, fileId, iconColor, _ in controller?.isInProgress = true - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, keepStack: .never).start() }, error: { _ in @@ -2441,10 +2441,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create) controller.navigationPresentation = .modal - controller.completion = { [weak controller] title, fileId, _ in + controller.completion = { [weak controller] title, fileId, iconColor, _ in controller?.isInProgress = true - let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId) + let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: iconColor, iconFileId: fileId) |> deliverOnMainQueue).start(next: { topicId in if let navigationController = (sourceController.navigationController as? NavigationController) { let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text, keepStack: .never).start() diff --git a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift index b4a7b82ea0..47e1f40e48 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItemStrings.swift @@ -127,10 +127,8 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: case _ as TelegramMediaImage: if message.text.isEmpty { messageText = strings.Message_Photo - } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if enableMediaEmoji { - messageText = "🖼 \(messageText)" - } + } else if enableMediaEmoji { + messageText = "🖼 \(messageText)" } case let fileMedia as TelegramMediaFile: var processed = false @@ -188,7 +186,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder: if message.text.isEmpty { messageText = strings.Message_Video processed = true - } else if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { + } else { if enableMediaEmoji { if !fileMedia.isAnimated { messageText = "📹 \(messageText)" diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift index 7035561189..b1a74d9c1f 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetControllerNode.swift @@ -209,15 +209,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.cancel = cancel self.effectView = UIVisualEffectView() - if #available(iOS 9.0, *) { - } else { - if self.presentationData.theme.rootController.keyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - self.effectView.alpha = 0.0 - } self.dimNode = ASDisplayNode() self.dimNode.alpha = 1.0 @@ -430,14 +421,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } self.presentationData = presentationData - if #available(iOS 9.0, *) { - } else { - if self.presentationData.theme.rootController.keyboardColor == .dark { - self.effectView.effect = UIBlurEffect(style: .dark) - } else { - self.effectView.effect = UIBlurEffect(style: .light) - } - } + self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) self.dimNode.backgroundColor = presentationData.theme.contextMenu.dimColor @@ -465,11 +449,7 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, self.textInputNode.textView.setContentOffset(self.textInputNode.textView.contentOffset, animated: false) UIView.animate(withDuration: 0.2, animations: { - if #available(iOS 9.0, *) { - self.effectView.effect = makeCustomZoomBlurEffect(isLight: !self.presentationData.theme.overallDarkAppearance) - } else { - self.effectView.alpha = 1.0 - } + self.effectView.effect = makeCustomZoomBlurEffect(isLight: self.presentationData.theme.rootController.keyboardColor == .light) }, completion: { _ in }) self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.contentContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) @@ -562,12 +542,8 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } } - UIView.animate(withDuration: 0.4, animations: { - if #available(iOS 9.0, *) { - self.effectView.effect = nil - } else { - self.effectView.alpha = 0.0 - } + UIView.animate(withDuration: 0.2, animations: { + self.effectView.effect = nil }, completion: { _ in completedEffect = true intermediateCompletion() @@ -596,7 +572,6 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode, } let duration = 0.4 - self.sendButtonNode.layer.animatePosition(from: self.sendButtonNode.position, to: self.sendButtonFrame.center, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in completedButton = true intermediateCompletion() diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index b964e37bdd..e157558914 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -427,7 +427,7 @@ private struct CreatePollControllerState: Equatable { var isEditingSolution: Bool = false } -private func createPollControllerEntries(presentationData: PresentationData, peer: EnginePeer, state: CreatePollControllerState, limitsConfiguration: EngineConfiguration.Limits, defaultIsQuiz: Bool?) -> [CreatePollEntry] { +private func createPollControllerEntries(presentationData: PresentationData, peer: EnginePeer, state: CreatePollControllerState, limitsConfiguration: EngineConfiguration.UserLimits, defaultIsQuiz: Bool?) -> [CreatePollEntry] { var entries: [CreatePollEntry] = [] var textLimitText = ItemListSectionHeaderAccessoryText(value: "", color: .generic) @@ -436,7 +436,7 @@ private func createPollControllerEntries(presentationData: PresentationData, pee textLimitText = ItemListSectionHeaderAccessoryText(value: "\(remainingCount)", color: remainingCount < 0 ? .destructive : .generic) } entries.append(.textHeader(presentationData.strings.CreatePoll_TextHeader, textLimitText)) - entries.append(.text(presentationData.strings.CreatePoll_TextPlaceholder, state.text, Int(limitsConfiguration.maxMediaCaptionLength))) + entries.append(.text(presentationData.strings.CreatePoll_TextPlaceholder, state.text, Int(limitsConfiguration.maxCaptionLength))) let optionsHeaderTitle: String if let defaultIsQuiz = defaultIsQuiz, defaultIsQuiz { optionsHeaderTitle = presentationData.strings.CreatePoll_QuizOptionsHeader @@ -866,7 +866,7 @@ public func createPollController(context: AccountContext, updatedPresentationDat let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), - context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.Limits()) + context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.UserLimits(isPremium: false)) ) |> map { presentationData, state, limitsConfiguration -> (ItemListControllerState, (ItemListNodeState, Any)) in var presentationData = presentationData diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index cdd597b009..a5d75d0db4 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -265,7 +265,7 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin textColor: titleColor) } - self.titleLabelNode.isUserInteractionEnabled = self.titleLabelNode.tapAttributeAction != nil + self.titleLabelNode.isUserInteractionEnabled = self.titleLabelNode.tapAttributeAction != nil && self.item.action == nil self.subtitleNode.attributedText = subtitle.flatMap { subtitle in return NSAttributedString( diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index ccd8868e4d..5686e645fb 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1860,6 +1860,10 @@ final class ControlledTransitionProperty { let toValue: AnyValue private let completion: ((Bool) -> Void)? + private lazy var animationKey: String = { + return "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())" + }() + init(layer: CALayer, path: String, fromValue: T, toValue: T, completion: ((Bool) -> Void)?) where T: AnyValueProviding { self.layer = layer self.path = path @@ -1871,7 +1875,7 @@ final class ControlledTransitionProperty { } deinit { - self.layer.removeAnimation(forKey: "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())") + self.layer.removeAnimation(forKey: self.animationKey) } func update(at fraction: CGFloat) { @@ -1887,7 +1891,7 @@ final class ControlledTransitionProperty { animation.toValue = value.nsValue animation.timingFunction = CAMediaTimingFunction(name: .linear) animation.isRemovedOnCompletion = false - self.layer.add(animation, forKey: "MyCustomAnimation_\(Unmanaged.passUnretained(self).toOpaque())") + self.layer.add(animation, forKey: self.animationKey) } func complete(atEnd: Bool) { diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 06471c28f0..0c7d4717c7 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -1292,7 +1292,12 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo overrideImage = .deletedIcon } strongSelf.avatarNode.imageNode.animateFirstTransition = item.animateFirstAvatarTransition - strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoad) + + var clipStyle: AvatarNodeClipStyle = .round + if case let .channel(channel) = item.peer, channel.isForum { + clipStyle = .roundedRect + } + strongSelf.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: item.peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: clipStyle, synchronousLoad: synchronousLoad) } } diff --git a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift index f6c79e6238..4438cb06a3 100644 --- a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift +++ b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift @@ -156,7 +156,7 @@ final class LocationSearchContainerNode: ASDisplayNode { let searchItems = self.searchQuery.get() |> mapToSignal { query -> Signal in if let query = query, !query.isEmpty { - return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + return (.complete() |> delay(1.0, queue: Queue.mainQueue())) |> then(.single(query)) } else { return .single(query) diff --git a/submodules/MtProtoKit/Sources/MTApiEnvironment.m b/submodules/MtProtoKit/Sources/MTApiEnvironment.m index ffdcb74d6f..e39218c327 100644 --- a/submodules/MtProtoKit/Sources/MTApiEnvironment.m +++ b/submodules/MtProtoKit/Sources/MTApiEnvironment.m @@ -711,6 +711,10 @@ NSString *suffix = @""; [platform isEqualToString:@"iPad11,7"]) return @"iPad (8th gen)"; + if ([platform isEqualToString:@"iPad12,1"] || + [platform isEqualToString:@"iPad12,2"]) + return @"iPad (9th gen)"; + if ([platform isEqualToString:@"iPad13,1"] || [platform isEqualToString:@"iPad13,2"]) return @"iPad Air (4th gen)"; diff --git a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift index f825699395..2e0ecd6a3a 100644 --- a/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelBannedMemberController.swift @@ -466,14 +466,41 @@ public func channelBannedMemberController(context: AccountContext, updatedPresen } else { effectiveRightsFlags = defaultBannedRightsFlags } - if value { - effectiveRightsFlags.remove(rights) - effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) + + + if rights == .banSendMedia { + if value { + effectiveRightsFlags.remove(rights) + for item in banSendMediaSubList() { + effectiveRightsFlags.remove(item.0) + } + } else { + effectiveRightsFlags.insert(rights) + for (right, _) in allGroupPermissionList(peer: EnginePeer(peer), expandMedia: false) { + if groupPermissionDependencies(right).contains(rights) { + effectiveRightsFlags.insert(right) + } + } + + for item in banSendMediaSubList() { + effectiveRightsFlags.insert(item.0) + for (right, _) in allGroupPermissionList(peer: EnginePeer(peer), expandMedia: false) { + if groupPermissionDependencies(right).contains(item.0) { + effectiveRightsFlags.insert(right) + } + } + } + } } else { - effectiveRightsFlags.insert(rights) - for (right, _) in allGroupPermissionList(peer: EnginePeer(peer), expandMedia: false) { - if groupPermissionDependencies(right).contains(rights) { - effectiveRightsFlags.insert(right) + if value { + effectiveRightsFlags.remove(rights) + effectiveRightsFlags = effectiveRightsFlags.subtracting(groupPermissionDependencies(rights)) + } else { + effectiveRightsFlags.insert(rights) + for (right, _) in allGroupPermissionList(peer: EnginePeer(peer), expandMedia: false) { + if groupPermissionDependencies(right).contains(rights) { + effectiveRightsFlags.insert(right) + } } } } diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 85cdc75b53..0850513f01 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -826,8 +826,12 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon case let .member(_, _, _, banInfo, _): if let banInfo = banInfo { var exceptionsString = "" + let sendMediaRights = banSendMediaSubList().map { $0.0 } for (rights, _) in allGroupPermissionList(peer: .channel(channel), expandMedia: true) { if banInfo.rights.flags.contains(rights) { + if banInfo.rights.flags.contains(.banSendMedia) && sendMediaRights.contains(rights) { + continue + } if !exceptionsString.isEmpty { exceptionsString.append(", ") } @@ -1086,8 +1090,12 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon case let .member(_, _, _, banInfo, _): if let banInfo = banInfo { var exceptionsString = "" + let sendMediaRights = banSendMediaSubList().map { $0.0 } for (rights, _) in allGroupPermissionList(peer: .legacyGroup(group), expandMedia: true) { if banInfo.rights.flags.contains(rights) { + if banInfo.rights.flags.contains(.banSendMedia) && sendMediaRights.contains(rights) { + continue + } if !exceptionsString.isEmpty { exceptionsString.append(", ") } diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 6031dda89c..b79d5c3d47 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -341,8 +341,12 @@ private enum ChannelPermissionsEntry: ItemListNodeEntry { case let .member(_, _, _, banInfo, _): var exceptionsString = "" if let banInfo = banInfo { + let sendMediaRights = banSendMediaSubList().map { $0.0 } for (rights, _) in internal_allPossibleGroupPermissionList { if !defaultBannedRights.contains(rights) && banInfo.rights.flags.contains(rights) { + if banInfo.rights.flags.contains(.banSendMedia) && sendMediaRights.contains(rights) { + continue + } if !exceptionsString.isEmpty { exceptionsString.append(", ") } diff --git a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift index 5d344a7a3c..c085b6ccec 100644 --- a/submodules/TelegramBaseController/Sources/TelegramBaseController.swift +++ b/submodules/TelegramBaseController/Sources/TelegramBaseController.swift @@ -303,7 +303,13 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder { disposable.set((callContext.context.panelData |> deliverOnMainQueue).start(next: { panelData in callContext.keep() - subscriber.putNext(panelData) + var updatedPanelData = panelData + if let panelData { + var updatedInfo = panelData.info + updatedInfo.subscribedToScheduled = activeCall.subscribedToScheduled + updatedPanelData = panelData.withInfo(updatedInfo) + } + subscriber.putNext(updatedPanelData) })) } diff --git a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift index 94bd233639..3adde6596e 100644 --- a/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift +++ b/submodules/TelegramCallsUI/Sources/GroupCallNavigationAccessoryPanel.swift @@ -64,6 +64,18 @@ public final class GroupCallPanelData { self.activeSpeakers = activeSpeakers self.groupCall = groupCall } + + public func withInfo(_ info: GroupCallInfo) -> GroupCallPanelData { + return GroupCallPanelData( + peerId: self.peerId, + isChannel: self.isChannel, + info: info, + topParticipants: self.topParticipants, + participantCount: self.participantCount, + activeSpeakers: self.activeSpeakers, + groupCall: self.groupCall + ) + } } private final class FakeAudioLevelGenerator { diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index d327f0853d..9af33d1ca0 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -116,12 +116,12 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { activeSpeakers: Set(), groupCall: nil )))*/ - + let state = engine.calls.getGroupCallParticipants(callId: call.id, accessHash: call.accessHash, offset: "", ssrcs: [], limit: 100, sortAscending: nil) - |> map(Optional.init) - |> `catch` { _ -> Signal in - return .single(nil) - } + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } self.disposable = (combineLatest(queue: .mainQueue(), state, @@ -139,7 +139,7 @@ public final class AccountGroupCallContextImpl: AccountGroupCallContext { state: state, previousServiceState: nil ) - + strongSelf.participantsContext = context strongSelf.panelDataPromise.set(combineLatest(queue: .mainQueue(), context.state, diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index aeb5bf16f0..5b1a321b0d 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -1026,7 +1026,7 @@ public final class AccountStateManager { } let _ = (signal - |> deliverOn(self.queue)).start(next: { [weak self] messages in + |> deliverOn(self.queue)).start(next: { [weak self] messages in if let strongSelf = self { strongSelf.notificationMessagesPipe.putNext(messages) } @@ -1953,6 +1953,9 @@ public func messagesForNotification(transaction: Transaction, id: MessageId, alw } if let channel = message.peers[message.id.peerId] as? TelegramChannel { + if !channel.flags.contains(.isForum) { + threadData = nil + } switch channel.participationStatus { case .kicked, .left: return ([], false, sound, false, threadData) diff --git a/submodules/TelegramCore/Sources/State/ManagedConfigurationUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedConfigurationUpdates.swift index a4bef755a9..2bbf5284a2 100644 --- a/submodules/TelegramCore/Sources/State/ManagedConfigurationUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedConfigurationUpdates.swift @@ -14,7 +14,7 @@ func managedConfigurationUpdates(accountManager: AccountManager mapToSignal { result, defaultHistoryTtl -> Signal in return postbox.transaction { transaction -> Signal in switch result { - case let .config(flags, _, _, _, _, dcOptions, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, _, _, _, _, _, editTimeLimit, revokeTimeLimit, revokePmTimeLimit, _, stickersRecentLimit, _, _, _, _, _, _, _, autoupdateUrlPrefix, gifSearchUsername, venueSearchUsername, imgSearchUsername, _, captionLengthMax, _, webfileDcId, suggestedLangCode, langPackVersion, baseLangPackVersion, reactionsDefault, autologinToken): + case let .config(flags, _, _, _, _, dcOptions, _, chatSizeMax, megagroupSizeMax, forwardedCountMax, _, _, _, _, _, _, _, _, editTimeLimit, revokeTimeLimit, revokePmTimeLimit, _, stickersRecentLimit, _, _, _, _, _, _, _, autoupdateUrlPrefix, gifSearchUsername, venueSearchUsername, imgSearchUsername, _, _, _, webfileDcId, suggestedLangCode, langPackVersion, baseLangPackVersion, reactionsDefault, autologinToken): var addressList: [Int: [MTDatacenterAddress]] = [:] for option in dcOptions { switch option { @@ -61,7 +61,7 @@ func managedConfigurationUpdates(accountManager: AccountManager LimitsConfiguration { return LimitsConfiguration( - maxPinnedChatCount: self.maxPinnedChatCount, - maxArchivedPinnedChatCount: self.maxArchivedPinnedChatCount, maxGroupMemberCount: self.maxGroupMemberCount, maxSupergroupMemberCount: self.maxSupergroupMemberCount, maxMessageForwardBatchSize: self.maxMessageForwardBatchSize, - maxSavedGifCount: self.maxSavedGifCount, maxRecentStickerCount: self.maxRecentStickerCount, - maxFavedStickerCount: self.maxFavedStickerCount, maxMessageEditingInterval: self.maxMessageEditingInterval, - maxMediaCaptionLength: self.maxMediaCaptionLength, canRemoveIncomingMessagesInPrivateChats: self.canRemoveIncomingMessagesInPrivateChats, maxMessageRevokeInterval: self.maxMessageRevokeInterval, maxMessageRevokeIntervalInPrivateChats: self.maxMessageRevokeIntervalInPrivateChats @@ -142,6 +120,7 @@ public extension EngineConfiguration.UserLimits { init(_ userLimitsConfiguration: UserLimitsConfiguration) { self.init( maxPinnedChatCount: userLimitsConfiguration.maxPinnedChatCount, + maxArchivedPinnedChatCount: userLimitsConfiguration.maxArchivedPinnedChatCount, maxChannelsCount: userLimitsConfiguration.maxChannelsCount, maxPublicLinksCount: userLimitsConfiguration.maxPublicLinksCount, maxSavedGifCount: userLimitsConfiguration.maxSavedGifCount, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift index 96d56034ed..8fbfbca7e5 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ChatList.swift @@ -457,8 +457,13 @@ extension EngineChatList.Item { let readCounters = readState.flatMap(EnginePeerReadCounters.init) - if let channel = renderedPeer.peer as? TelegramChannel, channel.flags.contains(.isForum) { - draft = nil + if let channel = renderedPeer.peer as? TelegramChannel { + if channel.flags.contains(.isForum) { + draft = nil + } else { + forumTopicDataValue = nil + topForumTopicItems = [] + } } self.init( diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift index 9a903484eb..378b4fe35c 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TogglePeerChatPinned.swift @@ -17,7 +17,6 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio let isPremium = transaction.getPeer(accountPeerId)?.isPremium ?? false let appConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration)?.get(AppConfiguration.self) ?? .defaultValue - let limitsConfiguration = transaction.getPreferencesEntry(key: PreferencesKeys.limitsConfiguration)?.get(LimitsConfiguration.self) ?? LimitsConfiguration.defaultValue let userLimitsConfiguration = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: isPremium) switch location { @@ -46,7 +45,7 @@ func _internal_toggleItemPinned(postbox: Postbox, accountPeerId: PeerId, locatio if case .root = groupId { limitCount = Int(userLimitsConfiguration.maxPinnedChatCount) } else { - limitCount = Int(limitsConfiguration.maxArchivedPinnedChatCount) + limitCount = Int(userLimitsConfiguration.maxArchivedPinnedChatCount) } let count = sameKind.count + additionalCount diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index 8d2c6c72b6..0da02b5a58 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -95,6 +95,7 @@ private final class TitleFieldComponent: Component { let iconColor: Int32 let text: String let placeholderText: String + let isEditing: Bool let textUpdated: (String) -> Void let iconPressed: () -> Void @@ -108,6 +109,7 @@ private final class TitleFieldComponent: Component { iconColor: Int32, text: String, placeholderText: String, + isEditing: Bool, textUpdated: @escaping (String) -> Void, iconPressed: @escaping () -> Void ) { @@ -120,6 +122,7 @@ private final class TitleFieldComponent: Component { self.iconColor = iconColor self.text = text self.placeholderText = placeholderText + self.isEditing = isEditing self.textUpdated = textUpdated self.iconPressed = iconPressed } @@ -152,6 +155,9 @@ private final class TitleFieldComponent: Component { if lhs.placeholderText != rhs.placeholderText { return false } + if lhs.isEditing != rhs.isEditing { + return false + } return true } @@ -237,6 +243,7 @@ private final class TitleFieldComponent: Component { iconContent = .animation(content: .customEmoji(fileId: component.fileId), size: CGSize(width: 48.0, height: 48.0), placeholderColor: component.placeholderColor, themeColor: component.accentColor, loopMode: .count(2)) self.iconButton.isUserInteractionEnabled = false } + self.iconButton.isUserInteractionEnabled = !component.isEditing let placeholderSize = self.placeholderView.update( transition: .easeInOut(duration: 0.2), @@ -471,15 +478,26 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { let mode: ForumCreateTopicScreen.Mode let titleUpdated: (String) -> Void let iconUpdated: (Int64?) -> Void + let iconColorUpdated: (Int32) -> Void let isHiddenUpdated: (Bool) -> Void let openPremium: () -> Void - init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { + init( + context: AccountContext, + peerId: EnginePeer.Id, + mode: ForumCreateTopicScreen.Mode, + titleUpdated: @escaping (String) -> Void, + iconUpdated: @escaping (Int64?) -> Void, + iconColorUpdated: @escaping (Int32) -> Void, + isHiddenUpdated: @escaping (Bool) -> Void, + openPremium: @escaping () -> Void + ) { self.context = context self.peerId = peerId self.mode = mode self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.iconColorUpdated = iconColorUpdated self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium } @@ -501,6 +519,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { private let context: AccountContext private let titleUpdated: (String) -> Void private let iconUpdated: (Int64?) -> Void + private let iconColorUpdated: (Int32) -> Void private let isHiddenUpdated: (Bool) -> Void private let openPremium: () -> Void @@ -520,10 +539,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { private var hasPremium: Bool = false - init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { + init(context: AccountContext, mode: ForumCreateTopicScreen.Mode, titleUpdated: @escaping (String) -> Void, iconUpdated: @escaping (Int64?) -> Void, iconColorUpdated: @escaping (Int32) -> Void, isHiddenUpdated: @escaping (Bool) -> Void, openPremium: @escaping () -> Void) { self.context = context self.titleUpdated = titleUpdated self.iconUpdated = iconUpdated + self.iconColorUpdated = iconColorUpdated self.isHiddenUpdated = isHiddenUpdated self.openPremium = openPremium @@ -534,6 +554,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.fileId = 0 self.iconColor = ForumCreateTopicScreen.iconColors.randomElement() ?? 0x0 self.isHidden = false + iconColorUpdated(self.iconColor) case let .edit(threadId, info, isHidden): self.isGeneral = threadId == 1 self.title = info.title @@ -647,6 +668,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { self.iconColor = colors.first ?? 0 } self.updated(transition: .immediate) + self.iconColorUpdated(self.iconColor) self.updateEmojiContent() } @@ -678,6 +700,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { mode: self.mode, titleUpdated: self.titleUpdated, iconUpdated: self.iconUpdated, + iconColorUpdated: self.iconColorUpdated, isHiddenUpdated: self.isHiddenUpdated, openPremium: self.openPremium ) @@ -753,6 +776,11 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { .position(CGPoint(x: context.availableSize.width / 2.0, y: contentHeight + titleBackground.size.height / 2.0)) ) + var isEditing = false + if case .edit = context.component.mode { + isEditing = true + } + let titleField = titleField.update( component: TitleFieldComponent( context: context.component.context, @@ -764,6 +792,7 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { iconColor: state.iconColor, text: state.title, placeholderText: environment.strings.CreateTopic_EnterTopicTitlePlaceholder, + isEditing: isEditing, textUpdated: { [weak state] text in state?.updateTitle(text) }, @@ -999,8 +1028,8 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { private var doneBarItem: UIBarButtonItem? - private var state: (String, Int64?, Bool?) = ("", nil, nil) - public var completion: (String, Int64?, Bool?) -> Void = { _, _, _ in } + private var state: (title: String, icon: Int64?, iconColor: Int32, isHidden: Bool?) = ("", nil, 0, nil) + public var completion: (_ title: String, _ icon: Int64?, _ iconColor: Int32, _ isHidden: Bool?) -> Void = { _, _, _, _ in } public var isInProgress: Bool = false { didSet { @@ -1021,6 +1050,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { var titleUpdatedImpl: ((String) -> Void)? var iconUpdatedImpl: ((Int64?) -> Void)? + var iconColorUpdatedImpl: ((Int32) -> Void)? var isHiddenUpdatedImpl: ((Bool) -> Void)? var openPremiumImpl: (() -> Void)? @@ -1028,6 +1058,8 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { titleUpdatedImpl?(title) }, iconUpdated: { fileId in iconUpdatedImpl?(fileId) + }, iconColorUpdated: { iconColor in + iconColorUpdatedImpl?(iconColor) }, isHiddenUpdated: { isHidden in isHiddenUpdatedImpl?(isHidden) }, openPremium: { @@ -1045,7 +1077,7 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { title = presentationData.strings.CreateTopic_EditTitle doneTitle = presentationData.strings.Common_Done - self.state = (topic.title, topic.icon, threadId == 1 ? isHidden : nil) + self.state = (topic.title, topic.icon, topic.iconColor, threadId == 1 ? isHidden : nil) } self.title = title @@ -1066,23 +1098,28 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } strongSelf.doneBarItem?.isEnabled = !title.isEmpty - strongSelf.state = (title, strongSelf.state.1, strongSelf.state.2) + strongSelf.state = (title, strongSelf.state.icon, strongSelf.state.iconColor, strongSelf.state.isHidden) } iconUpdatedImpl = { [weak self] fileId in guard let strongSelf = self else { return } - - strongSelf.state = (strongSelf.state.0, fileId, strongSelf.state.2) + strongSelf.state = (strongSelf.state.title, fileId, strongSelf.state.iconColor, strongSelf.state.isHidden) + } + + iconColorUpdatedImpl = { [weak self] iconColor in + guard let strongSelf = self else { + return + } + strongSelf.state = (strongSelf.state.title, strongSelf.state.icon, iconColor, strongSelf.state.isHidden) } isHiddenUpdatedImpl = { [weak self] isHidden in guard let strongSelf = self else { return } - - strongSelf.state = (strongSelf.state.0, strongSelf.state.1, isHidden) + strongSelf.state = (strongSelf.state.title, strongSelf.state.icon, strongSelf.state.iconColor, isHidden) } openPremiumImpl = { [weak self] in @@ -1113,6 +1150,6 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer { } @objc private func createPressed() { - self.completion(self.state.0, self.state.1, self.state.2) + self.completion(self.state.title, self.state.icon, self.state.iconColor, self.state.isHidden) } } diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 6c233f21da..3c145fc513 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2401,7 +2401,7 @@ private func extractAccountManagerState(records: AccountRecordsView map { messageIds -> MessageId? in if messageIds.isEmpty { return nil diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c218c1d6f8..863a15e0d0 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3913,7 +3913,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] { - legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: NSAttributedString(string: message.text), snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in + let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText + legacyMediaEditor(context: strongSelf.context, peer: peer, threadTitle: strongSelf.threadInfo?.title, media: mediaReference, initialCaption: inputText, snapshots: [], transitionCompletion: nil, getCaptionPanelView: { [weak self] in return self?.getCaptionPanelView() }, sendMessagesWithSignals: { [weak self] signals, _, _ in if let strongSelf = self { @@ -7891,7 +7892,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var webpageUrl: String? for media in message.media { if media is TelegramMediaImage || media is TelegramMediaFile { - inputTextMaxLength = strongSelf.context.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength + inputTextMaxLength = strongSelf.context.userLimits.maxCaptionLength } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content { webpageUrl = content.url } @@ -8128,16 +8129,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId { let presentationData = strongSelf.presentationData - let forwardOptions: Signal - if peerId.namespace == Namespaces.Peer.SecretChat { - forwardOptions = .single(ChatControllerSubject.ForwardOptions(hideNames: true, hideCaptions: false)) - } else { - forwardOptions = strongSelf.presentationInterfaceStatePromise.get() - |> map { state -> ChatControllerSubject.ForwardOptions in - return ChatControllerSubject.ForwardOptions(hideNames: state.interfaceState.forwardOptionsState?.hideNames ?? false, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false) + let forwardOptions = strongSelf.presentationInterfaceStatePromise.get() + |> map { state -> ChatControllerSubject.ForwardOptions in + var hideNames = state.interfaceState.forwardOptionsState?.hideNames ?? false + if peerId.namespace == Namespaces.Peer.SecretChat { + hideNames = true } - |> distinctUntilChanged + return ChatControllerSubject.ForwardOptions(hideNames: hideNames, hideCaptions: state.interfaceState.forwardOptionsState?.hideCaptions ?? false) } + |> distinctUntilChanged let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peerId), subject: .forwardedMessages(peerIds: [peerId], ids: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds ?? [], options: forwardOptions), botStart: nil, mode: .standard(previewing: true)) chatController.canReadHistory.set(false) @@ -8197,88 +8197,90 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - let canHideNames = hasNotOwnMessages && hasOther - + var canHideNames = hasNotOwnMessages && hasOther + if case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + canHideNames = false + } let hideNames = forwardOptions.hideNames let hideCaptions = forwardOptions.hideCaptions - if case let .peer(peerId) = strongSelf.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { + if canHideNames { + items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in + if hideNames { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideNames = false + updated.hideCaptions = false + updated.unhideNamesOnCaptionChange = false + return updated + }) + }))) - } else { - if canHideNames { - items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_ShowSendersName : presentationData.strings.Conversation_ForwardOptions_ShowSendersNames, icon: { theme in - if hideNames { - return nil - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideNames = false - updated.hideCaptions = false - updated.unhideNamesOnCaptionChange = false - return updated - }) - }))) - - items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_HideSendersName : presentationData.strings.Conversation_ForwardOptions_HideSendersNames, icon: { theme in - if hideNames { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil - } - }, action: { _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideNames = true - updated.unhideNamesOnCaptionChange = false - return updated - }) - }))) - - items.append(.separator) - } + items.append(.action(ContextMenuActionItem(text: uniquePeerIds.count == 1 ? presentationData.strings.Conversation_ForwardOptions_HideSendersName : presentationData.strings.Conversation_ForwardOptions_HideSendersNames, icon: { theme in + if hideNames { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideNames = true + updated.unhideNamesOnCaptionChange = false + return updated + }) + }))) - if hasCaptions { - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in - if hideCaptions { - return nil - } else { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } - }, action: { [weak self] _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideCaptions = false + items.append(.separator) + } + + if hasCaptions { + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ShowCaption, icon: { theme in + if hideCaptions { + return nil + } else { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } + }, action: { [weak self] _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideCaptions = false + if canHideNames { if updated.unhideNamesOnCaptionChange { updated.unhideNamesOnCaptionChange = false updated.hideNames = false } - return updated - }) - }))) - - items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideCaption, icon: { theme in - if hideCaptions { - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) - } else { - return nil } - }, action: { _, f in - self?.interfaceInteraction?.updateForwardOptionsState({ current in - var updated = current - updated.hideCaptions = true + return updated + }) + }))) + + items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_HideCaption, icon: { theme in + if hideCaptions { + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) + } else { + return nil + } + }, action: { _, f in + self?.interfaceInteraction?.updateForwardOptionsState({ current in + var updated = current + updated.hideCaptions = true + if canHideNames { if !updated.hideNames { updated.hideNames = true updated.unhideNamesOnCaptionChange = true } - return updated - }) - }))) - - items.append(.separator) - } + } + return updated + }) + }))) + + items.append(.separator) } items.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ForwardOptions_ChangeRecipient, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { c, f in @@ -11348,7 +11350,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var layout = layout - if case .compact = layout.metrics.widthClass, let _ = self.attachmentController { + if case .compact = layout.metrics.widthClass, let attachmentController = self.attachmentController, attachmentController.window != nil { layout = layout.withUpdatedInputHeight(nil) } @@ -15989,7 +15991,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.dismiss() let navigateToLocation: NavigateToChatControllerParams.Location - if let message = messages.first, let threadId = message.threadId, threadId != 1 || (message.peers[message.id.peerId] as? TelegramChannel)?.flags.contains(.isForum) == true { + if let message = messages.first, let threadId = message.threadId, let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.flags.contains(.isForum) { navigateToLocation = .replyThread(ChatReplyThreadMessage(messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId)), channelMessageId: nil, isChannelPost: false, isForumPost: true, maxMessage: nil, maxReadIncomingMessageId: nil, maxReadOutgoingMessageId: nil, unreadCount: 0, initialFilledHoles: IndexSet(), initialAnchor: .automatic, isNotAvailable: false)) } else { navigateToLocation = .peer(peer) diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift index 0cbe2d7bb7..4bb41e754a 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift @@ -438,7 +438,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) let fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0) let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize) - animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .cached) + animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: item.stickerItem.file.resource, isVideo: item.stickerItem.file.isVideoSticker), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift index 3fdc0070e2..6e8a97de99 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -70,7 +70,6 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { self.maskForeground.masksToBounds = true self.maskLayer.addSublayer(self.maskForeground) - self.addSubnode(self.interactiveFileNode) self.addSubnode(self.interactiveVideoNode) self.interactiveVideoNode.requestUpdateLayout = { [weak self] _ in @@ -285,14 +284,9 @@ class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentNode { return (finalSize, { [weak self] animation, synchronousLoads, applyInfo in if let strongSelf = self { - let firstTime = strongSelf.item == nil strongSelf.item = item strongSelf.isExpanded = isExpanded - if firstTime { - strongSelf.interactiveFileNode.isHidden = true - } - strongSelf.bubbleBackgroundNode?.layer.mask = strongSelf.maskLayer if let bubbleBackdropNode = strongSelf.bubbleBackdropNode, bubbleBackdropNode.hasImage && strongSelf.backdropMaskForeground.superlayer == nil { strongSelf.bubbleBackdropNode?.overrideMask = true diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 554564f166..96aab400b7 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1601,7 +1601,9 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let duration: Double = 0.2 node.alpha = 1.0 - node.isHidden = false + if node.supernode == nil { + self.supernode?.insertSubnode(node, belowSubnode: self) + } self.alpha = 0.0 self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) @@ -1715,7 +1717,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { node.alpha = 0.0 node.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, completion: { _ in - node.isHidden = true + node.removeFromSupernode() }) node.waveformView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index c241ca3da9..3d10f3ae39 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -1315,7 +1315,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio strongSelf.animatedStickerNode = animatedStickerNode let dimensions = updatedAnimatedStickerFile.dimensions ?? PixelDimensions(width: 512, height: 512) let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0)) - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: updatedAnimatedStickerFile.resource, isVideo: updatedAnimatedStickerFile.isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: updatedAnimatedStickerFile.resource, isVideo: updatedAnimatedStickerFile.isVideo), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct(cachePathPrefix: nil)) strongSelf.pinchContainerNode.contentNode.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode) animatedStickerNode.visibility = strongSelf.visibility } @@ -1687,8 +1687,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio state = .none badgeContent = nil } else if wideLayout { - if let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if let size = file.size, size > 0 && size != .max { + let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" if let duration = file.duration, !message.flags.contains(.Unsent) { let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) if isMediaStreamable(message: message, media: file) { @@ -1721,8 +1721,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio state = automaticPlayback ? .none : state } } else { - if isMediaStreamable(message: message, media: file), let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max { + let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))" if message.flags.contains(.Unsent), let duration = file.duration { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) @@ -1749,8 +1749,8 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio if let duration = file.duration, !file.isAnimated { let durationString = stringForDuration(playerDuration > 0 ? playerDuration : duration, position: playerPosition) - if automaticPlayback, let size = file.size { - let sizeString = "\(dataSizeString(Int(Float(size) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(size, forceDecimal: true, formatting: formatting))" + if automaticPlayback, let fileSize = file.size, fileSize > 0 && fileSize != .max { + let sizeString = "\(dataSizeString(Int64(Float(fileSize) * progress), forceDecimal: true, formatting: formatting)) / \(dataSizeString(fileSize, forceDecimal: true, formatting: formatting))" mediaDownloadState = .fetching(progress: progress) badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: active ? sizeString : nil, muted: muted, active: active) } else { @@ -1800,9 +1800,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio do { let durationString = file.isAnimated ? gifTitle : stringForDuration(playerDuration > 0 ? playerDuration : (file.duration ?? 0), position: playerPosition) if wideLayout { - if isMediaStreamable(message: message, media: file) { + if isMediaStreamable(message: message, media: file), let fileSize = file.size, fileSize > 0 && fileSize != .max { state = automaticPlayback ? .none : .play(messageTheme.mediaOverlayControlColors.foregroundColor) - badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(file.size ?? 0, formatting: formatting), muted: muted, active: true) + badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: durationString, size: dataSizeString(fileSize, formatting: formatting), muted: muted, active: true) mediaDownloadState = .remote } else { state = automaticPlayback ? .none : state diff --git a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift index 662a37a751..311ab53b93 100644 --- a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift @@ -263,19 +263,16 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } else if item.messages[0].id.peerId.namespace == Namespaces.Peer.CloudGroup { isGroup = true } + title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) if isChannel { switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGE_DOCS_TEXT(Int32(item.messages.count)) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHANNEL_MESSAGES_TEXT(Int32(item.messages.count)) } } else if isGroup, var author = item.messages[0].author { @@ -284,31 +281,23 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { } switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGE_DOCS_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_CHAT_MESSAGES_TEXT(Int32(item.messages.count)).replacingOccurrences(of: "{author}", with: EnginePeer(author).compactDisplayTitle) } } else { switch kind { case .image: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_PHOTOS_TEXT(Int32(item.messages.count)) case .video: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_VIDEOS_TEXT(Int32(item.messages.count)) case .file: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGE_FILES_TEXT(Int32(item.messages.count)) default: - title = EnginePeer(peer).displayTitle(strings: item.strings, displayOrder: item.nameDisplayOrder) messageText = presentationData.strings.PUSH_MESSAGES_TEXT(Int32(item.messages.count)) } } @@ -325,6 +314,10 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { title = "📅 \(currentTitle)" } + if let attribute = item.messages.first?.attributes.first(where: { $0 is NotificationInfoMessageAttribute }) as? NotificationInfoMessageAttribute, attribute.flags.contains(.muted), let currentTitle = title { + title = "\(currentTitle) 🔕" + } + let textFont = compact ? Font.regular(15.0) : Font.regular(16.0) let textColor = presentationData.theme.inAppNotification.primaryTextColor var attributedMessageText: NSAttributedString diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index fe7f59fcae..908b2bb393 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -183,7 +183,8 @@ final class PeerInfoScreenData { let chatPeer: Peer? let cachedData: CachedPeerData? let status: PeerInfoStatusData? - let notificationSettings: TelegramPeerNotificationSettings? + let peerNotificationSettings: TelegramPeerNotificationSettings? + let threadNotificationSettings: TelegramPeerNotificationSettings? let globalNotificationSettings: EngineGlobalNotificationSettings? let isContact: Bool let availablePanes: [PeerInfoPaneKey] @@ -204,7 +205,8 @@ final class PeerInfoScreenData { chatPeer: Peer?, cachedData: CachedPeerData?, status: PeerInfoStatusData?, - notificationSettings: TelegramPeerNotificationSettings?, + peerNotificationSettings: TelegramPeerNotificationSettings?, + threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, isContact: Bool, availablePanes: [PeerInfoPaneKey], @@ -224,7 +226,8 @@ final class PeerInfoScreenData { self.chatPeer = chatPeer self.cachedData = cachedData self.status = status - self.notificationSettings = notificationSettings + self.peerNotificationSettings = peerNotificationSettings + self.threadNotificationSettings = threadNotificationSettings self.globalNotificationSettings = globalNotificationSettings self.isContact = isContact self.availablePanes = availablePanes @@ -521,7 +524,8 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, chatPeer: peer, cachedData: peerView.cachedData, status: nil, - notificationSettings: nil, + peerNotificationSettings: nil, + threadNotificationSettings: nil, globalNotificationSettings: nil, isContact: false, availablePanes: [], @@ -550,7 +554,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen chatPeer: nil, cachedData: nil, status: nil, - notificationSettings: nil, + peerNotificationSettings: nil, + threadNotificationSettings: nil, globalNotificationSettings: nil, isContact: false, availablePanes: [], @@ -683,7 +688,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen chatPeer: peerView.peers[peerId], cachedData: peerView.cachedData, status: status, - notificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, + peerNotificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, + threadNotificationSettings: nil, globalNotificationSettings: globalNotificationSettings, isContact: peerView.peerIsContact, availablePanes: availablePanes ?? [], @@ -761,7 +767,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen chatPeer: peerView.peers[peerId], cachedData: peerView.cachedData, status: status, - notificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, + peerNotificationSettings: peerView.notificationSettings as? TelegramPeerNotificationSettings, + threadNotificationSettings: nil, globalNotificationSettings: globalNotificationSettings, isContact: peerView.peerIsContact, availablePanes: availablePanes ?? [], @@ -948,12 +955,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen } } - var notificationSettings: TelegramPeerNotificationSettings? - if let threadData = threadData { - notificationSettings = threadData.notificationSettings - } else { - notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings - } + let peerNotificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings + let threadNotificationSettings = threadData?.notificationSettings let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration]?.get(AppConfiguration.self) ?? .defaultValue @@ -962,7 +965,8 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen chatPeer: peerView.peers[groupId], cachedData: peerView.cachedData, status: status, - notificationSettings: notificationSettings, + peerNotificationSettings: peerNotificationSettings, + threadNotificationSettings: threadNotificationSettings, globalNotificationSettings: globalNotificationSettings, isContact: peerView.peerIsContact, availablePanes: availablePanes ?? [], @@ -1305,3 +1309,42 @@ func peerInfoCanEdit(peer: Peer?, chatLocation: ChatLocation, threadData: Messag } return false } + +func peerInfoIsChatMuted(peer: Peer?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?) -> Bool { + func isPeerMuted(peer: Peer?, peerNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?) -> Bool { + var peerIsMuted = false + if let peerNotificationSettings { + if case .muted = peerNotificationSettings.muteState { + peerIsMuted = true + } else if case .default = peerNotificationSettings.muteState, let globalNotificationSettings { + if let peer { + if peer is TelegramUser { + peerIsMuted = !globalNotificationSettings.privateChats.enabled + } else if peer is TelegramGroup { + peerIsMuted = !globalNotificationSettings.groupChats.enabled + } else if let channel = peer as? TelegramChannel { + switch channel.info { + case .group: + peerIsMuted = !globalNotificationSettings.groupChats.enabled + case .broadcast: + peerIsMuted = !globalNotificationSettings.channels.enabled + } + } + } + } + } + return peerIsMuted + } + + var chatIsMuted = false + if let threadNotificationSettings { + if case .muted = threadNotificationSettings.muteState { + chatIsMuted = true + } else if let peerNotificationSettings { + chatIsMuted = isPeerMuted(peer: peer, peerNotificationSettings: peerNotificationSettings, globalNotificationSettings: globalNotificationSettings) + } + } else { + chatIsMuted = isPeerMuted(peer: peer, peerNotificationSettings: peerNotificationSettings, globalNotificationSettings: globalNotificationSettings) + } + return chatIsMuted +} diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index b5f4eb2dc5..ac0a55c722 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2542,7 +2542,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { private var currentCredibilityIcon: CredibilityIcon? private var currentPanelStatusData: PeerInfoStatusData? - func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, notificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { + func update(width: CGFloat, containerHeight: CGFloat, containerInset: CGFloat, statusBarHeight: CGFloat, navigationHeight: CGFloat, isModalOverlay: Bool, isMediaOnly: Bool, contentOffset: CGFloat, paneContainerY: CGFloat, presentationData: PresentationData, peer: Peer?, cachedData: CachedPeerData?, threadData: MessageHistoryThreadData?, peerNotificationSettings: TelegramPeerNotificationSettings?, threadNotificationSettings: TelegramPeerNotificationSettings?, globalNotificationSettings: EngineGlobalNotificationSettings?, statusData: PeerInfoStatusData?, panelStatusData: (PeerInfoStatusData?, PeerInfoStatusData?, CGFloat?), isSecretChat: Bool, isContact: Bool, isSettings: Bool, state: PeerInfoState, metrics: LayoutMetrics, deviceMetrics: DeviceMetrics, transition: ContainedViewLayoutTransition, additive: Bool) -> CGFloat { self.state = state self.peer = peer self.threadData = threadData @@ -3456,28 +3456,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { } buttonIcon = .voiceChat case .mute: - var peerIsMuted = false - if let notificationSettings { - if case .muted = notificationSettings.muteState { - peerIsMuted = true - } else if case .default = notificationSettings.muteState, let globalNotificationSettings { - if let peer { - if peer is TelegramUser { - peerIsMuted = !globalNotificationSettings.privateChats.enabled - } else if peer is TelegramGroup { - peerIsMuted = !globalNotificationSettings.groupChats.enabled - } else if let channel = peer as? TelegramChannel { - switch channel.info { - case .group: - peerIsMuted = !globalNotificationSettings.groupChats.enabled - case .broadcast: - peerIsMuted = !globalNotificationSettings.channels.enabled - } - } - } - } - } - if peerIsMuted { + let chatIsMuted = peerInfoIsChatMuted(peer: peer, peerNotificationSettings: peerNotificationSettings, threadNotificationSettings: threadNotificationSettings, globalNotificationSettings: globalNotificationSettings) + if chatIsMuted { buttonText = presentationData.strings.PeerInfo_ButtonUnmute buttonIcon = .unmute } else { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 6e48b0f56f..a4d7f460d5 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3053,7 +3053,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let controller = ForumCreateTopicScreen(context: strongSelf.context, peerId: strongSelf.peerId, mode: .edit(threadId: threadId, threadInfo: threadData.info, isHidden: threadData.isHidden)) controller.navigationPresentation = .modal let context = strongSelf.context - controller.completion = { [weak controller] title, fileId, isHidden in + controller.completion = { [weak controller] title, fileId, _, isHidden in let _ = (context.engine.peers.editForumChannelTopic(id: peerId, threadId: threadId, title: title, iconFileId: fileId) |> deliverOnMainQueue).start(completed: { controller?.dismiss() @@ -4258,30 +4258,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.requestCall(isVideo: false, gesture: gesture) case .mute: var displayCustomNotificationSettings = false - - var peerIsMuted = false - if let notificationSettings = self.data?.notificationSettings { - if case .muted = notificationSettings.muteState { - peerIsMuted = true - } else if case .default = notificationSettings.muteState, let globalNotificationSettings = self.data?.globalNotificationSettings { - if let peer = self.data?.peer { - if peer is TelegramUser { - peerIsMuted = !globalNotificationSettings.privateChats.enabled - } else if peer is TelegramGroup { - peerIsMuted = !globalNotificationSettings.groupChats.enabled - } else if let channel = peer as? TelegramChannel { - switch channel.info { - case .group: - peerIsMuted = !globalNotificationSettings.groupChats.enabled - case .broadcast: - peerIsMuted = !globalNotificationSettings.channels.enabled - } - } - } - } - } - - if peerIsMuted { + + let chatIsMuted = peerInfoIsChatMuted(peer: self.data?.peer, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings) + if chatIsMuted { } else { displayCustomNotificationSettings = true } @@ -4358,7 +4337,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate items.append(.separator) var isSoundEnabled = true - if let notificationSettings = self.data?.notificationSettings { + let notificationSettings = self.data?.threadNotificationSettings ?? self.data?.peerNotificationSettings + if let notificationSettings { switch notificationSettings.messageSound { case .none: isSoundEnabled = false @@ -4367,53 +4347,34 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } } - if let notificationSettings = self.data?.notificationSettings, case .muted = notificationSettings.muteState { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_ButtonUnmute, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOn"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let self else { - return - } - - let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: nil).start() - - let iconColor: UIColor = .white - self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } else if !isSoundEnabled { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_EnableSound, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOn"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).start() - - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) - } else { - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOff"), color: theme.contextMenu.primaryColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).start() - - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) + if !chatIsMuted { + if !isSoundEnabled { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_EnableSound, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOn"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .default).start() + + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_on", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundEnabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } else { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_DisableSound, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/SoundOff"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + let _ = strongSelf.context.engine.peers.updatePeerNotificationSoundInteractive(peerId: peerId, threadId: strongSelf.chatLocation.threadId, sound: .none).start() + + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_sound_off", scale: 0.056, colors: [:], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipSoundDisabled, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } } let context = self.context @@ -4503,26 +4464,49 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }) }))) - items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: theme.contextMenu.destructiveColor) - }, action: { [weak self] _, f in - f(.default) - - guard let strongSelf = self else { - return - } - - let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: Int32.max).start() - - let iconColor: UIColor = .white - strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ - "Middle.Group 1.Fill 1": iconColor, - "Top.Group 1.Fill 1": iconColor, - "Bottom.Group 1.Fill 1": iconColor, - "EXAMPLE.Group 1.Fill 1": iconColor, - "Line.Group 1.Stroke 1": iconColor - ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) - }))) + if chatIsMuted { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_ButtonUnmute, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unmute"), color: theme.contextMenu.primaryColor) + }, action: { [weak self] _, f in + f(.default) + + guard let self else { + return + } + + let _ = self.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: self.chatLocation.threadId, muteInterval: 0).start() + + let iconColor: UIColor = .white + self.controller?.present(UndoOverlayController(presentationData: self.presentationData, content: .universal(animation: "anim_profileunmute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: self.presentationData.strings.PeerInfo_TooltipUnmuted, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } else { + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.PeerInfo_MuteForever, textColor: .destructive, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: theme.contextMenu.destructiveColor) + }, action: { [weak self] _, f in + f(.default) + + guard let strongSelf = self else { + return + } + + let _ = strongSelf.context.engine.peers.updatePeerMuteSetting(peerId: peerId, threadId: strongSelf.chatLocation.threadId, muteInterval: Int32.max).start() + + let iconColor: UIColor = .white + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_profilemute", scale: 0.075, colors: [ + "Middle.Group 1.Fill 1": iconColor, + "Top.Group 1.Fill 1": iconColor, + "Bottom.Group 1.Fill 1": iconColor, + "EXAMPLE.Group 1.Fill 1": iconColor, + "Line.Group 1.Stroke 1": iconColor + ], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever, customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current) + }))) + } var tip: ContextController.Tip? tip = nil @@ -8687,7 +8671,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } let headerInset = sectionInset - var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, notificationSettings: self.data?.notificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) + var headerHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : self.scrollNode.view.contentOffset.y, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive) if !self.isSettings && !self.state.isEditing { headerHeight += 71.0 } @@ -9052,7 +9036,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } let headerInset = sectionInset - let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, notificationSettings: self.data?.notificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, transition: transition, additive: additive) + let _ = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: navigationHeight, isModalOverlay: layout.isModalOverlay, isMediaOnly: self.isMediaOnly, contentOffset: self.isMediaOnly ? 212.0 : offsetY, paneContainerY: self.paneContainerNode.frame.minY, presentationData: self.presentationData, peer: self.data?.peer, cachedData: self.data?.cachedData, threadData: self.data?.threadData, peerNotificationSettings: self.data?.peerNotificationSettings, threadNotificationSettings: self.data?.threadNotificationSettings, globalNotificationSettings: self.data?.globalNotificationSettings, statusData: self.data?.status, panelStatusData: self.customStatusData, isSecretChat: self.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.data?.isContact ?? false, isSettings: self.isSettings, state: self.state, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: additive) } let paneAreaExpansionDistance: CGFloat = 32.0 @@ -10261,7 +10245,7 @@ private final class PeerInfoNavigationTransitionNode: ASDisplayNode, CustomNavig } let headerInset = sectionInset - topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, notificationSettings: self.screenNode.data?.notificationSettings, globalNotificationSettings: self.screenNode.data?.globalNotificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, transition: transition, additive: false) + topHeight = self.headerNode.update(width: layout.size.width, containerHeight: layout.size.height, containerInset: headerInset, statusBarHeight: layout.statusBarHeight ?? 0.0, navigationHeight: topNavigationBar.bounds.height, isModalOverlay: layout.isModalOverlay, isMediaOnly: false, contentOffset: 0.0, paneContainerY: 0.0, presentationData: self.presentationData, peer: self.screenNode.data?.peer, cachedData: self.screenNode.data?.cachedData, threadData: self.screenNode.data?.threadData, peerNotificationSettings: self.screenNode.data?.peerNotificationSettings, threadNotificationSettings: self.screenNode.data?.threadNotificationSettings, globalNotificationSettings: self.screenNode.data?.globalNotificationSettings, statusData: self.screenNode.data?.status, panelStatusData: (nil, nil, nil), isSecretChat: self.screenNode.peerId.namespace == Namespaces.Peer.SecretChat, isContact: self.screenNode.data?.isContact ?? false, isSettings: self.screenNode.isSettings, state: self.screenNode.state, metrics: layout.metrics, deviceMetrics: layout.deviceMetrics, transition: transition, additive: false) } let titleScale = (fraction * previousTitleNode.view.bounds.height + (1.0 - fraction) * self.headerNode.titleNodeRawContainer.bounds.height) / previousTitleNode.view.bounds.height diff --git a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift index 8cc15ce477..ae8af31130 100644 --- a/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift +++ b/submodules/TelegramUI/Sources/PeerMessagesMediaPlaylist.swift @@ -60,7 +60,7 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { return MessageMediaPlaylistItemStableId(stableId: message.stableId) } - var playbackData: SharedMediaPlaybackData? { + lazy var playbackData: SharedMediaPlaybackData? = { if let file = extractFileMedia(self.message) { let fileReference = FileMediaReference.message(message: MessageReference(self.message), media: file) let source = SharedMediaPlaybackDataSource.telegramFile(reference: fileReference, isCopyProtected: self.message.isCopyProtected()) @@ -93,9 +93,9 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { } } return nil - } + }() - var displayData: SharedMediaPlaybackDisplayData? { + lazy var displayData: SharedMediaPlaybackDisplayData? = { if let file = extractFileMedia(self.message) { let text = self.message.text var entities: [MessageTextEntity] = [] @@ -108,40 +108,42 @@ final class MessageMediaPlaylistItem: SharedMediaPlaylistItem { for attribute in file.attributes { switch attribute { - case let .Audio(isVoice, duration, title, performer, _): - if isVoice { - return SharedMediaPlaybackDisplayData.voice(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId]) - } else { - var updatedTitle = title - let updatedPerformer = performer - if (title ?? "").isEmpty && (performer ?? "").isEmpty { - updatedTitle = file.fileName ?? "" - } - - let albumArt: SharedMediaPlaybackAlbumArt? - if file.fileName?.lowercased().hasSuffix(".ogg") == true { - albumArt = nil - } else { - albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) - } - - return SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption) + case let .Audio(isVoice, duration, title, performer, _): + let displayData: SharedMediaPlaybackDisplayData + if isVoice { + displayData = SharedMediaPlaybackDisplayData.voice(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId]) + } else { + var updatedTitle = title + let updatedPerformer = performer + if (title ?? "").isEmpty && (performer ?? "").isEmpty { + updatedTitle = file.fileName ?? "" } - case let .Video(_, _, flags): - if flags.contains(.instantRoundVideo) { - return SharedMediaPlaybackDisplayData.instantVideo(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId], timestamp: self.message.timestamp) + + let albumArt: SharedMediaPlaybackAlbumArt? + if file.fileName?.lowercased().hasSuffix(".ogg") == true { + albumArt = nil } else { - return nil + albumArt = SharedMediaPlaybackAlbumArt(thumbnailResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(file: .message(message: MessageReference(self.message), media: file), title: updatedTitle ?? "", performer: updatedPerformer ?? "", isThumbnail: false)) } - default: - break + + displayData = SharedMediaPlaybackDisplayData.music(title: updatedTitle, performer: updatedPerformer, albumArt: albumArt, long: CGFloat(duration) > 10.0 * 60.0, caption: caption) + } + return displayData + case let .Video(_, _, flags): + if flags.contains(.instantRoundVideo) { + return SharedMediaPlaybackDisplayData.instantVideo(author: self.message.effectiveAuthor, peer: self.message.peers[self.message.id.peerId], timestamp: self.message.timestamp) + } else { + return nil + } + default: + break } } return SharedMediaPlaybackDisplayData.music(title: file.fileName ?? "", performer: self.message.effectiveAuthor?.debugDisplayTitle ?? "", albumArt: nil, long: false, caption: caption) } return nil - } + }() } private enum NavigatedMessageFromViewPosition { diff --git a/submodules/TelegramUI/Sources/PrefetchManager.swift b/submodules/TelegramUI/Sources/PrefetchManager.swift index 00ef7a3755..60b69c7f6e 100644 --- a/submodules/TelegramUI/Sources/PrefetchManager.swift +++ b/submodules/TelegramUI/Sources/PrefetchManager.swift @@ -249,9 +249,11 @@ private final class PrefetchManagerInnerImpl { self.preloadGreetingStickerDisposable.set((self.preloadedGreetingStickerPromise.get() |> mapToSignal { sticker -> Signal in if let sticker = sticker { - let _ = freeMediaFileInteractiveFetched(account: account, userLocation: .other, fileReference: .standalone(media: sticker)).start() - return chatMessageAnimationData(mediaBox: account.postbox.mediaBox, resource: sticker.resource, fitzModifier: nil, isVideo: sticker.isVideoSticker, width: 384, height: 384, synchronousLoad: false) - |> mapToSignal { _ -> Signal in + return freeMediaFileInteractiveFetched(account: account, userLocation: .other, fileReference: .standalone(media: sticker)) + |> map { _ -> Void in + return Void() + } + |> `catch` { _ -> Signal in return .complete() } } else { diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 062f6272c9..d161627afc 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -295,7 +295,7 @@ public final class WebSearchController: ViewController { let throttledSearchQuery = self.searchQueryPromise.get() |> mapToSignal { query -> Signal in if !query.isEmpty { - return (.complete() |> delay(0.6, queue: Queue.mainQueue())) + return (.complete() |> delay(1.0, queue: Queue.mainQueue())) |> then(.single(query)) } else { return .single(query) diff --git a/submodules/rlottie/LottieInstance.mm b/submodules/rlottie/LottieInstance.mm index 61ac431c8f..f9da05da3e 100755 --- a/submodules/rlottie/LottieInstance.mm +++ b/submodules/rlottie/LottieInstance.mm @@ -61,7 +61,7 @@ _dimensions = CGSizeMake(width, height); - if ((_frameRate > 60) || _animation->duration() > 7.0) { + if ((_frameRate > 60) || _animation->duration() > 9.0) { return nil; } } From f6f110fde9e4b7838a09aa255d2146656879cd9f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 14 Mar 2023 19:15:14 +0400 Subject: [PATCH 3/7] Raise iOS API version --- .../UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m index 6965fff430..bba1cd4bda 100644 --- a/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m +++ b/submodules/UIKitRuntimeUtils/Source/UIKitRuntimeUtils/UIKitUtils.m @@ -185,13 +185,13 @@ UIView * _Nullable makePortalView(bool matchPosition) { return nil; } - if (@available(iOS 13.0, *)) { + if (@available(iOS 14.0, *)) { view.forwardsClientHitTestingToSourceView = false; } view.matchesPosition = matchPosition; view.matchesTransform = matchPosition; view.matchesAlpha = false; - if (@available(iOS 13.0, *)) { + if (@available(iOS 14.0, *)) { view.allowsHitTesting = false; } From e447b8e7a468691ebbb4b9228d792aeaebb37973 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Mar 2023 01:22:13 +0400 Subject: [PATCH 4/7] Fix chat background transition --- submodules/Display/Source/ListView.swift | 4 ++++ .../TelegramUI/Sources/ChatControllerNode.swift | 13 ++++++++++++- .../TelegramUI/Sources/ChatHistoryListNode.swift | 15 ++++++++++----- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index 0832ceeb75..ccc2979e2e 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -195,6 +195,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture private final var lastContentOffset: CGPoint = CGPoint() private final var lastContentOffsetTimestamp: CFAbsoluteTime = 0.0 private final var ignoreScrollingEvents: Bool = false + public final var globalIgnoreScrollingEvents: Bool = false private let infiniteScrollSize: CGFloat @@ -939,6 +940,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture if self.ignoreScrollingEvents || scroller !== self.scroller { return } + if self.globalIgnoreScrollingEvents { + return + } /*let timestamp = CACurrentMediaTime() if !self.previousDidScrollTimestamp.isZero { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 54aff54c94..099bba5b56 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -3440,6 +3440,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } final class SnapshotState { + let backgroundNode: WallpaperBackgroundNode fileprivate let historySnapshotState: ChatHistoryListNode.SnapshotState let titleViewSnapshotState: ChatTitleView.SnapshotState? let avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState? @@ -3450,6 +3451,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let inputPanelOverscrollNodeSnapshot: UIView? fileprivate init( + backgroundNode: WallpaperBackgroundNode, historySnapshotState: ChatHistoryListNode.SnapshotState, titleViewSnapshotState: ChatTitleView.SnapshotState?, avatarSnapshotState: ChatAvatarNavigationNode.SnapshotState?, @@ -3459,6 +3461,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { inputPanelNodeSnapshot: UIView?, inputPanelOverscrollNodeSnapshot: UIView? ) { + self.backgroundNode = backgroundNode self.historySnapshotState = historySnapshotState self.titleViewSnapshotState = titleViewSnapshotState self.avatarSnapshotState = avatarSnapshotState @@ -3490,6 +3493,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { inputPanelOverscrollNodeSnapshot = snapshot } return SnapshotState( + backgroundNode: self.backgroundNode, historySnapshotState: self.historyNode.prepareSnapshotState(), titleViewSnapshotState: titleViewSnapshotState, avatarSnapshotState: avatarSnapshotState, @@ -3502,7 +3506,14 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } func animateFromSnapshot(_ snapshotState: SnapshotState, completion: @escaping () -> Void) { - self.historyNode.animateFromSnapshot(snapshotState.historySnapshotState, completion: completion) + let previousBackgroundNode = snapshotState.backgroundNode + self.backgroundNode.supernode?.insertSubnode(previousBackgroundNode, belowSubnode: self.backgroundNode) + + self.historyNode.animateFromSnapshot(snapshotState.historySnapshotState, completion: { [weak previousBackgroundNode] in + previousBackgroundNode?.removeFromSupernode() + + completion() + }) self.navigateButtons.animateFromSnapshot(snapshotState.navigationButtonsSnapshotState) if let titleAccessoryPanelSnapshot = snapshotState.titleAccessoryPanelSnapshot { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 1efa37c56b..a8afc7c2fe 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -3779,15 +3779,16 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } - let snapshotView = self.view.snapshotView(afterScreenUpdates: false)! + let snapshotView = self.view//.snapshotView(afterScreenUpdates: false)! + self.globalIgnoreScrollingEvents = true - snapshotView.frame = self.view.bounds - if let sublayers = self.layer.sublayers { + //snapshotView.frame = self.view.bounds + /*if let sublayers = self.layer.sublayers { for sublayer in sublayers { sublayer.isHidden = true } - } - self.view.addSubview(snapshotView) + }*/ + //self.view.addSubview(snapshotView) let overscrollView = self.overscrollView if let overscrollView = overscrollView { @@ -3825,6 +3826,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { snapshotParentView.frame = self.view.frame snapshotState.snapshotView.frame = snapshotParentView.bounds + + snapshotState.snapshotView.clipsToBounds = true + snapshotState.snapshotView.layer.sublayerTransform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0) + self.view.superview?.insertSubview(snapshotParentView, belowSubview: self.view) snapshotParentView.layer.animatePosition(from: CGPoint(x: 0.0, y: 0.0), to: CGPoint(x: 0.0, y: -self.view.bounds.height - snapshotState.snapshotBottomInset - snapshotTopInset), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true, completion: { [weak snapshotParentView] _ in From 5f5d6acab847203ebb01222b4ba0f5565adfe07c Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Mar 2023 13:38:28 +0400 Subject: [PATCH 5/7] Fix ChatReplyCountItem background --- submodules/TelegramUI/Sources/ChatReplyCountItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatReplyCountItem.swift b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift index ec4627f166..c8e722a973 100644 --- a/submodules/TelegramUI/Sources/ChatReplyCountItem.swift +++ b/submodules/TelegramUI/Sources/ChatReplyCountItem.swift @@ -162,7 +162,7 @@ class ChatReplyCountItemNode: ListViewItemNode { if strongSelf.backgroundNode == nil { if let backgroundNode = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { strongSelf.backgroundNode = backgroundNode - backgroundNode.addSubnode(strongSelf.backgroundColorNode) + //backgroundNode.addSubnode(strongSelf.backgroundColorNode) strongSelf.insertSubnode(backgroundNode, at: 0) } } From 7a3aea37a6e83bc5459d3d8168c8a3c255bcf656 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Mar 2023 13:56:54 +0400 Subject: [PATCH 6/7] Update CallSessionManager --- .../Sources/State/CallSessionManager.swift | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramCore/Sources/State/CallSessionManager.swift b/submodules/TelegramCore/Sources/State/CallSessionManager.swift index 6251ed0950..93fd01e995 100644 --- a/submodules/TelegramCore/Sources/State/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/State/CallSessionManager.swift @@ -369,6 +369,7 @@ private final class CallSessionManagerContext { private let ringingSubscribers = Bag<([CallSessionRingingState]) -> Void>() private var contexts: [CallSessionInternalId: CallSessionContext] = [:] + private var futureContextSubscribers: [CallSessionInternalId: Bag<(CallSession) -> Void>] = [:] private var contextIdByStableId: [CallSessionStableId: CallSessionInternalId] = [:] private var enqueuedSignalingData: [Int64: [Data]] = [:] @@ -443,7 +444,10 @@ private final class CallSessionManagerContext { return Signal { [weak self] subscriber in let disposable = MetaDisposable() queue.async { - if let strongSelf = self, let context = strongSelf.contexts[internalId] { + guard let strongSelf = self else { + return + } + if let context = strongSelf.contexts[internalId] { let index = context.subscribers.add { next in subscriber.putNext(next) } @@ -459,8 +463,22 @@ private final class CallSessionManagerContext { } }) } else { - subscriber.putNext(CallSession(id: internalId, stableId: nil, isOutgoing: false, type: .audio, state: .terminated(id: nil, reason: .error(.generic), options: []), isVideoPossible: true)) - subscriber.putCompletion() + if strongSelf.futureContextSubscribers[internalId] == nil { + strongSelf.futureContextSubscribers[internalId] = Bag() + } + let index = strongSelf.futureContextSubscribers[internalId]?.add({ session in + subscriber.putNext(session) + }) + if let index = index { + disposable.set(ActionDisposable { + queue.async { + guard let strongSelf = self else { + return + } + strongSelf.futureContextSubscribers[internalId]?.remove(index) + } + }) + } } } return disposable @@ -517,6 +535,11 @@ private final class CallSessionManagerContext { for subscriber in context.subscribers.copyItems() { subscriber(session) } + if let futureSubscribers = self.futureContextSubscribers[internalId] { + for subscriber in futureSubscribers.copyItems() { + subscriber(session) + } + } } } From ac9dee2ca1a4e63c74edbf038ab82fa8a234c5b4 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Mar 2023 23:09:13 +0400 Subject: [PATCH 7/7] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 78c0df0670..df40ca8ac5 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "9.5.3", + "app": "9.5.4", "bazel": "5.3.1", "xcode": "14.2" }