Merge commit 'd40cf1023e3a508b2de2c1362dd463aa80bff961' into macos-11-release

This commit is contained in:
Mikhail Filimonov 2024-08-16 10:16:57 -03:00
commit f2399930af
13 changed files with 293 additions and 64 deletions

View File

@ -124,13 +124,14 @@ def load_codesigning_data_from_git(working_dir, repo_url, temp_key_path, branch,
encrypted_working_dir = working_dir + '/encrypted' encrypted_working_dir = working_dir + '/encrypted'
if os.path.exists(encrypted_working_dir): if os.path.exists(encrypted_working_dir):
original_working_dir = os.getcwd()
os.chdir(encrypted_working_dir)
if always_fetch: if always_fetch:
original_working_dir = os.getcwd()
os.chdir(encrypted_working_dir)
check_run_system('GIT_SSH_COMMAND="{ssh_command}" git fetch'.format(ssh_command=ssh_command)) check_run_system('GIT_SSH_COMMAND="{ssh_command}" git fetch'.format(ssh_command=ssh_command))
check_run_system('git checkout "{branch}"'.format(branch=branch)) check_run_system('git checkout "{branch}"'.format(branch=branch))
if always_fetch:
check_run_system('GIT_SSH_COMMAND="{ssh_command}" git pull'.format(ssh_command=ssh_command)) check_run_system('GIT_SSH_COMMAND="{ssh_command}" git pull'.format(ssh_command=ssh_command))
os.chdir(original_working_dir) os.chdir(original_working_dir)
else: else:
os.makedirs(encrypted_working_dir, exist_ok=True) os.makedirs(encrypted_working_dir, exist_ok=True)
original_working_dir = os.getcwd() original_working_dir = os.getcwd()

View File

@ -172,7 +172,7 @@ public func updateMessageReactionsInteractively(account: Account, messageIds: [M
|> ignoreValues |> ignoreValues
} }
public func sendStarsReactionsInteractively(account: Account, messageId: MessageId, count: Int, isAnonymous: Bool) -> 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
@ -182,15 +182,28 @@ public func sendStarsReactionsInteractively(account: Account, messageId: Message
} }
var mappedCount = Int32(count) var mappedCount = Int32(count)
var attributes = currentMessage.attributes var attributes = currentMessage.attributes
var resolvedIsAnonymous = false
for attribute in attributes {
if let attribute = attribute as? ReactionsMessageAttribute {
if let myReaction = attribute.topPeers.first(where: { $0.isMy }) {
resolvedIsAnonymous = myReaction.isAnonymous
}
}
}
loop: for j in 0 ..< attributes.count { loop: for j in 0 ..< attributes.count {
if let current = attributes[j] as? PendingStarsReactionsMessageAttribute { if let current = attributes[j] as? PendingStarsReactionsMessageAttribute {
mappedCount += current.count mappedCount += current.count
resolvedIsAnonymous = current.isAnonymous
attributes.remove(at: j) attributes.remove(at: j)
break loop break loop
} }
} }
if let isAnonymous {
resolvedIsAnonymous = isAnonymous
}
attributes.append(PendingStarsReactionsMessageAttribute(accountPeerId: account.peerId, count: mappedCount, isAnonymous: isAnonymous)) attributes.append(PendingStarsReactionsMessageAttribute(accountPeerId: account.peerId, count: mappedCount, isAnonymous: resolvedIsAnonymous))
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))
}) })
@ -395,7 +408,6 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM
} }
|> mapToSignal { result -> Signal<Never, RequestUpdateMessageReactionError> in |> mapToSignal { result -> Signal<Never, RequestUpdateMessageReactionError> in
stateManager.starsContext?.add(balance: Int64(-count), addTransaction: false) stateManager.starsContext?.add(balance: Int64(-count), addTransaction: false)
//stateManager.starsContext?.load(force: true)
return postbox.transaction { transaction -> Void in return postbox.transaction { transaction -> Void in
transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: UpdateMessageReactionsAction()) transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: UpdateMessageReactionsAction())

View File

@ -334,7 +334,7 @@ public extension TelegramEngine {
).startStandalone() ).startStandalone()
} }
public func sendStarsReaction(id: EngineMessage.Id, count: Int, isAnonymous: Bool) { public func sendStarsReaction(id: EngineMessage.Id, count: Int, isAnonymous: Bool?) {
let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count, isAnonymous: isAnonymous).startStandalone() let _ = sendStarsReactionsInteractively(account: self.account, messageId: id, count: count, isAnonymous: isAnonymous).startStandalone()
} }

View File

@ -842,12 +842,11 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
case let .peer(peerId): case let .peer(peerId):
if peerId != item.context.account.peerId { if peerId != item.context.account.peerId {
if peerId.isGroupOrChannel && item.message.author != nil { if peerId.isGroupOrChannel && item.message.author != nil {
var isBroadcastChannel = false if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case let .broadcast(info) = peer.info {
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { if info.flags.contains(.messagesShouldHaveProfiles) {
isBroadcastChannel = true hasAvatar = incoming
} }
} else {
if !isBroadcastChannel {
hasAvatar = true hasAvatar = true
} }
} }

View File

@ -1550,7 +1550,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
} }
} }
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, firstMessage.author?.id != channel.id { if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info {
if info.flags.contains(.messagesShouldHaveProfiles) { if info.flags.contains(.messagesShouldHaveProfiles) {
var allowAuthor = incoming var allowAuthor = incoming
overrideEffectiveAuthor = true overrideEffectiveAuthor = true

View File

@ -75,6 +75,13 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs
} }
} }
if let channel = lhs.peers[lhs.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info {
if info.flags.contains(.messagesShouldHaveProfiles) {
lhsEffectiveAuthor = lhs.author
rhsEffectiveAuthor = rhs.author
}
}
var sameChat = true var sameChat = true
if lhs.id.peerId != rhs.id.peerId { if lhs.id.peerId != rhs.id.peerId {
sameChat = false sameChat = false
@ -344,7 +351,7 @@ public final class ChatMessageItemImpl: ChatMessageItem, CustomStringConvertible
if !hasActionMedia { if !hasActionMedia {
if !isBroadcastChannel { if !isBroadcastChannel {
hasAvatar = true hasAvatar = true
} else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, message.author?.id != channel.id { } else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info {
if info.flags.contains(.messagesShouldHaveProfiles) { if info.flags.contains(.messagesShouldHaveProfiles) {
hasAvatar = true hasAvatar = true
effectiveAuthor = message.author effectiveAuthor = message.author

View File

@ -464,14 +464,15 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
case let .peer(peerId): case let .peer(peerId):
if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) { if !peerId.isRepliesOrSavedMessages(accountPeerId: item.context.account.peerId) {
if peerId.isGroupOrChannel && item.message.author != nil { if peerId.isGroupOrChannel && item.message.author != nil {
var isBroadcastChannel = false if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case let .broadcast(info) = peer.info {
if let peer = item.message.peers[item.message.id.peerId] as? TelegramChannel, case .broadcast = peer.info { if info.flags.contains(.messagesShouldHaveProfiles) {
isBroadcastChannel = true hasAvatar = incoming
}
} else {
hasAvatar = true
} }
if !isBroadcastChannel { if case .customChatContents = item.chatLocation {
hasAvatar = true
} else if case .customChatContents = item.chatLocation {
hasAvatar = false hasAvatar = false
} }
} }

View File

@ -227,6 +227,14 @@ private final class BadgeComponent: Component {
required init(coder: NSCoder) { required init(coder: NSCoder) {
preconditionFailure() preconditionFailure()
} }
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.badgeView.frame.contains(point) {
return self
} else {
return nil
}
}
func update(component: BadgeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize { func update(component: BadgeComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component == nil { if self.component == nil {
@ -471,14 +479,14 @@ private final class PeerComponent: Component {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let peer: EnginePeer? let peer: EnginePeer?
let count: Int let count: String
init( init(
context: AccountContext, context: AccountContext,
theme: PresentationTheme, theme: PresentationTheme,
strings: PresentationStrings, strings: PresentationStrings,
peer: EnginePeer?, peer: EnginePeer?,
count: Int count: String
) { ) {
self.context = context self.context = context
self.theme = theme self.theme = theme
@ -547,7 +555,7 @@ private final class PeerComponent: Component {
transition: .immediate, transition: .immediate,
component: AnyComponent(PeerBadgeComponent( component: AnyComponent(PeerBadgeComponent(
theme: component.theme, theme: component.theme,
title: "\(component.count)" title: component.count
)), )),
environment: {}, environment: {},
containerSize: CGSize(width: 200.0, height: 200.0) containerSize: CGSize(width: 200.0, height: 200.0)
@ -806,7 +814,7 @@ private final class SliderBackgroundComponent: Component {
topBackgroundTextView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: animateTopTextAdditionalX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.3, damping: 100.0, additive: true) topBackgroundTextView.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: animateTopTextAdditionalX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.3, damping: 100.0, additive: true)
} }
topForegroundTextView.isHidden = component.topCutoff == nil topForegroundTextView.isHidden = component.topCutoff == nil || topLineFrame.maxX + topTextSize.width + 20.0 > availableSize.width
topBackgroundTextView.isHidden = topForegroundTextView.isHidden topBackgroundTextView.isHidden = topForegroundTextView.isHidden
self.topBackgroundLine.isHidden = topX < 10.0 self.topBackgroundLine.isHidden = topX < 10.0
self.topForegroundLine.isHidden = self.topBackgroundLine.isHidden self.topForegroundLine.isHidden = self.topBackgroundLine.isHidden
@ -915,6 +923,83 @@ private final class ChatSendStarsScreenComponent: Component {
} }
} }
private struct Amount: Equatable {
private let sliderSteps: [Int]
private let maxRealValue: Int
let maxSliderValue: Int
private let isLogarithmic: Bool
private(set) var realValue: Int
private(set) var sliderValue: Int
private static func makeSliderSteps(maxRealValue: Int, isLogarithmic: Bool) -> [Int] {
if isLogarithmic {
var sliderSteps: [Int] = [ 1, 10, 50, 100, 500, 1_000, 2_000, 5_000, 7_500, 10_000 ]
sliderSteps.removeAll(where: { $0 >= maxRealValue })
sliderSteps.append(maxRealValue)
return sliderSteps
} else {
return [1, maxRealValue]
}
}
private static func remapValueToSlider(realValue: Int, maxSliderValue: Int, steps: [Int]) -> Int {
guard realValue >= steps.first!, realValue <= steps.last! else { return 0 }
for i in 0 ..< steps.count - 1 {
if realValue >= steps[i] && realValue <= steps[i + 1] {
let range = steps[i + 1] - steps[i]
let relativeValue = realValue - steps[i]
let stepFraction = Float(relativeValue) / Float(range)
return Int(Float(i) * Float(maxSliderValue) / Float(steps.count - 1)) + Int(stepFraction * Float(maxSliderValue) / Float(steps.count - 1))
}
}
return maxSliderValue // Return max slider position if value equals the last step
}
private static func remapSliderToValue(sliderValue: Int, maxSliderValue: Int, steps: [Int]) -> Int {
guard sliderValue >= 0, sliderValue <= maxSliderValue else { return steps.first! }
let stepIndex = Int(Float(sliderValue) / Float(maxSliderValue) * Float(steps.count - 1))
let fraction = Float(sliderValue) / Float(maxSliderValue) * Float(steps.count - 1) - Float(stepIndex)
if stepIndex >= steps.count - 1 {
return steps.last!
} else {
let range = steps[stepIndex + 1] - steps[stepIndex]
return steps[stepIndex] + Int(fraction * Float(range))
}
}
init(realValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) {
self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic)
self.maxRealValue = maxRealValue
self.maxSliderValue = maxSliderValue
self.isLogarithmic = isLogarithmic
self.realValue = realValue
self.sliderValue = Amount.remapValueToSlider(realValue: self.realValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps)
}
init(sliderValue: Int, maxRealValue: Int, maxSliderValue: Int, isLogarithmic: Bool) {
self.sliderSteps = Amount.makeSliderSteps(maxRealValue: maxRealValue, isLogarithmic: isLogarithmic)
self.maxRealValue = maxRealValue
self.maxSliderValue = maxSliderValue
self.isLogarithmic = isLogarithmic
self.sliderValue = sliderValue
self.realValue = Amount.remapSliderToValue(sliderValue: self.sliderValue, maxSliderValue: self.maxSliderValue, steps: self.sliderSteps)
}
func withRealValue(_ realValue: Int) -> Amount {
return Amount(realValue: realValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic)
}
func withSliderValue(_ sliderValue: Int) -> Amount {
return Amount(sliderValue: sliderValue, maxRealValue: self.maxRealValue, maxSliderValue: self.maxSliderValue, isLogarithmic: self.isLogarithmic)
}
}
final class View: UIView, UIScrollViewDelegate { final class View: UIView, UIScrollViewDelegate {
private let dimView: UIView private let dimView: UIView
private let backgroundLayer: SimpleLayer private let backgroundLayer: SimpleLayer
@ -959,7 +1044,10 @@ private final class ChatSendStarsScreenComponent: Component {
private var topOffsetDistance: CGFloat? private var topOffsetDistance: CGFloat?
private var balance: Int64? private var balance: Int64?
private var amount: Int64 = 1
private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true)
private var didChangeAmount: Bool = false
private var isAnonymous: Bool = false private var isAnonymous: Bool = false
private var cachedStarImage: (UIImage, PresentationTheme)? private var cachedStarImage: (UIImage, PresentationTheme)?
private var cachedCloseImage: UIImage? private var cachedCloseImage: UIImage?
@ -1058,6 +1146,12 @@ private final class ChatSendStarsScreenComponent: Component {
return result return result
} }
if let badgeView = self.badge.view, badgeView.hitTest(self.convert(point, to: badgeView), with: event) != nil {
if let sliderView = self.slider.view as? SliderComponent.View, let hitTestTarget = sliderView.hitTestTarget {
return hitTestTarget
}
}
let result = super.hitTest(point, with: event) let result = super.hitTest(point, with: event)
return result return result
} }
@ -1135,7 +1229,11 @@ private final class ChatSendStarsScreenComponent: Component {
if self.component == nil { if self.component == nil {
self.balance = component.balance self.balance = component.balance
self.amount = 50 var isLogarithmic = true
if let data = component.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_stars_reaction_logarithmic_scale"] as? Double {
isLogarithmic = Int(value) != 0
}
self.amount = Amount(realValue: 50, maxRealValue: component.maxAmount, maxSliderValue: 999, isLogarithmic: isLogarithmic)
if let myTopPeer = component.myTopPeer { if let myTopPeer = component.myTopPeer {
self.isAnonymous = myTopPeer.isAnonymous self.isAnonymous = myTopPeer.isAnonymous
} }
@ -1182,8 +1280,8 @@ private final class ChatSendStarsScreenComponent: Component {
let sliderSize = self.slider.update( let sliderSize = self.slider.update(
transition: transition, transition: transition,
component: AnyComponent(SliderComponent( component: AnyComponent(SliderComponent(
valueCount: component.maxAmount, valueCount: self.amount.maxSliderValue + 1,
value: Int(self.amount), value: self.amount.sliderValue,
markPositions: false, markPositions: false,
trackBackgroundColor: .clear, trackBackgroundColor: .clear,
trackForegroundColor: .clear, trackForegroundColor: .clear,
@ -1193,7 +1291,9 @@ private final class ChatSendStarsScreenComponent: Component {
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
self.amount = 1 + Int64(value) self.amount = self.amount.withSliderValue(value)
self.didChangeAmount = true
self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint())) self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint()))
let sliderValue = Float(value) / Float(component.maxAmount) let sliderValue = Float(value) / Float(component.maxAmount)
@ -1250,7 +1350,7 @@ private final class ChatSendStarsScreenComponent: Component {
let sliderFrame = CGRect(origin: CGPoint(x: sliderInset, y: contentHeight + 127.0), size: sliderSize) let sliderFrame = CGRect(origin: CGPoint(x: sliderInset, y: contentHeight + 127.0), size: sliderSize)
let sliderBackgroundFrame = CGRect(origin: CGPoint(x: sliderFrame.minX - 8.0, y: sliderFrame.minY + 7.0), size: CGSize(width: sliderFrame.width + 16.0, height: sliderFrame.height - 14.0)) let sliderBackgroundFrame = CGRect(origin: CGPoint(x: sliderFrame.minX - 8.0, y: sliderFrame.minY + 7.0), size: CGSize(width: sliderFrame.width + 16.0, height: sliderFrame.height - 14.0))
let progressFraction: CGFloat = CGFloat(self.amount) / CGFloat(component.maxAmount - 1) let progressFraction: CGFloat = CGFloat(self.amount.sliderValue) / CGFloat(self.amount.maxSliderValue)
let topOthersCount: Int? = component.topPeers.filter({ !$0.isMy }).max(by: { $0.count < $1.count })?.count let topOthersCount: Int? = component.topPeers.filter({ !$0.isMy }).max(by: { $0.count < $1.count })?.count
var topCount: Int? var topCount: Int?
@ -1310,7 +1410,7 @@ private final class ChatSendStarsScreenComponent: Component {
transition: transition, transition: transition,
component: AnyComponent(BadgeComponent( component: AnyComponent(BadgeComponent(
theme: environment.theme, theme: environment.theme,
title: "\(self.amount)", title: "\(self.amount.realValue)",
inertiaDirection: effectiveInertiaDirection inertiaDirection: effectiveInertiaDirection
)), )),
environment: {}, environment: {},
@ -1542,15 +1642,24 @@ private final class ChatSendStarsScreenComponent: Component {
if let index = mappedTopPeers.firstIndex(where: { $0.isMy }) { if let index = mappedTopPeers.firstIndex(where: { $0.isMy }) {
mappedTopPeers.remove(at: index) mappedTopPeers.remove(at: index)
} }
var myCount = Int(self.amount)
var myCount = 0
if let myTopPeer = component.myTopPeer { if let myTopPeer = component.myTopPeer {
myCount += myTopPeer.count myCount += myTopPeer.count
} }
mappedTopPeers.append(ChatSendStarsScreen.TopPeer( var myCountAddition = 0
peer: self.isAnonymous ? nil : component.myPeer, if self.didChangeAmount {
isMy: true, myCountAddition = Int(self.amount.realValue)
count: myCount }
)) myCount += myCountAddition
if myCount != 0 {
mappedTopPeers.append(ChatSendStarsScreen.TopPeer(
randomIndex: -1,
peer: self.isAnonymous ? nil : component.myPeer,
isMy: true,
count: myCount
))
}
mappedTopPeers.sort(by: { $0.count > $1.count }) mappedTopPeers.sort(by: { $0.count > $1.count })
if mappedTopPeers.count > 3 { if mappedTopPeers.count > 3 {
mappedTopPeers = Array(mappedTopPeers.prefix(3)) mappedTopPeers = Array(mappedTopPeers.prefix(3))
@ -1578,6 +1687,11 @@ private final class ChatSendStarsScreenComponent: Component {
self.topPeerItems[topPeer.id] = itemView self.topPeerItems[topPeer.id] = itemView
} }
let itemCountString = "\(topPeer.count)"
/*if topPeer.isMy && myCountAddition != 0 && topPeer.count > myCountAddition {
itemCountString = "\(topPeer.count - myCountAddition) +\(myCountAddition)"
}*/
let itemSize = itemView.update( let itemSize = itemView.update(
transition: .immediate, transition: .immediate,
component: AnyComponent(PlainButtonComponent( component: AnyComponent(PlainButtonComponent(
@ -1586,7 +1700,7 @@ private final class ChatSendStarsScreenComponent: Component {
theme: environment.theme, theme: environment.theme,
strings: environment.strings, strings: environment.strings,
peer: topPeer.peer, peer: topPeer.peer,
count: topPeer.count count: itemCountString
)), )),
effectAlignment: .center, effectAlignment: .center,
action: { [weak self] in action: { [weak self] in
@ -1747,7 +1861,7 @@ private final class ChatSendStarsScreenComponent: Component {
self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme) self.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, environment.theme)
} }
let buttonString = environment.strings.SendStarReactions_SendButtonTitle("\(self.amount)").string let buttonString = environment.strings.SendStarReactions_SendButtonTitle("\(self.amount.realValue)").string
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center) let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: .white, paragraphAlignment: .center)
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 { if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string)) buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
@ -1778,7 +1892,7 @@ private final class ChatSendStarsScreenComponent: Component {
return return
} }
if balance < self.amount { if balance < self.amount.realValue {
let _ = (component.context.engine.payments.starsTopUpOptions() let _ = (component.context.engine.payments.starsTopUpOptions()
|> take(1) |> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak self] options in |> deliverOnMainQueue).startStandalone(next: { [weak self] options in
@ -1789,7 +1903,7 @@ private final class ChatSendStarsScreenComponent: Component {
return return
} }
let purchaseScreen = component.context.sharedContext.makeStarsPurchaseScreen(context: component.context, starsContext: starsContext, options: options, purpose: .transfer(peerId: component.peer.id, requiredStars: self.amount), completion: { result in let purchaseScreen = component.context.sharedContext.makeStarsPurchaseScreen(context: component.context, starsContext: starsContext, options: options, purpose: .transfer(peerId: component.peer.id, requiredStars: Int64(self.amount.realValue)), completion: { result in
let _ = result let _ = result
//TODO:release //TODO:release
}) })
@ -1805,13 +1919,13 @@ private final class ChatSendStarsScreenComponent: Component {
} }
let isBecomingTop: Bool let isBecomingTop: Bool
if let topCount { if let topCount {
isBecomingTop = self.amount > topCount isBecomingTop = self.amount.realValue > topCount
} else { } else {
isBecomingTop = true isBecomingTop = true
} }
component.completion( component.completion(
self.amount, Int64(self.amount.realValue),
self.isAnonymous, self.isAnonymous,
isBecomingTop, isBecomingTop,
ChatSendStarsScreen.TransitionOut( ChatSendStarsScreen.TransitionOut(
@ -1953,7 +2067,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
fileprivate final class TopPeer: Equatable { fileprivate final class TopPeer: Equatable {
enum Id: Hashable { enum Id: Hashable {
case anonymous case anonymous(Int)
case my case my
case peer(EnginePeer.Id) case peer(EnginePeer.Id)
} }
@ -1964,7 +2078,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
} else if let peer = self.peer { } else if let peer = self.peer {
return .peer(peer.id) return .peer(peer.id)
} else { } else {
return .anonymous return .anonymous(self.randomIndex)
} }
} }
@ -1972,17 +2086,22 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
return self.peer == nil return self.peer == nil
} }
let randomIndex: Int
let peer: EnginePeer? let peer: EnginePeer?
let isMy: Bool let isMy: Bool
let count: Int let count: Int
init(peer: EnginePeer?, isMy: Bool, count: Int) { init(randomIndex: Int, peer: EnginePeer?, isMy: Bool, count: Int) {
self.randomIndex = randomIndex
self.peer = peer self.peer = peer
self.isMy = isMy self.isMy = isMy
self.count = count self.count = count
} }
static func ==(lhs: TopPeer, rhs: TopPeer) -> Bool { static func ==(lhs: TopPeer, rhs: TopPeer) -> Bool {
if lhs.randomIndex != rhs.randomIndex {
return false
}
if lhs.peer != rhs.peer { if lhs.peer != rhs.peer {
return false return false
} }
@ -2099,6 +2218,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
return nil return nil
} }
var nextRandomIndex = 0
return InitialData( return InitialData(
peer: peer, peer: peer,
myPeer: myPeer, myPeer: myPeer,
@ -2107,7 +2227,10 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
currentSentAmount: currentSentAmount, currentSentAmount: currentSentAmount,
topPeers: topPeers.compactMap { topPeer -> ChatSendStarsScreen.TopPeer? in topPeers: topPeers.compactMap { topPeer -> ChatSendStarsScreen.TopPeer? in
guard let topPeerId = topPeer.peerId else { guard let topPeerId = topPeer.peerId else {
let randomIndex = nextRandomIndex
nextRandomIndex += 1
return ChatSendStarsScreen.TopPeer( return ChatSendStarsScreen.TopPeer(
randomIndex: randomIndex,
peer: nil, peer: nil,
isMy: topPeer.isMy, isMy: topPeer.isMy,
count: Int(topPeer.count) count: Int(topPeer.count)
@ -2119,7 +2242,10 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
guard let topPeerValue else { guard let topPeerValue else {
return nil return nil
} }
let randomIndex = nextRandomIndex
nextRandomIndex += 1
return ChatSendStarsScreen.TopPeer( return ChatSendStarsScreen.TopPeer(
randomIndex: randomIndex,
peer: topPeer.isAnonymous ? nil : topPeerValue, peer: topPeer.isAnonymous ? nil : topPeerValue,
isMy: topPeer.isMy, isMy: topPeer.isMy,
count: Int(topPeer.count) count: Int(topPeer.count)
@ -2128,6 +2254,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
myTopPeer: myTopPeer.flatMap { topPeer -> ChatSendStarsScreen.TopPeer? in myTopPeer: myTopPeer.flatMap { topPeer -> ChatSendStarsScreen.TopPeer? in
guard let topPeerId = topPeer.peerId else { guard let topPeerId = topPeer.peerId else {
return ChatSendStarsScreen.TopPeer( return ChatSendStarsScreen.TopPeer(
randomIndex: -1,
peer: nil, peer: nil,
isMy: topPeer.isMy, isMy: topPeer.isMy,
count: Int(topPeer.count) count: Int(topPeer.count)
@ -2140,6 +2267,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
return nil return nil
} }
return ChatSendStarsScreen.TopPeer( return ChatSendStarsScreen.TopPeer(
randomIndex: -1,
peer: topPeer.isAnonymous ? nil : topPeerValue, peer: topPeer.isAnonymous ? nil : topPeerValue,
isMy: topPeer.isMy, isMy: topPeer.isMy,
count: Int(topPeer.count) count: Int(topPeer.count)

View File

@ -70,6 +70,10 @@ public final class SliderComponent: Component {
private var component: SliderComponent? private var component: SliderComponent?
private weak var state: EmptyComponentState? private weak var state: EmptyComponentState?
public var hitTestTarget: UIView? {
return self.sliderView
}
override public init(frame: CGRect) { override public init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
} }

View File

@ -387,8 +387,46 @@ extension ChatControllerImpl {
} }
} }
self.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: false) guard let starsContext = self.context.starsContext else {
self.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1) return
}
let _ = (starsContext.state
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let strongSelf = self, let balance = state?.balance else {
return
}
if balance < 1 {
controller?.dismiss(completion: {
guard let strongSelf = self else {
return
}
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] options in
guard let strongSelf, let peerId = strongSelf.chatLocation.peerId else {
return
}
guard let starsContext = strongSelf.context.starsContext else {
return
}
let purchaseScreen = strongSelf.context.sharedContext.makeStarsPurchaseScreen(context: strongSelf.context, starsContext: starsContext, options: options, purpose: .transfer(peerId: peerId, requiredStars: 1), completion: { result in
let _ = result
//TODO:release
})
strongSelf.push(purchaseScreen)
})
})
return
}
strongSelf.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: nil)
strongSelf.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1)
})
} else { } else {
let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction let chosenReaction: MessageReaction.Reaction = chosenUpdatedReaction.reaction

View File

@ -1729,7 +1729,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return return
} }
strongSelf.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: false) strongSelf.context.engine.messages.sendStarsReaction(id: message.id, count: 1, isAnonymous: nil)
strongSelf.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1) strongSelf.displayOrUpdateSendStarsUndo(messageId: message.id, count: 1)
}) })
} else { } else {

View File

@ -29,6 +29,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let elevatedLayout: Bool private let elevatedLayout: Bool
private let placementPosition: UndoOverlayController.Position private let placementPosition: UndoOverlayController.Position
private var statusNode: RadialStatusNode? private var statusNode: RadialStatusNode?
private var didStartStatusNode: Bool = false
private let timerTextNode: ImmediateTextNode private let timerTextNode: ImmediateTextNode
private let avatarNode: AvatarNode? private let avatarNode: AvatarNode?
private let iconNode: ASImageNode? private let iconNode: ASImageNode?
@ -67,6 +68,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
private var isTimeoutDisabled: Bool = false private var isTimeoutDisabled: Bool = false
private var originalRemainingSeconds: Double private var originalRemainingSeconds: Double
private var remainingSeconds: Double private var remainingSeconds: Double
private let undoTextColor: UIColor
private var timer: SwiftSignalKit.Timer? private var timer: SwiftSignalKit.Timer?
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
@ -413,8 +415,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animatedTextItems = textItems self.animatedTextItems = textItems
displayUndo = true displayUndo = true
self.originalRemainingSeconds = 4.5 self.originalRemainingSeconds = 4.9
isUserInteractionEnabled = true isUserInteractionEnabled = true
self.statusNode = RadialStatusNode(backgroundNodeColor: .clear)
case let .messagesUnpinned(title, text, undo, isHidden): case let .messagesUnpinned(title, text, undo, isHidden):
self.avatarNode = nil self.avatarNode = nil
self.iconNode = nil self.iconNode = nil
@ -1246,6 +1250,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
} }
self.remainingSeconds = self.originalRemainingSeconds self.remainingSeconds = self.originalRemainingSeconds
self.undoTextColor = undoTextColor
self.undoButtonTextNode = ImmediateTextNode() self.undoButtonTextNode = ImmediateTextNode()
self.undoButtonTextNode.displaysAsynchronously = false self.undoButtonTextNode.displaysAsynchronously = false
@ -1271,7 +1276,15 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
switch content { switch content {
case .removedChat: case .removedChat:
self.panelWrapperNode.addSubnode(self.timerTextNode) self.panelWrapperNode.addSubnode(self.timerTextNode)
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .starsSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal, .premiumPaywall, .peers, .messageTagged: case .starsSent:
self.panelWrapperNode.addSubnode(self.timerTextNode)
if self.textNode.tapAttributeAction != nil || displayUndo {
self.isUserInteractionEnabled = true
} else {
self.isUserInteractionEnabled = false
}
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal, .premiumPaywall, .peers, .messageTagged:
if self.textNode.tapAttributeAction != nil || displayUndo { if self.textNode.tapAttributeAction != nil || displayUndo {
self.isUserInteractionEnabled = true self.isUserInteractionEnabled = true
} else { } else {
@ -1420,7 +1433,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
let _ = self.action(.commit) let _ = self.action(.commit)
self.dismiss() self.dismiss()
} else { } else {
if Int(self.remainingSeconds) != previousRemainingSeconds || (self.timerTextNode.attributedText?.string ?? "").isEmpty { let remainingSecondsString = "\(Int(self.remainingSeconds))"
if Int(self.remainingSeconds) != previousRemainingSeconds || self.timerTextNode.attributedText?.string != remainingSecondsString {
if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() { if !self.timerTextNode.bounds.size.width.isZero, let snapshot = self.timerTextNode.view.snapshotContentTree() {
self.panelNode.view.insertSubview(snapshot, aboveSubview: self.timerTextNode.view) self.panelNode.view.insertSubview(snapshot, aboveSubview: self.timerTextNode.view)
snapshot.frame = self.timerTextNode.frame snapshot.frame = self.timerTextNode.frame
@ -1431,7 +1445,13 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
snapshot?.removeFromSuperview() snapshot?.removeFromSuperview()
}) })
} }
self.timerTextNode.attributedText = NSAttributedString(string: "\(Int(self.remainingSeconds))", font: Font.regular(16.0), textColor: .white) let timerColor: UIColor
if case .starsSent = self.content {
timerColor = self.undoTextColor
} else {
timerColor = .white
}
self.timerTextNode.attributedText = NSAttributedString(string: remainingSecondsString, font: Font.regular(16.0), textColor: timerColor)
if let validLayout = self.validLayout { if let validLayout = self.validLayout {
self.containerLayoutUpdated(layout: validLayout, transition: .immediate) self.containerLayoutUpdated(layout: validLayout, transition: .immediate)
} }
@ -1450,6 +1470,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.timer?.invalidate() self.timer?.invalidate()
self.timer = nil self.timer = nil
self.remainingSeconds = self.originalRemainingSeconds self.remainingSeconds = self.originalRemainingSeconds
self.didStartStatusNode = false
self.checkTimer() self.checkTimer()
} }
@ -1508,7 +1529,6 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
} }
func containerLayoutUpdated(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let firstLayout = self.validLayout == nil
self.validLayout = layout self.validLayout = layout
var preferredSize: CGSize? var preferredSize: CGSize?
@ -1630,10 +1650,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
transition.updateFrame(node: self.panelWrapperNode, frame: panelWrapperFrame) transition.updateFrame(node: self.panelWrapperNode, frame: panelWrapperFrame)
self.effectView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width - leftMargin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight) self.effectView.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width - leftMargin * 2.0 - layout.safeInsets.left - layout.safeInsets.right, height: contentHeight)
let buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - leftMargin * 2.0, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize) var buttonTextFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - leftMargin * 2.0, y: floor((contentHeight - buttonTextSize.height) / 2.0)), size: buttonTextSize)
var undoButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - leftMargin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + leftMargin, height: contentHeight))
if case .starsSent = self.content {
let buttonOffset: CGFloat = -34.0
undoButtonFrame.origin.x += buttonOffset
buttonTextFrame.origin.x += buttonOffset
}
transition.updateFrame(node: self.undoButtonTextNode, frame: buttonTextFrame) transition.updateFrame(node: self.undoButtonTextNode, frame: buttonTextFrame)
let undoButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - leftMargin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + leftMargin, height: contentHeight))
self.undoButtonNode.frame = undoButtonFrame self.undoButtonNode.frame = undoButtonFrame
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.undoButtonNode.supernode == nil ? panelFrame.width : undoButtonFrame.minX, height: contentHeight)) self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.undoButtonNode.supernode == nil ? panelFrame.width : undoButtonFrame.minX, height: contentHeight))
@ -1728,13 +1752,28 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
} }
let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0)) let timerTextSize = self.timerTextNode.updateLayout(CGSize(width: 100.0, height: 100.0))
transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize)) if case .starsSent = self.content {
transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset + floor((rightInset - timerTextSize.width) * 0.5) - 46.0)), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize))
} else {
transition.updateFrame(node: self.timerTextNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - timerTextSize.width) / 2.0), y: floor((contentHeight - timerTextSize.height) / 2.0)), size: timerTextSize))
}
if let statusNode = self.statusNode { if let statusNode = self.statusNode {
let statusSize: CGFloat = 30.0 let statusSize: CGFloat = 30.0
transition.updateFrame(node: statusNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))) var statusFrame = CGRect(origin: CGPoint(x: floor((leftInset - statusSize) / 2.0), y: floor((contentHeight - statusSize) / 2.0)), size: CGSize(width: statusSize, height: statusSize))
if firstLayout { if case .starsSent = self.content {
statusNode.transitionToState(.secretTimeout(color: .white, icon: .none, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {}) statusFrame.origin.x = layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - statusSize - 23.0
}
transition.updateFrame(node: statusNode, frame: statusFrame)
if !self.didStartStatusNode {
let statusColor: UIColor
if case .starsSent = self.content {
statusColor = self.undoTextColor
} else {
statusColor = .white
}
statusNode.transitionToState(.secretTimeout(color: statusColor, icon: .none, beginTime: CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970, timeout: Double(self.remainingSeconds), sparks: false), completion: {})
self.didStartStatusNode = true
} }
} }

View File

@ -1,5 +1,5 @@
{ {
"app": "11.0", "app": "11.0.1",
"xcode": "15.2", "xcode": "15.2",
"bazel": "7.1.1", "bazel": "7.1.1",
"macos": "13.0" "macos": "13.0"