diff --git a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift index 63a08160d5..e43fa823b3 100644 --- a/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelAdminsController.swift @@ -498,7 +498,9 @@ private func channelAdminsControllerEntries(presentationData: PresentationData, entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled, showAuthorProfilesEnabled)) //TODO:localize entries.append(.showAuthorProfiles(presentationData.theme, "Show Authors' Profiles", showAuthorProfilesEnabled, signMessagesEnabled)) - entries.append(.signMessagesInfo(presentationData.theme, "Add names and photos of admins to the messages they post, linking to their profiles.")) + if signMessagesEnabled { + entries.append(.signMessagesInfo(presentationData.theme, "Add names and photos of admins to the messages they post, linking to their profiles.")) + } } } } else if case let .legacyGroup(peer) = peer { diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift index 3515c36ff4..9ee8da783b 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift @@ -64,8 +64,9 @@ extension ReactionsMessageAttribute { peerId: peerId?.peerId, count: count, isTop: (flags & (1 << 0)) != 0, - isMy: (flags & (1 << 1)) != 0) - ) + isMy: (flags & (1 << 1)) != 0, + isAnonymous: (flags & (1 << 2)) != 0 + )) } } } @@ -254,8 +255,9 @@ extension ReactionsMessageAttribute { peerId: peerId?.peerId, count: count, isTop: (flags & (1 << 0)) != 0, - isMy: (flags & (1 << 1)) != 0) - ) + isMy: (flags & (1 << 1)) != 0, + isAnonymous: (flags & (1 << 2)) != 0 + )) } } } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index 7e458b2cb4..97ea3f2d53 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -172,7 +172,7 @@ public func updateMessageReactionsInteractively(account: Account, messageIds: [M |> ignoreValues } -public func sendStarsReactionsInteractively(account: Account, messageId: MessageId, count: Int) -> Signal { +public func sendStarsReactionsInteractively(account: Account, messageId: MessageId, count: Int, isAnonymous: Bool) -> Signal { return account.postbox.transaction { transaction -> Void in transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: SendStarsReactionsAction(randomId: Int64.random(in: Int64.min ... Int64.max))) transaction.updateMessage(messageId, update: { currentMessage in @@ -190,7 +190,7 @@ public func sendStarsReactionsInteractively(account: Account, messageId: Message } } - attributes.append(PendingStarsReactionsMessageAttribute(accountPeerId: account.peerId, count: mappedCount)) + attributes.append(PendingStarsReactionsMessageAttribute(accountPeerId: account.peerId, count: mappedCount, isAnonymous: isAnonymous)) return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) @@ -220,6 +220,41 @@ func cancelPendingSendStarsReactionInteractively(account: Account, messageId: Me |> ignoreValues } +func _internal_updateStarsReactionIsAnonymous(account: Account, messageId: MessageId, isAnonymous: Bool) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + transaction.updateMessage(messageId, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags) + } + var attributes = currentMessage.attributes + for j in (0 ..< attributes.count).reversed() { + if let attribute = attributes[j] as? ReactionsMessageAttribute { + var updatedTopPeers = attribute.topPeers + if let index = updatedTopPeers.firstIndex(where: { $0.isMy }) { + updatedTopPeers[index].isAnonymous = isAnonymous + } + attributes[j] = ReactionsMessageAttribute(canViewList: attribute.canViewList, isTags: attribute.isTags, reactions: attribute.reactions, recentPeers: attribute.recentPeers, topPeers: updatedTopPeers) + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) + }) + + return transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer else { + return .complete() + } + + return account.network.request(Api.functions.messages.togglePaidReactionPrivacy(peer: inputPeer, msgId: messageId.id, private: isAnonymous ? .boolTrue : .boolFalse)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } +} + private enum RequestUpdateMessageReactionError { case generic } @@ -308,7 +343,7 @@ private func requestUpdateMessageReaction(postbox: Postbox, network: Network, st } private func requestSendStarsReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal { - return postbox.transaction { transaction -> (Peer, Int32)? in + return postbox.transaction { transaction -> (Peer, Int32, Bool)? in guard let peer = transaction.getPeer(messageId.peerId) else { return nil } @@ -316,17 +351,19 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM return nil } var count: Int32 = 0 + var isAnonymous = false for attribute in message.attributes { if let attribute = attribute as? PendingStarsReactionsMessageAttribute { count += attribute.count + isAnonymous = attribute.isAnonymous break } } - return (peer, count) + return (peer, count, isAnonymous) } |> castError(RequestUpdateMessageReactionError.self) |> mapToSignal { peerAndValue in - guard let (peer, count) = peerAndValue else { + guard let (peer, count, isAnonymous) = peerAndValue else { return .fail(.generic) } guard let inputPeer = apiInputPeer(peer) else { @@ -341,6 +378,11 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM let timestampPart = UInt64(UInt32(bitPattern: Int32(Date().timeIntervalSince1970))) let randomId = (timestampPart << 32) | randomPartId + var flags: Int32 = 0 + if isAnonymous { + flags |= 1 << 0 + } + let signal: Signal = network.request(Api.functions.messages.sendPaidReaction(flags: 0, peer: inputPeer, msgId: messageId.id, count: count, randomId: Int64(bitPattern: randomId))) |> mapError { _ -> RequestUpdateMessageReactionError in return .generic diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index 9db3fa1087..5b0fa01ee0 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -333,12 +333,14 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { public var count: Int32 public var isTop: Bool public var isMy: Bool + public var isAnonymous: Bool - public init(peerId: PeerId?, count: Int32, isTop: Bool, isMy: Bool) { + public init(peerId: PeerId?, count: Int32, isTop: Bool, isMy: Bool, isAnonymous: Bool) { self.peerId = peerId self.count = count self.isMy = isMy self.isTop = isTop + self.isAnonymous = isAnonymous } public init(decoder: PostboxDecoder) { @@ -350,6 +352,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { self.count = decoder.decodeInt32ForKey("c", orElse: 0) self.isTop = decoder.decodeBoolForKey("t", orElse: false) self.isMy = decoder.decodeBoolForKey("m", orElse: false) + self.isAnonymous = decoder.decodeBoolForKey("anon", orElse: false) } public func encode(_ encoder: PostboxEncoder) { @@ -361,6 +364,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { encoder.encodeInt32(self.count, forKey: "c") encoder.encodeBool(self.isTop, forKey: "t") encoder.encodeBool(self.isMy, forKey: "m") + encoder.encodeBool(self.isAnonymous, forKey: "anon") } } @@ -561,6 +565,7 @@ public final class PendingReactionsMessageAttribute: MessageAttribute { public final class PendingStarsReactionsMessageAttribute: MessageAttribute { public let accountPeerId: PeerId? public let count: Int32 + public let isAnonymous: Bool public var associatedPeerIds: [PeerId] { var peerIds: [PeerId] = [] @@ -570,14 +575,16 @@ public final class PendingStarsReactionsMessageAttribute: MessageAttribute { return peerIds } - public init(accountPeerId: PeerId?, count: Int32) { + public init(accountPeerId: PeerId?, count: Int32, isAnonymous: Bool) { self.accountPeerId = accountPeerId self.count = count + self.isAnonymous = isAnonymous } required public init(decoder: PostboxDecoder) { self.accountPeerId = decoder.decodeOptionalInt64ForKey("ap").flatMap(PeerId.init) self.count = decoder.decodeInt32ForKey("cnt", orElse: 1) + self.isAnonymous = decoder.decodeBoolForKey("anon", orElse: false) } public func encode(_ encoder: PostboxEncoder) { @@ -587,5 +594,6 @@ public final class PendingStarsReactionsMessageAttribute: MessageAttribute { encoder.encodeNil(forKey: "ap") } encoder.encodeInt32(self.count, forKey: "cnt") + encoder.encodeBool(self.isAnonymous, forKey: "anon") } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index e4bf60422d..3031c0b69a 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -334,13 +334,17 @@ public extension TelegramEngine { ).startStandalone() } - public func sendStarsReaction(id: EngineMessage.Id, count: Int) { - let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count).startStandalone() + public func sendStarsReaction(id: EngineMessage.Id, count: Int, isAnonymous: Bool) { + let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count, isAnonymous: isAnonymous).startStandalone() } public func cancelPendingSendStarsReaction(id: EngineMessage.Id) { let _ = cancelPendingSendStarsReactionInteractively(account: self.account, messageId: id).startStandalone() } + + public func updateStarsReactionIsAnonymous(id: EngineMessage.Id, isAnonymous: Bool) -> Signal { + return _internal_updateStarsReactionIsAnonymous(account: self.account, messageId: id, isAnonymous: isAnonymous) + } public func requestChatContextResults(botId: PeerId, peerId: PeerId, query: String, location: Signal<(Double, Double)?, NoError> = .single(nil), offset: String, incompleteResults: Bool = false, staleCachedResults: Bool = false) -> Signal { return _internal_requestChatContextResults(account: self.account, botId: botId, peerId: peerId, query: query, location: location, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults) diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD index 5d003e453b..30d8e6c87f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/BUILD @@ -31,6 +31,7 @@ swift_library( "//submodules/TelegramUI/Components/Utils/RoundedRectWithTailPath", "//submodules/AvatarNode", "//submodules/Components/BundleIconComponent", + "//submodules/CheckNode", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift index 01eaa356b0..19daa346d5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendStarsScreen/Sources/ChatSendStarsScreen.swift @@ -18,6 +18,7 @@ import SliderComponent import RoundedRectWithTailPath import AvatarNode import BundleIconComponent +import CheckNode private final class BalanceComponent: CombinedComponent { let context: AccountContext @@ -739,27 +740,33 @@ private final class ChatSendStarsScreenComponent: Component { let context: AccountContext let peer: EnginePeer + let messageId: EngineMessage.Id let maxAmount: Int let balance: Int64? let currentSentAmount: Int? let topPeers: [ChatSendStarsScreen.TopPeer] - let completion: (Int64, Bool, ChatSendStarsScreen.TransitionOut) -> Void + let myTopPeer: ChatSendStarsScreen.TopPeer? + let completion: (Int64, Bool, Bool, ChatSendStarsScreen.TransitionOut) -> Void init( context: AccountContext, peer: EnginePeer, + messageId: EngineMessage.Id, maxAmount: Int, balance: Int64?, currentSentAmount: Int?, topPeers: [ChatSendStarsScreen.TopPeer], - completion: @escaping (Int64, Bool, ChatSendStarsScreen.TransitionOut) -> Void + myTopPeer: ChatSendStarsScreen.TopPeer?, + completion: @escaping (Int64, Bool, Bool, ChatSendStarsScreen.TransitionOut) -> Void ) { self.context = context self.peer = peer + self.messageId = messageId self.maxAmount = maxAmount self.balance = balance self.currentSentAmount = currentSentAmount self.topPeers = topPeers + self.myTopPeer = myTopPeer self.completion = completion } @@ -782,6 +789,9 @@ private final class ChatSendStarsScreenComponent: Component { if lhs.topPeers != rhs.topPeers { return false } + if lhs.myTopPeer != rhs.myTopPeer { + return false + } return true } @@ -829,6 +839,9 @@ private final class ChatSendStarsScreenComponent: Component { private var topPeersTitleBackground: SimpleLayer? private var topPeersTitle: ComponentView? + private var anonymousSeparator = SimpleLayer() + private var anonymousContents = ComponentView() + private var topPeerItems: [ChatSendStarsScreen.TopPeer.Id: ComponentView] = [:] private let actionButton = ComponentView() @@ -846,6 +859,7 @@ private final class ChatSendStarsScreenComponent: Component { private var topOffsetDistance: CGFloat? private var amount: Int64 = 1 + private var isAnonymous: Bool = false private var cachedStarImage: (UIImage, PresentationTheme)? private var cachedCloseImage: UIImage? @@ -1014,6 +1028,9 @@ private final class ChatSendStarsScreenComponent: Component { if self.component == nil { self.amount = 50 + if let myTopPeer = component.myTopPeer { + self.isAnonymous = myTopPeer.isAnonymous + } } self.component = component @@ -1470,6 +1487,73 @@ private final class ChatSendStarsScreenComponent: Component { contentHeight += 161.0 } + do { + if !component.topPeers.isEmpty { + contentHeight += 2.0 + } + + if self.anonymousSeparator.superlayer == nil { + self.scrollContentView.layer.addSublayer(self.anonymousSeparator) + } + + self.anonymousSeparator.backgroundColor = environment.theme.list.itemPlainSeparatorColor.cgColor + + let checkTheme = CheckComponent.Theme( + backgroundColor: environment.theme.list.itemCheckColors.fillColor, + strokeColor: environment.theme.list.itemCheckColors.foregroundColor, + borderColor: environment.theme.list.itemCheckColors.strokeColor, + overlayBorder: false, + hasInset: false, + hasShadow: false + ) + let anonymousContentsSize = self.anonymousContents.update( + transition: transition, + component: AnyComponent(PlainButtonComponent( + content: AnyComponent(HStack([ + AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent( + theme: checkTheme, + selected: !self.isAnonymous + ))), + AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: "Show me in Top Senders", font: Font.regular(16.0), textColor: environment.theme.list.itemPrimaryTextColor)) + ))) + ], + spacing: 10.0 + )), + effectAlignment: .center, + action: { [weak self] in + guard let self, let component = self.component else { + return + } + self.isAnonymous = !self.isAnonymous + self.state?.updated(transition: .easeInOut(duration: 0.2)) + + if component.myTopPeer != nil { + let _ = component.context.engine.messages.updateStarsReactionIsAnonymous(id: component.messageId, isAnonymous: self.isAnonymous).startStandalone() + } + }, + animateAlpha: false, + animateScale: false + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + + transition.setFrame(layer: self.anonymousSeparator, frame: CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: UIScreenPixel))) + + contentHeight += 21.0 + + let anonymousContentsFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - anonymousContentsSize.width) * 0.5), y: contentHeight), size: anonymousContentsSize) + if let anonymousContentsView = self.anonymousContents.view { + if anonymousContentsView.superview == nil { + self.scrollContentView.addSubview(anonymousContentsView) + } + transition.setFrame(view: anonymousContentsView, frame: anonymousContentsFrame) + } + + contentHeight += anonymousContentsSize.height + 27.0 + } + initialContentHeight = contentHeight if self.cachedStarImage == nil || self.cachedStarImage?.1 !== environment.theme { @@ -1541,6 +1625,7 @@ private final class ChatSendStarsScreenComponent: Component { component.completion( self.amount, + self.isAnonymous, isBecomingTop, ChatSendStarsScreen.TransitionOut( sourceView: badgeView.badgeIcon @@ -1639,20 +1724,26 @@ private final class ChatSendStarsScreenComponent: Component { public class ChatSendStarsScreen: ViewControllerComponentContainer { public final class InitialData { fileprivate let peer: EnginePeer + fileprivate let messageId: EngineMessage.Id fileprivate let balance: Int64? fileprivate let currentSentAmount: Int? fileprivate let topPeers: [ChatSendStarsScreen.TopPeer] + fileprivate let myTopPeer: ChatSendStarsScreen.TopPeer? fileprivate init( peer: EnginePeer, + messageId: EngineMessage.Id, balance: Int64?, currentSentAmount: Int?, - topPeers: [ChatSendStarsScreen.TopPeer] + topPeers: [ChatSendStarsScreen.TopPeer], + myTopPeer: ChatSendStarsScreen.TopPeer? ) { self.peer = peer + self.messageId = messageId self.balance = balance self.currentSentAmount = currentSentAmount self.topPeers = topPeers + self.myTopPeer = myTopPeer } } @@ -1669,6 +1760,10 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { return Id(self.peer?.id) } + var isAnonymous: Bool { + return self.peer == nil + } + let peer: EnginePeer? let count: Int @@ -1703,7 +1798,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { private var presenceDisposable: Disposable? - public init(context: AccountContext, initialData: InitialData, completion: @escaping (Int64, Bool, TransitionOut) -> Void) { + public init(context: AccountContext, initialData: InitialData, completion: @escaping (Int64, Bool, Bool, TransitionOut) -> Void) { self.context = context var maxAmount = 2500 @@ -1714,10 +1809,12 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { super.init(context: context, component: ChatSendStarsScreenComponent( context: context, peer: initialData.peer, + messageId: initialData.messageId, maxAmount: maxAmount, balance: initialData.balance, currentSentAmount: initialData.currentSentAmount, topPeers: initialData.topPeers, + myTopPeer: initialData.myTopPeer, completion: completion ), navigationBarAppearance: .none) @@ -1748,7 +1845,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { } } - public static func initialData(context: AccountContext, peerId: EnginePeer.Id, topPeers: [ReactionsMessageAttribute.TopPeer]) -> Signal { + public static func initialData(context: AccountContext, peerId: EnginePeer.Id, messageId: EngineMessage.Id, topPeers: [ReactionsMessageAttribute.TopPeer]) -> Signal { let balance: Signal if let starsContext = context.starsContext { balance = starsContext.state @@ -1761,10 +1858,14 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { } var currentSentAmount: Int? + var myTopPeer: ReactionsMessageAttribute.TopPeer? if let myPeer = topPeers.first(where: { $0.isMy }) { + myTopPeer = myPeer currentSentAmount = Int(myPeer.count) } + let allPeerIds = topPeers.compactMap(\.peerId) + var topPeers = topPeers.sorted(by: { $0.count > $1.count }) if topPeers.count > 3 { topPeers = Array(topPeers.prefix(3)) @@ -1773,9 +1874,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { return combineLatest( context.engine.data.get( TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), - EngineDataMap(topPeers.map(\.peerId).compactMap { - $0.flatMap(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)) - }) + EngineDataMap(allPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) ), balance ) @@ -1787,6 +1886,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { return InitialData( peer: peer, + messageId: messageId, balance: balance, currentSentAmount: currentSentAmount, topPeers: topPeers.compactMap { topPeer -> ChatSendStarsScreen.TopPeer? in @@ -1803,7 +1903,25 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer { return nil } return ChatSendStarsScreen.TopPeer( - peer: topPeerValue, + peer: topPeer.isAnonymous ? nil : topPeerValue, + count: Int(topPeer.count) + ) + }, + myTopPeer: myTopPeer.flatMap { topPeer -> ChatSendStarsScreen.TopPeer? in + guard let topPeerId = topPeer.peerId else { + return ChatSendStarsScreen.TopPeer( + peer: nil, + count: Int(topPeer.count) + ) + } + guard let topPeerValue = topPeerMap[topPeerId] else { + return nil + } + guard let topPeerValue else { + return nil + } + return ChatSendStarsScreen.TopPeer( + peer: topPeer.isAnonymous ? nil : topPeerValue, count: Int(topPeer.count) ) } @@ -2042,3 +2160,97 @@ private final class SliderStarsView: UIView { self.emitterLayer.emitterSize = size } } + +private final class CheckComponent: Component { + struct Theme: Equatable { + public let backgroundColor: UIColor + public let strokeColor: UIColor + public let borderColor: UIColor + public let overlayBorder: Bool + public let hasInset: Bool + public let hasShadow: Bool + public let filledBorder: Bool + public let borderWidth: CGFloat? + + public init(backgroundColor: UIColor, strokeColor: UIColor, borderColor: UIColor, overlayBorder: Bool, hasInset: Bool, hasShadow: Bool, filledBorder: Bool = false, borderWidth: CGFloat? = nil) { + self.backgroundColor = backgroundColor + self.strokeColor = strokeColor + self.borderColor = borderColor + self.overlayBorder = overlayBorder + self.hasInset = hasInset + self.hasShadow = hasShadow + self.filledBorder = filledBorder + self.borderWidth = borderWidth + } + + var checkNodeTheme: CheckNodeTheme { + return CheckNodeTheme( + backgroundColor: self.backgroundColor, + strokeColor: self.strokeColor, + borderColor: self.borderColor, + overlayBorder: self.overlayBorder, + hasInset: self.hasInset, + hasShadow: self.hasShadow, + filledBorder: self.filledBorder, + borderWidth: self.borderWidth + ) + } + } + + let theme: Theme + let selected: Bool + + init( + theme: Theme, + selected: Bool + ) { + self.theme = theme + self.selected = selected + } + + static func ==(lhs: CheckComponent, rhs: CheckComponent) -> Bool { + if lhs.theme != rhs.theme { + return false + } + if lhs.selected != rhs.selected { + return false + } + return true + } + + final class View: UIView { + private var currentValue: CGFloat? + private var animator: DisplayLinkAnimator? + + private var checkLayer: CheckLayer { + return self.layer as! CheckLayer + } + + override class var layerClass: AnyClass { + return CheckLayer.self + } + + init() { + super.init(frame: CGRect()) + } + + required init?(coder aDecoder: NSCoder) { + preconditionFailure() + } + + func update(component: CheckComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { + self.checkLayer.setSelected(component.selected, animated: true) + self.checkLayer.theme = component.theme.checkNodeTheme + + return CGSize(width: 22.0, height: 22.0) + } + } + + func makeView() -> View { + return View() + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index 95e30d9470..a37b02902b 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -387,7 +387,7 @@ extension ChatControllerImpl { } } - self.context.engine.messages.sendStarsReaction(id: message.id, count: 1) + self.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: false) self.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1) } else { let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 0c26f48fbc..1b4f8bd829 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1729,7 +1729,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - strongSelf.context.engine.messages.sendStarsReaction(id: message.id, count: 1) + strongSelf.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: false) strongSelf.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1) }) } else { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index 174045146e..da1d1899b0 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -369,16 +369,14 @@ extension ChatControllerImpl { } func openMessageSendStarsScreen(message: Message) { - guard let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false) else { - return - } - let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, topPeers: reactionsAttribute.topPeers) + let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false) + let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, messageId: message.id, topPeers: reactionsAttribute?.topPeers ?? []) |> deliverOnMainQueue).start(next: { [weak self] initialData in guard let self, let initialData else { return } HapticFeedback().tap() - self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, isBecomingTop, transitionOut in + self.push(ChatSendStarsScreen(context: self.context, initialData: initialData, completion: { [weak self] amount, isAnonymous, isBecomingTop, transitionOut in guard let self, amount > 0 else { return } @@ -463,10 +461,7 @@ extension ChatControllerImpl { } } - #if !DEBUG - let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount)) - #endif - + let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount), isAnonymous: isAnonymous) self.displayOrUpdateSendStarsUndo(messageId: message.id, count: Int(amount)) })) })