mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
Merge commit 'bc4b8152c35f20944679b4e86be22054b6c87b97'
This commit is contained in:
commit
4c0dc35661
@ -13896,6 +13896,7 @@ Sorry for the inconvenience.";
|
||||
"Chat.NonContactUser.UpdatedDays_any" = "%@ days ago";
|
||||
|
||||
"Chat.InputTextPaidMessagePlaceholder" = "Message for %@";
|
||||
"Chat.InputTextPaidCommentPlaceholder" = "Comment for %@";
|
||||
|
||||
"Privacy.Messages.Stars_1" = "%@ Star";
|
||||
"Privacy.Messages.Stars_any" = "%@ Stars";
|
||||
@ -13943,6 +13944,7 @@ Sorry for the inconvenience.";
|
||||
"Chat.PaidMessage.Confirm.PayForMessage_1" = "Pay for %@ Message";
|
||||
"Chat.PaidMessage.Confirm.PayForMessage_any" = "Pay for %@ Messages";
|
||||
"Chat.PaidMessage.Confirm.Single.Text" = "**%1$@** charges **%2$@** per incoming message. Would you like to pay **%3$@** to send %4$@?";
|
||||
"Chat.PaidMessage.Confirm.SingleComment.Text" = "**%1$@** charges **%2$@** per comment. Would you like to pay **%3$@** to send %4$@?";
|
||||
"Chat.PaidMessage.Confirm.Multiple.Text" = "You selected %1$@ who charge Stars for messages. Would you like to pay **%2$@** Stars to send %3$@?";
|
||||
"Chat.PaidMessage.Confirm.Text.Users_1" = "**%@** user";
|
||||
"Chat.PaidMessage.Confirm.Text.Users_any" = "**%@** users";
|
||||
|
@ -936,7 +936,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[779004698] = { return Api.StarsSubscription.parse_starsSubscription($0) }
|
||||
dict[88173912] = { return Api.StarsSubscriptionPricing.parse_starsSubscriptionPricing($0) }
|
||||
dict[198776256] = { return Api.StarsTopupOption.parse_starsTopupOption($0) }
|
||||
dict[-321582812] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-1549805238] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[-670195363] = { return Api.StarsTransactionPeer.parse_starsTransactionPeer($0) }
|
||||
dict[-110658899] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAPI($0) }
|
||||
dict[1617438738] = { return Api.StarsTransactionPeer.parse_starsTransactionPeerAds($0) }
|
||||
|
@ -334,13 +334,13 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsTransaction: TypeConstructorDescription {
|
||||
case starsTransaction(flags: Int32, id: String, stars: Api.StarsAmount, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?, stargift: Api.StarGift?, floodskipNumber: Int32?, starrefCommissionPermille: Int32?, starrefPeer: Api.Peer?, starrefAmount: Api.StarsAmount?, paidMessages: Int32?)
|
||||
case starsTransaction(flags: Int32, id: String, stars: Api.StarsAmount, date: Int32, peer: Api.StarsTransactionPeer, title: String?, description: String?, photo: Api.WebDocument?, transactionDate: Int32?, transactionUrl: String?, botPayload: Buffer?, msgId: Int32?, extendedMedia: [Api.MessageMedia]?, subscriptionPeriod: Int32?, giveawayPostId: Int32?, stargift: Api.StarGift?, floodskipNumber: Int32?, starrefCommissionPermille: Int32?, starrefPeer: Api.Peer?, starrefAmount: Api.StarsAmount?, paidMessages: Int32?, premiumGiftMonths: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipNumber, let starrefCommissionPermille, let starrefPeer, let starrefAmount, let paidMessages):
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipNumber, let starrefCommissionPermille, let starrefPeer, let starrefAmount, let paidMessages, let premiumGiftMonths):
|
||||
if boxed {
|
||||
buffer.appendInt32(-321582812)
|
||||
buffer.appendInt32(-1549805238)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
@ -367,14 +367,15 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 17) != 0 {starrefPeer!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {starrefAmount!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 19) != 0 {serializeInt32(paidMessages!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 20) != 0 {serializeInt32(premiumGiftMonths!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipNumber, let starrefCommissionPermille, let starrefPeer, let starrefAmount, let paidMessages):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any), ("stargift", stargift as Any), ("floodskipNumber", floodskipNumber as Any), ("starrefCommissionPermille", starrefCommissionPermille as Any), ("starrefPeer", starrefPeer as Any), ("starrefAmount", starrefAmount as Any), ("paidMessages", paidMessages as Any)])
|
||||
case .starsTransaction(let flags, let id, let stars, let date, let peer, let title, let description, let photo, let transactionDate, let transactionUrl, let botPayload, let msgId, let extendedMedia, let subscriptionPeriod, let giveawayPostId, let stargift, let floodskipNumber, let starrefCommissionPermille, let starrefPeer, let starrefAmount, let paidMessages, let premiumGiftMonths):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("date", date as Any), ("peer", peer as Any), ("title", title as Any), ("description", description as Any), ("photo", photo as Any), ("transactionDate", transactionDate as Any), ("transactionUrl", transactionUrl as Any), ("botPayload", botPayload as Any), ("msgId", msgId as Any), ("extendedMedia", extendedMedia as Any), ("subscriptionPeriod", subscriptionPeriod as Any), ("giveawayPostId", giveawayPostId as Any), ("stargift", stargift as Any), ("floodskipNumber", floodskipNumber as Any), ("starrefCommissionPermille", starrefCommissionPermille as Any), ("starrefPeer", starrefPeer as Any), ("starrefAmount", starrefAmount as Any), ("paidMessages", paidMessages as Any), ("premiumGiftMonths", premiumGiftMonths as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -435,6 +436,8 @@ public extension Api {
|
||||
} }
|
||||
var _21: Int32?
|
||||
if Int(_1!) & Int(1 << 19) != 0 {_21 = reader.readInt32() }
|
||||
var _22: Int32?
|
||||
if Int(_1!) & Int(1 << 20) != 0 {_22 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -456,8 +459,9 @@ public extension Api {
|
||||
let _c19 = (Int(_1!) & Int(1 << 17) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 17) == 0) || _20 != nil
|
||||
let _c21 = (Int(_1!) & Int(1 << 19) == 0) || _21 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16, floodskipNumber: _17, starrefCommissionPermille: _18, starrefPeer: _19, starrefAmount: _20, paidMessages: _21)
|
||||
let _c22 = (Int(_1!) & Int(1 << 20) == 0) || _22 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, date: _4!, peer: _5!, title: _6, description: _7, photo: _8, transactionDate: _9, transactionUrl: _10, botPayload: _11, msgId: _12, extendedMedia: _13, subscriptionPeriod: _14, giveawayPostId: _15, stargift: _16, floodskipNumber: _17, starrefCommissionPermille: _18, starrefPeer: _19, starrefAmount: _20, paidMessages: _21, premiumGiftMonths: _22)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -551,7 +551,7 @@ private final class StarsContextImpl {
|
||||
}
|
||||
var transactions = state.transactions
|
||||
if addTransaction {
|
||||
transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil, starGift: nil, floodskipNumber: nil, starrefCommissionPermille: nil, starrefPeerId: nil, starrefAmount: nil, paidMessageCount: nil), at: 0)
|
||||
transactions.insert(.init(flags: [.isLocal], id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil, transactionDate: nil, transactionUrl: nil, paidMessageId: nil, giveawayMessageId: nil, media: [], subscriptionPeriod: nil, starGift: nil, floodskipNumber: nil, starrefCommissionPermille: nil, starrefPeerId: nil, starrefAmount: nil, paidMessageCount: nil, premiumGiftMonths: nil), at: 0)
|
||||
}
|
||||
|
||||
self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: max(StarsAmount(value: 0, nanos: 0), state.balance + balance), subscriptions: state.subscriptions, canLoadMoreSubscriptions: state.canLoadMoreSubscriptions, transactions: transactions, canLoadMoreTransactions: state.canLoadMoreTransactions, isLoading: state.isLoading))
|
||||
@ -573,7 +573,7 @@ private final class StarsContextImpl {
|
||||
private extension StarsContext.State.Transaction {
|
||||
init?(apiTransaction: Api.StarsTransaction, peerId: EnginePeer.Id?, transaction: Transaction) {
|
||||
switch apiTransaction {
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipNumber, starrefCommissionPermille, starrefPeer, starrefAmount, paidMessageCount):
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipNumber, starrefCommissionPermille, starrefPeer, starrefAmount, paidMessageCount, premiumGiftMonths):
|
||||
let parsedPeer: StarsContext.State.Transaction.Peer
|
||||
var paidMessageId: MessageId?
|
||||
var giveawayMessageId: MessageId?
|
||||
@ -636,7 +636,7 @@ private extension StarsContext.State.Transaction {
|
||||
let media = extendedMedia.flatMap({ $0.compactMap { textMediaAndExpirationTimerFromApiMedia($0, PeerId(0)).media } }) ?? []
|
||||
let _ = subscriptionPeriod
|
||||
|
||||
self.init(flags: flags, id: id, count: StarsAmount(apiAmount: stars), date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod, starGift: starGift.flatMap { StarGift(apiStarGift: $0) }, floodskipNumber: floodskipNumber, starrefCommissionPermille: starrefCommissionPermille, starrefPeerId: starrefPeer?.peerId, starrefAmount: starrefAmount.flatMap(StarsAmount.init(apiAmount:)), paidMessageCount: paidMessageCount)
|
||||
self.init(flags: flags, id: id, count: StarsAmount(apiAmount: stars), date: date, peer: parsedPeer, title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), transactionDate: transactionDate, transactionUrl: transactionUrl, paidMessageId: paidMessageId, giveawayMessageId: giveawayMessageId, media: media, subscriptionPeriod: subscriptionPeriod, starGift: starGift.flatMap { StarGift(apiStarGift: $0) }, floodskipNumber: floodskipNumber, starrefCommissionPermille: starrefCommissionPermille, starrefPeerId: starrefPeer?.peerId, starrefAmount: starrefAmount.flatMap(StarsAmount.init(apiAmount:)), paidMessageCount: paidMessageCount, premiumGiftMonths: premiumGiftMonths)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -717,6 +717,7 @@ public final class StarsContext {
|
||||
public let starrefPeerId: PeerId?
|
||||
public let starrefAmount: StarsAmount?
|
||||
public let paidMessageCount: Int32?
|
||||
public let premiumGiftMonths: Int32?
|
||||
|
||||
public init(
|
||||
flags: Flags,
|
||||
@ -738,7 +739,8 @@ public final class StarsContext {
|
||||
starrefCommissionPermille: Int32?,
|
||||
starrefPeerId: PeerId?,
|
||||
starrefAmount: StarsAmount?,
|
||||
paidMessageCount: Int32?
|
||||
paidMessageCount: Int32?,
|
||||
premiumGiftMonths: Int32?
|
||||
) {
|
||||
self.flags = flags
|
||||
self.id = id
|
||||
@ -760,6 +762,7 @@ public final class StarsContext {
|
||||
self.starrefPeerId = starrefPeerId
|
||||
self.starrefAmount = starrefAmount
|
||||
self.paidMessageCount = paidMessageCount
|
||||
self.premiumGiftMonths = premiumGiftMonths
|
||||
}
|
||||
|
||||
public static func == (lhs: Transaction, rhs: Transaction) -> Bool {
|
||||
@ -823,6 +826,9 @@ public final class StarsContext {
|
||||
if lhs.paidMessageCount != rhs.paidMessageCount {
|
||||
return false
|
||||
}
|
||||
if lhs.premiumGiftMonths != rhs.premiumGiftMonths {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -1268,7 +1268,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE
|
||||
public func updateLayout(interfaceState: ChatPresentationInterfaceState, subject: ChatEmptyNode.Subject, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let serviceColor = serviceMessageColorComponents(theme: interfaceState.theme, wallpaper: interfaceState.chatWallpaper)
|
||||
|
||||
let maxWidth = min(200.0, size.width)
|
||||
let maxWidth = min(270.0, size.width)
|
||||
|
||||
let sideInset: CGFloat = 22.0
|
||||
let topInset: CGFloat = 16.0
|
||||
|
@ -439,7 +439,11 @@ public func chatMessagePaymentAlertController(
|
||||
if peers.count == 1, let peer = peers.first {
|
||||
let amountString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(amount.value))
|
||||
let totalString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Stars(Int32(amount.value * Int64(count)))
|
||||
text = presentationData.strings.Chat_PaidMessage_Confirm_Single_Text(peer.compactDisplayTitle, amountString, totalString, messagesString).string
|
||||
if case let .channel(channel) = peer, case .broadcast = channel.info {
|
||||
text = presentationData.strings.Chat_PaidMessage_Confirm_SingleComment_Text(peer.compactDisplayTitle, amountString, totalString, messagesString).string
|
||||
} else {
|
||||
text = presentationData.strings.Chat_PaidMessage_Confirm_Single_Text(peer.compactDisplayTitle, amountString, totalString, messagesString).string
|
||||
}
|
||||
} else {
|
||||
let amount = totalAmount ?? amount
|
||||
let usersString = presentationData.strings.Chat_PaidMessage_Confirm_Text_Users(Int32(peers.count))
|
||||
|
@ -92,7 +92,7 @@ public final class ChatUserInfoItem: ListViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatUserInfoItemNode: ListViewItemNode {
|
||||
public final class ChatUserInfoItemNode: ListViewItemNode, ASGestureRecognizerDelegate {
|
||||
public var controllerInteraction: ChatControllerInteraction?
|
||||
|
||||
public let offsetContainer: ASDisplayNode
|
||||
@ -210,6 +210,29 @@ public final class ChatUserInfoItemNode: ListViewItemNode {
|
||||
self.groupsButtonNode.addTarget(self, action: #selector(self.groupsPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
tapRecognizer.delegate = self.wrappedGestureRecognizerDelegate
|
||||
self.offsetContainer.view.addGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
|
||||
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
let location = gestureRecognizer.location(in: self.offsetContainer.view)
|
||||
if let backgroundContent = self.backgroundContent, backgroundContent.frame.contains(location) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.openPeer(item.peer, .info(nil), nil, .default)
|
||||
}
|
||||
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
|
||||
super.updateAbsoluteRect(rect, within: containerSize)
|
||||
|
||||
|
@ -4703,6 +4703,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
return
|
||||
}
|
||||
let sourceRect = sourceView.convert(sourceView.bounds, to: controller.view)
|
||||
guard sourceRect.minY > 44.0 else {
|
||||
return
|
||||
}
|
||||
|
||||
let backgroundColor: UIColor
|
||||
if !self.headerNode.isAvatarExpanded, let contentButtonBackgroundColor = self.headerNode.contentButtonBackgroundColor {
|
||||
|
@ -26,6 +26,7 @@ final class StarsBalanceComponent: Component {
|
||||
let action: () -> Void
|
||||
let secondaryActionTitle: String?
|
||||
let secondaryActionIcon: UIImage?
|
||||
let secondaryActionCooldownUntilTimestamp: Int32?
|
||||
let secondaryAction: (() -> Void)?
|
||||
let additionalAction: AnyComponent<Empty>?
|
||||
|
||||
@ -43,6 +44,7 @@ final class StarsBalanceComponent: Component {
|
||||
action: @escaping () -> Void,
|
||||
secondaryActionTitle: String? = nil,
|
||||
secondaryActionIcon: UIImage? = nil,
|
||||
secondaryActionCooldownUntilTimestamp: Int32? = nil,
|
||||
secondaryAction: (() -> Void)? = nil,
|
||||
additionalAction: AnyComponent<Empty>? = nil
|
||||
) {
|
||||
@ -58,6 +60,7 @@ final class StarsBalanceComponent: Component {
|
||||
self.actionIcon = actionIcon
|
||||
self.action = action
|
||||
self.secondaryActionTitle = secondaryActionTitle
|
||||
self.secondaryActionCooldownUntilTimestamp = secondaryActionCooldownUntilTimestamp
|
||||
self.secondaryActionIcon = secondaryActionIcon
|
||||
self.secondaryAction = secondaryAction
|
||||
self.additionalAction = additionalAction
|
||||
@ -88,6 +91,9 @@ final class StarsBalanceComponent: Component {
|
||||
if lhs.secondaryActionTitle != rhs.secondaryActionTitle {
|
||||
return false
|
||||
}
|
||||
if lhs.secondaryActionCooldownUntilTimestamp != rhs.secondaryActionCooldownUntilTimestamp {
|
||||
return false
|
||||
}
|
||||
if lhs.count != rhs.count {
|
||||
return false
|
||||
}
|
||||
@ -133,7 +139,13 @@ final class StarsBalanceComponent: Component {
|
||||
remainingCooldownSeconds = max(0, remainingCooldownSeconds)
|
||||
}
|
||||
|
||||
if remainingCooldownSeconds > 0 {
|
||||
var remainingSecondaryCooldownSeconds: Int32 = 0
|
||||
if let cooldownUntilTimestamp = component.secondaryActionCooldownUntilTimestamp {
|
||||
remainingSecondaryCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970)
|
||||
remainingSecondaryCooldownSeconds = max(0, remainingSecondaryCooldownSeconds)
|
||||
}
|
||||
|
||||
if remainingCooldownSeconds > 0 || remainingSecondaryCooldownSeconds > 0 {
|
||||
if self.timer == nil {
|
||||
self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
@ -278,11 +290,30 @@ final class StarsBalanceComponent: Component {
|
||||
}
|
||||
|
||||
if let secondaryActionTitle = component.secondaryActionTitle {
|
||||
let content: AnyComponentWithIdentity<Empty>
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
if let icon = component.secondaryActionIcon {
|
||||
items.append(AnyComponentWithIdentity(id: "icon", component: AnyComponent(Image(image: icon, tintColor: component.theme.list.itemCheckColors.foregroundColor, size: icon.size))))
|
||||
}
|
||||
items.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: secondaryActionTitle, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))))
|
||||
if remainingSecondaryCooldownSeconds > 0 {
|
||||
items.append(AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(
|
||||
VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(1 as Int), component: AnyComponent(Text(text: secondaryActionTitle, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(HStack([
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(BundleIconComponent(name: "Chat List/StatusLockIcon", tintColor: component.theme.list.itemCheckColors.fillColor.mixedWith(component.theme.list.itemCheckColors.foregroundColor, alpha: 0.7)))),
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(Text(text: stringForRemainingTime(remainingSecondaryCooldownSeconds), font: Font.with(size: 11.0, weight: .medium, traits: [.monospacedNumbers]), color: component.theme.list.itemCheckColors.fillColor.mixedWith(component.theme.list.itemCheckColors.foregroundColor, alpha: 0.7))))
|
||||
], spacing: 3.0)))
|
||||
], spacing: 1.0)
|
||||
)))
|
||||
} else {
|
||||
items.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(Text(text: secondaryActionTitle, font: Font.semibold(17.0), color: component.theme.list.itemCheckColors.foregroundColor))))
|
||||
}
|
||||
content = AnyComponentWithIdentity(
|
||||
id: AnyHashable(0 as Int),
|
||||
component: AnyComponent(
|
||||
HStack(items, spacing: 7.0)
|
||||
)
|
||||
)
|
||||
|
||||
let buttonSize = self.secondaryButton.update(
|
||||
transition: transition,
|
||||
@ -292,12 +323,7 @@ final class StarsBalanceComponent: Component {
|
||||
foreground: component.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: component.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
|
||||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0 as Int),
|
||||
component: AnyComponent(
|
||||
HStack(items, spacing: 7.0)
|
||||
)
|
||||
),
|
||||
content: content,
|
||||
isEnabled: component.actionIsEnabled,
|
||||
allowActionWhenDisabled: false,
|
||||
displaysProgress: false,
|
||||
|
@ -32,30 +32,36 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
|
||||
let context: AccountContext
|
||||
let starsContext: StarsContext
|
||||
let starsRevenueStatsContext: StarsRevenueStatsContext
|
||||
let subscriptionsContext: StarsSubscriptionsContext
|
||||
let openTransaction: (StarsContext.State.Transaction) -> Void
|
||||
let openSubscription: (StarsContext.State.Subscription) -> Void
|
||||
let buy: () -> Void
|
||||
let withdraw: () -> Void
|
||||
let showTimeoutTooltip: (Int32) -> Void
|
||||
let gift: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
starsContext: StarsContext,
|
||||
starsRevenueStatsContext: StarsRevenueStatsContext,
|
||||
subscriptionsContext: StarsSubscriptionsContext,
|
||||
openTransaction: @escaping (StarsContext.State.Transaction) -> Void,
|
||||
openSubscription: @escaping (StarsContext.State.Subscription) -> Void,
|
||||
buy: @escaping () -> Void,
|
||||
withdraw: @escaping () -> Void,
|
||||
showTimeoutTooltip: @escaping (Int32) -> Void,
|
||||
gift: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.starsContext = starsContext
|
||||
self.starsRevenueStatsContext = starsRevenueStatsContext
|
||||
self.subscriptionsContext = subscriptionsContext
|
||||
self.openTransaction = openTransaction
|
||||
self.openSubscription = openSubscription
|
||||
self.buy = buy
|
||||
self.withdraw = withdraw
|
||||
self.showTimeoutTooltip = showTimeoutTooltip
|
||||
self.gift = gift
|
||||
}
|
||||
|
||||
@ -134,6 +140,9 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
private var stateDisposable: Disposable?
|
||||
private var starsState: StarsContext.State?
|
||||
|
||||
private var revenueStateDisposable: Disposable?
|
||||
private var revenueState: StarsRevenueStats?
|
||||
|
||||
private var previousBalance: StarsAmount?
|
||||
|
||||
private var subscriptionsStateDisposable: Disposable?
|
||||
@ -190,6 +199,8 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
|
||||
deinit {
|
||||
self.stateDisposable?.dispose()
|
||||
self.revenueStateDisposable?.dispose()
|
||||
self.subscriptionsStateDisposable?.dispose()
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
@ -376,6 +387,18 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
}
|
||||
})
|
||||
|
||||
self.revenueStateDisposable = (component.starsRevenueStatsContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.revenueState = state.stats
|
||||
|
||||
if !self.isUpdating {
|
||||
self.state?.updated()
|
||||
}
|
||||
})
|
||||
|
||||
self.subscriptionsStateDisposable = (component.subscriptionsContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
@ -607,14 +630,8 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
contentHeight += descriptionSize.height
|
||||
contentHeight += 29.0
|
||||
|
||||
let withdrawAvailable: Bool
|
||||
#if DEBUG
|
||||
withdrawAvailable = "".isEmpty
|
||||
#else
|
||||
withdrawAvailable = "".isEmpty
|
||||
#endif
|
||||
|
||||
|
||||
let withdrawAvailable = self.revenueState?.balances.withdrawEnabled ?? false
|
||||
|
||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
let balanceSize = self.balanceView.update(
|
||||
transition: .immediate,
|
||||
@ -641,11 +658,24 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
},
|
||||
secondaryActionTitle: withdrawAvailable ? environment.strings.Stars_Intro_Withdraw : nil,
|
||||
secondaryActionIcon: withdrawAvailable ? PresentationResourcesItemList.itemListRoundWithdrawIcon(environment.theme) : nil,
|
||||
secondaryActionCooldownUntilTimestamp: self.revenueState?.balances.nextWithdrawalTimestamp,
|
||||
secondaryAction: withdrawAvailable ? { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.withdraw()
|
||||
var remainingCooldownSeconds: Int32 = 0
|
||||
if let cooldownUntilTimestamp = self.revenueState?.balances.nextWithdrawalTimestamp {
|
||||
remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970)
|
||||
remainingCooldownSeconds = max(0, remainingCooldownSeconds)
|
||||
|
||||
if remainingCooldownSeconds > 0 {
|
||||
component.showTimeoutTooltip(cooldownUntilTimestamp)
|
||||
} else {
|
||||
component.withdraw()
|
||||
}
|
||||
} else {
|
||||
component.withdraw()
|
||||
}
|
||||
} : nil,
|
||||
additionalAction: (premiumConfiguration.starsGiftsPurchaseAvailable && !premiumConfiguration.isPremiumDisabled) ? AnyComponent(
|
||||
Button(
|
||||
@ -1064,26 +1094,33 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
private let context: AccountContext
|
||||
private let starsContext: StarsContext
|
||||
private let starsRevenueStatsContext: StarsRevenueStatsContext
|
||||
private let subscriptionsContext: StarsSubscriptionsContext
|
||||
|
||||
private let options = Promise<[StarsTopUpOption]>()
|
||||
|
||||
private let navigateDisposable = MetaDisposable()
|
||||
|
||||
private weak var tooltipScreen: UndoOverlayController?
|
||||
private var timer: Foundation.Timer?
|
||||
|
||||
public init(context: AccountContext, starsContext: StarsContext, forceDark: Bool = false) {
|
||||
self.context = context
|
||||
self.starsContext = starsContext
|
||||
|
||||
self.starsRevenueStatsContext = context.engine.payments.peerStarsRevenueContext(peerId: context.account.peerId)
|
||||
self.subscriptionsContext = context.engine.payments.peerStarsSubscriptionsContext(starsContext: starsContext)
|
||||
|
||||
var buyImpl: (() -> Void)?
|
||||
var withdrawImpl: (() -> Void)?
|
||||
var showTimeoutTooltipImpl: ((Int32) -> Void)?
|
||||
var giftImpl: (() -> Void)?
|
||||
var openTransactionImpl: ((StarsContext.State.Transaction) -> Void)?
|
||||
var openSubscriptionImpl: ((StarsContext.State.Subscription) -> Void)?
|
||||
super.init(context: context, component: StarsTransactionsScreenComponent(
|
||||
context: context,
|
||||
starsContext: starsContext,
|
||||
context: self.context,
|
||||
starsContext: self.starsContext,
|
||||
starsRevenueStatsContext: self.starsRevenueStatsContext,
|
||||
subscriptionsContext: self.subscriptionsContext,
|
||||
openTransaction: { transaction in
|
||||
openTransactionImpl?(transaction)
|
||||
@ -1097,6 +1134,9 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
withdraw: {
|
||||
withdrawImpl?()
|
||||
},
|
||||
showTimeoutTooltip: { timestamp in
|
||||
showTimeoutTooltipImpl?(timestamp)
|
||||
},
|
||||
gift: {
|
||||
giftImpl?()
|
||||
}
|
||||
@ -1205,23 +1245,30 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
case .serverProvided:
|
||||
return
|
||||
case .requestPassword:
|
||||
let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in
|
||||
let _ = (self.starsRevenueStatsContext.state
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root))
|
||||
}, completion: { url in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
|
||||
Queue.mainQueue().after(2.0) {
|
||||
context.starsContext?.load(force: true)
|
||||
let controller = self.context.sharedContext.makeStarsWithdrawalScreen(context: context, completion: { [weak self] amount in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let controller = confirmStarsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: amount, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root))
|
||||
}, completion: { [weak self] url in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: url, forceExternal: true, presentationData: presentationData, navigationController: nil, dismissInput: {})
|
||||
|
||||
Queue.mainQueue().after(2.0) {
|
||||
self?.starsRevenueStatsContext.reload()
|
||||
}
|
||||
})
|
||||
self.present(controller, in: .window(.root))
|
||||
})
|
||||
self.present(controller, in: .window(.root))
|
||||
self.push(controller)
|
||||
})
|
||||
self.push(controller)
|
||||
default:
|
||||
let controller = starsRevenueWithdrawalController(context: context, peerId: context.account.peerId, amount: 0, initialError: error, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root))
|
||||
@ -1233,6 +1280,59 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
})
|
||||
}
|
||||
|
||||
showTimeoutTooltipImpl = { [weak self] cooldownUntilTimestamp in
|
||||
guard let self, self.tooltipScreen == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970)
|
||||
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let content: UndoOverlayContent = .universal(
|
||||
animation: "anim_clock",
|
||||
scale: 0.058,
|
||||
colors: [:],
|
||||
title: nil,
|
||||
text: presentationData.strings.Stars_Withdraw_Withdraw_ErrorTimeout(stringForRemainingTime(remainingCooldownSeconds)).string,
|
||||
customUndoText: nil,
|
||||
timeout: nil
|
||||
)
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in
|
||||
return true
|
||||
})
|
||||
self.tooltipScreen = controller
|
||||
self.present(controller, in: .window(.root))
|
||||
|
||||
if remainingCooldownSeconds < 3600 {
|
||||
if self.timer == nil {
|
||||
self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let tooltipScreen = self.tooltipScreen {
|
||||
let remainingCooldownSeconds = cooldownUntilTimestamp - Int32(Date().timeIntervalSince1970)
|
||||
let content: UndoOverlayContent = .universal(
|
||||
animation: "anim_clock",
|
||||
scale: 0.058,
|
||||
colors: [:],
|
||||
title: nil,
|
||||
text: presentationData.strings.Stars_Withdraw_Withdraw_ErrorTimeout(stringForRemainingTime(remainingCooldownSeconds)).string,
|
||||
customUndoText: nil,
|
||||
timeout: nil
|
||||
)
|
||||
tooltipScreen.content = content
|
||||
} else {
|
||||
if let timer = self.timer {
|
||||
self.timer = nil
|
||||
timer.invalidate()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
giftImpl = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -36,6 +36,10 @@ extension ChatControllerImpl {
|
||||
if forceDark {
|
||||
presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme)
|
||||
}
|
||||
var peer = peer
|
||||
if let peerDiscussionId = self.presentationInterfaceState.peerDiscussionId, let channel = self.peerView?.peers[peerDiscussionId] {
|
||||
peer = EnginePeer(channel)
|
||||
}
|
||||
let controller = chatMessagePaymentAlertController(
|
||||
context: self.context,
|
||||
presentationData: presentationData,
|
||||
|
@ -5742,12 +5742,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
||||
if case .broadcast = peer.info {
|
||||
starGiftsAvailable = cachedData.flags.contains(.starGiftsAvailable)
|
||||
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||
peerDiscussionId = value
|
||||
}
|
||||
} else {
|
||||
peerGeoLocation = cachedData.peerGeoLocation
|
||||
}
|
||||
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||
peerDiscussionId = value
|
||||
}
|
||||
}
|
||||
var renderedPeer: RenderedPeer?
|
||||
var contactStatus: ChatContactStatus?
|
||||
@ -5786,7 +5786,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) || channel.adminRights != nil {
|
||||
|
||||
} else {
|
||||
sendPaidMessageStars = channel.sendPaidMessageStars
|
||||
}
|
||||
@ -5944,7 +5943,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
if strongSelf.preloadHistoryPeerId != peerDiscussionId {
|
||||
strongSelf.preloadHistoryPeerId = peerDiscussionId
|
||||
if let peerDiscussionId = peerDiscussionId {
|
||||
if let peerDiscussionId = peerDiscussionId, let channel = peerView.peers[peerView.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
let combinedDisposable = DisposableSet()
|
||||
strongSelf.preloadHistoryPeerIdDisposable.set(combinedDisposable)
|
||||
combinedDisposable.add(strongSelf.context.account.viewTracker.polledChannel(peerId: peerDiscussionId).startStrict())
|
||||
@ -6346,6 +6345,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var contactStatus: ChatContactStatus?
|
||||
var copyProtectionEnabled: Bool = false
|
||||
var businessIntro: TelegramBusinessIntro?
|
||||
var sendPaidMessageStars: StarsAmount?
|
||||
if let peer = peerView.peers[peerView.peerId] {
|
||||
copyProtectionEnabled = peer.isCopyProtectionEnabled
|
||||
if let cachedData = peerView.cachedData as? CachedUserData {
|
||||
@ -6374,6 +6374,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
contactStatus = ChatContactStatus(canAddContact: false, canReportIrrelevantLocation: canReportIrrelevantLocation, peerStatusSettings: cachedData.peerStatusSettings, invitedBy: invitedBy, managingBot: managingBot)
|
||||
|
||||
if let channel = peerView.peers[peerView.peerId] as? TelegramChannel {
|
||||
if channel.flags.contains(.isCreator) || channel.adminRights != nil {
|
||||
} else {
|
||||
sendPaidMessageStars = channel.sendPaidMessageStars
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
@ -6516,13 +6523,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
var currentSendAsPeerId: PeerId?
|
||||
if let peer = peerView.peers[peerView.peerId] as? TelegramChannel, let cachedData = peerView.cachedData as? CachedChannelData {
|
||||
currentSendAsPeerId = cachedData.sendAsPeerId
|
||||
if case .broadcast = peer.info {
|
||||
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||
peerDiscussionId = value
|
||||
}
|
||||
} else {
|
||||
if case .group = peer.info {
|
||||
peerGeoLocation = cachedData.peerGeoLocation
|
||||
}
|
||||
if case let .known(value) = cachedData.linkedDiscussionPeerId {
|
||||
peerDiscussionId = value
|
||||
}
|
||||
}
|
||||
|
||||
var isNotAccessible: Bool = false
|
||||
@ -6637,6 +6643,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
.updatedAppliedBoosts(appliedBoosts)
|
||||
.updatedBoostsToUnrestrict(boostsToUnrestrict)
|
||||
.updatedBusinessIntro(businessIntro)
|
||||
.updatedSendPaidMessageStars(sendPaidMessageStars)
|
||||
.updatedInterfaceState { interfaceState in
|
||||
var interfaceState = interfaceState
|
||||
|
||||
|
@ -1915,7 +1915,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
||||
placeholder = interfaceState.strings.Conversation_InputTextAnonymousPlaceholder
|
||||
} else if case let .replyThread(replyThreadMessage) = interfaceState.chatLocation, !replyThreadMessage.isForumPost, replyThreadMessage.peerId != self.context?.account.peerId {
|
||||
if replyThreadMessage.isChannelPost {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
|
||||
if let sendPaidMessageStars = interfaceState.sendPaidMessageStars {
|
||||
placeholder = interfaceState.strings.Chat_InputTextPaidCommentPlaceholder(" # \(presentationStringsFormattedNumber(Int32(sendPaidMessageStars.value), interfaceState.dateTimeFormat.groupingSeparator))").string
|
||||
placeholderHasStar = true
|
||||
} else {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderComment
|
||||
}
|
||||
} else {
|
||||
placeholder = interfaceState.strings.Conversation_InputTextPlaceholderReply
|
||||
}
|
||||
|
@ -162,6 +162,21 @@ func preparedChatHistoryViewTransition(from fromView: ChatHistoryView?, to toVie
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
|
||||
if let currentScrollToItem = scrollToItem {
|
||||
index = 0
|
||||
for entry in toView.filteredEntries.reversed() {
|
||||
if index > currentScrollToItem.index {
|
||||
if entry.index.timestamp > 10 {
|
||||
break
|
||||
} else if case .ChatInfoEntry = entry {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: .bottom(0.0), animated: false, curve: curve, directionHint: .Down)
|
||||
break
|
||||
}
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scrollToItem == nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user