From c2578a0f97bffc8a3634aac08ab10096cc95101f Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 6 Mar 2017 02:10:10 +0300 Subject: [PATCH] no message --- TelegramUI/ChatBotInfoItem.swift | 2 +- TelegramUI/ChatController.swift | 36 +++++- TelegramUI/ChatControllerInteraction.swift | 5 + TelegramUI/ChatMessageBackground.swift | 63 +++++----- TelegramUI/ChatMessageBubbleImages.swift | 12 +- TelegramUI/ChatMessageBubbleItemNode.swift | 52 +++++++-- TelegramUI/ChatMessageItem.swift | 2 + TelegramUI/ChatMessageItemView.swift | 3 + TelegramUI/GroupAdminsController.swift | 129 +++++++++++++++++---- TelegramUI/ItemListPeerItem.swift | 48 +++++++- 10 files changed, 280 insertions(+), 72 deletions(-) diff --git a/TelegramUI/ChatBotInfoItem.swift b/TelegramUI/ChatBotInfoItem.swift index 4e3c49e167..be2c0a5f94 100644 --- a/TelegramUI/ChatBotInfoItem.swift +++ b/TelegramUI/ChatBotInfoItem.swift @@ -59,7 +59,7 @@ final class ChatBotInfoItem: ListViewItem { } } -private let infoItemBackground = messageSingleBubbleLikeImage(incoming: true) +private let infoItemBackground = messageSingleBubbleLikeImage(incoming: true, highlighted: false) final class ChatBotInfoItemNode: ListViewItemNode { var controllerInteraction: ChatControllerInteraction? diff --git a/TelegramUI/ChatController.swift b/TelegramUI/ChatController.swift index 3b44a9bf3b..042e7adaf9 100644 --- a/TelegramUI/ChatController.swift +++ b/TelegramUI/ChatController.swift @@ -210,6 +210,20 @@ public class ChatController: TelegramController { if let strongSelf = self, strongSelf.isNodeLoaded { if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(id) { if let contextMenuController = contextMenuForChatPresentationIntefaceState(strongSelf.presentationInterfaceState, account: strongSelf.account, message: message, interfaceInteraction: strongSelf.interfaceInteraction) { + if let controllerInteraction = strongSelf.controllerInteraction { + controllerInteraction.highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId) + strongSelf.updateItemNodesHighlightedStates(animated: true) + } + + contextMenuController.dismissed = { + if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction { + if controllerInteraction.highlightedState?.messageStableId == message.stableId { + controllerInteraction.highlightedState = nil + strongSelf.updateItemNodesHighlightedStates(animated: true) + } + } + } + strongSelf.present(contextMenuController, in: .window, with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak node] in if let node = node { return (node, frame) @@ -1418,11 +1432,23 @@ public class ChatController: TelegramController { if updatedChatPresentationInterfaceState.interfaceState.selectionState != controllerInteraction.selectionState { let animated = controllerInteraction.selectionState == nil || updatedChatPresentationInterfaceState.interfaceState.selectionState == nil controllerInteraction.selectionState = updatedChatPresentationInterfaceState.interfaceState.selectionState - self.chatDisplayNode.historyNode.forEachItemNode { itemNode in - if let itemNode = itemNode as? ChatMessageItemView { - itemNode.updateSelectionState(animated: animated) - } - } + self.updateItemNodesSelectionStates(animated: animated) + } + } + } + + private func updateItemNodesSelectionStates(animated: Bool) { + self.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView { + itemNode.updateSelectionState(animated: animated) + } + } + } + + private func updateItemNodesHighlightedStates(animated: Bool) { + self.chatDisplayNode.historyNode.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView { + itemNode.updateHighlightedState(animated: animated) } } } diff --git a/TelegramUI/ChatControllerInteraction.swift b/TelegramUI/ChatControllerInteraction.swift index d7bb2e16c6..0c1901a84d 100644 --- a/TelegramUI/ChatControllerInteraction.swift +++ b/TelegramUI/ChatControllerInteraction.swift @@ -19,6 +19,10 @@ public enum ChatControllerInteractionNavigateToPeer { case withBotStartPayload(ChatControllerInitialBotStart) } +struct ChatInterfaceHighlightedState { + let messageStableId: UInt32 +} + public final class ChatControllerInteraction { let openMessage: (MessageId) -> Void let openSecretMessagePreview: (MessageId) -> Void @@ -30,6 +34,7 @@ public final class ChatControllerInteraction { let clickThroughMessage: () -> Void var hiddenMedia: [MessageId: [Media]] = [:] var selectionState: ChatInterfaceSelectionState? + var highlightedState: ChatInterfaceHighlightedState? let toggleMessageSelection: (MessageId) -> Void let sendMessage: (String) -> Void let sendSticker: (TelegramMediaFile) -> Void diff --git a/TelegramUI/ChatMessageBackground.swift b/TelegramUI/ChatMessageBackground.swift index 841f2f660e..405be527ab 100644 --- a/TelegramUI/ChatMessageBackground.swift +++ b/TelegramUI/ChatMessageBackground.swift @@ -40,18 +40,28 @@ enum ChatMessageBackgroundType: Equatable { } } -private let chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, neighbors: .none) -private let chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, neighbors: .top) -private let chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, neighbors: .bottom) -private let chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(incoming: true, neighbors: .both) +private let chatMessageBackgroundIncomingImage = messageBubbleImage(incoming: true, highlighted: false, neighbors: .none) +private let chatMessageBackgroundIncomingHighlightedImage = messageBubbleImage(incoming: true, highlighted: true, neighbors: .none) +private let chatMessageBackgroundIncomingMergedTopImage = messageBubbleImage(incoming: true, highlighted: false, neighbors: .top) +private let chatMessageBackgroundIncomingMergedTopHighlightedImage = messageBubbleImage(incoming: true, highlighted: true, neighbors: .top) +private let chatMessageBackgroundIncomingMergedBottomImage = messageBubbleImage(incoming: true, highlighted: false, neighbors: .bottom) +private let chatMessageBackgroundIncomingMergedBottomHighlightedImage = messageBubbleImage(incoming: true, highlighted: true, neighbors: .bottom) +private let chatMessageBackgroundIncomingMergedBothImage = messageBubbleImage(incoming: true, highlighted: false, neighbors: .both) +private let chatMessageBackgroundIncomingMergedBothHighlightedImage = messageBubbleImage(incoming: true, highlighted: true, neighbors: .both) + +private let chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, highlighted: false, neighbors: .none) +private let chatMessageBackgroundOutgoingHighlightedImage = messageBubbleImage(incoming: false, highlighted: true, neighbors: .none) +private let chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, highlighted: false, neighbors: .top) +private let chatMessageBackgroundOutgoingMergedTopHighlightedImage = messageBubbleImage(incoming: false, highlighted: true, neighbors: .top) +private let chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, highlighted: false, neighbors: .bottom) +private let chatMessageBackgroundOutgoingMergedBottomHighlightedImage = messageBubbleImage(incoming: false, highlighted: true, neighbors: .bottom) +private let chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, highlighted: false, neighbors: .both) +private let chatMessageBackgroundOutgoingMergedBothHighlightedImage = messageBubbleImage(incoming: false, highlighted: true, neighbors: .both) -private let chatMessageBackgroundOutgoingImage = messageBubbleImage(incoming: false, neighbors: .none) -private let chatMessageBackgroundOutgoingMergedTopImage = messageBubbleImage(incoming: false, neighbors: .top) -private let chatMessageBackgroundOutgoingMergedBottomImage = messageBubbleImage(incoming: false, neighbors: .bottom) -private let chatMessageBackgroundOutgoingMergedBothImage = messageBubbleImage(incoming: false, neighbors: .both) class ChatMessageBackground: ASImageNode { private var type: ChatMessageBackgroundType? + private var currentHighlighted = false override init() { super.init() @@ -61,35 +71,36 @@ class ChatMessageBackground: ASImageNode { self.displayWithoutProcessing = true } - func setType(type: ChatMessageBackgroundType) { - if let currentType = self.type, currentType == type { + func setType(type: ChatMessageBackgroundType, highlighted: Bool) { + if let currentType = self.type, currentType == type, self.currentHighlighted == highlighted { return } self.type = type + self.currentHighlighted = highlighted let image: UIImage? switch type { case let .Incoming(mergeType): switch mergeType { - case .None: - image = chatMessageBackgroundIncomingImage - case .Top: - image = chatMessageBackgroundIncomingMergedTopImage - case .Bottom: - image = chatMessageBackgroundIncomingMergedBottomImage - case .Both: - image = chatMessageBackgroundIncomingMergedBothImage + case .None: + image = highlighted ? chatMessageBackgroundIncomingHighlightedImage : chatMessageBackgroundIncomingImage + case .Top: + image = highlighted ? chatMessageBackgroundIncomingMergedTopHighlightedImage : chatMessageBackgroundIncomingMergedTopImage + case .Bottom: + image = highlighted ? chatMessageBackgroundIncomingMergedBottomHighlightedImage : chatMessageBackgroundIncomingMergedBottomImage + case .Both: + image = highlighted ? chatMessageBackgroundIncomingMergedBothHighlightedImage : chatMessageBackgroundIncomingMergedBothImage } case let .Outgoing(mergeType): switch mergeType { - case .None: - image = chatMessageBackgroundOutgoingImage - case .Top: - image = chatMessageBackgroundOutgoingMergedTopImage - case .Bottom: - image = chatMessageBackgroundOutgoingMergedBottomImage - case .Both: - image = chatMessageBackgroundOutgoingMergedBothImage + case .None: + image = highlighted ? chatMessageBackgroundOutgoingHighlightedImage : chatMessageBackgroundOutgoingImage + case .Top: + image = highlighted ? chatMessageBackgroundOutgoingMergedTopHighlightedImage : chatMessageBackgroundOutgoingMergedTopImage + case .Bottom: + image = highlighted ? chatMessageBackgroundOutgoingMergedBottomHighlightedImage : chatMessageBackgroundOutgoingMergedBottomImage + case .Both: + image = highlighted ? chatMessageBackgroundOutgoingMergedBothHighlightedImage : chatMessageBackgroundOutgoingMergedBothImage } } self.image = image diff --git a/TelegramUI/ChatMessageBubbleImages.swift b/TelegramUI/ChatMessageBubbleImages.swift index 917777a1f4..d03be07c08 100644 --- a/TelegramUI/ChatMessageBubbleImages.swift +++ b/TelegramUI/ChatMessageBubbleImages.swift @@ -2,9 +2,11 @@ import Foundation import Display private let incomingFillColor = UIColor(0xffffff) +private let incomingFillHighlightedColor = UIColor(0xaaaaff) private let incomingStrokeColor = UIColor(0x86A9C9, 0.5) private let outgoingFillColor = UIColor(0xE1FFC7) +private let outgoingFillHighlightedColor = UIColor(0xaaffaa) private let outgoingStrokeColor = UIColor(0x86A9C9, 0.5) enum MessageBubbleImageNeighbors { @@ -14,21 +16,21 @@ enum MessageBubbleImageNeighbors { case both } -func messageSingleBubbleLikeImage(incoming: Bool) -> UIImage { +func messageSingleBubbleLikeImage(incoming: Bool, highlighted: Bool) -> UIImage { let diameter: CGFloat = 36.0 return generateImage(CGSize(width: 36.0, height: diameter), contextGenerator: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) let lineWidth: CGFloat = 0.5 - context.setFillColor((incoming ? incomingStrokeColor : outgoingStrokeColor).cgColor) + context.setFillColor((incoming ? (highlighted ? incomingStrokeColor : incomingStrokeColor) : (highlighted ? outgoingStrokeColor : outgoingStrokeColor)).cgColor) context.fillEllipse(in: CGRect(origin: CGPoint(), size: size)) - context.setFillColor((incoming ? incomingFillColor : outgoingFillColor).cgColor) + context.setFillColor((incoming ? (highlighted ? incomingFillHighlightedColor : incomingFillColor) : (highlighted ? outgoingFillHighlightedColor : outgoingFillColor)).cgColor) context.fillEllipse(in: CGRect(origin: CGPoint(x: lineWidth, y: lineWidth), size: CGSize(width: size.width - lineWidth * 2.0, height: size.height - lineWidth * 2.0))) })!.stretchableImage(withLeftCapWidth: Int(diameter / 2.0), topCapHeight: Int(diameter / 2.0)) } -func messageBubbleImage(incoming: Bool, neighbors: MessageBubbleImageNeighbors) -> UIImage { +func messageBubbleImage(incoming: Bool, highlighted: Bool, neighbors: MessageBubbleImageNeighbors) -> UIImage { let diameter: CGFloat = 36.0 let corner: CGFloat = 7.0 return generateImage(CGSize(width: 42.0, height: diameter), contextGenerator: { size, context in @@ -48,7 +50,7 @@ func messageBubbleImage(incoming: Bool, neighbors: MessageBubbleImageNeighbors) let lineWidth: CGFloat = 1.0 - context.setFillColor((incoming ? incomingFillColor : outgoingFillColor).cgColor) + context.setFillColor((incoming ? (highlighted ? incomingFillHighlightedColor : incomingFillColor) : (highlighted ? outgoingFillHighlightedColor : outgoingFillColor)).cgColor) context.setLineWidth(lineWidth) context.setStrokeColor((incoming ? incomingStrokeColor : outgoingStrokeColor).cgColor) diff --git a/TelegramUI/ChatMessageBubbleItemNode.swift b/TelegramUI/ChatMessageBubbleItemNode.swift index 87a8796c00..ad2e5906af 100644 --- a/TelegramUI/ChatMessageBubbleItemNode.swift +++ b/TelegramUI/ChatMessageBubbleItemNode.swift @@ -68,6 +68,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { private var actionButtonsNode: ChatMessageActionButtonsNode? private var messageId: MessageId? + private var messageStableId: UInt32? + private var backgroundType: ChatMessageBackgroundType? + private var highlightedState: Bool = false private var backgroundFrameTransition: (CGRect, CGRect)? @@ -416,6 +419,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { return (layout, { [weak self] animation in if let strongSelf = self { strongSelf.messageId = message.id + strongSelf.messageStableId = message.stableId + + let mergeType = ChatMessageBackgroundMergeType(top: mergedBottom, bottom: mergedTop) + let backgroundType: ChatMessageBackgroundType + if !incoming { + backgroundType = .Outgoing(mergeType) + } else { + backgroundType = .Incoming(mergeType) + } + strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState) + + strongSelf.backgroundType = backgroundType if let nameNode = nameNodeSizeApply.1() { strongSelf.nameNode = nameNode @@ -528,13 +543,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { contentNodeOrigin.y += size.height } - let mergeType = ChatMessageBackgroundMergeType(top: mergedBottom, bottom: mergedTop) - if !incoming { - strongSelf.backgroundNode.setType(type: .Outgoing(mergeType)) - } else { - strongSelf.backgroundNode.setType(type: .Incoming(mergeType)) - } - if case .System = animation { if !strongSelf.backgroundNode.frame.equalTo(backgroundFrame) { strongSelf.backgroundFrameTransition = (strongSelf.backgroundNode.frame, backgroundFrame) @@ -874,6 +882,36 @@ class ChatMessageBubbleItemNode: ChatMessageItemView { } } + override func updateHighlightedState(animated: Bool) { + if let controllerInteraction = self.controllerInteraction { + var highlighted = false + if let messageStableId = self.messageStableId, let highlightedState = controllerInteraction.highlightedState { + if highlightedState.messageStableId == messageStableId { + highlighted = true + } + } + + if self.highlightedState != highlighted { + self.highlightedState = highlighted + if let backgroundType = self.backgroundType { + if highlighted { + self.backgroundNode.setType(type: backgroundType, highlighted: true) + } else { + if let previousContents = self.backgroundNode.layer.contents, animated { + self.backgroundNode.setType(type: backgroundType, highlighted: false) + + if let updatedContents = self.backgroundNode.layer.contents { + self.backgroundNode.layer.animate(from: previousContents as AnyObject, to: updatedContents as AnyObject, keyPath: "contents", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: 0.3) + } + } else { + self.backgroundNode.setType(type: backgroundType, highlighted: false) + } + } + } + } + } + } + private func performMessageButtonAction(button: ReplyMarkupButton) { if let item = self.item, let controllerInteraction = self.controllerInteraction { switch button.action { diff --git a/TelegramUI/ChatMessageItem.swift b/TelegramUI/ChatMessageItem.swift index 5eeda1fa1d..ab10420f96 100644 --- a/TelegramUI/ChatMessageItem.swift +++ b/TelegramUI/ChatMessageItem.swift @@ -137,6 +137,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { let (layout, apply) = nodeLayout(self, width, top, bottom, dateAtBottom) node.updateSelectionState(animated: false) + node.updateHighlightedState(animated: false) node.contentSize = layout.contentSize node.insets = layout.insets @@ -202,6 +203,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { completion(layout, { apply(animation) node.updateSelectionState(animated: false) + node.updateHighlightedState(animated: false) }) } } diff --git a/TelegramUI/ChatMessageItemView.swift b/TelegramUI/ChatMessageItemView.swift index e992e5e916..36ec89871f 100644 --- a/TelegramUI/ChatMessageItemView.swift +++ b/TelegramUI/ChatMessageItemView.swift @@ -127,6 +127,9 @@ public class ChatMessageItemView: ListViewItemNode { func updateSelectionState(animated: Bool) { } + func updateHighlightedState(animated: Bool) { + } + override public func header() -> ListViewItemHeader? { if let item = self.item { return item.header diff --git a/TelegramUI/GroupAdminsController.swift b/TelegramUI/GroupAdminsController.swift index 4c30e3d20b..8828540f2f 100644 --- a/TelegramUI/GroupAdminsController.swift +++ b/TelegramUI/GroupAdminsController.swift @@ -145,27 +145,33 @@ private enum GroupAdminsEntry: ItemListNodeEntry { switch self { case let .allAdmins(value): return ItemListSwitchItem(title: "All Members Are Admins", value: value, sectionId: self.section, style: .blocks, updated: { updatedValue in - + arguments.updateAllAreAdmins(updatedValue) }) case let .allAdminsInfo(text): return ItemListTextItem(text: text, sectionId: self.section) case let .peerItem(_, peer, presence, toggled, enabled): - return ItemListPeerItem(account: arguments.account, peer: peer, presence: presence, text: .presence, label: nil, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: nil, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _ in }, removePeer: { _ in }) + return ItemListPeerItem(account: arguments.account, peer: peer, presence: presence, text: .presence, label: nil, editing: ItemListPeerItemEditing(editable: false, editing: false, revealed: false), switchValue: toggled, enabled: enabled, sectionId: self.section, action: nil, setPeerIdWithRevealedOptions: { _ in }, removePeer: { _ in }, toggleUpdated: { value in + arguments.updatePeerIsAdmin(peer.id, value) + }) } } } private struct GroupAdminsControllerState: Equatable { let updatingAllAdminsValue: Bool? + let updatedAllAdminsValue: Bool? + let updatingAdminValue: [PeerId: Bool] init() { self.updatingAllAdminsValue = nil + self.updatedAllAdminsValue = nil self.updatingAdminValue = [:] } - init(updatingAllAdminsValue: Bool?, updatingAdminValue: [PeerId: Bool]) { + init(updatingAllAdminsValue: Bool?, updatedAllAdminsValue: Bool?, updatingAdminValue: [PeerId: Bool]) { self.updatingAllAdminsValue = updatingAllAdminsValue + self.updatedAllAdminsValue = updatedAllAdminsValue self.updatingAdminValue = updatingAdminValue } @@ -173,6 +179,9 @@ private struct GroupAdminsControllerState: Equatable { if lhs.updatingAllAdminsValue != rhs.updatingAllAdminsValue { return false } + if lhs.updatedAllAdminsValue != rhs.updatedAllAdminsValue { + return false + } if lhs.updatingAdminValue != rhs.updatingAdminValue { return false } @@ -181,11 +190,15 @@ private struct GroupAdminsControllerState: Equatable { } func withUpdatedUpdatingAllAdminsValue(_ updatingAllAdminsValue: Bool?) -> GroupAdminsControllerState { - return GroupAdminsControllerState(updatingAllAdminsValue: updatingAllAdminsValue, updatingAdminValue: self.updatingAdminValue) + return GroupAdminsControllerState(updatingAllAdminsValue: updatingAllAdminsValue, updatedAllAdminsValue: self.updatedAllAdminsValue, updatingAdminValue: self.updatingAdminValue) + } + + func withUpdatedUpdatedAllAdminsValue(_ updatedAllAdminsValue: Bool?) -> GroupAdminsControllerState { + return GroupAdminsControllerState(updatingAllAdminsValue: self.updatingAllAdminsValue, updatedAllAdminsValue: updatedAllAdminsValue, updatingAdminValue: self.updatingAdminValue) } func withUpdatedUpdatingAdminValue(_ updatingAdminValue: [PeerId: Bool]) -> GroupAdminsControllerState { - return GroupAdminsControllerState(updatingAllAdminsValue: self.updatingAllAdminsValue, updatingAdminValue: updatingAdminValue) + return GroupAdminsControllerState(updatingAllAdminsValue: self.updatingAllAdminsValue, updatedAllAdminsValue: self.updatedAllAdminsValue, updatingAdminValue: updatingAdminValue) } } @@ -193,8 +206,15 @@ private func groupAdminsControllerEntries(account: Account, view: PeerView, stat var entries: [GroupAdminsEntry] = [] if let peer = view.peers[view.peerId] as? TelegramGroup, let cachedData = view.cachedData as? CachedGroupData, let participants = cachedData.participants { - entries.append(.allAdmins(!peer.flags.contains(.adminsEnabled))) - if peer.flags.contains(.adminsEnabled) { + let effectiveAdminsEnabled: Bool + if let updatingAllAdminsValue = state.updatingAllAdminsValue { + effectiveAdminsEnabled = updatingAllAdminsValue + } else { + effectiveAdminsEnabled = peer.flags.contains(.adminsEnabled) + } + + entries.append(.allAdmins(!effectiveAdminsEnabled)) + if effectiveAdminsEnabled { entries.append(.allAdminsInfo("Only admins can add and remove members, edit name and photo of this group.")) } else { entries.append(.allAdminsInfo("Group members can add new members, edit name and photo of this group.")) @@ -226,7 +246,31 @@ private func groupAdminsControllerEntries(account: Account, view: PeerView, stat var index: Int32 = 0 for participant in sortedParticipants { if let peer = view.peers[participant.peerId] { - entries.append(.peerItem(index, peer, view.peerPresences[participant.peerId], false, false)) + var isAdmin = false + var isEnabled = true + if !effectiveAdminsEnabled { + isAdmin = true + isEnabled = false + } else { + switch participant { + case .creator: + isAdmin = true + isEnabled = false + case .admin: + if let value = state.updatingAdminValue[peer.id] { + isAdmin = value + } else { + isAdmin = true + } + case .member: + if let value = state.updatingAdminValue[peer.id] { + isAdmin = value + } else { + isAdmin = false + } + } + } + entries.append(.peerItem(index, peer, view.peerPresences[participant.peerId], isAdmin, isEnabled)) index += 1 } } @@ -242,22 +286,63 @@ public func groupAdminsController(account: Account, peerId: PeerId) -> ViewContr statePromise.set(stateValue.modify { f($0) }) } - var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)? - let actionsDisposable = DisposableSet() let toggleAllAdminsDisposable = MetaDisposable() actionsDisposable.add(toggleAllAdminsDisposable) - let toggleAdminsMetaDisposable = MetaDisposable() - let toggleAdminsDisposable = DisposableSet() - toggleAdminsMetaDisposable.set(toggleAdminsDisposable) - actionsDisposable.add(toggleAdminsMetaDisposable) + let toggleAdminsDisposables = DisposableDict() + actionsDisposable.add(toggleAdminsDisposables) let arguments = GroupAdminsControllerArguments(account: account, updateAllAreAdmins: { value in + updateState { state in + return state.withUpdatedUpdatingAllAdminsValue(value) + } + toggleAllAdminsDisposable.set((updateGroupManagementType(account: account, peerId: peerId, type: value ? .unrestricted : .restrictedToAdmins) |> deliverOnMainQueue).start(error: { + updateState { state in + return state.withUpdatedUpdatingAllAdminsValue(nil) + } + }, completed: { + updateState { state in + return state.withUpdatedUpdatingAllAdminsValue(nil).withUpdatedUpdatedAllAdminsValue(value) + } + })) + }, updatePeerIsAdmin: { memberId, value in + updateState { state in + var updatingAdminValue = state.updatingAdminValue + updatingAdminValue[memberId] = value + return state.withUpdatedUpdatingAdminValue(updatingAdminValue) + } - }, updatePeerIsAdmin: { peerId, value in - + if value { + toggleAdminsDisposables.set((addPeerAdmin(account: account, peerId: peerId, adminId: memberId) |> deliverOnMainQueue).start(error: { _ in + updateState { state in + var updatingAdminValue = state.updatingAdminValue + updatingAdminValue.removeValue(forKey: memberId) + return state.withUpdatedUpdatingAdminValue(updatingAdminValue) + } + }, completed: { + updateState { state in + var updatingAdminValue = state.updatingAdminValue + updatingAdminValue.removeValue(forKey: memberId) + return state.withUpdatedUpdatingAdminValue(updatingAdminValue) + } + }), forKey: memberId) + } else { + toggleAdminsDisposables.set((removePeerAdmin(account: account, peerId: peerId, adminId: memberId) |> deliverOnMainQueue).start(error: { _ in + updateState { state in + var updatingAdminValue = state.updatingAdminValue + updatingAdminValue.removeValue(forKey: memberId) + return state.withUpdatedUpdatingAdminValue(updatingAdminValue) + } + }, completed: { + updateState { state in + var updatingAdminValue = state.updatingAdminValue + updatingAdminValue.removeValue(forKey: memberId) + return state.withUpdatedUpdatingAdminValue(updatingAdminValue) + } + }), forKey: memberId) + } }) let peerView = account.viewTracker.peerView(peerId) @@ -271,7 +356,12 @@ public func groupAdminsController(account: Account, peerId: PeerId) -> ViewContr emptyStateItem = ItemListLoadingIndicatorEmptyStateItem() } - let controllerState = ItemListControllerState(title: "Admins", leftNavigationButton: nil, rightNavigationButton: nil, animateChanges: true) + var rightNavigationButton: ItemListNavigationButton? + if !state.updatingAdminValue.isEmpty || state.updatingAllAdminsValue != nil { + rightNavigationButton = ItemListNavigationButton(title: "", style: .activity, enabled: true, action: {}) + } + + let controllerState = ItemListControllerState(title: "Admins", leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, animateChanges: true) let listState = ItemListNodeState(entries: groupAdminsControllerEntries(account: account, view: view, state: state), style: .blocks, emptyStateItem: emptyStateItem, animateChanges: true) return (controllerState, (listState, arguments)) @@ -280,10 +370,5 @@ public func groupAdminsController(account: Account, peerId: PeerId) -> ViewContr } let controller = ItemListController(signal) - presentControllerImpl = { [weak controller] c, p in - if let controller = controller { - controller.present(c, in: .window, with: p) - } - } return controller } diff --git a/TelegramUI/ItemListPeerItem.swift b/TelegramUI/ItemListPeerItem.swift index 06f059b78b..1db9cacc1d 100644 --- a/TelegramUI/ItemListPeerItem.swift +++ b/TelegramUI/ItemListPeerItem.swift @@ -43,8 +43,9 @@ final class ItemListPeerItem: ListViewItem, ItemListItem { let action: (() -> Void)? let setPeerIdWithRevealedOptions: (PeerId?, PeerId?) -> Void let removePeer: (PeerId) -> Void + let toggleUpdated: ((Bool) -> Void)? - init(account: Account, peer: Peer, presence: PeerPresence?, text: ItemListPeerItemText, label: String?, editing: ItemListPeerItemEditing, switchValue: Bool?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void) { + init(account: Account, peer: Peer, presence: PeerPresence?, text: ItemListPeerItemText, label: String?, editing: ItemListPeerItemEditing, switchValue: Bool?, enabled: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, removePeer: @escaping (PeerId) -> Void, toggleUpdated: ((Bool) -> Void)? = nil) { self.account = account self.peer = peer self.presence = presence @@ -57,6 +58,7 @@ final class ItemListPeerItem: ListViewItem, ItemListItem { self.action = action self.setPeerIdWithRevealedOptions = setPeerIdWithRevealedOptions self.removePeer = removePeer + self.toggleUpdated = toggleUpdated } func nodeConfiguredForWidth(async: @escaping (@escaping () -> Void) -> Void, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, () -> Void)) -> Void) { @@ -210,10 +212,14 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { peerRevealOptions = [] } - if let switchValue = item.switchValue { + var rightInset: CGFloat = 0.0 + let switchSize = CGSize(width: 51.0, height: 31.0) + + if let _ = item.switchValue { if currentSwitchNode == nil { currentSwitchNode = SwitchNode() } + rightInset += switchSize.width } else { currentSwitchNode = nil } @@ -270,10 +276,10 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { editingOffset = 0.0 } - let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset, height: CGFloat.greatestFiniteMagnitude), nil) + let (labelLayout, labelApply) = makeLabelLayout(labelAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) - let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - labelLayout.size.width - editingOffset, height: CGFloat.greatestFiniteMagnitude), nil) - let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - (labelLayout.size.width > 0.0 ? (labelLayout.size.width) + 15.0 : 0.0) - editingOffset, height: CGFloat.greatestFiniteMagnitude), nil) + let (titleLayout, titleApply) = makeTitleLayout(titleAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - labelLayout.size.width - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) + let (statusLayout, statusApply) = makeStatusLayout(statusAttributedString, nil, 1, .end, CGSize(width: width - leftInset - 8.0 - (labelLayout.size.width > 0.0 ? (labelLayout.size.width) + 15.0 : 0.0) - editingOffset - rightInset, height: CGFloat.greatestFiniteMagnitude), nil) let insets = itemListNeighborsGroupedInsets(neighbors) let contentSize = CGSize(width: width, height: 48.0) @@ -386,7 +392,31 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { transition.updateFrame(node: strongSelf.titleNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: statusAttributedString == nil ? 13.0 : 5.0), size: titleLayout.size)) transition.updateFrame(node: strongSelf.statusNode, frame: CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: 25.0), size: statusLayout.size)) - transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + width - labelLayout.size.width - 15.0, y: floor((contentSize.height - labelLayout.size.height) / 2.0 - labelLayout.size.height / 10.0)), size: labelLayout.size)) + + if let currentSwitchNode = currentSwitchNode { + if currentSwitchNode !== strongSelf.switchNode { + strongSelf.switchNode = currentSwitchNode + if let disabledOverlayNode = strongSelf.disabledOverlayNode, disabledOverlayNode.supernode != nil { + strongSelf.insertSubnode(currentSwitchNode, belowSubnode: disabledOverlayNode) + } else { + strongSelf.addSubnode(currentSwitchNode) + } + currentSwitchNode.valueUpdated = { value in + if let strongSelf = self { + strongSelf.toggleUpdated(value) + } + } + } + currentSwitchNode.frame = CGRect(origin: CGPoint(x: revealOffset + width - switchSize.width - 15.0, y: floor((contentSize.height - switchSize.height) / 2.0)), size: switchSize) + if let switchValue = item.switchValue { + currentSwitchNode.setOn(switchValue, animated: animated) + } + } else if let switchNode = strongSelf.switchNode { + switchNode.removeFromSupernode() + strongSelf.switchNode = nil + } + + transition.updateFrame(node: strongSelf.labelNode, frame: CGRect(origin: CGPoint(x: revealOffset + width - labelLayout.size.width - 15.0 - rightInset, y: floor((contentSize.height - labelLayout.size.height) / 2.0 - labelLayout.size.height / 10.0)), size: labelLayout.size)) transition.updateFrame(node: strongSelf.avatarNode, frame: CGRect(origin: CGPoint(x: revealOffset + editingOffset + 12.0, y: 4.0), size: CGSize(width: 40.0, height: 40.0))) strongSelf.avatarNode.setPeer(account: item.account, peer: item.peer) @@ -493,4 +523,10 @@ class ItemListPeerItemNode: ItemListRevealOptionsItemNode { item.removePeer(item.peer.id) } } + + private func toggleUpdated(_ value: Bool) { + if let (item, _, _) = self.layoutParams { + item.toggleUpdated?(value) + } + } }