mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-08 08:31:13 +00:00
Stars reactions
This commit is contained in:
parent
8acb1d12ee
commit
a95fef1c4c
@ -498,7 +498,9 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
|||||||
entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled, showAuthorProfilesEnabled))
|
entries.append(.signMessages(presentationData.theme, presentationData.strings.Channel_SignMessages, signMessagesEnabled, showAuthorProfilesEnabled))
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
entries.append(.showAuthorProfiles(presentationData.theme, "Show Authors' Profiles", showAuthorProfilesEnabled, signMessagesEnabled))
|
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 {
|
} else if case let .legacyGroup(peer) = peer {
|
||||||
|
@ -64,8 +64,9 @@ extension ReactionsMessageAttribute {
|
|||||||
peerId: peerId?.peerId,
|
peerId: peerId?.peerId,
|
||||||
count: count,
|
count: count,
|
||||||
isTop: (flags & (1 << 0)) != 0,
|
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,
|
peerId: peerId?.peerId,
|
||||||
count: count,
|
count: count,
|
||||||
isTop: (flags & (1 << 0)) != 0,
|
isTop: (flags & (1 << 0)) != 0,
|
||||||
isMy: (flags & (1 << 1)) != 0)
|
isMy: (flags & (1 << 1)) != 0,
|
||||||
)
|
isAnonymous: (flags & (1 << 2)) != 0
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ public func updateMessageReactionsInteractively(account: Account, messageIds: [M
|
|||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendStarsReactionsInteractively(account: Account, messageId: MessageId, count: Int) -> Signal<Never, NoError> {
|
public func sendStarsReactionsInteractively(account: Account, messageId: MessageId, count: Int, isAnonymous: Bool) -> Signal<Never, NoError> {
|
||||||
return account.postbox.transaction { transaction -> Void in
|
return account.postbox.transaction { transaction -> Void in
|
||||||
transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: SendStarsReactionsAction(randomId: Int64.random(in: Int64.min ... Int64.max)))
|
transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: SendStarsReactionsAction(randomId: Int64.random(in: Int64.min ... Int64.max)))
|
||||||
transaction.updateMessage(messageId, update: { currentMessage in
|
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))
|
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
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _internal_updateStarsReactionIsAnonymous(account: Account, messageId: MessageId, isAnonymous: Bool) -> Signal<Never, NoError> {
|
||||||
|
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<Never, NoError> 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<Api.Bool, NoError> in
|
||||||
|
return .single(.boolFalse)
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private enum RequestUpdateMessageReactionError {
|
private enum RequestUpdateMessageReactionError {
|
||||||
case generic
|
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<Never, RequestUpdateMessageReactionError> {
|
private func requestSendStarsReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal<Never, RequestUpdateMessageReactionError> {
|
||||||
return postbox.transaction { transaction -> (Peer, Int32)? in
|
return postbox.transaction { transaction -> (Peer, Int32, Bool)? in
|
||||||
guard let peer = transaction.getPeer(messageId.peerId) else {
|
guard let peer = transaction.getPeer(messageId.peerId) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -316,17 +351,19 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var count: Int32 = 0
|
var count: Int32 = 0
|
||||||
|
var isAnonymous = false
|
||||||
for attribute in message.attributes {
|
for attribute in message.attributes {
|
||||||
if let attribute = attribute as? PendingStarsReactionsMessageAttribute {
|
if let attribute = attribute as? PendingStarsReactionsMessageAttribute {
|
||||||
count += attribute.count
|
count += attribute.count
|
||||||
|
isAnonymous = attribute.isAnonymous
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (peer, count)
|
return (peer, count, isAnonymous)
|
||||||
}
|
}
|
||||||
|> castError(RequestUpdateMessageReactionError.self)
|
|> castError(RequestUpdateMessageReactionError.self)
|
||||||
|> mapToSignal { peerAndValue in
|
|> mapToSignal { peerAndValue in
|
||||||
guard let (peer, count) = peerAndValue else {
|
guard let (peer, count, isAnonymous) = peerAndValue else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
guard let inputPeer = apiInputPeer(peer) else {
|
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 timestampPart = UInt64(UInt32(bitPattern: Int32(Date().timeIntervalSince1970)))
|
||||||
let randomId = (timestampPart << 32) | randomPartId
|
let randomId = (timestampPart << 32) | randomPartId
|
||||||
|
|
||||||
|
var flags: Int32 = 0
|
||||||
|
if isAnonymous {
|
||||||
|
flags |= 1 << 0
|
||||||
|
}
|
||||||
|
|
||||||
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendPaidReaction(flags: 0, peer: inputPeer, msgId: messageId.id, count: count, randomId: Int64(bitPattern: randomId)))
|
let signal: Signal<Never, RequestUpdateMessageReactionError> = network.request(Api.functions.messages.sendPaidReaction(flags: 0, peer: inputPeer, msgId: messageId.id, count: count, randomId: Int64(bitPattern: randomId)))
|
||||||
|> mapError { _ -> RequestUpdateMessageReactionError in
|
|> mapError { _ -> RequestUpdateMessageReactionError in
|
||||||
return .generic
|
return .generic
|
||||||
|
@ -333,12 +333,14 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
|||||||
public var count: Int32
|
public var count: Int32
|
||||||
public var isTop: Bool
|
public var isTop: Bool
|
||||||
public var isMy: 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.peerId = peerId
|
||||||
self.count = count
|
self.count = count
|
||||||
self.isMy = isMy
|
self.isMy = isMy
|
||||||
self.isTop = isTop
|
self.isTop = isTop
|
||||||
|
self.isAnonymous = isAnonymous
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -350,6 +352,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
|||||||
self.count = decoder.decodeInt32ForKey("c", orElse: 0)
|
self.count = decoder.decodeInt32ForKey("c", orElse: 0)
|
||||||
self.isTop = decoder.decodeBoolForKey("t", orElse: false)
|
self.isTop = decoder.decodeBoolForKey("t", orElse: false)
|
||||||
self.isMy = decoder.decodeBoolForKey("m", orElse: false)
|
self.isMy = decoder.decodeBoolForKey("m", orElse: false)
|
||||||
|
self.isAnonymous = decoder.decodeBoolForKey("anon", orElse: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -361,6 +364,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute {
|
|||||||
encoder.encodeInt32(self.count, forKey: "c")
|
encoder.encodeInt32(self.count, forKey: "c")
|
||||||
encoder.encodeBool(self.isTop, forKey: "t")
|
encoder.encodeBool(self.isTop, forKey: "t")
|
||||||
encoder.encodeBool(self.isMy, forKey: "m")
|
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 final class PendingStarsReactionsMessageAttribute: MessageAttribute {
|
||||||
public let accountPeerId: PeerId?
|
public let accountPeerId: PeerId?
|
||||||
public let count: Int32
|
public let count: Int32
|
||||||
|
public let isAnonymous: Bool
|
||||||
|
|
||||||
public var associatedPeerIds: [PeerId] {
|
public var associatedPeerIds: [PeerId] {
|
||||||
var peerIds: [PeerId] = []
|
var peerIds: [PeerId] = []
|
||||||
@ -570,14 +575,16 @@ public final class PendingStarsReactionsMessageAttribute: MessageAttribute {
|
|||||||
return peerIds
|
return peerIds
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(accountPeerId: PeerId?, count: Int32) {
|
public init(accountPeerId: PeerId?, count: Int32, isAnonymous: Bool) {
|
||||||
self.accountPeerId = accountPeerId
|
self.accountPeerId = accountPeerId
|
||||||
self.count = count
|
self.count = count
|
||||||
|
self.isAnonymous = isAnonymous
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(decoder: PostboxDecoder) {
|
required public init(decoder: PostboxDecoder) {
|
||||||
self.accountPeerId = decoder.decodeOptionalInt64ForKey("ap").flatMap(PeerId.init)
|
self.accountPeerId = decoder.decodeOptionalInt64ForKey("ap").flatMap(PeerId.init)
|
||||||
self.count = decoder.decodeInt32ForKey("cnt", orElse: 1)
|
self.count = decoder.decodeInt32ForKey("cnt", orElse: 1)
|
||||||
|
self.isAnonymous = decoder.decodeBoolForKey("anon", orElse: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -587,5 +594,6 @@ public final class PendingStarsReactionsMessageAttribute: MessageAttribute {
|
|||||||
encoder.encodeNil(forKey: "ap")
|
encoder.encodeNil(forKey: "ap")
|
||||||
}
|
}
|
||||||
encoder.encodeInt32(self.count, forKey: "cnt")
|
encoder.encodeInt32(self.count, forKey: "cnt")
|
||||||
|
encoder.encodeBool(self.isAnonymous, forKey: "anon")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,14 +334,18 @@ public extension TelegramEngine {
|
|||||||
).startStandalone()
|
).startStandalone()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sendStarsReaction(id: EngineMessage.Id, count: Int) {
|
public func sendStarsReaction(id: EngineMessage.Id, count: Int, isAnonymous: Bool) {
|
||||||
let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count).startStandalone()
|
let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count, isAnonymous: isAnonymous).startStandalone()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func cancelPendingSendStarsReaction(id: EngineMessage.Id) {
|
public func cancelPendingSendStarsReaction(id: EngineMessage.Id) {
|
||||||
let _ = cancelPendingSendStarsReactionInteractively(account: self.account, messageId: id).startStandalone()
|
let _ = cancelPendingSendStarsReactionInteractively(account: self.account, messageId: id).startStandalone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updateStarsReactionIsAnonymous(id: EngineMessage.Id, isAnonymous: Bool) -> Signal<Never, NoError> {
|
||||||
|
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<RequestChatContextResultsResult?, RequestChatContextResultsError> {
|
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<RequestChatContextResultsResult?, RequestChatContextResultsError> {
|
||||||
return _internal_requestChatContextResults(account: self.account, botId: botId, peerId: peerId, query: query, location: location, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults)
|
return _internal_requestChatContextResults(account: self.account, botId: botId, peerId: peerId, query: query, location: location, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Utils/RoundedRectWithTailPath",
|
"//submodules/TelegramUI/Components/Utils/RoundedRectWithTailPath",
|
||||||
"//submodules/AvatarNode",
|
"//submodules/AvatarNode",
|
||||||
"//submodules/Components/BundleIconComponent",
|
"//submodules/Components/BundleIconComponent",
|
||||||
|
"//submodules/CheckNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -18,6 +18,7 @@ import SliderComponent
|
|||||||
import RoundedRectWithTailPath
|
import RoundedRectWithTailPath
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
|
import CheckNode
|
||||||
|
|
||||||
private final class BalanceComponent: CombinedComponent {
|
private final class BalanceComponent: CombinedComponent {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
@ -739,27 +740,33 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let peer: EnginePeer
|
let peer: EnginePeer
|
||||||
|
let messageId: EngineMessage.Id
|
||||||
let maxAmount: Int
|
let maxAmount: Int
|
||||||
let balance: Int64?
|
let balance: Int64?
|
||||||
let currentSentAmount: Int?
|
let currentSentAmount: Int?
|
||||||
let topPeers: [ChatSendStarsScreen.TopPeer]
|
let topPeers: [ChatSendStarsScreen.TopPeer]
|
||||||
let completion: (Int64, Bool, ChatSendStarsScreen.TransitionOut) -> Void
|
let myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||||
|
let completion: (Int64, Bool, Bool, ChatSendStarsScreen.TransitionOut) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
|
messageId: EngineMessage.Id,
|
||||||
maxAmount: Int,
|
maxAmount: Int,
|
||||||
balance: Int64?,
|
balance: Int64?,
|
||||||
currentSentAmount: Int?,
|
currentSentAmount: Int?,
|
||||||
topPeers: [ChatSendStarsScreen.TopPeer],
|
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.context = context
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
|
self.messageId = messageId
|
||||||
self.maxAmount = maxAmount
|
self.maxAmount = maxAmount
|
||||||
self.balance = balance
|
self.balance = balance
|
||||||
self.currentSentAmount = currentSentAmount
|
self.currentSentAmount = currentSentAmount
|
||||||
self.topPeers = topPeers
|
self.topPeers = topPeers
|
||||||
|
self.myTopPeer = myTopPeer
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,6 +789,9 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
if lhs.topPeers != rhs.topPeers {
|
if lhs.topPeers != rhs.topPeers {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.myTopPeer != rhs.myTopPeer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,6 +839,9 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
private var topPeersTitleBackground: SimpleLayer?
|
private var topPeersTitleBackground: SimpleLayer?
|
||||||
private var topPeersTitle: ComponentView<Empty>?
|
private var topPeersTitle: ComponentView<Empty>?
|
||||||
|
|
||||||
|
private var anonymousSeparator = SimpleLayer()
|
||||||
|
private var anonymousContents = ComponentView<Empty>()
|
||||||
|
|
||||||
private var topPeerItems: [ChatSendStarsScreen.TopPeer.Id: ComponentView<Empty>] = [:]
|
private var topPeerItems: [ChatSendStarsScreen.TopPeer.Id: ComponentView<Empty>] = [:]
|
||||||
|
|
||||||
private let actionButton = ComponentView<Empty>()
|
private let actionButton = ComponentView<Empty>()
|
||||||
@ -846,6 +859,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
private var topOffsetDistance: CGFloat?
|
private var topOffsetDistance: CGFloat?
|
||||||
|
|
||||||
private var amount: Int64 = 1
|
private var amount: Int64 = 1
|
||||||
|
private var isAnonymous: Bool = false
|
||||||
private var cachedStarImage: (UIImage, PresentationTheme)?
|
private var cachedStarImage: (UIImage, PresentationTheme)?
|
||||||
private var cachedCloseImage: UIImage?
|
private var cachedCloseImage: UIImage?
|
||||||
|
|
||||||
@ -1014,6 +1028,9 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
if self.component == nil {
|
if self.component == nil {
|
||||||
self.amount = 50
|
self.amount = 50
|
||||||
|
if let myTopPeer = component.myTopPeer {
|
||||||
|
self.isAnonymous = myTopPeer.isAnonymous
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
@ -1470,6 +1487,73 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
contentHeight += 161.0
|
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
|
initialContentHeight = contentHeight
|
||||||
|
|
||||||
if self.cachedStarImage == nil || self.cachedStarImage?.1 !== environment.theme {
|
if self.cachedStarImage == nil || self.cachedStarImage?.1 !== environment.theme {
|
||||||
@ -1541,6 +1625,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
|
|
||||||
component.completion(
|
component.completion(
|
||||||
self.amount,
|
self.amount,
|
||||||
|
self.isAnonymous,
|
||||||
isBecomingTop,
|
isBecomingTop,
|
||||||
ChatSendStarsScreen.TransitionOut(
|
ChatSendStarsScreen.TransitionOut(
|
||||||
sourceView: badgeView.badgeIcon
|
sourceView: badgeView.badgeIcon
|
||||||
@ -1639,20 +1724,26 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
||||||
public final class InitialData {
|
public final class InitialData {
|
||||||
fileprivate let peer: EnginePeer
|
fileprivate let peer: EnginePeer
|
||||||
|
fileprivate let messageId: EngineMessage.Id
|
||||||
fileprivate let balance: Int64?
|
fileprivate let balance: Int64?
|
||||||
fileprivate let currentSentAmount: Int?
|
fileprivate let currentSentAmount: Int?
|
||||||
fileprivate let topPeers: [ChatSendStarsScreen.TopPeer]
|
fileprivate let topPeers: [ChatSendStarsScreen.TopPeer]
|
||||||
|
fileprivate let myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||||
|
|
||||||
fileprivate init(
|
fileprivate init(
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
|
messageId: EngineMessage.Id,
|
||||||
balance: Int64?,
|
balance: Int64?,
|
||||||
currentSentAmount: Int?,
|
currentSentAmount: Int?,
|
||||||
topPeers: [ChatSendStarsScreen.TopPeer]
|
topPeers: [ChatSendStarsScreen.TopPeer],
|
||||||
|
myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||||
) {
|
) {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
|
self.messageId = messageId
|
||||||
self.balance = balance
|
self.balance = balance
|
||||||
self.currentSentAmount = currentSentAmount
|
self.currentSentAmount = currentSentAmount
|
||||||
self.topPeers = topPeers
|
self.topPeers = topPeers
|
||||||
|
self.myTopPeer = myTopPeer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1669,6 +1760,10 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
return Id(self.peer?.id)
|
return Id(self.peer?.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isAnonymous: Bool {
|
||||||
|
return self.peer == nil
|
||||||
|
}
|
||||||
|
|
||||||
let peer: EnginePeer?
|
let peer: EnginePeer?
|
||||||
let count: Int
|
let count: Int
|
||||||
|
|
||||||
@ -1703,7 +1798,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
private var presenceDisposable: Disposable?
|
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
|
self.context = context
|
||||||
|
|
||||||
var maxAmount = 2500
|
var maxAmount = 2500
|
||||||
@ -1714,10 +1809,12 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
super.init(context: context, component: ChatSendStarsScreenComponent(
|
super.init(context: context, component: ChatSendStarsScreenComponent(
|
||||||
context: context,
|
context: context,
|
||||||
peer: initialData.peer,
|
peer: initialData.peer,
|
||||||
|
messageId: initialData.messageId,
|
||||||
maxAmount: maxAmount,
|
maxAmount: maxAmount,
|
||||||
balance: initialData.balance,
|
balance: initialData.balance,
|
||||||
currentSentAmount: initialData.currentSentAmount,
|
currentSentAmount: initialData.currentSentAmount,
|
||||||
topPeers: initialData.topPeers,
|
topPeers: initialData.topPeers,
|
||||||
|
myTopPeer: initialData.myTopPeer,
|
||||||
completion: completion
|
completion: completion
|
||||||
), navigationBarAppearance: .none)
|
), navigationBarAppearance: .none)
|
||||||
|
|
||||||
@ -1748,7 +1845,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func initialData(context: AccountContext, peerId: EnginePeer.Id, topPeers: [ReactionsMessageAttribute.TopPeer]) -> Signal<InitialData?, NoError> {
|
public static func initialData(context: AccountContext, peerId: EnginePeer.Id, messageId: EngineMessage.Id, topPeers: [ReactionsMessageAttribute.TopPeer]) -> Signal<InitialData?, NoError> {
|
||||||
let balance: Signal<Int64?, NoError>
|
let balance: Signal<Int64?, NoError>
|
||||||
if let starsContext = context.starsContext {
|
if let starsContext = context.starsContext {
|
||||||
balance = starsContext.state
|
balance = starsContext.state
|
||||||
@ -1761,10 +1858,14 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentSentAmount: Int?
|
var currentSentAmount: Int?
|
||||||
|
var myTopPeer: ReactionsMessageAttribute.TopPeer?
|
||||||
if let myPeer = topPeers.first(where: { $0.isMy }) {
|
if let myPeer = topPeers.first(where: { $0.isMy }) {
|
||||||
|
myTopPeer = myPeer
|
||||||
currentSentAmount = Int(myPeer.count)
|
currentSentAmount = Int(myPeer.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let allPeerIds = topPeers.compactMap(\.peerId)
|
||||||
|
|
||||||
var topPeers = topPeers.sorted(by: { $0.count > $1.count })
|
var topPeers = topPeers.sorted(by: { $0.count > $1.count })
|
||||||
if topPeers.count > 3 {
|
if topPeers.count > 3 {
|
||||||
topPeers = Array(topPeers.prefix(3))
|
topPeers = Array(topPeers.prefix(3))
|
||||||
@ -1773,9 +1874,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
return combineLatest(
|
return combineLatest(
|
||||||
context.engine.data.get(
|
context.engine.data.get(
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
|
||||||
EngineDataMap(topPeers.map(\.peerId).compactMap {
|
EngineDataMap(allPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
|
||||||
$0.flatMap(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))
|
|
||||||
})
|
|
||||||
),
|
),
|
||||||
balance
|
balance
|
||||||
)
|
)
|
||||||
@ -1787,6 +1886,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
return InitialData(
|
return InitialData(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
|
messageId: messageId,
|
||||||
balance: balance,
|
balance: balance,
|
||||||
currentSentAmount: currentSentAmount,
|
currentSentAmount: currentSentAmount,
|
||||||
topPeers: topPeers.compactMap { topPeer -> ChatSendStarsScreen.TopPeer? in
|
topPeers: topPeers.compactMap { topPeer -> ChatSendStarsScreen.TopPeer? in
|
||||||
@ -1803,7 +1903,25 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ChatSendStarsScreen.TopPeer(
|
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)
|
count: Int(topPeer.count)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2042,3 +2160,97 @@ private final class SliderStarsView: UIView {
|
|||||||
self.emitterLayer.emitterSize = size
|
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<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1)
|
||||||
} else {
|
} else {
|
||||||
let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction
|
let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction
|
||||||
|
@ -1729,7 +1729,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
return
|
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)
|
strongSelf.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -369,16 +369,14 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func openMessageSendStarsScreen(message: Message) {
|
func openMessageSendStarsScreen(message: Message) {
|
||||||
guard let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false) else {
|
let reactionsAttribute = mergedMessageReactions(attributes: message.attributes, isTags: false)
|
||||||
return
|
let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, messageId: message.id, topPeers: reactionsAttribute?.topPeers ?? [])
|
||||||
}
|
|
||||||
let _ = (ChatSendStarsScreen.initialData(context: self.context, peerId: message.id.peerId, topPeers: reactionsAttribute.topPeers)
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] initialData in
|
|> deliverOnMainQueue).start(next: { [weak self] initialData in
|
||||||
guard let self, let initialData else {
|
guard let self, let initialData else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
HapticFeedback().tap()
|
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 {
|
guard let self, amount > 0 else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -463,10 +461,7 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !DEBUG
|
let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount), isAnonymous: isAnonymous)
|
||||||
let _ = self.context.engine.messages.sendStarsReaction(id: message.id, count: Int(amount))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: Int(amount))
|
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: Int(amount))
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user