diff --git a/TelegramUI/ArhivedStickerPacksController.swift b/TelegramUI/ArhivedStickerPacksController.swift index ecc80c2344..4d4d569fc9 100644 --- a/TelegramUI/ArhivedStickerPacksController.swift +++ b/TelegramUI/ArhivedStickerPacksController.swift @@ -221,7 +221,7 @@ private func archivedStickerPacksControllerEntries(presentationData: Presentatio return entries } -public func archivedStickerPacksController(account: Account, archived: [ArchivedStickerPackItem]?) -> ViewController { +public func archivedStickerPacksController(account: Account, archived: [ArchivedStickerPackItem]?, updatedPacks: @escaping([ArchivedStickerPackItem]?)->Void) -> ViewController { let statePromise = ValuePromise(ArchivedStickerPacksControllerState(), ignoreRepeated: true) let stateValue = Atomic(value: ArchivedStickerPacksControllerState()) let updateState: ((ArchivedStickerPacksControllerState) -> ArchivedStickerPacksControllerState) -> Void = { f in @@ -241,6 +241,11 @@ public func archivedStickerPacksController(account: Account, archived: [Archived let stickerPacks = Promise<[ArchivedStickerPackItem]?>() stickerPacks.set(.single(archived) |> then(archivedStickerPacks(account: account) |> map(Optional.init))) + + actionsDisposable.add(stickerPacks.get().start(next: { packs in + updatedPacks(packs) + })) + let installedStickerPacks = Promise() installedStickerPacks.set(account.postbox.combinedView(keys: [.itemCollectionIds(namespaces: [Namespaces.ItemCollection.CloudStickerPacks])])) diff --git a/TelegramUI/ChannelAdminsController.swift b/TelegramUI/ChannelAdminsController.swift index 6269a74b53..26c36ee619 100644 --- a/TelegramUI/ChannelAdminsController.swift +++ b/TelegramUI/ChannelAdminsController.swift @@ -560,13 +560,16 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon } })) }, addAdmin: { - presentControllerImpl?(ChannelMembersSearchController(account: account, peerId: peerId, mode: .promote, openPeer: { peer, participant in - let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - if peer.id == account.peerId { - return - } - if let participant = participant { - switch participant.participant { + updateState { current in + + + presentControllerImpl?(ChannelMembersSearchController(account: account, peerId: peerId, mode: .promote, filters: [.exclude(current.temporaryAdmins.map({$0.peer.id}))], openPeer: { peer, participant in + let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } + if peer.id == account.peerId { + return + } + if let participant = participant { + switch participant.participant { case .creator: return case let .member(_, _, _, banInfo): @@ -574,11 +577,15 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.Channel_Members_AddAdminErrorBlacklisted, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) return } + } } - } - presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in + presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: peer.id, initialParticipant: participant?.participant, updated: { _ in + }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }), ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + + return current + } + }, openAdmin: { participant in if case let .member(adminId, _, _, _) = participant { presentControllerImpl?(channelAdminController(account: account, peerId: peerId, adminId: participant.peerId, initialParticipant: participant, updated: { _ in @@ -619,6 +626,8 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon }) } + + if !state.editing { if rightNavigationButton == nil { rightNavigationButton = ItemListNavigationButton(content: .icon(.search), style: .regular, enabled: true, action: { @@ -634,6 +643,8 @@ public func channelAdminsController(account: Account, peerId: PeerId) -> ViewCon }) } } + + _ = stateValue.swap(state.withUpdatedTemporaryAdmins(admins)) } let previous = previousPeers diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index 9a499ee5fb..9992708b53 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -142,7 +142,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)> - init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { + init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { self.account = account self.openPeer = openPeer self.mode = mode @@ -227,6 +227,12 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod var entries: [ChannelMembersSearchEntry] = [] var existingPeerIds = Set() + for filter in filters { + switch filter { + case let .exclude(ids): + existingPeerIds = existingPeerIds.union(ids) + } + } switch mode { case .inviteActions, .banAndPromoteActions: existingPeerIds.insert(account.peerId) diff --git a/TelegramUI/ChannelMembersSearchController.swift b/TelegramUI/ChannelMembersSearchController.swift index 4fd788a395..280951c153 100644 --- a/TelegramUI/ChannelMembersSearchController.swift +++ b/TelegramUI/ChannelMembersSearchController.swift @@ -9,12 +9,17 @@ enum ChannelMembersSearchControllerMode { case ban } +enum ChannelMembersSearchFilter { + case exclude([PeerId]) +} + final class ChannelMembersSearchController: ViewController { private let queue = Queue() private let account: Account private let peerId: PeerId private let mode: ChannelMembersSearchControllerMode + private let filters: [ChannelMembersSearchFilter] private let openPeer: (Peer, RenderedChannelParticipant?) -> Void private var presentationData: PresentationData @@ -25,12 +30,12 @@ final class ChannelMembersSearchController: ViewController { return self.displayNode as! ChannelMembersSearchControllerNode } - init(account: Account, peerId: PeerId, mode: ChannelMembersSearchControllerMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { + init(account: Account, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter] = [], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { self.account = account self.peerId = peerId self.mode = mode self.openPeer = openPeer - + self.filters = filters self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) @@ -52,7 +57,7 @@ final class ChannelMembersSearchController: ViewController { } override func loadDisplayNode() { - self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peerId: self.peerId, mode: self.mode) + self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peerId: self.peerId, mode: self.mode, filters: self.filters) self.controllerNode.navigationBar = self.navigationBar self.controllerNode.requestActivateSearch = { [weak self] in self?.activateSearch() diff --git a/TelegramUI/ChannelMembersSearchControllerNode.swift b/TelegramUI/ChannelMembersSearchControllerNode.swift index d066426f22..500acb7939 100644 --- a/TelegramUI/ChannelMembersSearchControllerNode.swift +++ b/TelegramUI/ChannelMembersSearchControllerNode.swift @@ -108,7 +108,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { private let account: Account private let peerId: PeerId private let mode: ChannelMembersSearchControllerMode - + private let filters: [ChannelMembersSearchFilter] let listNode: ListView var navigationBar: NavigationBar? @@ -127,12 +127,12 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { private var disposable: Disposable? private var listControl: PeerChannelMemberCategoryControl? - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerId: PeerId, mode: ChannelMembersSearchControllerMode) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter]) { self.account = account self.listNode = ListView() self.peerId = peerId self.mode = mode - + self.filters = filters self.themeAndStrings = (theme, strings) super.init() @@ -168,10 +168,26 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if participant.peer.id == account.peerId { continue } + for filter in filters { + switch filter { + case let .exclude(ids): + if ids.contains(participant.peer.id) { + continue + } + } + } case .promote: if participant.peer.id == account.peerId { continue } + for filter in filters { + switch filter { + case let .exclude(ids): + if ids.contains(participant.peer.id) { + continue + } + } + } if case .creator = participant.participant { label = strings.Channel_Management_LabelCreator enabled = false @@ -268,7 +284,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { } if let placeholderNode = maybePlaceholderNode { - self.searchDisplayController = SearchDisplayController(theme: self.themeAndStrings.0, strings: self.themeAndStrings.1, contentNode: ChannelMembersSearchContainerNode(account: self.account, peerId: self.peerId, mode: .banAndPromoteActions, openPeer: { [weak self] peer, participant in + self.searchDisplayController = SearchDisplayController(theme: self.themeAndStrings.0, strings: self.themeAndStrings.1, contentNode: ChannelMembersSearchContainerNode(account: self.account, peerId: self.peerId, mode: .banAndPromoteActions, filters: self.filters, openPeer: { [weak self] peer, participant in self?.requestOpenPeerFromSearch?(peer, participant) }), cancel: { [weak self] in if let requestDeactivateSearch = self?.requestDeactivateSearch { diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index d030b16afc..ef39b2325c 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -307,6 +307,26 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin controllerInteraction.hiddenMedia = messageIdAndMedia + strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView { + itemNode.updateHiddenMedia() + } + } + } + })) + } + }, chatAvatarHiddenMedia: { signal, media in + if let strongSelf = self { + strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { messageId in + if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { + var messageIdAndMedia: [MessageId: [Media]] = [:] + + if let messageId = messageId { + messageIdAndMedia[messageId] = [media] + } + + controllerInteraction.hiddenMedia = messageIdAndMedia + strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in if let itemNode = itemNode as? ChatMessageItemView { itemNode.updateHiddenMedia() @@ -3828,7 +3848,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin searchDisposable = MetaDisposable() self.searchDisposable = searchDisposable } - searchDisposable.set((searchMessages(account: self.account, location: location, query: query) + searchDisposable.set((searchMessages(account: self.account, location: location, query: query) |> map {$0.0} |> delay(0.2, queue: Queue.mainQueue()) |> deliverOnMainQueue).start(next: { [weak self] results in if let strongSelf = self { diff --git a/TelegramUI/ChatHistoryGridNode.swift b/TelegramUI/ChatHistoryGridNode.swift index 4c013d8a0b..9b9f405ccb 100644 --- a/TelegramUI/ChatHistoryGridNode.swift +++ b/TelegramUI/ChatHistoryGridNode.swift @@ -5,6 +5,65 @@ import Display import AsyncDisplayKit import TelegramCore + +private class ChatGridLiveSelectorRecognizer: UIPanGestureRecognizer { + + private let selectionGestureActivationThreshold: CGFloat = 2.0 + private let selectionGestureVerticalFailureThreshold: CGFloat = 5.0 + + var validatedGesture: Bool? = nil + var firstLocation: CGPoint = CGPoint() + + var shouldBegin: (() -> Bool)? + + override init(target: Any?, action: Selector?) { + super.init(target: target, action: action) + + self.maximumNumberOfTouches = 1 + } + + override func reset() { + super.reset() + + self.validatedGesture = nil + } + + override func touchesBegan(_ touches: Set, with event: UIEvent) { + super.touchesBegan(touches, with: event) + + if let shouldBegin = self.shouldBegin, !shouldBegin() { + self.state = .failed + } else { + let touch = touches.first! + self.firstLocation = touch.location(in: self.view) + } + } + + + override func touchesMoved(_ touches: Set, with event: UIEvent) { + let location = touches.first!.location(in: self.view) + let translation = CGPoint(x: location.x - firstLocation.x, y: location.y - firstLocation.y) + + if validatedGesture == nil { + if (fabs(translation.y) >= selectionGestureVerticalFailureThreshold) + { + validatedGesture = false + } + else if (fabs(translation.x) >= selectionGestureActivationThreshold) { + validatedGesture = true + } + } + + if let validatedGesture = validatedGesture { + if validatedGesture { + super.touchesMoved(touches, with: event) + } + } + + + } +} + struct ChatHistoryGridViewTransition { let historyView: ChatHistoryView let topOffsetWithinMonth: Int @@ -180,13 +239,13 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode { public private(set) var loadState: ChatHistoryNodeLoadState? private var loadStateUpdated: ((ChatHistoryNodeLoadState, Bool) -> Void)? - + private let controllerInteraction: ChatControllerInteraction public init(account: Account, peerId: PeerId, messageId: MessageId?, tagMask: MessageTags?, controllerInteraction: ChatControllerInteraction) { self.account = account self.peerId = peerId self.messageId = messageId self.tagMask = tagMask - + self.controllerInteraction = controllerInteraction self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } super.init() @@ -283,6 +342,39 @@ public final class ChatHistoryGridNode: GridNode, ChatHistoryNode { } } } + + let selectorRecogizner = ChatGridLiveSelectorRecognizer(target: self, action: #selector(self.panGesture(_:))) + selectorRecogizner.shouldBegin = { [weak controllerInteraction] in + return controllerInteraction?.selectionState != nil + } + self.view.addGestureRecognizer(selectorRecogizner) + } + + public override func didLoad() { + super.didLoad() + } + + private var liveSelectingState: (selecting: Bool, currentMessageId: MessageId)? + + @objc private func panGesture(_ recognizer: UIGestureRecognizer) -> Void { + guard let selectionState = controllerInteraction.selectionState else {return} + + switch recognizer.state { + case .began: + if let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId { + liveSelectingState = (selecting: !selectionState.selectedIds.contains(messageId), currentMessageId: messageId) + controllerInteraction.toggleMessagesSelection([messageId], !selectionState.selectedIds.contains(messageId)) + } + case .changed: + if let liveSelectingState = liveSelectingState, let itemNode = self.itemNodeAtPoint(recognizer.location(in: self.view)) as? GridMessageItemNode, let messageId = itemNode.messageId, messageId != liveSelectingState.currentMessageId { + controllerInteraction.toggleMessagesSelection([messageId], liveSelectingState.selecting) + self.liveSelectingState?.currentMessageId = messageId + } + case .ended, .failed, .cancelled: + liveSelectingState = nil + case .possible: + break + } } required public init?(coder aDecoder: NSCoder) { diff --git a/TelegramUI/ChatHistorySearchContainerNode.swift b/TelegramUI/ChatHistorySearchContainerNode.swift index 1372c3b3a9..c9ec89b1bd 100644 --- a/TelegramUI/ChatHistorySearchContainerNode.swift +++ b/TelegramUI/ChatHistorySearchContainerNode.swift @@ -151,7 +151,7 @@ final class ChatHistorySearchContainerNode: SearchDisplayControllerContentNode { if let strongSelf = self { let signal: Signal<[ChatHistorySearchEntry]?, NoError> if let query = query, !query.isEmpty { - let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query) + let foundRemoteMessages: Signal<[Message], NoError> = searchMessages(account: account, location: .peer(peerId: peerId, fromId: nil, tags: tagMask), query: query) |> map {$0.0} |> delay(0.2, queue: Queue.concurrentDefaultQueue()) signal = combineLatest(foundRemoteMessages, themeAndStringsPromise.get()) diff --git a/TelegramUI/ChatInterfaceStateContextMenus.swift b/TelegramUI/ChatInterfaceStateContextMenus.swift index b48d5355ee..29261525bc 100644 --- a/TelegramUI/ChatInterfaceStateContextMenus.swift +++ b/TelegramUI/ChatInterfaceStateContextMenus.swift @@ -268,14 +268,16 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } } + var hasUneditableAttributes = false + + if let peer = message.peers[message.id.peerId] as? TelegramChannel { if peer.hasBannedRights(.banSendMessages) { - restrictEdit = true + hasUneditableAttributes = true } } if hasEditRights { - var hasUneditableAttributes = false for attribute in message.attributes { if let _ = attribute as? InlineBotMessageAttribute { hasUneditableAttributes = true @@ -298,13 +300,16 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } else if let _ = media as? TelegramMediaExpiredContent { hasUneditableAttributes = true break + } else if let _ = media as? TelegramMediaMap { + hasUneditableAttributes = true + break } } if !hasUneditableAttributes { let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) if canPerformEditingActions(limits: limitsConfiguration, accountPeerId: account.peerId, message: message) { - canEdit = !restrictEdit + canEdit = true } } } diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index 571715d15a..cfa94360f9 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -235,7 +235,7 @@ enum ChatListSearchEntryStableId: Hashable { enum ChatListSearchEntry: Comparable, Identifiable { case localPeer(Peer, Peer?, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings) case globalPeer(FoundPeer, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings) - case message(Message, ChatListPresentationData) + case message(Message, CombinedPeerReadState?, ChatListPresentationData) var stableId: ChatListSearchEntryStableId { switch self { @@ -243,7 +243,7 @@ enum ChatListSearchEntry: Comparable, Identifiable { return .localPeerId(peer.id) case let .globalPeer(peer, _, _, _, _): return .globalPeerId(peer.peer.id) - case let .message(message, _): + case let .message(message, _, _): return .messageId(message.id) } } @@ -262,8 +262,8 @@ enum ChatListSearchEntry: Comparable, Identifiable { } else { return false } - case let .message(lhsMessage, lhsPresentationData): - if case let .message(rhsMessage, rhsPresentationData) = rhs { + case let .message(lhsMessage, lhsCombinedPeerReadState, lhsPresentationData): + if case let .message(rhsMessage, rhsCombinedPeerReadState, rhsPresentationData) = rhs { if lhsMessage.id != rhsMessage.id { return false } @@ -273,6 +273,9 @@ enum ChatListSearchEntry: Comparable, Identifiable { if lhsPresentationData !== rhsPresentationData { return false } + if lhsCombinedPeerReadState != rhsCombinedPeerReadState { + return false + } return true } else { return false @@ -297,8 +300,8 @@ enum ChatListSearchEntry: Comparable, Identifiable { case .message: return true } - case let .message(lhsMessage, _): - if case let .message(rhsMessage, _) = rhs { + case let .message(lhsMessage, _, _): + if case let .message(rhsMessage, _, _) = rhs { return MessageIndex(lhsMessage) < MessageIndex(rhsMessage) } else { return false @@ -393,8 +396,8 @@ enum ChatListSearchEntry: Comparable, Identifiable { return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in interaction.peerSelected(peer.peer) }) - case let .message(message, presentationData): - return ChatListItem(presentationData: presentationData, account: account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(message)), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: nil, notificationSettings: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction) + case let .message(message, readState, presentationData): + return ChatListItem(presentationData: presentationData, account: account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: MessageIndex(message)), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: readState, notificationSettings: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction) } } } @@ -566,7 +569,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { } else { location = .general } - let foundRemoteMessages: Signal<([Message], Bool), NoError> = .single(([], true)) |> then(searchMessages(account: account, location: location, query: query) + let foundRemoteMessages: Signal<(([Message], [PeerId : CombinedPeerReadState]), Bool), NoError> = .single((([], [:]), true)) |> then(searchMessages(account: account, location: location, query: query) |> map { ($0, false) } |> delay(0.2, queue: Queue.concurrentDefaultQueue())) @@ -619,8 +622,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { if !foundRemotePeers.2 { index = 0 - for message in foundRemoteMessages.0 { - entries.append(.message(message, presentationData)) + for message in foundRemoteMessages.0.0 { + entries.append(.message(message, foundRemoteMessages.0.1[message.id.peerId], presentationData)) index += 1 } } diff --git a/TelegramUI/ChatMessageActionItemNode.swift b/TelegramUI/ChatMessageActionItemNode.swift index fef2d0d36b..44d82e5502 100644 --- a/TelegramUI/ChatMessageActionItemNode.swift +++ b/TelegramUI/ChatMessageActionItemNode.swift @@ -546,6 +546,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { if j != 0 && index + j >= 0 && index + j < sortedIndices.count { if abs(labelRects[index + j].width - labelRects[index].width) < 40.0 { labelRects[index + j].size.width = max(labelRects[index + j].width, labelRects[index].width) + labelRects[index].size.width = labelRects[index + j].size.width } } } @@ -557,6 +558,7 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { labelRects[i].origin.x = floor((labelLayout.size.width - labelRects[i].width) / 2.0) } + let backgroundApply = backgroundLayout(item.presentationData.theme.theme.chat.serviceMessage.serviceMessageFillColor, labelRects, 10.0, 10.0, 0.0) var backgroundSize = CGSize(width: labelLayout.size.width + 8.0 + 8.0, height: labelLayout.size.height + 4.0) @@ -584,9 +586,9 @@ class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { let apply = imageNode.asyncLayout()(arguments) apply() - strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(account: item.account, photoReference: .message(message: MessageReference(item.message), media: image)).start()) } - let updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: ImageMediaReference.message(message: MessageReference(item.message), media: image)) + strongSelf.fetchDisposable.set(chatMessagePhotoInteractiveFetched(account: item.account, photoReference: .message(message: MessageReference(item.message), media: image)).start()) + let updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: .message(message: MessageReference(item.message), media: image)) imageNode.setSignal(updateImageSignal) diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 489656b161..126131ba88 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -264,7 +264,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } for media in item.content.firstMessage.media { if let media = media as? TelegramMediaAction, case .phoneCall(_, _, _) = media.action { - } else { return false } } diff --git a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index 2b0aea6a01..106130d4df 100644 --- a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -214,7 +214,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: strings.Conversation_PinnedMessage, font: Font.medium(15.0), textColor: theme.chat.inputPanel.panelControlAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) - let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: !message.media.isEmpty ? theme.chat.inputPanel.secondaryTextColor : theme.chat.inputPanel.primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: width - textLineInset - contentLeftInset - rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets(top: 2.0, left: 0.0, bottom: 2.0, right: 0.0))) Queue.mainQueue().async { if let strongSelf = self { diff --git a/TelegramUI/ChatRecentActionsControllerNode.swift b/TelegramUI/ChatRecentActionsControllerNode.swift index f3f099b121..5bf951b87d 100644 --- a/TelegramUI/ChatRecentActionsControllerNode.swift +++ b/TelegramUI/ChatRecentActionsControllerNode.swift @@ -167,27 +167,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, callPeer: { peerId in self?.controllerInteraction?.callPeer(peerId) }, enqueueMessage: { _ in - }, sendSticker: nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in - if let strongSelf = self { - /*strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in - if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { - var messageIdAndMedia: [MessageId: [Media]] = [:] - - if let entry = entry, entry.index == centralIndex { - messageIdAndMedia[message.id] = [galleryMedia] - } - - controllerInteraction.hiddenMedia = messageIdAndMedia - - strongSelf.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView { - itemNode.updateHiddenMedia() - } - } - } - }))*/ - } - }) + }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in}) } return false }, openPeer: { [weak self] peerId, _, message in diff --git a/TelegramUI/GalleryThumbnailContainerNode.swift b/TelegramUI/GalleryThumbnailContainerNode.swift index fcbac4e3e2..1c812dc80e 100644 --- a/TelegramUI/GalleryThumbnailContainerNode.swift +++ b/TelegramUI/GalleryThumbnailContainerNode.swift @@ -63,6 +63,7 @@ final class GalleryThumbnailContainerNode: ASDisplayNode { } func updateItems(_ items: [GalleryThumbnailItem], centralIndex: Int, progress: CGFloat) { + var items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items var updated = false if self.items.count == items.count { for i in 0 ..< self.items.count { diff --git a/TelegramUI/GridMessageItem.swift b/TelegramUI/GridMessageItem.swift index fd4f276b69..d7f30fd379 100644 --- a/TelegramUI/GridMessageItem.swift +++ b/TelegramUI/GridMessageItem.swift @@ -5,6 +5,42 @@ import TelegramCore import Postbox import SwiftSignalKit +private let videoAccessoryFont: UIFont = Font.regular(11) + +private final class GridMessageVideoAccessoryNode : ASDisplayNode { + + private let textNode: ImmediateTextNode = ImmediateTextNode() + + override init() { + super.init() + self.textNode.displaysAsynchronously = false + self.textNode.maximumNumberOfLines = 1 + self.textNode.isLayerBacked = true + self.textNode.textAlignment = .left + self.textNode.lineSpacing = 0.1 + addSubnode(self.textNode) + backgroundColor = UIColor(white: 0.0, alpha: 0.6) + } + + var contentSize: CGSize { + return CGSize(width: textSize.width + 10, height: 16) + } + private var textSize: CGSize = CGSize() + + func setup(_ duration: String) { + textNode.attributedText = NSAttributedString(string: duration, font: videoAccessoryFont, textColor: .white, paragraphAlignment: nil) + textSize = self.textNode.updateLayout(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)) + } + + override func layout() { + if let _ = self.textNode.attributedText { + self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((frame.width - textSize.width) / 2.0), y: floorToScreenPixels((frame.height - textSize.height) / 2.0) + 0.5), size: textSize) + } + } + + +} + private func mediaForMessage(_ message: Message) -> Media? { for media in message.media { if let media = media as? TelegramMediaImage { @@ -111,7 +147,6 @@ final class GridMessageItem: GridItem { private let account: Account fileprivate let message: Message private let controllerInteraction: ChatControllerInteraction - let section: GridSection? init(theme: PresentationTheme, strings: PresentationStrings, account: Account, message: Message, controllerInteraction: ChatControllerInteraction) { @@ -145,11 +180,12 @@ final class GridMessageItem: GridItem { final class GridMessageItemNode: GridItemNode { private var currentState: (Account, Media, CGSize)? private let imageNode: TransformImageNode - private var messageId: MessageId? + private(set) var messageId: MessageId? private var item: GridMessageItem? private var controllerInteraction: ChatControllerInteraction? private var statusNode: RadialStatusNode - + private let videoAccessoryNode = GridMessageVideoAccessoryNode() + private var selectionNode: GridMessageSelectionNode? private let fetchStatusDisposable = MetaDisposable() @@ -164,6 +200,7 @@ final class GridMessageItemNode: GridItemNode { super.init() self.addSubnode(self.imageNode) + self.imageNode.addSubnode(videoAccessoryNode) } deinit { @@ -193,11 +230,20 @@ final class GridMessageItemNode: GridItemNode { self.statusNode.transitionToState(.none, completion: { [weak self] in self?.statusNode.isHidden = true }) + videoAccessoryNode.isHidden = true self.resourceStatus = nil } else if let file = media as? TelegramMediaFile, file.isVideo { mediaDimensions = file.dimensions self.imageNode.setSignal(mediaGridMessageVideo(postbox: account.postbox, videoReference: .message(message: MessageReference(item.message), media: file))) + if let duration = file.duration { + videoAccessoryNode.setup(String(format: "%d:%02d", duration / 60, duration % 60)) + videoAccessoryNode.isHidden = false + } else { + videoAccessoryNode.isHidden = true + } + + self.resourceStatus = nil self.fetchStatusDisposable.set((messageMediaFileStatus(account: account, messageId: messageId, file: file) |> deliverOnMainQueue).start(next: { [weak self] status in if let strongSelf = self { @@ -233,6 +279,8 @@ final class GridMessageItemNode: GridItemNode { if self.statusNode.supernode == nil { self.imageNode.addSubnode(self.statusNode) } + } else { + videoAccessoryNode.isHidden = true } if let mediaDimensions = mediaDimensions { @@ -263,6 +311,8 @@ final class GridMessageItemNode: GridItemNode { self.selectionNode?.frame = CGRect(origin: CGPoint(), size: self.bounds.size) let progressDiameter: CGFloat = 40.0 self.statusNode.frame = CGRect(origin: CGPoint(x: floor((imageFrame.size.width - progressDiameter) / 2.0), y: floor((imageFrame.size.height - progressDiameter) / 2.0)), size: CGSize(width: progressDiameter, height: progressDiameter)) + + videoAccessoryNode.frame = CGRect(origin: CGPoint(x: imageFrame.maxX - videoAccessoryNode.contentSize.width - 5, y: imageFrame.maxY - videoAccessoryNode.contentSize.height - 5), size: videoAccessoryNode.contentSize) } func updateSelectionState(animated: Bool) { diff --git a/TelegramUI/GroupInfoSearchItem.swift b/TelegramUI/GroupInfoSearchItem.swift index 6e46967b84..164ec05504 100644 --- a/TelegramUI/GroupInfoSearchItem.swift +++ b/TelegramUI/GroupInfoSearchItem.swift @@ -51,7 +51,7 @@ private final class ChannelMembersSearchItemNode: ItemListControllerSearchNode { private let containerNode: ChannelMembersSearchContainerNode init(account: Account, peerId: PeerId, searchMode: ChannelMembersSearchMode, openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void, cancel: @escaping () -> Void) { - self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, openPeer: { peer, participant in + self.containerNode = ChannelMembersSearchContainerNode(account: account, peerId: peerId, mode: searchMode, filters: [], openPeer: { peer, participant in openPeer(peer, participant) }) self.containerNode.cancel = { diff --git a/TelegramUI/HashtagSearchController.swift b/TelegramUI/HashtagSearchController.swift index 9769687c1e..c9ca216d94 100644 --- a/TelegramUI/HashtagSearchController.swift +++ b/TelegramUI/HashtagSearchController.swift @@ -38,7 +38,9 @@ final class HashtagSearchController: TelegramController { let location: SearchMessagesLocation = .general let search = searchMessages(account: account, location: location, query: query) let foundMessages: Signal<[ChatListSearchEntry], NoError> = search - |> map { return $0.map({ .message($0, chatListPresentationData) }) } + |> map { result in + return result.0.map({ .message($0, result.1[$0.id.peerId], chatListPresentationData) }) + } let interaction = ChatListNodeInteraction(activateSearch: { }, peerSelected: { peer in diff --git a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift index bd06acd81e..9e2486892a 100644 --- a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift +++ b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift @@ -77,11 +77,14 @@ private let iconTextBackgroundImage = generateStretchableFilledCircleImage(radiu final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode { private let imageNodeBackground: ASDisplayNode private let imageNode: TransformImageNode + private var statusNode: RadialStatusNode? private var videoLayer: (SoftwareVideoThumbnailLayer, SoftwareVideoLayerFrameManager, SampleBufferLayer)? private var currentImageResource: TelegramMediaResource? private var currentVideoFile: TelegramMediaFile? - + private var resourceStatus: MediaResourceStatus? private(set) var item: HorizontalListContextResultsChatInputPanelItem? + private var statusDisposable = MetaDisposable() + override var visibility: ListViewItemNodeVisibility { didSet { @@ -165,6 +168,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode displayLink.isPaused = true displayLink.invalidate() } + statusDisposable.dispose() } override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { @@ -189,7 +193,10 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode let sideInset: CGFloat = 4.0 var updateImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - + var updatedStatusSignal: Signal? +//messageFileMediaResourceStatus(account: account, file: file, message: message, isRecentActions: isRecentActions) + + var imageResource: TelegramMediaResource? var stickerFile: TelegramMediaFile? var videoFile: TelegramMediaFile? @@ -206,6 +213,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode videoFile = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource)], mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]) imageResource = nil } + + if let file = videoFile { + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource) + } else if let imageResource = imageResource { + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource) + } case let .internalReference(_, _, _, title, _, image, file, _): if let image = image { if let largestRepresentation = largestImageRepresentation(image.representations) { @@ -230,7 +243,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode if file.isVideo && file.isAnimated { videoFile = file imageResource = nil + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(file.resource) + } else if let imageResource = imageResource { + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource) } + } else if let imageResource = imageResource { + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource) } } @@ -282,6 +300,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode } } + let nodeLayout = ListViewItemNodeLayout(contentSize: CGSize(width: height, height: croppedImageDimensions.width + sideInset), insets: UIEdgeInsets()) return (nodeLayout, { _ in @@ -333,6 +352,52 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode } } + let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((nodeLayout.contentSize.width - 37) / 2), y: floorToScreenPixels((nodeLayout.contentSize.height - 37) / 2)), size: CGSize(width: 37, height: 37)) + + + if let updatedStatusSignal = updatedStatusSignal { + strongSelf.statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status in + displayLinkDispatcher.dispatch { + if let strongSelf = strongSelf { + strongSelf.resourceStatus = status + + if strongSelf.statusNode == nil { + let statusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) + strongSelf.statusNode = statusNode + strongSelf.addSubnode(statusNode) + } + + strongSelf.statusNode?.frame = progressFrame + + + let state: RadialStatusNodeState + let statusForegroundColor: UIColor = .white + + switch status { + case let .Fetching(_, progress): + state = RadialStatusNodeState.progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(progress), cancelEnabled: false) + case .Remote: + state = .download(statusForegroundColor) + case .Local: + state = .none + } + + + if let statusNode = strongSelf.statusNode { + if state == .none { + strongSelf.statusNode = nil + } + statusNode.transitionToState(state, completion: { [weak statusNode] in + if state == .none { + statusNode?.removeFromSupernode() + } + }) + } + } + } + })) + } + if let (thumbnailLayer, _, layer) = strongSelf.videoLayer { thumbnailLayer.bounds = CGRect(origin: CGPoint(), size: CGSize(width: croppedImageDimensions.width, height: croppedImageDimensions.height)) thumbnailLayer.position = CGPoint(x: height / 2.0, y: (nodeLayout.contentSize.height - sideInset) / 2.0 + sideInset) diff --git a/TelegramUI/InstalledStickerPacksController.swift b/TelegramUI/InstalledStickerPacksController.swift index e304bbd458..8e1d4145d3 100644 --- a/TelegramUI/InstalledStickerPacksController.swift +++ b/TelegramUI/InstalledStickerPacksController.swift @@ -378,7 +378,7 @@ public enum InstalledStickerPacksControllerMode { case masks } -public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks:[ArchivedStickerPackItem]? = nil) -> ViewController { +public func installedStickerPacksController(account: Account, mode: InstalledStickerPacksControllerMode, archivedPacks:[ArchivedStickerPackItem]? = nil, updatedPacks: @escaping([ArchivedStickerPackItem]?) -> Void = { _ in}) -> ViewController { let initialState = InstalledStickerPacksControllerState().withUpdatedEditing(mode == .modal) let statePromise = ValuePromise(initialState, ignoreRepeated: true) let stateValue = Atomic(value: initialState) @@ -396,6 +396,9 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti let resolveDisposable = MetaDisposable() actionsDisposable.add(resolveDisposable) + let archivedPromise = Promise<[ArchivedStickerPackItem]?>() + + var presentStickerPackController: ((StickerPackCollectionInfo) -> Void)? let arguments = InstalledStickerPacksControllerArguments(account: account, openStickerPack: { info in @@ -436,11 +439,14 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti } })) }, openMasks: { - pushControllerImpl?(installedStickerPacksController(account: account, mode: .masks, archivedPacks: archivedPacks)) + pushControllerImpl?(installedStickerPacksController(account: account, mode: .masks, archivedPacks: archivedPacks, updatedPacks: { _ in})) }, openFeatured: { pushControllerImpl?(featuredStickerPacksController(account: account)) }, openArchived: { archived in - pushControllerImpl?(archivedStickerPacksController(account: account, archived: archived)) + pushControllerImpl?(archivedStickerPacksController(account: account, archived: archived, updatedPacks: { packs in + archivedPromise.set(.single(packs)) + updatedPacks(packs) + })) }, openSuggestOptions: { let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let controller = ActionSheetController(presentationTheme: presentationData.theme) @@ -484,7 +490,6 @@ public func installedStickerPacksController(account: Account, mode: InstalledSti stickerPacks.set(account.postbox.combinedView(keys: [.itemCollectionInfos(namespaces: [namespaceForMode(mode)])])) let featured = Promise<[FeaturedStickerPackItem]>() - let archivedPromise = Promise<[ArchivedStickerPackItem]?>() switch mode { case .general, .modal: diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index d6f88cd645..b0e8bb057a 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -151,7 +151,7 @@ func chatMessagePreviewControllerData(account: Account, message: Message, standa return nil } -func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal, Int, Media) -> Void) -> Bool { +func openChatMessage(account: Account, message: Message, standalone: Bool, reverseMessageGalleryOrder: Bool, navigationController: NavigationController?, modal: Bool = false, dismissInput: @escaping () -> Void, present: @escaping (ViewController, Any?) -> Void, transitionNode: @escaping (MessageId, Media) -> (ASDisplayNode, () -> UIView?)?, addToTransitionSurface: @escaping (UIView) -> Void, openUrl: @escaping (String) -> Void, openPeer: @escaping (Peer, ChatControllerInteractionNavigateToPeer) -> Void, callPeer: @escaping (PeerId) -> Void, enqueueMessage: @escaping (EnqueueMessage) -> Void, sendSticker: ((FileMediaReference) -> Void)?, setupTemporaryHiddenMedia: @escaping (Signal, Int, Media) -> Void, chatAvatarHiddenMedia: @escaping (Signal, Media) -> Void) -> Bool { if let mediaData = chatMessageGalleryControllerData(account: account, message: message, navigationController: navigationController, standalone: standalone, reverseMessageGalleryOrder: reverseMessageGalleryOrder, synchronousLoad: false) { switch mediaData { case let .url(url): @@ -327,7 +327,14 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever return true } case let .chatAvatars(controller, media): - + chatAvatarHiddenMedia(controller.hiddenMedia |> map { value -> MessageId? in + if value != nil { + return message.id + } else { + return nil + } + }, media) + present(controller, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in if let selectedTransitionNode = transitionNode(message.id, media) { return GalleryTransitionArguments(transitionNode: selectedTransitionNode, addToTransitionSurface: addToTransitionSurface) diff --git a/TelegramUI/OverlayPlayerControllerNode.swift b/TelegramUI/OverlayPlayerControllerNode.swift index 7716494906..bcb8283916 100644 --- a/TelegramUI/OverlayPlayerControllerNode.swift +++ b/TelegramUI/OverlayPlayerControllerNode.swift @@ -156,7 +156,7 @@ final class OverlayPlayerControllerNode: ViewControllerTracingNode, UIGestureRec openMessageImpl = { [weak self] id in if let strongSelf = self, strongSelf.isNodeLoaded, let message = strongSelf.historyNode.messageInCurrentHistoryView(id) { - return openChatMessage(account: strongSelf.account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }) + return openChatMessage(account: strongSelf.account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in}) } return false } diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 7cbbee5c42..6ab80558c8 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -104,8 +104,7 @@ public class PeerMediaCollectionController: TelegramController { }, callPeer: { peerId in self?.controllerInteraction?.callPeer(peerId) }, enqueueMessage: { _ in - }, sendSticker: nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in - }) + }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in}) } return false }, openPeer: { [weak self] id, navigation, _ in @@ -517,6 +516,7 @@ public class PeerMediaCollectionController: TelegramController { func updateInterfaceState(animated: Bool = true, _ f: (PeerMediaCollectionInterfaceState) -> PeerMediaCollectionInterfaceState) { let updatedInterfaceState = f(self.interfaceState) + if self.isNodeLoaded { self.mediaCollectionDisplayNode.updateMediaCollectionInterfaceState(updatedInterfaceState, animated: animated) } @@ -545,6 +545,7 @@ public class PeerMediaCollectionController: TelegramController { } self.mediaCollectionDisplayNode.selectedMessages = updatedInterfaceState.selectionState?.selectedIds + view.disablesInteractiveTransitionGestureRecognizer = updatedInterfaceState.selectionState != nil } } } diff --git a/TelegramUI/SettingsController.swift b/TelegramUI/SettingsController.swift index e201219378..1c62710f97 100644 --- a/TelegramUI/SettingsController.swift +++ b/TelegramUI/SettingsController.swift @@ -45,6 +45,7 @@ private struct SettingsItemArguments { let openSupport: () -> Void let openFaq: () -> Void let openEditing: () -> Void + let updateArchivedPacks: ([ArchivedStickerPackItem]?) -> Void } private enum SettingsSection: Int32 { @@ -285,7 +286,9 @@ private enum SettingsEntry: ItemListNodeEntry { }) case let .stickers(theme, image, text, value, archivedPacks): return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: value, labelStyle: .badge, sectionId: ItemListSectionId(self.section), style: .blocks, action: { - arguments.pushController(installedStickerPacksController(account: arguments.account, mode: .general, archivedPacks: archivedPacks)) + arguments.pushController(installedStickerPacksController(account: arguments.account, mode: .general, archivedPacks: archivedPacks, updatedPacks: { packs in + arguments.updateArchivedPacks(packs) + })) }) case let .notificationsAndSounds(theme, image, text): return ItemListDisclosureItem(theme: theme, icon: image, title: text, label: "", sectionId: ItemListSectionId(self.section), style: .blocks, action: { @@ -426,6 +429,9 @@ public func settingsController(account: Account, accountManager: AccountManager) var changeProfilePhotoImpl: (() -> Void)? var openSavedMessagesImpl: (() -> Void)? + let archivedPacks = Promise<[ArchivedStickerPackItem]?>() + + let openFaq: () -> Void = { let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } var faqUrl = presentationData.strings.Settings_FAQ_URL @@ -517,6 +523,8 @@ public func settingsController(account: Account, accountManager: AccountManager) pushControllerImpl?(editSettingsController(account: account, currentName: .personName(firstName: peer.firstName ?? "", lastName: peer.lastName ?? ""), currentBioText: cachedData.about ?? "", accountManager: accountManager)) } }) + }, updateArchivedPacks: { packs in + archivedPacks.set(.single(packs)) }) changeProfilePhotoImpl = { @@ -601,7 +609,6 @@ public func settingsController(account: Account, accountManager: AccountManager) let peerView = account.viewTracker.peerView(account.peerId) - let archivedPacks = Promise<[ArchivedStickerPackItem]?>() archivedPacks.set(.single(nil) |> then(archivedStickerPacks(account: account) |> map(Optional.init))) let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get(), peerView, account.postbox.preferencesView(keys: [PreferencesKeys.proxySettings]), combineLatest(account.viewTracker.featuredStickerPacks(), archivedPacks.get())) diff --git a/TelegramUI/TelegramController.swift b/TelegramUI/TelegramController.swift index cd16dcbdb6..3109252df1 100644 --- a/TelegramUI/TelegramController.swift +++ b/TelegramUI/TelegramController.swift @@ -27,8 +27,7 @@ private func presentLiveLocationController(account: Account, peerId: PeerId, con }, openPeer: { peer, navigation in }, callPeer: { _ in }, enqueueMessage: { _ in - }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in - }) + }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in}) } }) }