diff --git a/TelegramUI/ChannelMemberCategoryListContext.swift b/TelegramUI/ChannelMemberCategoryListContext.swift index 9d63557a0f..d0b1ea3854 100644 --- a/TelegramUI/ChannelMemberCategoryListContext.swift +++ b/TelegramUI/ChannelMemberCategoryListContext.swift @@ -58,7 +58,7 @@ private protocol ChannelMemberCategoryListContext { var listStateValue: ChannelMemberListState { get } var listState: Signal { get } func loadMore() - func reset() + func reset(_ force: Bool) func replayUpdates(_ updates: [(ChannelParticipant?, RenderedChannelParticipant?)]) func forceUpdateHead() } @@ -136,12 +136,12 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor })) } - func reset() { + func reset(_ force: Bool) { if case .loading = self.listStateValue.loadingState, self.listStateValue.list.isEmpty { } else { var list = self.listStateValue.list - var loadingState: ChannelMemberListLoadingState = .ready(hasMore: false) - if list.count > Int(initialBatchSize) { + var loadingState: ChannelMemberListLoadingState = .ready(hasMore: true) + if list.count > Int(initialBatchSize) && !force { list.removeSubrange(Int(initialBatchSize) ..< list.count) loadingState = .ready(hasMore: true) } @@ -151,6 +151,7 @@ private final class ChannelMemberSingleCategoryListContext: ChannelMemberCategor } } + private func loadSignal(offset: Int32, count: Int32, hash: Int32) -> Signal<[RenderedChannelParticipant]?, NoError> { let requestCategory: ChannelMembersCategory var adminQuery: String? = nil @@ -458,9 +459,9 @@ private final class ChannelMemberMultiCategoryListContext: ChannelMemberCategory } } - func reset() { + func reset(_ force: Bool) { for context in self.contexts { - context.reset() + context.reset(force) } } @@ -508,7 +509,7 @@ private final class PeerChannelMemberContextWithSubscribers { } private func resetAndBeginEmptyTimer() { - self.context.reset() + self.context.reset(false) self.emptyTimer?.invalidate() let emptyTimer = SwiftSignalKit.Timer(timeout: emptyTimeout, repeat: false, completion: { [weak self] in if let strongSelf = self { @@ -559,6 +560,15 @@ final class PeerChannelMemberCategoriesContext { self.becameEmpty = becameEmpty } + func reset(_ key: PeerChannelMemberContextKey) { + for (contextKey, context) in contexts { + if contextKey == key { + context.context.reset(true) + context.context.loadMore() + } + } + } + func getContext(key: PeerChannelMemberContextKey, requestUpdate: Bool, updated: @escaping (ChannelMemberListState) -> Void) -> (Disposable, PeerChannelMemberCategoryControl) { assert(Queue.mainQueue().isCurrent()) if let current = self.contexts[key] { diff --git a/TelegramUI/ChannelMembersController.swift b/TelegramUI/ChannelMembersController.swift index 40ece32655..57d286f5e2 100644 --- a/TelegramUI/ChannelMembersController.swift +++ b/TelegramUI/ChannelMembersController.swift @@ -319,91 +319,41 @@ public func channelMembersController(account: Account, peerId: PeerId) -> ViewCo let peersPromise = Promise<[RenderedChannelParticipant]?>(nil) let arguments = ChannelMembersControllerArguments(account: account, addMember: { - var confirmationImpl: ((PeerId) -> Signal)? - let contactsController = ContactSelectionController(account: account, title: { $0.GroupInfo_AddParticipantTitle }, confirmation: { peer in - if let confirmationImpl = confirmationImpl, case let .peer(peer, _) = peer { - return confirmationImpl(peer.id) - } else { - return .single(false) - } - }) - confirmationImpl = { [weak contactsController] selectedId in - return combineLatest(account.postbox.loadedPeerWithId(selectedId), account.postbox.loadedPeerWithId(peerId)) - |> deliverOnMainQueue - |> mapToSignal { peer, channelPeer in - let result = ValuePromise() - - if let contactsController = contactsController { - let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - let confirmationText: String - if let channel = channelPeer as? TelegramChannel { - switch channel.info { - case .broadcast: - confirmationText = presentationData.strings.ChannelInfo_AddParticipantConfirmation(peer.displayTitle).0 - case .group: - confirmationText = presentationData.strings.GroupInfo_AddParticipantConfirmation(peer.displayTitle).0 - } - } else { - confirmationText = presentationData.strings.GroupInfo_AddParticipantConfirmation(peer.displayTitle).0 - } - let alertController = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: confirmationText, actions: [ - TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { - result.set(false) - }), - TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: { - result.set(true) - }) - ]) - contactsController.present(alertController, in: .window(.root)) - } - - return result.get() - } - } - let addMember = contactsController.result - |> mapError { _ -> AddPeerMemberError in return .generic } - |> deliverOnMainQueue - |> mapToSignal { memberPeer -> Signal in - if let memberPeer = memberPeer, case let .peer(selectedPeer, _) = memberPeer { - let memberId = selectedPeer.id - let applyMembers: Signal = peersPromise.get() - |> filter { $0 != nil } - |> take(1) - |> mapToSignal { peers -> Signal in - return account.postbox.transaction { transaction -> Peer? in - return transaction.getPeer(memberId) - } - |> deliverOnMainQueue - |> mapToSignal { peer -> Signal in - if let peer = peer, let peers = peers { - var updatedPeers = peers - var found = false - for i in 0 ..< updatedPeers.count { - if updatedPeers[i].peer.id == memberId { - found = true - break - } - } - if !found { - let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) - updatedPeers.append(RenderedChannelParticipant(participant: ChannelParticipant.member(id: peer.id, invitedAt: timestamp, adminInfo: nil, banInfo: nil), peer: peer, peers: [:])) - peersPromise.set(.single(updatedPeers)) - } - } - return .complete() - } - } - |> mapError { _ -> AddPeerMemberError in return .generic } + actionsDisposable.add((peersPromise.get() |> take(1) |> deliverOnMainQueue).start(next: { members in + let disabledIds = members?.compactMap({$0.peer.id}) ?? [] + let contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: [], filters: [.excludeSelf, .disable(disabledIds)]) - return addPeerMember(account: account, peerId: peerId, memberId: memberId) - |> then(applyMembers) - } else { - return .complete() + let addMembers: ([ContactListPeerId]) -> Signal = { members -> Signal in + let peerIds = members.compactMap { contact -> PeerId? in + switch contact { + case let .peer(peerId): + return peerId + default: + return nil + } + } + return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.addMembers(account: account, peerId: peerId, memberIds: peerIds) } - } - presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - addMembersDisposable.set(addMember.start()) + + peersPromise.set(contactsController.result + |> deliverOnMainQueue |> mapToSignal { [weak contactsController] contacts in + contactsController?.displayProgress = true + + return addMembers(contacts) |> mapToSignal { _ in + return channelMembers(postbox: account.postbox, network: account.network, peerId: peerId) + } |> deliverOnMainQueue |> afterNext { _ in + contactsController?.dismiss() + } + }) + + contactsController.dismissed = { + + } + + presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) + })) + }, setPeerIdWithRevealedOptions: { peerId, fromPeerId in updateState { state in if (peerId == nil && fromPeerId == state.peerIdWithRevealedOptions) || (peerId != nil && fromPeerId == nil) { diff --git a/TelegramUI/ChannelMembersSearchContainerNode.swift b/TelegramUI/ChannelMembersSearchContainerNode.swift index 9992708b53..f043e536cf 100644 --- a/TelegramUI/ChannelMembersSearchContainerNode.swift +++ b/TelegramUI/ChannelMembersSearchContainerNode.swift @@ -231,6 +231,8 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod switch filter { case let .exclude(ids): existingPeerIds = existingPeerIds.union(ids) + case .disable: + break } } switch mode { diff --git a/TelegramUI/ChannelMembersSearchController.swift b/TelegramUI/ChannelMembersSearchController.swift index 280951c153..12a5e32346 100644 --- a/TelegramUI/ChannelMembersSearchController.swift +++ b/TelegramUI/ChannelMembersSearchController.swift @@ -11,6 +11,7 @@ enum ChannelMembersSearchControllerMode { enum ChannelMembersSearchFilter { case exclude([PeerId]) + case disable([PeerId]) } final class ChannelMembersSearchController: ViewController { diff --git a/TelegramUI/ChannelMembersSearchControllerNode.swift b/TelegramUI/ChannelMembersSearchControllerNode.swift index 500acb7939..8a5a8c66c6 100644 --- a/TelegramUI/ChannelMembersSearchControllerNode.swift +++ b/TelegramUI/ChannelMembersSearchControllerNode.swift @@ -174,6 +174,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if ids.contains(participant.peer.id) { continue } + case .disable: + break } } case .promote: @@ -186,6 +188,8 @@ class ChannelMembersSearchControllerNode: ASDisplayNode { if ids.contains(participant.peer.id) { continue } + case .disable: + break } } if case .creator = participant.participant { diff --git a/TelegramUI/ChannelVisibilityController.swift b/TelegramUI/ChannelVisibilityController.swift index 2104c33198..c69d34ec75 100644 --- a/TelegramUI/ChannelVisibilityController.swift +++ b/TelegramUI/ChannelVisibilityController.swift @@ -663,7 +663,9 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: let revokeLinkDisposable = MetaDisposable() actionsDisposable.add(revokeLinkDisposable) - actionsDisposable.add(ensuredExistingPeerExportedInvitation(account: account, peerId: peerId).start()) + actionsDisposable.add( (account.viewTracker.peerView(peerId) |> filter { $0.cachedData != nil } |> take(1) |> mapToSignal { view -> Signal in + return ensuredExistingPeerExportedInvitation(account: account, peerId: peerId) + } ).start()) let arguments = ChannelVisibilityControllerArguments(account: account, updateCurrentType: { type in updateState { state in @@ -786,6 +788,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: }) }) + let peerView = account.viewTracker.peerView(peerId) |> deliverOnMainQueue @@ -935,7 +938,7 @@ public func channelVisibilityController(account: Account, peerId: PeerId, mode: nextImpl = { [weak controller] in if let controller = controller { if case .initialSetup = mode { - let selectionController = ContactMultiselectionController(account: account, mode: .channelCreation) + let selectionController = ContactMultiselectionController(account: account, mode: .channelCreation, options: []) (controller.navigationController as? NavigationController)?.replaceAllButRootController(selectionController, animated: true) let _ = (selectionController.result |> deliverOnMainQueue).start(next: { [weak selectionController] peerIds in diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 8cc76af17e..64463b68c2 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -265,7 +265,6 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } } } - return openChatMessage(account: account, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: { self?.chatDisplayNode.dismissInput() }, present: { c, a in @@ -1281,7 +1280,7 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat { self.screenCaptureEventsDisposable = screenCaptureEvents().start(next: { [weak self] _ in - if let strongSelf = self, strongSelf.canReadHistoryValue { + if let strongSelf = self, strongSelf.canReadHistoryValue, strongSelf.traceVisibility() { let _ = addSecretChatMessageScreenshot(account: account, peerId: peerId).start() } }) @@ -3585,7 +3584,10 @@ public final class ChatController: TelegramController, UIViewControllerPreviewin } private func enqueueChatContextResult(_ results: ChatContextResultCollection, _ result: ChatContextResult) { - if let message = outgoingMessageWithChatContextResult(results, result), canSendMessagesToChat(self.presentationInterfaceState) { + guard case let .peer(peerId) = self.chatLocation else { + return + } + if let message = outgoingMessageWithChatContextResult(to: peerId, results: results, result: result), canSendMessagesToChat(self.presentationInterfaceState) { let replyMessageId = self.presentationInterfaceState.interfaceState.replyMessageId self.chatDisplayNode.setupSendActionOnViewUpdate({ [weak self] in if let strongSelf = self { diff --git a/TelegramUI/ChatListController.swift b/TelegramUI/ChatListController.swift index a2c5dfd239..9199f8986b 100644 --- a/TelegramUI/ChatListController.swift +++ b/TelegramUI/ChatListController.swift @@ -641,7 +641,7 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD previewingContext.sourceRect = sourceRect } switch item.content { - case let .peer(_, peer, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _): if peer.peerId.namespace != Namespaces.Peer.SecretChat { let chatController = ChatController(account: self.account, chatLocation: .peer(peer.peerId), mode: .standard(previewing: true)) chatController.canReadHistory.set(false) diff --git a/TelegramUI/ChatListItem.swift b/TelegramUI/ChatListItem.swift index c3c0b60de5..5b61823a27 100644 --- a/TelegramUI/ChatListItem.swift +++ b/TelegramUI/ChatListItem.swift @@ -7,12 +7,12 @@ import SwiftSignalKit import TelegramCore enum ChatListItemContent { - case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool) + case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool) case groupReference(groupId: PeerGroupId, message: Message?, topPeers: [Peer], counters: GroupReferenceUnreadCounters) var chatLocation: ChatLocation { switch self { - case let .peer(_, peer, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _): return .peer(peer.peerId) case let .groupReference(groupId, _, _, _): return .group(groupId) @@ -95,7 +95,7 @@ class ChatListItem: ListViewItem { func selected(listView: ListView) { switch self.content { - case let .peer(message, peer, _, _, _, _, _, _): + case let .peer(message, peer, _, _, _, _, _, _, _): if let message = message { self.interaction.messageSelected(message) } else if let peer = peer.peers[peer.peerId] { @@ -321,7 +321,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var peer: Peer? switch item.content { - case let .peer(message, peerValue, _, _, _, _, _, _): + case let .peer(message, peerValue, _, _, _, _, _, _, _): if let message = message { peer = messageMainPeer(message) } else { @@ -420,11 +420,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var multipleAvatarsApply: ((Bool) -> MultipleAvatarsNode)? switch item.content { - case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue): + case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge): message = messageValue itemPeer = peerValue combinedReadState = combinedReadStateValue - if let combinedReadState = combinedReadState, !isAdValue { + if let combinedReadState = combinedReadState, !isAdValue && !ignoreUnreadBadge { unreadCount = (combinedReadState.count, combinedReadState.isUnread, notificationSettingsValue?.isRemovedFromTotalUnreadCount ?? false) } else { unreadCount = (0, false, false) diff --git a/TelegramUI/ChatListNode.swift b/TelegramUI/ChatListNode.swift index 2bb29d8080..9e2b0616ff 100644 --- a/TelegramUI/ChatListNode.swift +++ b/TelegramUI/ChatListNode.swift @@ -131,7 +131,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, inputActivities, isAd): switch mode { case .chatList: - return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint) + return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint) case let .peers(filter): let itemPeer = peer.chatMainPeer var chatPeer: Peer? @@ -192,7 +192,7 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, summaryInfo, editing, hasActiveRevealControls, inputActivities, isAd): switch mode { case .chatList: - return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint) + return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint) case let .peers(filter): let itemPeer = peer.chatMainPeer var chatPeer: Peer? diff --git a/TelegramUI/ChatListSearchContainerNode.swift b/TelegramUI/ChatListSearchContainerNode.swift index cfa94360f9..fd07d75fd8 100644 --- a/TelegramUI/ChatListSearchContainerNode.swift +++ b/TelegramUI/ChatListSearchContainerNode.swift @@ -397,7 +397,7 @@ enum ChatListSearchEntry: Comparable, Identifiable { interaction.peerSelected(peer.peer) }) 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) + 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, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction) } } } @@ -919,7 +919,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode { return (selectedItemNode.view, peer.id) } else if let selectedItemNode = selectedItemNode as? ChatListItemNode, let item = selectedItemNode.item { switch item.content { - case let .peer(message, peer, _, _, _, _, _, _): + case let .peer(message, peer, _, _, _, _, _, _, _): return (selectedItemNode.view, message?.id ?? peer.peerId) case let .groupReference(groupId, _, _, _): return (selectedItemNode.view, groupId) diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 500fc57ac6..e7e3ab20de 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -385,7 +385,16 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } } } - if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo { + + if let info = item.message.forwardInfo { + if let author = info.author as? TelegramUser, let _ = author.botInfo { + needShareButton = true + } else if let author = info.author as? TelegramChannel, case .broadcast = author.info, !(item.message.media.first is TelegramMediaAction) { + needShareButton = true + } + } + + if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !(item.message.media.first is TelegramMediaAction) { needShareButton = true } if !needShareButton { diff --git a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift index f764a9afb2..dda806b94d 100644 --- a/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift +++ b/TelegramUI/ChatMessageInteractiveInstantVideoNode.swift @@ -285,9 +285,12 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { })) } + + dateAndStatusApply(false) switch layoutData { case let .unconstrained(width): + // let dateAndStatusOversized: Bool = videoFrame.maxX + dateAndStatusSize.width > width strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: min(floor(videoFrame.midX) + 55.0, width - dateAndStatusSize.width - 4.0), y: videoFrame.height - dateAndStatusSize.height), size: dateAndStatusSize) case let .constrained(_, right): strongSelf.dateAndStatusNode.frame = CGRect(origin: CGPoint(x: min(floor(videoFrame.midX) + 55.0, videoFrame.maxX + right - dateAndStatusSize.width - 4.0), y: videoFrame.maxY - dateAndStatusSize.height), size: dateAndStatusSize) diff --git a/TelegramUI/ChatMessageStickerItemNode.swift b/TelegramUI/ChatMessageStickerItemNode.swift index 44a98db9c7..7577adb86c 100644 --- a/TelegramUI/ChatMessageStickerItemNode.swift +++ b/TelegramUI/ChatMessageStickerItemNode.swift @@ -159,7 +159,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } } - if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo { + if !needShareButton, let author = item.message.author as? TelegramUser, let _ = author.botInfo, !item.message.media.isEmpty { needShareButton = true } if !needShareButton { @@ -311,7 +311,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.replyInfoNode = replyInfoNode strongSelf.addSubnode(replyInfoNode) } - let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - replyInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)), y: imageSize.height - replyInfoSize.height - 8.0), size: replyInfoSize) + let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - replyInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)), y: 8.0), size: replyInfoSize) replyInfoNode.frame = replyInfoFrame strongSelf.replyBackgroundNode?.frame = CGRect(origin: CGPoint(x: replyInfoFrame.minX - 4.0, y: replyInfoFrame.minY - 2.0), size: CGSize(width: replyInfoFrame.size.width + 8.0, height: replyInfoFrame.size.height + 5.0)) } else if let replyInfoNode = strongSelf.replyInfoNode { diff --git a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift index 106130d4df..aff1fa12d9 100644 --- a/TelegramUI/ChatPinnedMessageTitlePanelNode.swift +++ b/TelegramUI/ChatPinnedMessageTitlePanelNode.swift @@ -214,7 +214,9 @@ 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: !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))) + + + let (textLayout, textApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: descriptionStringForMessage(message, strings: strings, accountPeerId: accountPeerId).0, font: Font.regular(15.0), textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor), 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/ChatTextInputPanelNode.swift b/TelegramUI/ChatTextInputPanelNode.swift index 9bf0dbd26f..43e436081f 100644 --- a/TelegramUI/ChatTextInputPanelNode.swift +++ b/TelegramUI/ChatTextInputPanelNode.swift @@ -524,7 +524,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { wasEditingMedia = !value.isEmpty } } - + var isMediaEnabled = true var isEditingMedia = false if let editMessageState = interfaceState.editMessageState { diff --git a/TelegramUI/ComposeController.swift b/TelegramUI/ComposeController.swift index 9e00ec3230..9b935690b3 100644 --- a/TelegramUI/ComposeController.swift +++ b/TelegramUI/ComposeController.swift @@ -100,7 +100,7 @@ public class ComposeController: ViewController { self.contactsNode.openCreateNewGroup = { [weak self] in if let strongSelf = self { - let controller = ContactMultiselectionController(account: strongSelf.account, mode: .groupCreation) + let controller = ContactMultiselectionController(account: strongSelf.account, mode: .groupCreation, options: []) (strongSelf.navigationController as? NavigationController)?.pushViewController(controller) strongSelf.createActionDisposable.set((controller.result |> deliverOnMainQueue).start(next: { [weak controller] peerIds in diff --git a/TelegramUI/ContactListNode.swift b/TelegramUI/ContactListNode.swift index 2589d88138..254007d7dd 100644 --- a/TelegramUI/ContactListNode.swift +++ b/TelegramUI/ContactListNode.swift @@ -118,7 +118,7 @@ enum ContactListPeer: Equatable { private enum ContactListNodeEntry: Comparable, Identifiable { case search(PresentationTheme, PresentationStrings) case option(Int, ContactListAdditionalOption, PresentationTheme, PresentationStrings) - case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationTimeFormat) + case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationTimeFormat, Bool) var stableId: ContactListNodeEntryId { switch self { @@ -126,7 +126,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { return .search case let .option(index, _, _, _): return .option(index: index) - case let .peer(_, peer, _, _, _, _, _, _): + case let .peer(_, peer, _, _, _, _, _, _, _): switch peer { case let .peer(peer, _): return .peerId(peer.id.toInt64()) @@ -144,7 +144,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { }) case let .option(_, option, theme, _): return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, action: option.action) - case let .peer(_, peer, presence, header, selection, theme, strings, timeFormat): + case let .peer(_, peer, presence, header, selection, theme, strings, timeFormat, enabled): let status: ContactsPeerItemStatus let itemPeer: ContactsPeerItemPeer switch peer { @@ -161,7 +161,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable { status = .none itemPeer = .deviceContact(stableId: id, contact: contact) } - return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: true, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in + return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in interaction.openPeer(peer) }) } @@ -181,9 +181,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable { } else { return false } - case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat): + case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsEnabled): switch rhs { - case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat): + case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsEnabled): if lhsIndex != rhsIndex { return false } @@ -212,6 +212,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable { if lhsTimeFormat != rhsTimeFormat { return false } + if lhsEnabled != rhsEnabled { + return false + } return true default: return false @@ -232,11 +235,11 @@ private enum ContactListNodeEntry: Comparable, Identifiable { case .peer: return true } - case let .peer(lhsIndex, _, _, _, _, _, _, _): + case let .peer(lhsIndex, _, _, _, _, _, _, _, _): switch rhs { case .search, .option: return false - case let .peer(rhsIndex, _, _, _, _, _, _, _): + case let .peer(rhsIndex, _, _, _, _, _, _, _, _): return lhsIndex < rhsIndex } } @@ -279,7 +282,7 @@ private extension PeerIndexNameRepresentation { } } -private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) -> [ContactListNodeEntry] { +private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, disabledPeerIds:Set) -> [ContactListNodeEntry] { var entries: [ContactListNodeEntry] = [] var orderedPeers: [ContactListPeer] @@ -409,7 +412,14 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer] if case let .peer(peer, _) = orderedPeers[i] { presence = presences[peer.id] } - entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, timeFormat)) + let enabled: Bool + switch orderedPeers[i] { + case let .peer(peer, _): + enabled = !disabledPeerIds.contains(peer.id) + default: + enabled = true + } + entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, timeFormat, enabled)) } return entries } @@ -474,20 +484,16 @@ struct ContactListNodeGroupSelectionState: Equatable { } } -struct ContactListFilter: OptionSet { - public var rawValue: Int32 - - public init(rawValue: Int32) { - self.rawValue = rawValue - } - - public static let excludeSelf = ContactListFilter(rawValue: 1 << 1) +enum ContactListFilter { + case excludeSelf + case exclude([PeerId]) + case disable([PeerId]) } final class ContactListNode: ASDisplayNode { private let account: Account private let presentation: ContactListPresentation - private let filter: ContactListFilter + private let filters: [ContactListFilter] let listNode: ListView @@ -537,10 +543,10 @@ final class ContactListNode: ASDisplayNode { private var presentationDataDisposable: Disposable? private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationTimeFormat)> - init(account: Account, presentation: ContactListPresentation, filter: ContactListFilter = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) { + init(account: Account, presentation: ContactListPresentation, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) { self.account = account self.presentation = presentation - self.filter = filter + self.filters = filters self.listNode = ListView() @@ -592,10 +598,20 @@ final class ContactListNode: ASDisplayNode { |> mapToQueue { localPeers, remotePeers, deviceContacts, selectionState, themeAndStrings -> Signal in let signal = deferred { () -> Signal in var existingPeerIds = Set() + var disabledPeerIds = Set() + var existingNormalizedPhoneNumbers = Set() - if filter.contains(.excludeSelf) { - existingPeerIds.insert(account.peerId) + for filter in filters { + switch filter { + case .excludeSelf: + existingPeerIds.insert(account.peerId) + case let .exclude(peerIds): + existingPeerIds = existingPeerIds.union(peerIds) + case let .disable(peerIds): + disabledPeerIds = disabledPeerIds.union(peerIds) + } } + var peers: [ContactListPeer] = [] for peer in localPeers { if !existingPeerIds.contains(peer.id) { @@ -639,7 +655,7 @@ final class ContactListNode: ASDisplayNode { peers.append(.deviceContact(stableId, contact)) } - let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: [:], presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2) + let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: [:], presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, disabledPeerIds: disabledPeerIds) let previous = previousEntries.swap(entries) return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, animated: false)) } @@ -655,7 +671,31 @@ final class ContactListNode: ASDisplayNode { transition = (combineLatest(self.contactPeersViewPromise.get(), selectionStateSignal, themeAndStringsPromise.get()) |> mapToQueue { view, selectionState, themeAndStrings -> Signal in let signal = deferred { () -> Signal in - let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: view.peers.map({ ContactListPeer.peer(peer: $0, isGlobal: false) }), presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2) + + var peers = view.peers.map({ ContactListPeer.peer(peer: $0, isGlobal: false) }) + var existingPeerIds = Set() + var disabledPeerIds = Set() + for filter in filters { + switch filter { + case .excludeSelf: + existingPeerIds.insert(account.peerId) + case let .exclude(peerIds): + existingPeerIds = existingPeerIds.union(peerIds) + case let .disable(peerIds): + disabledPeerIds = disabledPeerIds.union(peerIds) + } + } + + peers = peers.filter { contact in + switch contact { + case let .peer(peer, _): + return !existingPeerIds.contains(peer.id) + default: + return true + } + } + + let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, disabledPeerIds: disabledPeerIds) let previous = previousEntries.swap(entries) let animated: Bool if let previous = previous { diff --git a/TelegramUI/ContactMultiselectionController.swift b/TelegramUI/ContactMultiselectionController.swift index ac4dffce6d..6af08d3d08 100644 --- a/TelegramUI/ContactMultiselectionController.swift +++ b/TelegramUI/ContactMultiselectionController.swift @@ -21,6 +21,9 @@ class ContactMultiselectionController: ViewController { return self.displayNode as! ContactMultiselectionControllerNode } + var dismissed: (() -> Void)? + + private let index: PeerNameIndex = .lastNameFirst private var _ready = Promise() @@ -57,11 +60,13 @@ class ContactMultiselectionController: ViewController { private var limitsConfiguration: LimitsConfiguration? private var limitsConfigurationDisposable: Disposable? - - init(account: Account, mode: ContactMultiselectionControllerMode) { + private let options: [ContactListAdditionalOption] + private let filters: [ContactListFilter] + init(account: Account, mode: ContactMultiselectionControllerMode, options: [ContactListAdditionalOption], filters: [ContactListFilter] = [.excludeSelf]) { self.account = account self.mode = mode - + self.options = options + self.filters = filters self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.titleView = CounterContollerTitleView(theme: self.presentationData.theme) @@ -148,7 +153,7 @@ class ContactMultiselectionController: ViewController { } override func loadDisplayNode() { - self.displayNode = ContactMultiselectionControllerNode(account: self.account) + self.displayNode = ContactMultiselectionControllerNode(account: self.account, options: self.options, filters: filters) self._listReady.set(self.contactsNode.contactListNode.ready) self.contactsNode.dismiss = { [weak self] in @@ -296,7 +301,16 @@ class ContactMultiselectionController: ViewController { } override open func dismiss(completion: (() -> Void)? = nil) { - self.contactsNode.animateOut(completion: completion) + if let presentationArguments = self.presentationArguments as? ViewControllerPresentationArguments { + switch presentationArguments.presentationAnimation { + case .modalSheet: + self.dismissed?() + self.contactsNode.animateOut(completion: completion) + case .none: + self.dismissed?() + completion?() + } + } } override func viewDidDisappear(_ animated: Bool) { @@ -313,6 +327,7 @@ class ContactMultiselectionController: ViewController { @objc func cancelPressed() { self._result.set(.single([])) + self.dismiss() } @objc func rightNavigationButtonPressed() { diff --git a/TelegramUI/ContactMultiselectionControllerNode.swift b/TelegramUI/ContactMultiselectionControllerNode.swift index 02a3ec8c9a..e507a5fb2f 100644 --- a/TelegramUI/ContactMultiselectionControllerNode.swift +++ b/TelegramUI/ContactMultiselectionControllerNode.swift @@ -39,17 +39,16 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { var editableTokens: [EditableTokenListToken] = [] private let searchResultsReadyDisposable = MetaDisposable() - var dismiss: (() -> Void)? private var presentationData: PresentationData private var presentationDataDisposable: Disposable? - init(account: Account) { + init(account: Account, options: [ContactListAdditionalOption], filters: [ContactListFilter]) { self.account = account self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } - self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, options: []), selectionState: ContactListNodeGroupSelectionState()) + self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, options: options), filters: filters, selectionState: ContactListNodeGroupSelectionState()) self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: self.presentationData.strings.Compose_TokenListPlaceholder) super.init() @@ -88,7 +87,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { selectionState = state return state } - let searchResultsNode = ContactListNode(account: account, presentation: .search(signal: searchText.get(), searchDeviceContacts: false), selectionState: selectionState) + let searchResultsNode = ContactListNode(account: account, presentation: .search(signal: searchText.get(), searchDeviceContacts: false), filters: filters, selectionState: selectionState) searchResultsNode.openPeer = { peer in self?.tokenListNode.setText("") self?.openPeer?(peer) diff --git a/TelegramUI/ContactsSearchContainerNode.swift b/TelegramUI/ContactsSearchContainerNode.swift index 35343d9012..a031fa7613 100644 --- a/TelegramUI/ContactsSearchContainerNode.swift +++ b/TelegramUI/ContactsSearchContainerNode.swift @@ -118,7 +118,7 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { private var containerViewLayout: (ContainerViewLayout, CGFloat)? private var enqueuedTransitions: [ContactListSearchContainerTransition] = [] - init(account: Account, onlyWriteable: Bool, categories: ContactsSearchCategories, filter: ContactListFilter = [.excludeSelf], openPeer: @escaping (ContactListPeer) -> Void) { + init(account: Account, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], openPeer: @escaping (ContactListPeer) -> Void) { self.account = account self.openPeer = openPeer @@ -173,8 +173,16 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode { |> map { localPeers, remotePeers, deviceContacts, themeAndStrings -> [ContactListSearchEntry] in var entries: [ContactListSearchEntry] = [] var existingPeerIds = Set() - if filter.contains(.excludeSelf) { - existingPeerIds.insert(account.peerId) + var disabledPeerIds = Set() + for filter in filters { + switch filter { + case .excludeSelf: + existingPeerIds.insert(account.peerId) + case let .exclude(peerIds): + existingPeerIds = existingPeerIds.union(peerIds) + case let .disable(peerIds): + disabledPeerIds = disabledPeerIds.union(peerIds) + } } var existingNormalizedPhoneNumbers = Set() var index = 0 diff --git a/TelegramUI/CreateChannelController.swift b/TelegramUI/CreateChannelController.swift index 35f75e6e21..783f759df6 100644 --- a/TelegramUI/CreateChannelController.swift +++ b/TelegramUI/CreateChannelController.swift @@ -203,7 +203,12 @@ public func createChannelController(account: Account) -> ViewController { let arguments = CreateChannelArguments(account: account, updateEditingName: { editingName in updateState { current in var current = current - current.editingName = editingName + switch editingName { + case let .title(title, type): + current.editingName = .title(title: String(title.prefix(255)), type: type) + case let .personName(firstName, lastName): + current.editingName = .personName(firstName: String(firstName.prefix(255)), lastName: String(lastName.prefix(255))) + } return current } }, updateEditingDescriptionText: { text in diff --git a/TelegramUI/DefaultPresentationTheme.swift b/TelegramUI/DefaultPresentationTheme.swift index bbe7851c16..d85cccf641 100644 --- a/TelegramUI/DefaultPresentationTheme.swift +++ b/TelegramUI/DefaultPresentationTheme.swift @@ -120,7 +120,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr unreadBadgeActiveTextColor: .white, unreadBadgeInactiveBackgroundColor: UIColor(rgb: 0xb6b6bb), unreadBadgeInactiveTextColor: .white, - pinnedBadgeColor: UIColor(rgb: 0x939399), + pinnedBadgeColor: UIColor(rgb: 0xb6b6bb), pinnedSearchBarColor: UIColor(rgb: 0xe5e5e5), regularSearchBarColor: UIColor(rgb: 0xe9e9e9), sectionHeaderFillColor: UIColor(rgb: 0xf7f7f7), @@ -310,7 +310,7 @@ private func makeDefaultPresentationTheme(accentColor: UIColor, day: Bool) -> Pr actionControlFillColor: accentColor, actionControlForegroundColor: .white, primaryTextColor: .black, - secondaryTextColor: UIColor(rgb: 0x5e5e5e), + secondaryTextColor: UIColor(rgb: 0x8e8e93), mediaRecordingDotColor: UIColor(rgb: 0xed2521), keyboardColor: .light, mediaRecordingControl: inputPanelMediaRecordingControl diff --git a/TelegramUI/GroupInfoController.swift b/TelegramUI/GroupInfoController.swift index 299f3e59a6..1c77ff3528 100644 --- a/TelegramUI/GroupInfoController.swift +++ b/TelegramUI/GroupInfoController.swift @@ -911,8 +911,30 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa } } } else if let channel = view.peers[view.peerId] as? TelegramChannel, let cachedChannelData = view.cachedData as? CachedChannelData, let memberCount = cachedChannelData.participantsSummary.memberCount { - let updatedParticipants = channelMembers - let disabledPeerIds = state.removingParticipantIds + var updatedParticipants = channelMembers + let existingParticipantIds = Set(updatedParticipants.map { $0.peer.id }) + + var peerPresences: [PeerId: PeerPresence] = view.peerPresences + var peers: [PeerId: Peer] = view.peers + var disabledPeerIds = state.removingParticipantIds + + + + + if !state.temporaryParticipants.isEmpty { + for participant in state.temporaryParticipants { + if !existingParticipantIds.contains(participant.peer.id) { + updatedParticipants.append(RenderedChannelParticipant(participant: ChannelParticipant.member(id: participant.peer.id, invitedAt: participant.timestamp, adminInfo: nil, banInfo: nil), peer: participant.peer)) + if let presence = participant.presence, peerPresences[participant.peer.id] == nil { + peerPresences[participant.peer.id] = presence + } + if peers[participant.peer.id] == nil { + peers[participant.peer.id] = participant.peer + } + //disabledPeerIds.insert(participant.peer.id) + } + } + } let sortedParticipants: [RenderedChannelParticipant] if memberCount < 200 { @@ -1307,8 +1329,21 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl } } }, addMember: { - let _ = (account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue).start(next: { groupPeer in + + let members: Promise<[PeerId]> = Promise() + if peerId.namespace == Namespaces.Peer.CloudChannel { + var membersDisposable: Disposable? + let (disposable, _) = account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.recent(postbox: account.postbox, network: account.network, peerId: peerId, updated: { listState in + members.set(.single(listState.list.map {$0.peer.id})) + membersDisposable?.dispose() + }) + membersDisposable = disposable + } else { + members.set(.single([])) + } + + let _ = (combineLatest(account.postbox.loadedPeerWithId(peerId) + |> deliverOnMainQueue, members.get() |> take(1) |> deliverOnMainQueue)).start(next: { groupPeer, recentIds in var confirmationImpl: ((PeerId) -> Signal)? var options: [ContactListAdditionalOption] = [] let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } @@ -1317,13 +1352,22 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl inviteByLinkImpl?() })) - let contactsController = ContactSelectionController(account: account, autoDismiss: false, title: { $0.GroupInfo_AddParticipantTitle }, options: options, confirmation: { peer in - if let confirmationImpl = confirmationImpl, case let .peer(peer, _) = peer { - return confirmationImpl(peer.id) - } else { - return .single(false) - } - }) + let contactsController: ViewController + if peerId.namespace == Namespaces.Peer.CloudGroup { + contactsController = ContactSelectionController(account: account, autoDismiss: false, title: { $0.GroupInfo_AddParticipantTitle }, options: options, confirmation: { peer in + if let confirmationImpl = confirmationImpl, case let .peer(peer, _) = peer { + return confirmationImpl(peer.id) + } else { + return .single(false) + } + }) + } else { + contactsController = ContactMultiselectionController(account: account, mode: .peerSelection, options: options, filters: [.excludeSelf, .disable(recentIds)]) + } + + + + confirmationImpl = { [weak contactsController] peerId in return account.postbox.loadedPeerWithId(peerId) |> deliverOnMainQueue @@ -1409,27 +1453,84 @@ public func groupInfoController(account: Account, peerId: PeerId) -> ViewControl return .complete() } } + + let addMembers: ([ContactListPeerId]) -> Signal = { members -> Signal in + let memberIds = members.compactMap { contact -> PeerId? in + switch contact { + case let .peer(peerId): + return peerId + default: + return nil + } + } + return account.postbox.multiplePeersView(memberIds) + |> take(1) + |> deliverOnMainQueue + |> mapToSignal { view -> Signal in + updateState { state in + var state = state + for (memberId, peer) in view.peers { + var found = false + for participant in state.temporaryParticipants { + if participant.peer.id == memberId { + found = true + break + } + } + if !found { + let timestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) + var temporaryParticipants = state.temporaryParticipants + temporaryParticipants.append(TemporaryParticipant(peer: peer, presence: view.presences[memberId], timestamp: timestamp)) + state = state.withUpdatedTemporaryParticipants(temporaryParticipants) + } + } + + return state + + } + return account.telegramApplicationContext.peerChannelMemberCategoriesContextsManager.addMembers(account: account, peerId: peerId, memberIds: memberIds) + } + } + inviteByLinkImpl = { [weak contactsController] in contactsController?.dismiss() presentControllerImpl?(channelVisibilityController(account: account, peerId: peerId, mode: .privateLink), ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet)) } - selectAddMemberDisposable.set((contactsController.result - |> deliverOnMainQueue).start(next: { [weak contactsController] memberPeer in - guard let memberPeer = memberPeer else { - return - } - - contactsController?.displayProgress = true - addMemberDisposable.set((addMember(memberPeer) - |> deliverOnMainQueue).start(completed: { - contactsController?.dismiss() - })) - })) + presentControllerImpl?(contactsController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - contactsController.dismissed = { - selectAddMemberDisposable.set(nil) - addMemberDisposable.set(nil) + if let contactsController = contactsController as? ContactSelectionController { + selectAddMemberDisposable.set((contactsController.result + |> deliverOnMainQueue).start(next: { [weak contactsController] memberPeer in + guard let memberPeer = memberPeer else { + return + } + + contactsController?.displayProgress = true + addMemberDisposable.set((addMember(memberPeer) + |> deliverOnMainQueue).start(completed: { + contactsController?.dismiss() + })) + })) + contactsController.dismissed = { + selectAddMemberDisposable.set(nil) + addMemberDisposable.set(nil) + } + } + if let contactsController = contactsController as? ContactMultiselectionController { + selectAddMemberDisposable.set((contactsController.result + |> deliverOnMainQueue).start(next: { [weak contactsController] peers in + + contactsController?.displayProgress = true + addMemberDisposable.set((addMembers(peers) + |> deliverOnMainQueue).start(completed: { + contactsController?.dismiss() + })) + })) + contactsController.dismissed = { + selectAddMemberDisposable.set(nil) + addMemberDisposable.set(nil) + } } }) }, promotePeer: { participant in diff --git a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift index 9e2486892a..2fbdbfad3a 100644 --- a/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift +++ b/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift @@ -77,14 +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() - + private let statusNode: RadialStatusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) + override var visibility: ListViewItemNodeVisibility { didSet { @@ -354,6 +354,11 @@ 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)) + strongSelf.statusNode.removeFromSupernode() + strongSelf.addSubnode(strongSelf.statusNode) + + strongSelf.statusNode.frame = progressFrame + if let updatedStatusSignal = updatedStatusSignal { strongSelf.statusDisposable.set((updatedStatusSignal |> deliverOnMainQueue).start(next: { [weak strongSelf] status in @@ -361,21 +366,13 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode 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) + state = .progress(color: statusForegroundColor, lineWidth: nil, value: CGFloat(max(progress, 0.2)), cancelEnabled: false) case .Remote: state = .download(statusForegroundColor) case .Local: @@ -383,19 +380,12 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode } - if let statusNode = strongSelf.statusNode { - if state == .none { - strongSelf.statusNode = nil - } - statusNode.transitionToState(state, completion: { [weak statusNode] in - if state == .none { - statusNode?.removeFromSupernode() - } - }) - } + strongSelf.statusNode.transitionToState(state, completion: { }) } } })) + } else { + strongSelf.statusNode.transitionToState(.none, completion: { }) } if let (thumbnailLayer, _, layer) = strongSelf.videoLayer { diff --git a/TelegramUI/ItemListAvatarAndNameItem.swift b/TelegramUI/ItemListAvatarAndNameItem.swift index c7c95a4518..97994d8b85 100644 --- a/TelegramUI/ItemListAvatarAndNameItem.swift +++ b/TelegramUI/ItemListAvatarAndNameItem.swift @@ -696,6 +696,8 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite strongSelf.inputSeparator = inputSeparator } + //let title = title.prefix(255) + if strongSelf.inputFirstField == nil { let inputFirstField = TextFieldNodeView() inputFirstField.font = Font.regular(17.0) @@ -710,12 +712,12 @@ class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNode, Ite placeholder = item.strings.GroupInfo_ChannelListNamePlaceholder } inputFirstField.attributedPlaceholder = NSAttributedString(string: placeholder, font: Font.regular(19.0), textColor: item.theme.list.itemPlaceholderTextColor) - inputFirstField.attributedText = NSAttributedString(string: title, font: Font.regular(19.0), textColor: item.theme.list.itemPrimaryTextColor) + inputFirstField.attributedText = NSAttributedString(string: String(title), font: Font.regular(19.0), textColor: item.theme.list.itemPrimaryTextColor) strongSelf.inputFirstField = inputFirstField strongSelf.view.addSubview(inputFirstField) inputFirstField.addTarget(self, action: #selector(strongSelf.textFieldDidChange(_:)), for: .editingChanged) - } else if strongSelf.inputFirstField?.text != title { - strongSelf.inputFirstField?.text = title + } else if strongSelf.inputFirstField?.text != String(title) { + strongSelf.inputFirstField?.text = String(title) } strongSelf.inputSeparator?.frame = CGRect(origin: CGPoint(x: params.leftInset + 100.0, y: 62.0), size: CGSize(width: params.width - params.leftInset - params.rightInset - 100.0, height: separatorHeight)) diff --git a/TelegramUI/OngoingCallThreadLocalContext.mm b/TelegramUI/OngoingCallThreadLocalContext.mm index f46c8e19a9..40894cafeb 100644 --- a/TelegramUI/OngoingCallThreadLocalContext.mm +++ b/TelegramUI/OngoingCallThreadLocalContext.mm @@ -251,8 +251,6 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { unsigned char peerTag[16]; [connection.peerTag getBytes:peerTag length:16]; endpoints.push_back(tgvoip::Endpoint(connection.connectionId, (uint16_t)connection.port, address, addressv6, tgvoip::Endpoint::TYPE_UDP_RELAY, peerTag)); - /*releasable*/ - //endpoints.push_back(tgvoip::Endpoint(connection.connectionId, (uint16_t)connection.port, address, addressv6, EP_TYPE_UDP_RELAY, peerTag)); } tgvoip::VoIPController::Config config(_callConnectTimeout, _callPacketTimeout, _dataSavingMode, false, true, true); @@ -270,9 +268,9 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { } - (void)stop { - if (_controller) { + if (_controller != nil) { char *buffer = (char *)malloc(_controller->GetDebugLogLength()); - /*releasable*/ + _controller->Stop(); _controller->GetDebugLog(buffer); NSString *debugLog = [[NSString alloc] initWithUTF8String:buffer]; @@ -296,7 +294,6 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { - (void)controllerStateChanged:(int)state { OngoingCallState callState = OngoingCallStateInitializing; - /*releasable*/ switch (state) { case tgvoip::STATE_ESTABLISHED: callState = OngoingCallStateConnected; @@ -307,16 +304,6 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { default: break; } - /*switch (state) { - case STATE_ESTABLISHED: - callState = OngoingCallStateConnected; - break; - case STATE_FAILED: - callState = OngoingCallStateFailed; - break; - default: - break; - }*/ if (callState != _state) { _state = callState; @@ -328,13 +315,17 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) { } - (void)setIsMuted:(bool)isMuted { - _controller->SetMicMute(isMuted); + if (_controller != nil) { + _controller->SetMicMute(isMuted); + } } - (void)setNetworkType:(OngoingCallNetworkType)networkType { if (_networkType != networkType) { _networkType = networkType; - _controller->SetNetworkType(callControllerNetworkTypeForType(networkType)); + if (_controller != nil) { + _controller->SetNetworkType(callControllerNetworkTypeForType(networkType)); + } } } diff --git a/TelegramUI/OpenChatMessage.swift b/TelegramUI/OpenChatMessage.swift index 5da0f02b66..b4ad146b3c 100644 --- a/TelegramUI/OpenChatMessage.swift +++ b/TelegramUI/OpenChatMessage.swift @@ -327,6 +327,7 @@ func openChatMessage(account: Account, message: Message, standalone: Bool, rever return true } case let .chatAvatars(controller, media): + dismissInput() chatAvatarHiddenMedia(controller.hiddenMedia |> map { value -> MessageId? in if value != nil { return message.id diff --git a/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift b/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift index f90827aea2..3040135ea0 100644 --- a/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift +++ b/TelegramUI/PeerChannelMemberCategoriesContextsManager.swift @@ -8,6 +8,38 @@ enum PeerChannelMemberContextKey: Hashable { case recentSearch(String) case admins(String?) case restrictedAndBanned(String?) + + var hashValue: Int { + return 0 + } + static func ==(lhs: PeerChannelMemberContextKey, rhs: PeerChannelMemberContextKey) -> Bool { + switch lhs { + case .recent: + if case .recent = rhs { + return true + } else { + return false + } + case let .recentSearch(query): + if case .recentSearch(query) = rhs { + return true + } else { + return false + } + case let .admins(query): + if case .admins(query) = rhs { + return true + } else { + return false + } + case let .restrictedAndBanned(query): + if case .restrictedAndBanned(query) = rhs { + return true + } else { + return false + } + } + } } private final class PeerChannelMemberCategoriesContextsManagerImpl { @@ -161,4 +193,22 @@ final class PeerChannelMemberCategoriesContextsManager { return .complete() } } + + func addMembers(account: Account, peerId: PeerId, memberIds: [PeerId]) -> Signal { + return addChannelMembers(account: account, peerId: peerId, memberIds: memberIds) |> deliverOnMainQueue + |> beforeNext { [weak self] result in + if let strongSelf = self { + strongSelf.impl.with { impl in + for (contextPeerId, context) in impl.contexts { + if peerId == contextPeerId { + context.reset(.recent) + } + } + } + } + } + |> mapToSignal { _ -> Signal in + return .complete() + } + } } diff --git a/TelegramUI/PeerMediaCollectionController.swift b/TelegramUI/PeerMediaCollectionController.swift index 9278e38c50..1eacf445b4 100644 --- a/TelegramUI/PeerMediaCollectionController.swift +++ b/TelegramUI/PeerMediaCollectionController.swift @@ -553,7 +553,7 @@ public class PeerMediaCollectionController: TelegramController { } self.mediaCollectionDisplayNode.selectedMessages = updatedInterfaceState.selectionState?.selectedIds - view.disablesInteractiveTransitionGestureRecognizer = updatedInterfaceState.selectionState != nil + view.disablesInteractiveTransitionGestureRecognizer = updatedInterfaceState.selectionState != nil && self.mediaCollectionDisplayNode.historyNode is ChatHistoryGridNode } } } diff --git a/TelegramUI/PeerReportController.swift b/TelegramUI/PeerReportController.swift index 6baf8804e2..c9bfb3895b 100644 --- a/TelegramUI/PeerReportController.swift +++ b/TelegramUI/PeerReportController.swift @@ -12,6 +12,7 @@ enum PeerReportSubject { private enum PeerReportOption { case spam case violence + case copyright case pornoghraphy case other } @@ -24,6 +25,7 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p .spam, .violence, .pornoghraphy, + .copyright, .other ] @@ -37,8 +39,8 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p title = presentationData.strings.ReportPeer_ReasonViolence case .pornoghraphy: title = presentationData.strings.ReportPeer_ReasonPornography - /*case .copyright: - title = presentationData.strings.ReportPeer_ReasonCopyright*/ + case .copyright: + title = presentationData.strings.ReportPeer_ReasonCopyright case .other: title = presentationData.strings.ReportPeer_ReasonOther } @@ -52,6 +54,8 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p reportReason = .violence case .pornoghraphy: reportReason = .porno + case .copyright: + reportReason = .copyright case .other: break } @@ -60,12 +64,18 @@ func peerReportOptionsController(account: Account, subject: PeerReportSubject, p case let .peer(peerId): let _ = (reportPeer(account: account, peerId: peerId, reason: reportReason) |> deliverOnMainQueue).start(completed: { - present(OverlayStatusController(theme: presentationData.theme, type: .success), nil) + let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: { + + })]) + present(alert, nil) }) case let .messages(messageIds): let _ = (reportPeerMessages(account: account, messageIds: messageIds, reason: reportReason) |> deliverOnMainQueue).start(completed: { - present(OverlayStatusController(theme: presentationData.theme, type: .success), nil) + let alert = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: nil, text: presentationData.strings.ReportPeer_AlertSuccess, actions: [TextAlertAction.init(type: TextAlertActionType.defaultAction, title: presentationData.strings.Common_OK, action: { + + })]) + present(alert, nil) }) } } else { diff --git a/TelegramUI/SecretMediaPreviewController.swift b/TelegramUI/SecretMediaPreviewController.swift index 6aec559048..89386999fd 100644 --- a/TelegramUI/SecretMediaPreviewController.swift +++ b/TelegramUI/SecretMediaPreviewController.swift @@ -168,7 +168,7 @@ public final class SecretMediaPreviewController: ViewController { self.screenCaptureEventsDisposable = (screenCaptureEvents() |> deliverOnMainQueue).start(next: { [weak self] _ in - if let _ = self { + if let strongSelf = self, strongSelf.traceVisibility() { let _ = addSecretChatMessageScreenshot(account: account, peerId: messageId.peerId).start() } }) diff --git a/TelegramUI/SelectivePrivacySettingsPeersController.swift b/TelegramUI/SelectivePrivacySettingsPeersController.swift index 97ff53a845..39f990c77b 100644 --- a/TelegramUI/SelectivePrivacySettingsPeersController.swift +++ b/TelegramUI/SelectivePrivacySettingsPeersController.swift @@ -245,7 +245,7 @@ public func selectivePrivacyPeersController(account: Account, title: String, ini removePeerDisposable.set(applyPeers.start()) }, addPeer: { - let controller = ContactMultiselectionController(account: account, mode: .peerSelection) + let controller = ContactMultiselectionController(account: account, mode: .peerSelection, options: []) addPeerDisposable.set((controller.result |> take(1) |> deliverOnMainQueue).start(next: { [weak controller] peerIds in let applyPeers: Signal = peersPromise.get() |> take(1) diff --git a/TelegramUI/StickerResources.swift b/TelegramUI/StickerResources.swift index a5d2da4c79..2170617863 100644 --- a/TelegramUI/StickerResources.swift +++ b/TelegramUI/StickerResources.swift @@ -35,15 +35,18 @@ private func imageFromAJpeg(data: Data) -> (UIImage, UIImage)? { } private func chatMessageStickerDatas(account: Account, file: TelegramMediaFile, small: Bool, fetched: Bool, onlyFullSize: Bool) -> Signal<(Data?, Data?, Bool), NoError> { - let maybeFetched = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: onlyFullSize) + let maybeFetched = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: false) - return maybeFetched |> take(1) |> mapToSignal { maybeData in + return maybeFetched + |> take(1) + |> mapToSignal { maybeData in if maybeData.complete { let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) return .single((nil, loadedData, true)) } else { - let fullSizeData = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: false) |> map { next in + let fullSizeData = account.postbox.mediaBox.cachedResourceRepresentation(file.resource, representation: CachedStickerAJpegRepresentation(size: small ? CGSize(width: 160.0, height: 160.0) : nil), complete: onlyFullSize) + |> map { next in return (next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedIfSafe), next.complete) } @@ -66,7 +69,8 @@ private func chatMessageStickerDatas(account: Account, file: TelegramMediaFile, } } } else { - return fullSizeData |> map { (data, complete) -> (Data?, Data?, Bool) in + return fullSizeData + |> map { (data, complete) -> (Data?, Data?, Bool) in return (nil, data, complete) } } diff --git a/TelegramUI/TransformOutgoingMessageMedia.swift b/TelegramUI/TransformOutgoingMessageMedia.swift index fa624be5e7..2a04df07a1 100644 --- a/TelegramUI/TransformOutgoingMessageMedia.swift +++ b/TelegramUI/TransformOutgoingMessageMedia.swift @@ -117,7 +117,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me case let image as TelegramMediaImage: if let representation = largestImageRepresentation(image.representations) { let signal = Signal { subscriber in - let fetch = postbox.mediaBox.fetchedResource(representation.resource, parameters: nil).start() + let fetch = fetchedMediaResource(postbox: postbox, reference: media.resourceReference(representation.resource)).start() let data = postbox.mediaBox.resourceData(representation.resource, option: .complete(waitUntilFetchStatus: true)).start(next: { next in subscriber.putNext(next) if next.complete { diff --git a/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift b/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift index 204f260a55..c64b3b565d 100644 --- a/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift +++ b/TelegramUI/VerticalListContextResultsChatInputPanelItem.swift @@ -86,7 +86,10 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { private let topSeparatorNode: ASDisplayNode private let separatorNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode - + private var statusDisposable = MetaDisposable() + private let statusNode: RadialStatusNode = RadialStatusNode(backgroundNodeColor: UIColor(white: 0.0, alpha: 0.5)) + private var resourceStatus: MediaResourceStatus? + private var currentIconImageResource: TelegramMediaResource? init() { @@ -123,6 +126,11 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { self.addSubnode(self.iconImageNode) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) + self.addSubnode(self.statusNode) + } + + deinit { + statusDisposable.dispose() } override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { @@ -155,7 +163,8 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { var iconImageRepresentation: TelegramMediaImageRepresentation? var updateIconImageSignal: Signal<(TransformImageArguments) -> DrawingContext?, NoError>? - + var updatedStatusSignal: Signal? + if let title = item.result.title { titleString = NSAttributedString(string: title, font: titleFont, textColor: item.theme.list.itemPrimaryTextColor) } @@ -206,6 +215,9 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { let imageCorners = ImageCorners(topLeft: .Corner(2.0), topRight: .Corner(2.0), bottomLeft: .Corner(2.0), bottomRight: .Corner(2.0)) let arguments = TransformImageArguments(corners: imageCorners, imageSize: iconSize, boundingSize: iconSize, intrinsicInsets: UIEdgeInsets()) iconImageApply = iconImageLayout(arguments) + + updatedStatusSignal = item.account.postbox.mediaBox.resourceStatus(imageResource) + } var updatedIconImageResource = false @@ -312,6 +324,40 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode { strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: leftInset, y: nodeLayout.contentSize.height - UIScreenPixel), size: CGSize(width: params.width - leftInset, height: UIScreenPixel)) strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: nodeLayout.size.height + UIScreenPixel)) + + + let progressFrame = CGRect(origin: CGPoint(x: iconFrame.minX + floorToScreenPixels((iconFrame.width - 37) / 2), y: iconFrame.minY + floorToScreenPixels((iconFrame.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 + + 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(max(progress, 0.2)), cancelEnabled: false) + case .Remote: + state = .download(statusForegroundColor) + case .Local: + state = .none + } + + + strongSelf.statusNode.transitionToState(state, completion: { }) + } + } + })) + } else { + strongSelf.statusNode.transitionToState(.none, completion: { }) + } + } }) }