mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
[WIP] Star refs
This commit is contained in:
parent
17bd869ddd
commit
be83150aba
@ -46,8 +46,9 @@ private class AvatarNodeParameters: NSObject {
|
||||
let explicitColorIndex: Int?
|
||||
let hasImage: Bool
|
||||
let clipStyle: AvatarNodeClipStyle
|
||||
let cutoutRect: CGRect?
|
||||
|
||||
init(theme: PresentationTheme?, accountPeerId: EnginePeer.Id?, peerId: EnginePeer.Id?, colors: [UIColor], letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle) {
|
||||
init(theme: PresentationTheme?, accountPeerId: EnginePeer.Id?, peerId: EnginePeer.Id?, colors: [UIColor], letters: [String], font: UIFont, icon: AvatarNodeIcon, explicitColorIndex: Int?, hasImage: Bool, clipStyle: AvatarNodeClipStyle, cutoutRect: CGRect?) {
|
||||
self.theme = theme
|
||||
self.accountPeerId = accountPeerId
|
||||
self.peerId = peerId
|
||||
@ -58,12 +59,13 @@ private class AvatarNodeParameters: NSObject {
|
||||
self.explicitColorIndex = explicitColorIndex
|
||||
self.hasImage = hasImage
|
||||
self.clipStyle = clipStyle
|
||||
self.cutoutRect = cutoutRect
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
func withUpdatedHasImage(_ hasImage: Bool) -> AvatarNodeParameters {
|
||||
return AvatarNodeParameters(theme: self.theme, accountPeerId: self.accountPeerId, peerId: self.peerId, colors: self.colors, letters: self.letters, font: self.font, icon: self.icon, explicitColorIndex: self.explicitColorIndex, hasImage: hasImage, clipStyle: self.clipStyle)
|
||||
return AvatarNodeParameters(theme: self.theme, accountPeerId: self.accountPeerId, peerId: self.peerId, colors: self.colors, letters: self.letters, font: self.font, icon: self.icon, explicitColorIndex: self.explicitColorIndex, hasImage: hasImage, clipStyle: self.clipStyle, cutoutRect: self.cutoutRect)
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +168,7 @@ public enum AvatarNodeExplicitIcon {
|
||||
|
||||
private enum AvatarNodeState: Equatable {
|
||||
case empty
|
||||
case peerAvatar(EnginePeer.Id, PeerNameColor?, [String], TelegramMediaImageRepresentation?, AvatarNodeClipStyle)
|
||||
case peerAvatar(EnginePeer.Id, PeerNameColor?, [String], TelegramMediaImageRepresentation?, AvatarNodeClipStyle, CGRect?)
|
||||
case custom(letter: [String], explicitColorIndex: Int?, explicitIcon: AvatarNodeExplicitIcon?)
|
||||
}
|
||||
|
||||
@ -174,8 +176,8 @@ private func ==(lhs: AvatarNodeState, rhs: AvatarNodeState) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.empty, .empty):
|
||||
return true
|
||||
case let (.peerAvatar(lhsPeerId, lhsPeerNameColor, lhsLetters, lhsPhotoRepresentations, lhsClipStyle), .peerAvatar(rhsPeerId, rhsPeerNameColor, rhsLetters, rhsPhotoRepresentations, rhsClipStyle)):
|
||||
return lhsPeerId == rhsPeerId && lhsPeerNameColor == rhsPeerNameColor && lhsLetters == rhsLetters && lhsPhotoRepresentations == rhsPhotoRepresentations && lhsClipStyle == rhsClipStyle
|
||||
case let (.peerAvatar(lhsPeerId, lhsPeerNameColor, lhsLetters, lhsPhotoRepresentations, lhsClipStyle, lhsCutoutRect), .peerAvatar(rhsPeerId, rhsPeerNameColor, rhsLetters, rhsPhotoRepresentations, rhsClipStyle, rhsCutoutRect)):
|
||||
return lhsPeerId == rhsPeerId && lhsPeerNameColor == rhsPeerNameColor && lhsLetters == rhsLetters && lhsPhotoRepresentations == rhsPhotoRepresentations && lhsClipStyle == rhsClipStyle && lhsCutoutRect == rhsCutoutRect
|
||||
case let (.custom(lhsLetters, lhsIndex, lhsIcon), .custom(rhsLetters, rhsIndex, rhsIcon)):
|
||||
return lhsLetters == rhsLetters && lhsIndex == rhsIndex && lhsIcon == rhsIcon
|
||||
default:
|
||||
@ -307,7 +309,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
didSet {
|
||||
if oldValue.pointSize != font.pointSize {
|
||||
if let parameters = self.parameters {
|
||||
self.parameters = AvatarNodeParameters(theme: parameters.theme, accountPeerId: parameters.accountPeerId, peerId: parameters.peerId, colors: parameters.colors, letters: parameters.letters, font: self.font, icon: parameters.icon, explicitColorIndex: parameters.explicitColorIndex, hasImage: parameters.hasImage, clipStyle: parameters.clipStyle)
|
||||
self.parameters = AvatarNodeParameters(theme: parameters.theme, accountPeerId: parameters.accountPeerId, peerId: parameters.peerId, colors: parameters.colors, letters: parameters.letters, font: self.font, icon: parameters.icon, explicitColorIndex: parameters.explicitColorIndex, hasImage: parameters.hasImage, clipStyle: parameters.clipStyle, cutoutRect: parameters.cutoutRect)
|
||||
}
|
||||
|
||||
if !self.displaySuspended {
|
||||
@ -334,7 +336,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
var clipStyle: AvatarNodeClipStyle {
|
||||
if let params = self.params {
|
||||
return params.clipStyle
|
||||
} else if case let .peerAvatar(_, _, _, _, clipStyle) = self.state {
|
||||
} else if case let .peerAvatar(_, _, _, _, clipStyle, _) = self.state {
|
||||
return clipStyle
|
||||
}
|
||||
return .none
|
||||
@ -498,7 +500,8 @@ public final class AvatarNode: ASDisplayNode {
|
||||
clipStyle: AvatarNodeClipStyle = .round,
|
||||
synchronousLoad: Bool = false,
|
||||
displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0),
|
||||
storeUnrounded: Bool = false
|
||||
storeUnrounded: Bool = false,
|
||||
cutoutRect: CGRect? = nil
|
||||
) {
|
||||
var synchronousLoad = synchronousLoad
|
||||
var representation: TelegramMediaImageRepresentation?
|
||||
@ -542,7 +545,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
representation = peer?.smallProfileImage
|
||||
}
|
||||
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.nameColor, peer?.displayLetters ?? [], representation, clipStyle)
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.nameColor, peer?.displayLetters ?? [], representation, clipStyle, cutoutRect)
|
||||
if updatedState != self.state || overrideImage != self.overrideImage || theme !== self.theme {
|
||||
self.state = updatedState
|
||||
self.overrideImage = overrideImage
|
||||
@ -550,7 +553,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
let parameters: AvatarNodeParameters
|
||||
|
||||
if let peer = peer, let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
|
||||
if let peer = peer, let signal = peerAvatarImage(postbox: postbox, network: network, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) {
|
||||
self.contents = nil
|
||||
self.displaySuspended = true
|
||||
self.imageReady.set(self.imageNode.contentReady)
|
||||
@ -577,7 +580,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
self.editOverlayNode?.isHidden = true
|
||||
}
|
||||
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer.id, colors: calculateAvatarColors(context: nil, explicitColorIndex: nil, peerId: peer.id, nameColor: peer.nameColor, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer.id, colors: calculateAvatarColors(context: nil, explicitColorIndex: nil, peerId: peer.id, nameColor: peer.nameColor, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle, cutoutRect: cutoutRect)
|
||||
} else {
|
||||
self.imageReady.set(.single(true))
|
||||
self.displaySuspended = false
|
||||
@ -587,7 +590,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
self.editOverlayNode?.isHidden = true
|
||||
let colors = calculateAvatarColors(context: nil, explicitColorIndex: nil, peerId: peer?.id ?? EnginePeer.Id(0), nameColor: peer?.nameColor, icon: icon, theme: theme)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: accountPeerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle, cutoutRect: cutoutRect)
|
||||
|
||||
if let badgeView = self.badgeView {
|
||||
let badgeColor: UIColor
|
||||
@ -673,7 +676,8 @@ public final class AvatarNode: ASDisplayNode {
|
||||
clipStyle: AvatarNodeClipStyle = .round,
|
||||
synchronousLoad: Bool = false,
|
||||
displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0),
|
||||
storeUnrounded: Bool = false
|
||||
storeUnrounded: Bool = false,
|
||||
cutoutRect: CGRect? = nil
|
||||
) {
|
||||
var synchronousLoad = synchronousLoad
|
||||
var representation: TelegramMediaImageRepresentation?
|
||||
@ -717,7 +721,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
representation = peer?.smallProfileImage
|
||||
}
|
||||
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.nameColor, peer?.displayLetters ?? [], representation, clipStyle)
|
||||
let updatedState: AvatarNodeState = .peerAvatar(peer?.id ?? EnginePeer.Id(0), peer?.nameColor, peer?.displayLetters ?? [], representation, clipStyle, cutoutRect)
|
||||
if updatedState != self.state || overrideImage != self.overrideImage || theme !== self.theme {
|
||||
self.state = updatedState
|
||||
self.overrideImage = overrideImage
|
||||
@ -727,7 +731,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
let account = account ?? genericContext.account
|
||||
|
||||
if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded) {
|
||||
if let peer = peer, let signal = peerAvatarImage(account: account, peerReference: PeerReference(peer._asPeer()), authorOfMessage: authorOfMessage, representation: representation, displayDimensions: displayDimensions, clipStyle: clipStyle, emptyColor: emptyColor, synchronousLoad: synchronousLoad, provideUnrounded: storeUnrounded, cutoutRect: cutoutRect) {
|
||||
self.contents = nil
|
||||
self.displaySuspended = true
|
||||
self.imageReady.set(self.imageNode.contentReady)
|
||||
@ -754,7 +758,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
self.editOverlayNode?.isHidden = true
|
||||
}
|
||||
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer.id, colors: calculateAvatarColors(context: genericContext, explicitColorIndex: nil, peerId: peer.id, nameColor: peer.nameColor, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer.id, colors: calculateAvatarColors(context: genericContext, explicitColorIndex: nil, peerId: peer.id, nameColor: peer.nameColor, icon: icon, theme: theme), letters: peer.displayLetters, font: self.font, icon: icon, explicitColorIndex: nil, hasImage: true, clipStyle: clipStyle, cutoutRect: cutoutRect)
|
||||
} else {
|
||||
self.imageReady.set(.single(true))
|
||||
self.displaySuspended = false
|
||||
@ -764,7 +768,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
self.editOverlayNode?.isHidden = true
|
||||
let colors = calculateAvatarColors(context: genericContext, explicitColorIndex: nil, peerId: peer?.id ?? EnginePeer.Id(0), nameColor: peer?.nameColor, icon: icon, theme: theme)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle)
|
||||
parameters = AvatarNodeParameters(theme: theme, accountPeerId: account.peerId, peerId: peer?.id ?? EnginePeer.Id(0), colors: colors, letters: peer?.displayLetters ?? [], font: self.font, icon: icon, explicitColorIndex: nil, hasImage: false, clipStyle: clipStyle, cutoutRect: cutoutRect)
|
||||
|
||||
if let badgeView = self.badgeView {
|
||||
let badgeColor: UIColor
|
||||
@ -786,7 +790,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public func setCustomLetters(_ letters: [String], explicitColor: AvatarNodeColorOverride? = nil, icon: AvatarNodeExplicitIcon? = nil) {
|
||||
public func setCustomLetters(_ letters: [String], explicitColor: AvatarNodeColorOverride? = nil, icon: AvatarNodeExplicitIcon? = nil, cutoutRect: CGRect? = nil) {
|
||||
var explicitIndex: Int?
|
||||
if let explicitColor = explicitColor {
|
||||
switch explicitColor {
|
||||
@ -800,9 +804,9 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
let parameters: AvatarNodeParameters
|
||||
if let icon = icon, case .phone = icon {
|
||||
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateAvatarColors(context: nil, explicitColorIndex: explicitIndex, peerId: nil, nameColor: nil, icon: .phoneIcon, theme: nil), letters: [], font: self.font, icon: .phoneIcon, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round)
|
||||
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateAvatarColors(context: nil, explicitColorIndex: explicitIndex, peerId: nil, nameColor: nil, icon: .phoneIcon, theme: nil), letters: [], font: self.font, icon: .phoneIcon, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round, cutoutRect: cutoutRect)
|
||||
} else {
|
||||
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateAvatarColors(context: nil, explicitColorIndex: explicitIndex, peerId: nil, nameColor: nil, icon: .none, theme: nil), letters: letters, font: self.font, icon: .none, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round)
|
||||
parameters = AvatarNodeParameters(theme: nil, accountPeerId: nil, peerId: nil, colors: calculateAvatarColors(context: nil, explicitColorIndex: explicitIndex, peerId: nil, nameColor: nil, icon: .none, theme: nil), letters: letters, font: self.font, icon: .none, explicitColorIndex: explicitIndex, hasImage: false, clipStyle: .round, cutoutRect: cutoutRect)
|
||||
}
|
||||
|
||||
self.displaySuspended = true
|
||||
@ -998,6 +1002,12 @@ public final class AvatarNode: ASDisplayNode {
|
||||
context.translateBy(x: -lineOrigin.x, y: -lineOrigin.y)
|
||||
}
|
||||
}
|
||||
|
||||
if let parameters = parameters as? AvatarNodeParameters, let cutoutRect = parameters.cutoutRect {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: cutoutRect.offsetBy(dx: 0.0, dy: bounds.height - cutoutRect.maxY - cutoutRect.height))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1190,7 +1200,8 @@ public final class AvatarNode: ASDisplayNode {
|
||||
clipStyle: AvatarNodeClipStyle = .round,
|
||||
synchronousLoad: Bool = false,
|
||||
displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0),
|
||||
storeUnrounded: Bool = false
|
||||
storeUnrounded: Bool = false,
|
||||
cutoutRect: CGRect? = nil
|
||||
) {
|
||||
self.contentNode.setPeer(
|
||||
context: context,
|
||||
@ -1203,7 +1214,8 @@ public final class AvatarNode: ASDisplayNode {
|
||||
clipStyle: clipStyle,
|
||||
synchronousLoad: synchronousLoad,
|
||||
displayDimensions: displayDimensions,
|
||||
storeUnrounded: storeUnrounded
|
||||
storeUnrounded: storeUnrounded,
|
||||
cutoutRect: cutoutRect
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En
|
||||
return iconSignal
|
||||
}
|
||||
|
||||
public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? {
|
||||
public func peerAvatarImage(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false, cutoutRect: CGRect? = nil) -> Signal<(UIImage, UIImage)?, NoError>? {
|
||||
return peerAvatarImage(
|
||||
postbox: account.postbox,
|
||||
network: account.network,
|
||||
@ -184,11 +184,12 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
|
||||
inset: inset,
|
||||
emptyColor: emptyColor,
|
||||
synchronousLoad: synchronousLoad,
|
||||
provideUnrounded: synchronousLoad
|
||||
provideUnrounded: synchronousLoad,
|
||||
cutoutRect: cutoutRect
|
||||
)
|
||||
}
|
||||
|
||||
public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false) -> Signal<(UIImage, UIImage)?, NoError>? {
|
||||
public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, displayDimensions: CGSize = CGSize(width: 60.0, height: 60.0), clipStyle: AvatarNodeClipStyle = .round, blurred: Bool = false, inset: CGFloat = 0.0, emptyColor: UIColor? = nil, synchronousLoad: Bool = false, provideUnrounded: Bool = false, cutoutRect: CGRect? = nil) -> Signal<(UIImage, UIImage)?, NoError>? {
|
||||
if let imageData = peerAvatarImageData(postbox: postbox, network: network, peerReference: peerReference, authorOfMessage: authorOfMessage, representation: representation, synchronousLoad: synchronousLoad) {
|
||||
return imageData
|
||||
|> mapToSignal { data -> Signal<(UIImage, UIImage)?, NoError> in
|
||||
@ -294,6 +295,12 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
context.fillPath()
|
||||
}
|
||||
}
|
||||
|
||||
if let cutoutRect {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: cutoutRect)
|
||||
}
|
||||
})
|
||||
let unroundedImage: UIImage?
|
||||
if provideUnrounded {
|
||||
|
@ -758,7 +758,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case .reviewLogin:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount)
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -1102,7 +1102,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
|
||||
case .reviewLogin:
|
||||
break
|
||||
case let .starsSubscriptionLowBalance(amount, _):
|
||||
nodeInteraction?.openStarsTopup(amount)
|
||||
nodeInteraction?.openStarsTopup(amount.value)
|
||||
}
|
||||
case .hide:
|
||||
nodeInteraction?.dismissNotice(notice)
|
||||
@ -2012,7 +2012,7 @@ public final class ChatListNode: ListView {
|
||||
if let starsSubscriptionsContext {
|
||||
return starsSubscriptionsContext.state
|
||||
|> map { state in
|
||||
if state.balance > 0 && !state.subscriptions.isEmpty {
|
||||
if state.balance > StarsAmount.zero && !state.subscriptions.isEmpty {
|
||||
return .starsSubscriptionLowBalance(
|
||||
amount: state.balance,
|
||||
peers: state.subscriptions.map { $0.peer }
|
||||
|
@ -90,7 +90,7 @@ public enum ChatListNotice: Equatable {
|
||||
case birthdayPremiumGift(peers: [EnginePeer], birthdays: [EnginePeer.Id: TelegramBirthday])
|
||||
case reviewLogin(newSessionReview: NewSessionReview, totalCount: Int)
|
||||
case premiumGrace
|
||||
case starsSubscriptionLowBalance(amount: Int64, peers: [EnginePeer])
|
||||
case starsSubscriptionLowBalance(amount: StarsAmount, peers: [EnginePeer])
|
||||
}
|
||||
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
|
@ -270,7 +270,7 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode {
|
||||
case let .starsSubscriptionLowBalance(amount, peers):
|
||||
let title: String
|
||||
let text: String
|
||||
let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(amount))
|
||||
let starsValue = item.strings.ChatList_SubscriptionsLowBalance_Stars(Int32(amount.value))
|
||||
if let peer = peers.first, peers.count == 1 {
|
||||
title = item.strings.ChatList_SubscriptionsLowBalance_Single_Title(starsValue, peer.compactDisplayTitle).string
|
||||
text = item.strings.ChatList_SubscriptionsLowBalance_Single_Text
|
||||
|
@ -8,13 +8,15 @@ public final class BundleIconComponent: Component {
|
||||
public let name: String
|
||||
public let tintColor: UIColor?
|
||||
public let maxSize: CGSize?
|
||||
public let scaleFactor: CGFloat
|
||||
public let shadowColor: UIColor?
|
||||
public let shadowBlur: CGFloat
|
||||
|
||||
public init(name: String, tintColor: UIColor?, maxSize: CGSize? = nil, shadowColor: UIColor? = nil, shadowBlur: CGFloat = 0.0) {
|
||||
public init(name: String, tintColor: UIColor?, maxSize: CGSize? = nil, scaleFactor: CGFloat = 1.0, shadowColor: UIColor? = nil, shadowBlur: CGFloat = 0.0) {
|
||||
self.name = name
|
||||
self.tintColor = tintColor
|
||||
self.maxSize = maxSize
|
||||
self.scaleFactor = scaleFactor
|
||||
self.shadowColor = shadowColor
|
||||
self.shadowBlur = shadowBlur
|
||||
}
|
||||
@ -29,6 +31,9 @@ public final class BundleIconComponent: Component {
|
||||
if lhs.maxSize != rhs.maxSize {
|
||||
return false
|
||||
}
|
||||
if lhs.scaleFactor != rhs.scaleFactor {
|
||||
return false
|
||||
}
|
||||
if lhs.shadowColor != rhs.shadowColor {
|
||||
return false
|
||||
}
|
||||
@ -75,6 +80,10 @@ public final class BundleIconComponent: Component {
|
||||
if let maxSize = component.maxSize {
|
||||
imageSize = imageSize.aspectFitted(maxSize)
|
||||
}
|
||||
if component.scaleFactor != 1.0 {
|
||||
imageSize.width = floor(imageSize.width * component.scaleFactor)
|
||||
imageSize.height = floor(imageSize.height * component.scaleFactor)
|
||||
}
|
||||
|
||||
return CGSize(width: min(imageSize.width, availableSize.width), height: min(imageSize.height, availableSize.height))
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
|
||||
|
||||
case subscriptionFeeToggle(PresentationTheme, String, Bool, Bool)
|
||||
case subscriptionFee(PresentationTheme, String, Bool, Int64?, String, Int64?)
|
||||
case subscriptionFee(PresentationTheme, String, Bool, StarsAmount?, String, StarsAmount?)
|
||||
case subscriptionFeeInfo(PresentationTheme, String)
|
||||
|
||||
case requestApproval(PresentationTheme, String, Bool, Bool)
|
||||
@ -328,7 +328,7 @@ private enum InviteLinksEditEntry: ItemListNodeEntry {
|
||||
return ItemListSingleLineInputItem(context: arguments.context, presentationData: presentationData, title: title, text: value.flatMap { "\($0)" } ?? "", placeholder: placeholder, label: label, type: .number, spacing: 3.0, enabled: enabled, tag: InviteLinksEditEntryTag.subscriptionFee, sectionId: self.section, textUpdated: { text in
|
||||
arguments.updateState { state in
|
||||
var updatedState = state
|
||||
if var value = Int64(text) {
|
||||
if var value = Int64(text).flatMap({ StarsAmount(value: $0, nanos: 0) }) {
|
||||
if let maxValue, value > maxValue {
|
||||
value = maxValue
|
||||
arguments.errorWithItem(.subscriptionFee)
|
||||
@ -492,14 +492,14 @@ private func inviteLinkEditControllerEntries(invite: ExportedInvitation?, state:
|
||||
entries.append(.subscriptionFeeToggle(presentationData.theme, presentationData.strings.InviteLink_Create_Fee, state.subscriptionEnabled, isEditingEnabled))
|
||||
if state.subscriptionEnabled {
|
||||
var label: String = ""
|
||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > 0 {
|
||||
if let subscriptionFee = state.subscriptionFee, subscriptionFee > StarsAmount.zero {
|
||||
var usdRate = 0.012
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate {
|
||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
}
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
label = presentationData.strings.InviteLink_Create_FeePerMonth("≈\(formatTonUsdValue(subscriptionFee.value, divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat))").string
|
||||
}
|
||||
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, configuration.maxFee))
|
||||
entries.append(.subscriptionFee(presentationData.theme, presentationData.strings.InviteLink_Create_FeePlaceholder, isEditingEnabled, state.subscriptionFee, label, configuration.maxFee.flatMap({ StarsAmount(value: $0, nanos: 0) })))
|
||||
}
|
||||
let infoText: String
|
||||
if let _ = invite, state.subscriptionEnabled {
|
||||
@ -566,7 +566,7 @@ private struct InviteLinkEditControllerState: Equatable {
|
||||
var time: InviteLinkTimeLimit
|
||||
var requestApproval = false
|
||||
var subscriptionEnabled = false
|
||||
var subscriptionFee: Int64?
|
||||
var subscriptionFee: StarsAmount?
|
||||
var pickingExpiryDate = false
|
||||
var pickingExpiryTime = false
|
||||
var pickingUsageLimit = false
|
||||
@ -698,7 +698,7 @@ public func inviteLinkEditController(context: AccountContext, updatedPresentatio
|
||||
|
||||
var doneIsEnabled = true
|
||||
if state.subscriptionEnabled {
|
||||
if (state.subscriptionFee ?? 0) == 0 {
|
||||
if (state.subscriptionFee ?? StarsAmount.zero) == StarsAmount.zero {
|
||||
doneIsEnabled = false
|
||||
}
|
||||
}
|
||||
|
@ -849,7 +849,7 @@ public final class InviteLinkViewController: ViewController {
|
||||
var subtitle = presentationData.strings.InviteLink_SubscriptionFee_NoOneJoined
|
||||
if state.count > 0 {
|
||||
title += " x \(state.count)"
|
||||
let usdValue = formatTonUsdValue(pricing.amount * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
let usdValue = formatTonUsdValue(pricing.amount.value * Int64(state.count), divide: false, rate: usdRate, dateTimeFormat: presentationData.dateTimeFormat)
|
||||
subtitle = presentationData.strings.InviteLink_SubscriptionFee_ApproximateIncome(usdValue).string
|
||||
}
|
||||
entries.append(.subscriptionPricing(presentationData.theme, title, subtitle))
|
||||
|
@ -4,10 +4,12 @@ import CoreMedia
|
||||
import FFMpegBinding
|
||||
import VideoToolbox
|
||||
|
||||
#if os(macOS)
|
||||
private let isHardwareAv1Supported: Bool = {
|
||||
let value = VTIsHardwareDecodeSupported(kCMVideoCodecType_AV1)
|
||||
return value
|
||||
}()
|
||||
#endif
|
||||
|
||||
public protocol MediaDataReader: AnyObject {
|
||||
var hasVideo: Bool { get }
|
||||
|
@ -1589,7 +1589,7 @@ private func monetizationEntries(
|
||||
entries.append(.adsProceedsOverview(presentationData.theme, canViewRevenue ? data : nil, canViewStarsRevenue ? starsData : nil))
|
||||
|
||||
let hasTonBalance = data.balances.overallRevenue > 0
|
||||
let hasStarsBalance = (starsData?.balances.overallRevenue ?? 0) > 0
|
||||
let hasStarsBalance = (starsData?.balances.overallRevenue ?? StarsAmount.zero) > StarsAmount.zero
|
||||
|
||||
let proceedsInfo: String
|
||||
if (canViewStarsRevenue && hasStarsBalance) && (canViewRevenue && hasTonBalance) {
|
||||
@ -1624,9 +1624,9 @@ private func monetizationEntries(
|
||||
}
|
||||
|
||||
if canViewStarsRevenue {
|
||||
if let starsData, starsData.balances.overallRevenue > 0 {
|
||||
if let starsData, starsData.balances.overallRevenue > StarsAmount.zero {
|
||||
entries.append(.adsStarsBalanceTitle(presentationData.theme, presentationData.strings.Monetization_StarsBalanceTitle))
|
||||
entries.append(.adsStarsBalance(presentationData.theme, starsData, isCreator && starsData.balances.availableBalance > 0, starsData.balances.withdrawEnabled, starsData.balances.nextWithdrawalTimestamp))
|
||||
entries.append(.adsStarsBalance(presentationData.theme, starsData, isCreator && starsData.balances.availableBalance > StarsAmount.zero, starsData.balances.withdrawEnabled, starsData.balances.nextWithdrawalTimestamp))
|
||||
entries.append(.adsStarsBalanceInfo(presentationData.theme, presentationData.strings.Monetization_Balance_StarsInfo))
|
||||
}
|
||||
}
|
||||
|
@ -180,8 +180,8 @@ final class MonetizationBalanceItemNode: ListViewItemNode, ItemListItemNode {
|
||||
amountString = tonAmountAttributedString(cryptoValue, integralFont: integralFont, fractionalFont: fractionalFont, color: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
} else if let stats = item.stats as? StarsRevenueStats {
|
||||
amountString = NSAttributedString(string: presentationStringsFormattedNumber(Int32(stats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator), font: integralFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
amountString = NSAttributedString(string: presentationStringsFormattedNumber(stats.balances.availableBalance, item.presentationData.dateTimeFormat.groupingSeparator), font: integralFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor)
|
||||
value = stats.balances.availableBalance == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||
isStars = true
|
||||
} else {
|
||||
fatalError()
|
||||
|
@ -277,8 +277,9 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
let itemLabel: NSAttributedString
|
||||
let labelString: String
|
||||
|
||||
let formattedLabel = presentationStringsFormattedNumber(abs(Int32(item.transaction.count)), item.presentationData.dateTimeFormat.groupingSeparator)
|
||||
if item.transaction.count < 0 {
|
||||
let absCount = StarsAmount(value: abs(item.transaction.count.value), nanos: abs(item.transaction.count.nanos))
|
||||
let formattedLabel = presentationStringsFormattedNumber(absCount, item.presentationData.dateTimeFormat.groupingSeparator)
|
||||
if item.transaction.count < StarsAmount.zero {
|
||||
labelString = "- \(formattedLabel)"
|
||||
} else {
|
||||
labelString = "+ \(formattedLabel)"
|
||||
|
@ -764,7 +764,7 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
height += topLeftItemLayoutAndApply!.0.height * 4.0 + verticalSpacing * 3.0
|
||||
}
|
||||
} else if let stats = item.stats as? RevenueStats {
|
||||
if let additionalStats = item.additionalStats as? StarsRevenueStats, additionalStats.balances.overallRevenue > 0 {
|
||||
if let additionalStats = item.additionalStats as? StarsRevenueStats, additionalStats.balances.overallRevenue > StarsAmount.zero {
|
||||
twoColumnLayout = true
|
||||
useMinLeftColumnWidth = true
|
||||
|
||||
@ -802,9 +802,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(additionalStats.balances.availableBalance, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.availableBalance, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.availableBalance == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.availableBalance.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -812,9 +812,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.currentBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(additionalStats.balances.currentBalance, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.currentBalance, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.currentBalance == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.currentBalance.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -822,9 +822,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(additionalStats.balances.overallRevenue), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(additionalStats.balances.overallRevenue, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
" ",
|
||||
(additionalStats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(additionalStats.balances.overallRevenue, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(additionalStats.balances.overallRevenue == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(additionalStats.balances.overallRevenue.value, divide: false, rate: additionalStats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -871,9 +871,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.availableBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(stats.balances.availableBalance, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Available,
|
||||
(stats.balances.availableBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.availableBalance == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.availableBalance.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -881,9 +881,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.currentBalance), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(stats.balances.currentBalance, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Current,
|
||||
(stats.balances.currentBalance == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.currentBalance == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.currentBalance.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
@ -891,9 +891,9 @@ class StatsOverviewItemNode: ListViewItemNode {
|
||||
item.context,
|
||||
params.width,
|
||||
item.presentationData,
|
||||
presentationStringsFormattedNumber(Int32(stats.balances.overallRevenue), item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
presentationStringsFormattedNumber(stats.balances.overallRevenue, item.presentationData.dateTimeFormat.groupingSeparator),
|
||||
item.presentationData.strings.Monetization_StarsProceeds_Total,
|
||||
(stats.balances.overallRevenue == 0 ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
(stats.balances.overallRevenue == StarsAmount.zero ? "" : "≈\(formatTonUsdValue(stats.balances.overallRevenue.value, divide: false, rate: stats.usdRate, dateTimeFormat: item.presentationData.dateTimeFormat))", .generic),
|
||||
.stars
|
||||
)
|
||||
|
||||
|
@ -904,6 +904,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
|
||||
dict[1237678029] = { return Api.StarGift.parse_starGift($0) }
|
||||
dict[708628759] = { return Api.StarRefProgram.parse_starRefProgram($0) }
|
||||
dict[-1145654109] = { return Api.StarsAmount.parse_starsAmount($0) }
|
||||
dict[1577421297] = { return Api.StarsGiftOption.parse_starsGiftOption($0) }
|
||||
dict[-1798404822] = { return Api.StarsGiveawayOption.parse_starsGiveawayOption($0) }
|
||||
dict[1411605001] = { return Api.StarsGiveawayWinnersOption.parse_starsGiveawayWinnersOption($0) }
|
||||
@ -911,7 +912,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[-1352584166] = { return Api.StarsTransaction.parse_starsTransaction($0) }
|
||||
dict[1692387622] = { 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) }
|
||||
@ -1089,7 +1090,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[2103604867] = { return Api.Update.parse_updateSentStoryReaction($0) }
|
||||
dict[-337352679] = { return Api.Update.parse_updateServiceNotification($0) }
|
||||
dict[-245208620] = { return Api.Update.parse_updateSmsJob($0) }
|
||||
dict[1042067513] = { return Api.Update.parse_updateStarsBalance($0) }
|
||||
dict[1317053305] = { return Api.Update.parse_updateStarsBalance($0) }
|
||||
dict[-1518030823] = { return Api.Update.parse_updateStarsRevenueStatus($0) }
|
||||
dict[834816008] = { return Api.Update.parse_updateStickerSets($0) }
|
||||
dict[196268545] = { return Api.Update.parse_updateStickerSetsOrder($0) }
|
||||
@ -1302,6 +1303,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[913709011] = { return Api.messages.ForumTopics.parse_forumTopics($0) }
|
||||
dict[-1963942446] = { return Api.messages.FoundStickerSets.parse_foundStickerSets($0) }
|
||||
dict[223655517] = { return Api.messages.FoundStickerSets.parse_foundStickerSetsNotModified($0) }
|
||||
dict[-2100698480] = { return Api.messages.FoundStickers.parse_foundStickers($0) }
|
||||
dict[1611711796] = { return Api.messages.FoundStickers.parse_foundStickersNotModified($0) }
|
||||
dict[-1707344487] = { return Api.messages.HighScores.parse_highScores($0) }
|
||||
dict[375566091] = { return Api.messages.HistoryImport.parse_historyImport($0) }
|
||||
dict[1578088377] = { return Api.messages.HistoryImportParsed.parse_historyImportParsed($0) }
|
||||
@ -1367,7 +1370,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[961445665] = { return Api.payments.StarsRevenueAdsAccountUrl.parse_starsRevenueAdsAccountUrl($0) }
|
||||
dict[-919881925] = { return Api.payments.StarsRevenueStats.parse_starsRevenueStats($0) }
|
||||
dict[497778871] = { return Api.payments.StarsRevenueWithdrawalUrl.parse_starsRevenueWithdrawalUrl($0) }
|
||||
dict[1447376356] = { return Api.payments.StarsStatus.parse_starsStatus($0) }
|
||||
dict[1822222573] = { return Api.payments.StarsStatus.parse_starsStatus($0) }
|
||||
dict[-937776981] = { return Api.payments.SuggestedStarRefBots.parse_suggestedStarRefBots($0) }
|
||||
dict[1801827607] = { return Api.payments.UserStarGifts.parse_userStarGifts($0) }
|
||||
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
|
||||
@ -2052,6 +2055,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarRefProgram:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarsAmount:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarsGiftOption:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StarsGiveawayOption:
|
||||
@ -2354,6 +2359,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FoundStickerSets:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FoundStickers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.HighScores:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.HistoryImport:
|
||||
|
@ -690,6 +690,46 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsAmount: TypeConstructorDescription {
|
||||
case starsAmount(amount: Int64, nanos: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsAmount(let amount, let nanos):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1145654109)
|
||||
}
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
serializeInt32(nanos, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsAmount(let amount, let nanos):
|
||||
return ("starsAmount", [("amount", amount as Any), ("nanos", nanos as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsAmount(_ reader: BufferReader) -> StarsAmount? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StarsAmount.starsAmount(amount: _1!, nanos: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsGiftOption: TypeConstructorDescription {
|
||||
case starsGiftOption(flags: Int32, stars: Int64, storeProduct: String?, currency: String, amount: Int64)
|
||||
@ -1018,55 +1058,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsTopupOption: TypeConstructorDescription {
|
||||
case starsTopupOption(flags: Int32, stars: Int64, storeProduct: String?, currency: String, amount: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsTopupOption(let flags, let stars, let storeProduct, let currency, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(198776256)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(stars, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(storeProduct!, buffer: buffer, boxed: false)}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTopupOption(let flags, let stars, let storeProduct, let currency, let amount):
|
||||
return ("starsTopupOption", [("flags", flags as Any), ("stars", stars as Any), ("storeProduct", storeProduct as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsTopupOption(_ reader: BufferReader) -> StarsTopupOption? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
_5 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.StarsTopupOption.starsTopupOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,68 @@
|
||||
public extension Api {
|
||||
enum StarsTransaction: TypeConstructorDescription {
|
||||
case starsTransaction(flags: Int32, id: String, stars: Int64, starNanos: Int32?, 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?)
|
||||
enum StarsTopupOption: TypeConstructorDescription {
|
||||
case starsTopupOption(flags: Int32, stars: Int64, storeProduct: String?, currency: String, amount: Int64)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let starNanos, 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):
|
||||
case .starsTopupOption(let flags, let stars, let storeProduct, let currency, let amount):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1352584166)
|
||||
buffer.appendInt32(198776256)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(stars, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(storeProduct!, buffer: buffer, boxed: false)}
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTopupOption(let flags, let stars, let storeProduct, let currency, let amount):
|
||||
return ("starsTopupOption", [("flags", flags as Any), ("stars", stars as Any), ("storeProduct", storeProduct as Any), ("currency", currency as Any), ("amount", amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsTopupOption(_ reader: BufferReader) -> StarsTopupOption? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: Int64?
|
||||
_5 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.StarsTopupOption.starsTopupOption(flags: _1!, stars: _2!, storeProduct: _3, currency: _4!, amount: _5!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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?)
|
||||
|
||||
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):
|
||||
if boxed {
|
||||
buffer.appendInt32(1692387622)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(stars, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 16) != 0 {serializeInt32(starNanos!, buffer: buffer, boxed: false)}
|
||||
stars.serialize(buffer, true)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
@ -30,14 +81,17 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 13) != 0 {serializeInt32(giveawayPostId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {stargift!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(floodskipNumber!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 16) != 0 {serializeInt32(starrefCommissionPermille!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {starrefPeer!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {starrefAmount!.serialize(buffer, true)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsTransaction(let flags, let id, let stars, let starNanos, 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):
|
||||
return ("starsTransaction", [("flags", flags as Any), ("id", id as Any), ("stars", stars as Any), ("starNanos", starNanos 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)])
|
||||
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):
|
||||
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)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,66 +100,78 @@ public extension Api {
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_4 = reader.readInt32() }
|
||||
var _5: Int32?
|
||||
_5 = reader.readInt32()
|
||||
var _6: Api.StarsTransactionPeer?
|
||||
var _3: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.StarsTransactionPeer
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _4: Int32?
|
||||
_4 = reader.readInt32()
|
||||
var _5: Api.StarsTransactionPeer?
|
||||
if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.StarsTransactionPeer
|
||||
}
|
||||
var _6: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_8 = parseString(reader) }
|
||||
var _9: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_7 = parseString(reader) }
|
||||
var _8: Api.WebDocument?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
_8 = Api.parse(reader, signature: signature) as? Api.WebDocument
|
||||
} }
|
||||
var _10: Int32?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {_10 = reader.readInt32() }
|
||||
var _11: String?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {_11 = parseString(reader) }
|
||||
var _12: Buffer?
|
||||
if Int(_1!) & Int(1 << 7) != 0 {_12 = parseBytes(reader) }
|
||||
var _13: Int32?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {_13 = reader.readInt32() }
|
||||
var _14: [Api.MessageMedia]?
|
||||
var _9: Int32?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {_9 = reader.readInt32() }
|
||||
var _10: String?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {_10 = parseString(reader) }
|
||||
var _11: Buffer?
|
||||
if Int(_1!) & Int(1 << 7) != 0 {_11 = parseBytes(reader) }
|
||||
var _12: Int32?
|
||||
if Int(_1!) & Int(1 << 8) != 0 {_12 = reader.readInt32() }
|
||||
var _13: [Api.MessageMedia]?
|
||||
if Int(_1!) & Int(1 << 9) != 0 {if let _ = reader.readInt32() {
|
||||
_14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageMedia.self)
|
||||
_13 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageMedia.self)
|
||||
} }
|
||||
var _14: Int32?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_14 = reader.readInt32() }
|
||||
var _15: Int32?
|
||||
if Int(_1!) & Int(1 << 12) != 0 {_15 = reader.readInt32() }
|
||||
var _16: Int32?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_16 = reader.readInt32() }
|
||||
var _17: Api.StarGift?
|
||||
if Int(_1!) & Int(1 << 13) != 0 {_15 = reader.readInt32() }
|
||||
var _16: Api.StarGift?
|
||||
if Int(_1!) & Int(1 << 14) != 0 {if let signature = reader.readInt32() {
|
||||
_17 = Api.parse(reader, signature: signature) as? Api.StarGift
|
||||
_16 = Api.parse(reader, signature: signature) as? Api.StarGift
|
||||
} }
|
||||
var _17: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_17 = reader.readInt32() }
|
||||
var _18: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_18 = reader.readInt32() }
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_18 = reader.readInt32() }
|
||||
var _19: Api.Peer?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
|
||||
_19 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
} }
|
||||
var _20: Api.StarsAmount?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {if let signature = reader.readInt32() {
|
||||
_20 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 16) == 0) || _4 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 5) == 0) || _9 != nil
|
||||
let _c10 = (Int(_1!) & Int(1 << 5) == 0) || _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 5) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 8) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 9) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 12) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 13) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 14) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 15) == 0) || _18 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
|
||||
return Api.StarsTransaction.starsTransaction(flags: _1!, id: _2!, stars: _3!, starNanos: _4, date: _5!, peer: _6!, title: _7, description: _8, photo: _9, transactionDate: _10, transactionUrl: _11, botPayload: _12, msgId: _13, extendedMedia: _14, subscriptionPeriod: _15, giveawayPostId: _16, stargift: _17, floodskipNumber: _18)
|
||||
let _c11 = (Int(_1!) & Int(1 << 7) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 8) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 9) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 12) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 13) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 14) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 15) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 16) == 0) || _18 != nil
|
||||
let _c19 = (Int(_1!) & Int(1 << 17) == 0) || _19 != nil
|
||||
let _c20 = (Int(_1!) & Int(1 << 17) == 0) || _20 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 {
|
||||
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)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -1196,101 +1262,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryReaction: TypeConstructorDescription {
|
||||
case storyReaction(peerId: Api.Peer, date: Int32, reaction: Api.Reaction)
|
||||
case storyReactionPublicForward(message: Api.Message)
|
||||
case storyReactionPublicRepost(peerId: Api.Peer, story: Api.StoryItem)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyReaction(let peerId, let date, let reaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(1620104917)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
reaction.serialize(buffer, true)
|
||||
break
|
||||
case .storyReactionPublicForward(let message):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1146411453)
|
||||
}
|
||||
message.serialize(buffer, true)
|
||||
break
|
||||
case .storyReactionPublicRepost(let peerId, let story):
|
||||
if boxed {
|
||||
buffer.appendInt32(-808644845)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
story.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyReaction(let peerId, let date, let reaction):
|
||||
return ("storyReaction", [("peerId", peerId as Any), ("date", date as Any), ("reaction", reaction as Any)])
|
||||
case .storyReactionPublicForward(let message):
|
||||
return ("storyReactionPublicForward", [("message", message as Any)])
|
||||
case .storyReactionPublicRepost(let peerId, let story):
|
||||
return ("storyReactionPublicRepost", [("peerId", peerId as Any), ("story", story as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyReaction(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.StoryReaction.storyReaction(peerId: _1!, date: _2!, reaction: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyReactionPublicForward(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Message?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Message
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StoryReaction.storyReactionPublicForward(message: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyReactionPublicRepost(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Api.StoryItem?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StoryItem
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StoryReaction.storyReactionPublicRepost(peerId: _1!, story: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,101 @@
|
||||
public extension Api {
|
||||
indirect enum StoryReaction: TypeConstructorDescription {
|
||||
case storyReaction(peerId: Api.Peer, date: Int32, reaction: Api.Reaction)
|
||||
case storyReactionPublicForward(message: Api.Message)
|
||||
case storyReactionPublicRepost(peerId: Api.Peer, story: Api.StoryItem)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .storyReaction(let peerId, let date, let reaction):
|
||||
if boxed {
|
||||
buffer.appendInt32(1620104917)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
reaction.serialize(buffer, true)
|
||||
break
|
||||
case .storyReactionPublicForward(let message):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1146411453)
|
||||
}
|
||||
message.serialize(buffer, true)
|
||||
break
|
||||
case .storyReactionPublicRepost(let peerId, let story):
|
||||
if boxed {
|
||||
buffer.appendInt32(-808644845)
|
||||
}
|
||||
peerId.serialize(buffer, true)
|
||||
story.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .storyReaction(let peerId, let date, let reaction):
|
||||
return ("storyReaction", [("peerId", peerId as Any), ("date", date as Any), ("reaction", reaction as Any)])
|
||||
case .storyReactionPublicForward(let message):
|
||||
return ("storyReactionPublicForward", [("message", message as Any)])
|
||||
case .storyReactionPublicRepost(let peerId, let story):
|
||||
return ("storyReactionPublicRepost", [("peerId", peerId as Any), ("story", story as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_storyReaction(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.Reaction?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.Reaction
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.StoryReaction.storyReaction(peerId: _1!, date: _2!, reaction: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyReactionPublicForward(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Message?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Message
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StoryReaction.storyReactionPublicForward(message: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_storyReactionPublicRepost(_ reader: BufferReader) -> StoryReaction? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Api.StoryItem?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StoryItem
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StoryReaction.storyReactionPublicRepost(peerId: _1!, story: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum StoryView: TypeConstructorDescription {
|
||||
case storyView(flags: Int32, userId: Int64, date: Int32, reaction: Api.Reaction?)
|
||||
@ -798,7 +896,7 @@ public extension Api {
|
||||
case updateSentStoryReaction(peer: Api.Peer, storyId: Int32, reaction: Api.Reaction)
|
||||
case updateServiceNotification(flags: Int32, inboxDate: Int32?, type: String, message: String, media: Api.MessageMedia, entities: [Api.MessageEntity])
|
||||
case updateSmsJob(jobId: String)
|
||||
case updateStarsBalance(flags: Int32, balance: Int64, balanceNanos: Int32?)
|
||||
case updateStarsBalance(balance: Api.StarsAmount)
|
||||
case updateStarsRevenueStatus(peer: Api.Peer, status: Api.StarsRevenueStatus)
|
||||
case updateStickerSets(flags: Int32)
|
||||
case updateStickerSetsOrder(flags: Int32, order: [Int64])
|
||||
@ -1919,13 +2017,11 @@ public extension Api {
|
||||
}
|
||||
serializeString(jobId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateStarsBalance(let flags, let balance, let balanceNanos):
|
||||
case .updateStarsBalance(let balance):
|
||||
if boxed {
|
||||
buffer.appendInt32(1042067513)
|
||||
buffer.appendInt32(1317053305)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(balance, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(balanceNanos!, buffer: buffer, boxed: false)}
|
||||
balance.serialize(buffer, true)
|
||||
break
|
||||
case .updateStarsRevenueStatus(let peer, let status):
|
||||
if boxed {
|
||||
@ -2301,8 +2397,8 @@ public extension Api {
|
||||
return ("updateServiceNotification", [("flags", flags as Any), ("inboxDate", inboxDate as Any), ("type", type as Any), ("message", message as Any), ("media", media as Any), ("entities", entities as Any)])
|
||||
case .updateSmsJob(let jobId):
|
||||
return ("updateSmsJob", [("jobId", jobId as Any)])
|
||||
case .updateStarsBalance(let flags, let balance, let balanceNanos):
|
||||
return ("updateStarsBalance", [("flags", flags as Any), ("balance", balance as Any), ("balanceNanos", balanceNanos as Any)])
|
||||
case .updateStarsBalance(let balance):
|
||||
return ("updateStarsBalance", [("balance", balance as Any)])
|
||||
case .updateStarsRevenueStatus(let peer, let status):
|
||||
return ("updateStarsRevenueStatus", [("peer", peer as Any), ("status", status as Any)])
|
||||
case .updateStickerSets(let flags):
|
||||
@ -4556,17 +4652,13 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public static func parse_updateStarsBalance(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _1: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateStarsBalance(flags: _1!, balance: _2!, balanceNanos: _3)
|
||||
if _c1 {
|
||||
return Api.Update.updateStarsBalance(balance: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -56,6 +56,84 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum FoundStickers: TypeConstructorDescription {
|
||||
case foundStickers(flags: Int32, nextOffset: Int32?, hash: Int64, stickers: [Api.Document])
|
||||
case foundStickersNotModified(flags: Int32, nextOffset: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .foundStickers(let flags, let nextOffset, let hash, let stickers):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2100698480)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextOffset!, buffer: buffer, boxed: false)}
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(stickers.count))
|
||||
for item in stickers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .foundStickersNotModified(let flags, let nextOffset):
|
||||
if boxed {
|
||||
buffer.appendInt32(1611711796)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextOffset!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .foundStickers(let flags, let nextOffset, let hash, let stickers):
|
||||
return ("foundStickers", [("flags", flags as Any), ("nextOffset", nextOffset as Any), ("hash", hash as Any), ("stickers", stickers as Any)])
|
||||
case .foundStickersNotModified(let flags, let nextOffset):
|
||||
return ("foundStickersNotModified", [("flags", flags as Any), ("nextOffset", nextOffset as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_foundStickers(_ reader: BufferReader) -> FoundStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.messages.FoundStickers.foundStickers(flags: _1!, nextOffset: _2, hash: _3!, stickers: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_foundStickersNotModified(_ reader: BufferReader) -> FoundStickers? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.messages.FoundStickers.foundStickersNotModified(flags: _1!, nextOffset: _2)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum HighScores: TypeConstructorDescription {
|
||||
case highScores(scores: [Api.HighScore], users: [Api.User])
|
||||
@ -1418,49 +1496,3 @@ public extension Api.messages {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum SearchCounter: TypeConstructorDescription {
|
||||
case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchCounter(let flags, let filter, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-398136321)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
filter.serialize(buffer, true)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchCounter(let flags, let filter, let count):
|
||||
return ("searchCounter", [("flags", flags as Any), ("filter", filter as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchCounter(_ reader: BufferReader) -> SearchCounter? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.MessagesFilter?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.MessagesFilter
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,49 @@
|
||||
public extension Api.messages {
|
||||
enum SearchCounter: TypeConstructorDescription {
|
||||
case searchCounter(flags: Int32, filter: Api.MessagesFilter, count: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .searchCounter(let flags, let filter, let count):
|
||||
if boxed {
|
||||
buffer.appendInt32(-398136321)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
filter.serialize(buffer, true)
|
||||
serializeInt32(count, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .searchCounter(let flags, let filter, let count):
|
||||
return ("searchCounter", [("flags", flags as Any), ("filter", filter as Any), ("count", count as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_searchCounter(_ reader: BufferReader) -> SearchCounter? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.MessagesFilter?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.MessagesFilter
|
||||
}
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.messages.SearchCounter.searchCounter(flags: _1!, filter: _2!, count: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api.messages {
|
||||
enum SearchResultsCalendar: TypeConstructorDescription {
|
||||
case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
|
||||
|
@ -120,17 +120,16 @@ public extension Api.payments {
|
||||
}
|
||||
public extension Api.payments {
|
||||
enum StarsStatus: TypeConstructorDescription {
|
||||
case starsStatus(flags: Int32, balance: Int64, balanceNanos: Int32?, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, subscriptionsMissingBalance: Int64?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||
case starsStatus(flags: Int32, balance: Api.StarsAmount, subscriptions: [Api.StarsSubscription]?, subscriptionsNextOffset: String?, subscriptionsMissingBalance: Int64?, history: [Api.StarsTransaction]?, nextOffset: String?, chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsStatus(let flags, let balance, let balanceNanos, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(1447376356)
|
||||
buffer.appendInt32(1822222573)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(balance, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 5) != 0 {serializeInt32(balanceNanos!, buffer: buffer, boxed: false)}
|
||||
balance.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(subscriptions!.count))
|
||||
for item in subscriptions! {
|
||||
@ -160,52 +159,51 @@ public extension Api.payments {
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsStatus(let flags, let balance, let balanceNanos, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("balanceNanos", balanceNanos as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("subscriptionsMissingBalance", subscriptionsMissingBalance as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
case .starsStatus(let flags, let balance, let subscriptions, let subscriptionsNextOffset, let subscriptionsMissingBalance, let history, let nextOffset, let chats, let users):
|
||||
return ("starsStatus", [("flags", flags as Any), ("balance", balance as Any), ("subscriptions", subscriptions as Any), ("subscriptionsNextOffset", subscriptionsNextOffset as Any), ("subscriptionsMissingBalance", subscriptionsMissingBalance as Any), ("history", history as Any), ("nextOffset", nextOffset as Any), ("chats", chats as Any), ("users", users as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsStatus(_ reader: BufferReader) -> StarsStatus? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 5) != 0 {_3 = reader.readInt32() }
|
||||
var _4: [Api.StarsSubscription]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsSubscription.self)
|
||||
} }
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) }
|
||||
var _6: Int64?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_6 = reader.readInt64() }
|
||||
var _7: [Api.StarsTransaction]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self)
|
||||
} }
|
||||
var _8: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_8 = parseString(reader) }
|
||||
var _9: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
var _2: Api.StarsAmount?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.StarsAmount
|
||||
}
|
||||
var _10: [Api.User]?
|
||||
var _3: [Api.StarsSubscription]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsSubscription.self)
|
||||
} }
|
||||
var _4: String?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_4 = parseString(reader) }
|
||||
var _5: Int64?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {_5 = reader.readInt64() }
|
||||
var _6: [Api.StarsTransaction]?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarsTransaction.self)
|
||||
} }
|
||||
var _7: String?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_7 = parseString(reader) }
|
||||
var _8: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
_10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||
}
|
||||
var _9: [Api.User]?
|
||||
if let _ = reader.readInt32() {
|
||||
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 3) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 0) == 0) || _8 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 0) == 0) || _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, balanceNanos: _3, subscriptions: _4, subscriptionsNextOffset: _5, subscriptionsMissingBalance: _6, history: _7, nextOffset: _8, chats: _9!, users: _10!)
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.payments.StarsStatus.starsStatus(flags: _1!, balance: _2!, subscriptions: _3, subscriptionsNextOffset: _4, subscriptionsMissingBalance: _5, history: _6, nextOffset: _7, chats: _8!, users: _9!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -7872,6 +7872,26 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func searchStickers(flags: Int32, q: String, langCode: String, offset: Int32, limit: Int32, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.FoundStickers>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1277568311)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(q, buffer: buffer, boxed: false)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(offset, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "messages.searchStickers", parameters: [("flags", String(describing: flags)), ("q", String(describing: q)), ("langCode", String(describing: langCode)), ("offset", String(describing: offset)), ("limit", String(describing: limit)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.FoundStickers? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.FoundStickers?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.FoundStickers
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func sendBotRequestedPeer(peer: Api.InputPeer, msgId: Int32, buttonId: Int32, requestedPeers: [Api.InputPeer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
import AVFoundation
|
||||
|
||||
|
@ -129,7 +129,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateNewAuthorization(isUnconfirmed: Bool, hash: Int64, date: Int32, device: String, location: String)
|
||||
case UpdateWallpaper(peerId: PeerId, wallpaper: TelegramWallpaper?)
|
||||
case UpdateRevenueBalances(peerId: PeerId, balances: RevenueStats.Balances)
|
||||
case UpdateStarsBalance(peerId: PeerId, balance: Int64)
|
||||
case UpdateStarsBalance(peerId: PeerId, balance: Api.StarsAmount)
|
||||
case UpdateStarsRevenueStatus(peerId: PeerId, status: StarsRevenueStats.Balances)
|
||||
case UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool)
|
||||
}
|
||||
@ -690,7 +690,7 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateRevenueBalances(peerId: peerId, balances: balances))
|
||||
}
|
||||
|
||||
mutating func updateStarsBalance(peerId: PeerId, balance: Int64) {
|
||||
mutating func updateStarsBalance(peerId: PeerId, balance: Api.StarsAmount) {
|
||||
self.addOperation(.UpdateStarsBalance(peerId: peerId, balance: balance))
|
||||
}
|
||||
|
||||
@ -849,7 +849,7 @@ struct AccountReplayedFinalState {
|
||||
let updateConfig: Bool
|
||||
let isPremiumUpdated: Bool
|
||||
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
|
||||
let updatedStarsBalance: [PeerId: Int64]
|
||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||
let sentScheduledMessageIds: Set<MessageId>
|
||||
}
|
||||
@ -880,14 +880,14 @@ struct AccountFinalStateEvents {
|
||||
let updateConfig: Bool
|
||||
let isPremiumUpdated: Bool
|
||||
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
|
||||
let updatedStarsBalance: [PeerId: Int64]
|
||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||
|
||||
var isEmpty: Bool {
|
||||
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedRevenueBalances.isEmpty && self.updatedStarsBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty
|
||||
}
|
||||
|
||||
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: Int64] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set()) {
|
||||
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set()) {
|
||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||
self.addedReactionEvents = addedReactionEvents
|
||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||
|
@ -1785,7 +1785,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
updatedState.updateWallpaper(peerId: peer.peerId, wallpaper: wallpaper.flatMap { TelegramWallpaper(apiWallpaper: $0) })
|
||||
case let .updateBroadcastRevenueTransactions(peer, balances):
|
||||
updatedState.updateRevenueBalances(peerId: peer.peerId, balances: RevenueStats.Balances(apiRevenueBalances: balances))
|
||||
case let .updateStarsBalance(_, balance, _):
|
||||
case let .updateStarsBalance(balance):
|
||||
updatedState.updateStarsBalance(peerId: accountPeerId, balance: balance)
|
||||
case let .updateStarsRevenueStatus(peer, status):
|
||||
updatedState.updateStarsRevenueStatus(peerId: peer.peerId, status: StarsRevenueStats.Balances(apiStarsRevenueStatus: status))
|
||||
@ -3418,7 +3418,7 @@ func replayFinalState(
|
||||
var syncAttachMenuBots = false
|
||||
var updateConfig = false
|
||||
var updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:]
|
||||
var updatedStarsBalance: [PeerId: Int64] = [:]
|
||||
var updatedStarsBalance: [PeerId: StarsAmount] = [:]
|
||||
var updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:]
|
||||
var updatedStarsReactionsAreAnonymousByDefault: Bool?
|
||||
|
||||
@ -4846,7 +4846,7 @@ func replayFinalState(
|
||||
case let .UpdateRevenueBalances(peerId, balances):
|
||||
updatedRevenueBalances[peerId] = balances
|
||||
case let .UpdateStarsBalance(peerId, balance):
|
||||
updatedStarsBalance[peerId] = balance
|
||||
updatedStarsBalance[peerId] = StarsAmount(apiAmount: balance)
|
||||
case let .UpdateStarsRevenueStatus(peerId, status):
|
||||
updatedStarsRevenueStatus[peerId] = status
|
||||
case let .UpdateStarsReactionsAreAnonymousByDefault(value):
|
||||
|
@ -49,7 +49,7 @@ private final class UpdatedRevenueBalancesSubscriberContext {
|
||||
}
|
||||
|
||||
private final class UpdatedStarsBalanceSubscriberContext {
|
||||
let subscribers = Bag<([PeerId: Int64]) -> Void>()
|
||||
let subscribers = Bag<([PeerId: StarsAmount]) -> Void>()
|
||||
}
|
||||
|
||||
private final class UpdatedStarsRevenueStatusSubscriberContext {
|
||||
@ -1705,7 +1705,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func updatedStarsBalance() -> Signal<[PeerId: Int64], NoError> {
|
||||
public func updatedStarsBalance() -> Signal<[PeerId: StarsAmount], NoError> {
|
||||
let queue = self.queue
|
||||
return Signal { [weak self] subscriber in
|
||||
let disposable = MetaDisposable()
|
||||
@ -1726,7 +1726,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyUpdatedStarsBalance(_ updatedStarsBalance: [PeerId: Int64]) {
|
||||
private func notifyUpdatedStarsBalance(_ updatedStarsBalance: [PeerId: StarsAmount]) {
|
||||
for subscriber in self.updatedStarsBalanceContext.subscribers.copyItems() {
|
||||
subscriber(updatedStarsBalance)
|
||||
}
|
||||
@ -2084,7 +2084,7 @@ public final class AccountStateManager {
|
||||
}
|
||||
}
|
||||
|
||||
public func updatedStarsBalance() -> Signal<[PeerId: Int64], NoError> {
|
||||
public func updatedStarsBalance() -> Signal<[PeerId: StarsAmount], NoError> {
|
||||
return self.impl.signalWith { impl, subscriber in
|
||||
return impl.updatedStarsBalance().start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
|
||||
}
|
||||
|
@ -412,7 +412,7 @@ private func requestSendStarsReaction(postbox: Postbox, network: Network, stateM
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, RequestUpdateMessageReactionError> in
|
||||
stateManager.starsContext?.add(balance: Int64(-count), addTransaction: false)
|
||||
stateManager.starsContext?.add(balance: StarsAmount(value: Int64(-count), nanos: 0), addTransaction: false)
|
||||
|
||||
return postbox.transaction { transaction -> Void in
|
||||
transaction.setPendingMessageAction(type: .sendStarsReaction, id: messageId, action: UpdateMessageReactionsAction())
|
||||
|
@ -24,18 +24,21 @@ public struct StarsRevenueStats: Equatable, Codable {
|
||||
case overallRevenue
|
||||
case withdrawEnabled
|
||||
case nextWithdrawalTimestamp
|
||||
case currentBalanceStars
|
||||
case availableBalanceStars
|
||||
case overallRevenueStars
|
||||
}
|
||||
|
||||
public let currentBalance: Int64
|
||||
public let availableBalance: Int64
|
||||
public let overallRevenue: Int64
|
||||
public let currentBalance: StarsAmount
|
||||
public let availableBalance: StarsAmount
|
||||
public let overallRevenue: StarsAmount
|
||||
public let withdrawEnabled: Bool
|
||||
public let nextWithdrawalTimestamp: Int32?
|
||||
|
||||
public init(
|
||||
currentBalance: Int64,
|
||||
availableBalance: Int64,
|
||||
overallRevenue: Int64,
|
||||
currentBalance: StarsAmount,
|
||||
availableBalance: StarsAmount,
|
||||
overallRevenue: StarsAmount,
|
||||
withdrawEnabled: Bool,
|
||||
nextWithdrawalTimestamp: Int32?
|
||||
) {
|
||||
@ -48,18 +51,35 @@ public struct StarsRevenueStats: Equatable, Codable {
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.currentBalance = try container.decode(Int64.self, forKey: .currentBalance)
|
||||
self.availableBalance = try container.decode(Int64.self, forKey: .availableBalance)
|
||||
self.overallRevenue = try container.decode(Int64.self, forKey: .overallRevenue)
|
||||
|
||||
if let legacyCurrentBalance = try container.decodeIfPresent(Int64.self, forKey: .currentBalance) {
|
||||
self.currentBalance = StarsAmount(value: legacyCurrentBalance, nanos: 0)
|
||||
} else {
|
||||
self.currentBalance = try container.decode(StarsAmount.self, forKey: .currentBalanceStars)
|
||||
}
|
||||
|
||||
if let legacyAvailableBalance = try container.decodeIfPresent(Int64.self, forKey: .availableBalance) {
|
||||
self.availableBalance = StarsAmount(value: legacyAvailableBalance, nanos: 0)
|
||||
} else {
|
||||
self.availableBalance = try container.decode(StarsAmount.self, forKey: .availableBalanceStars)
|
||||
}
|
||||
|
||||
|
||||
if let legacyOverallRevenue = try container.decodeIfPresent(Int64.self, forKey: .overallRevenue) {
|
||||
self.overallRevenue = StarsAmount(value: legacyOverallRevenue, nanos: 0)
|
||||
} else {
|
||||
self.overallRevenue = try container.decode(StarsAmount.self, forKey: .overallRevenueStars)
|
||||
}
|
||||
|
||||
self.withdrawEnabled = try container.decode(Bool.self, forKey: .withdrawEnabled)
|
||||
self.nextWithdrawalTimestamp = try container.decodeIfPresent(Int32.self, forKey: .nextWithdrawalTimestamp)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.currentBalance, forKey: .currentBalance)
|
||||
try container.encode(self.availableBalance, forKey: .availableBalance)
|
||||
try container.encode(self.overallRevenue, forKey: .overallRevenue)
|
||||
try container.encode(self.currentBalance, forKey: .currentBalanceStars)
|
||||
try container.encode(self.availableBalance, forKey: .availableBalanceStars)
|
||||
try container.encode(self.overallRevenue, forKey: .overallRevenueStars)
|
||||
try container.encode(self.withdrawEnabled, forKey: .withdrawEnabled)
|
||||
try container.encodeIfPresent(self.nextWithdrawalTimestamp, forKey: .nextWithdrawalTimestamp)
|
||||
}
|
||||
@ -126,7 +146,7 @@ extension StarsRevenueStats.Balances {
|
||||
init(apiStarsRevenueStatus: Api.StarsRevenueStatus) {
|
||||
switch apiStarsRevenueStatus {
|
||||
case let .starsRevenueStatus(flags, currentBalance, availableBalance, overallRevenue, nextWithdrawalAt):
|
||||
self.init(currentBalance: currentBalance, availableBalance: availableBalance, overallRevenue: overallRevenue, withdrawEnabled: ((flags & (1 << 0)) != 0), nextWithdrawalTimestamp: nextWithdrawalAt)
|
||||
self.init(currentBalance: StarsAmount(value: currentBalance, nanos: 0), availableBalance: StarsAmount(value: availableBalance, nanos: 0), overallRevenue: StarsAmount(value: overallRevenue, nanos: 0), withdrawEnabled: ((flags & (1 << 0)) != 0), nextWithdrawalTimestamp: nextWithdrawalAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -749,7 +749,11 @@ func _internal_requestConnectedStarRefBots(account: Account, id: EnginePeer.Id,
|
||||
}
|
||||
}
|
||||
|
||||
public final class TelegramSuggestedStarRefBotList : Equatable {
|
||||
public final class TelegramSuggestedStarRefBotList: Equatable {
|
||||
public enum SortMode {
|
||||
case date
|
||||
case commission
|
||||
}
|
||||
|
||||
public final class Item: Equatable {
|
||||
public let peer: EnginePeer
|
||||
|
@ -269,9 +269,70 @@ func _internal_starsGiveawayOptions(account: Account) -> Signal<[StarsGiveawayOp
|
||||
}
|
||||
}
|
||||
|
||||
public struct StarsAmount: Equatable, Comparable, Hashable, Codable, CustomStringConvertible {
|
||||
public static let zero: StarsAmount = StarsAmount(value: 0, nanos: 0)
|
||||
|
||||
public var value: Int64
|
||||
public var nanos: Int32
|
||||
|
||||
public init(value: Int64, nanos: Int32) {
|
||||
self.value = value
|
||||
self.nanos = nanos
|
||||
}
|
||||
|
||||
public var stringValue: String {
|
||||
if self.nanos == 0 {
|
||||
return "\(self.value)"
|
||||
} else {
|
||||
let totalValue = (Double(self.value) * 1e9 + Double(self.nanos)) / 1e9
|
||||
return "\(totalValue)"
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
return self.stringValue
|
||||
}
|
||||
|
||||
public static func <(lhs: StarsAmount, rhs: StarsAmount) -> Bool {
|
||||
if lhs.value == rhs.value {
|
||||
return lhs.nanos < rhs.nanos
|
||||
} else {
|
||||
return lhs.value < rhs.value
|
||||
}
|
||||
}
|
||||
|
||||
public static func +(lhs: StarsAmount, rhs: StarsAmount) -> StarsAmount {
|
||||
let totalNanos = Int64(lhs.nanos) + Int64(rhs.nanos)
|
||||
let overflow = totalNanos / 1_000_000_000
|
||||
let remainingNanos = totalNanos % 1_000_000_000
|
||||
return StarsAmount(value: lhs.value + rhs.value + overflow, nanos: Int32(remainingNanos))
|
||||
}
|
||||
|
||||
public static func -(lhs: StarsAmount, rhs: StarsAmount) -> StarsAmount {
|
||||
var totalNanos = Int64(lhs.nanos) - Int64(rhs.nanos)
|
||||
var totalValue = lhs.value - rhs.value
|
||||
|
||||
if totalNanos < 0 {
|
||||
totalValue -= 1
|
||||
totalNanos += 1_000_000_000
|
||||
}
|
||||
|
||||
return StarsAmount(value: totalValue, nanos: Int32(totalNanos))
|
||||
}
|
||||
}
|
||||
|
||||
extension StarsAmount {
|
||||
init(apiAmount: Api.StarsAmount) {
|
||||
switch apiAmount {
|
||||
case let .starsAmount(amount, nanos):
|
||||
self.init(value: amount, nanos: nanos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalStarsStatus {
|
||||
let balance: Int64
|
||||
let subscriptionsMissingBalance: Int64?
|
||||
let balance: StarsAmount
|
||||
let subscriptionsMissingBalance: StarsAmount?
|
||||
let subscriptions: [StarsContext.State.Subscription]
|
||||
let nextSubscriptionsOffset: String?
|
||||
let transactions: [StarsContext.State.Transaction]
|
||||
@ -317,8 +378,7 @@ private func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id
|
||||
|> mapToSignal { result -> Signal<InternalStarsStatus, RequestStarsStateError> in
|
||||
return account.postbox.transaction { transaction -> InternalStarsStatus in
|
||||
switch result {
|
||||
case let .starsStatus(flags: _, balance, balanceNanos, _, _, subscriptionsMissingBalance, transactions, nextTransactionsOffset, chats, users):
|
||||
let _ = balanceNanos
|
||||
case let .starsStatus(flags: _, balance, _, _, subscriptionsMissingBalance, transactions, nextTransactionsOffset, chats, users):
|
||||
let peers = AccumulatedPeers(chats: chats, users: users)
|
||||
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: peers)
|
||||
|
||||
@ -331,8 +391,8 @@ private func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id
|
||||
}
|
||||
}
|
||||
return InternalStarsStatus(
|
||||
balance: balance,
|
||||
subscriptionsMissingBalance: subscriptionsMissingBalance,
|
||||
balance: StarsAmount(apiAmount: balance),
|
||||
subscriptionsMissingBalance: subscriptionsMissingBalance.flatMap { StarsAmount(value: $0, nanos: 0) },
|
||||
subscriptions: [],
|
||||
nextSubscriptionsOffset: nil,
|
||||
transactions: parsedTransactions,
|
||||
@ -368,8 +428,7 @@ private func _internal_requestStarsSubscriptions(account: Account, peerId: Engin
|
||||
|> mapToSignal { result -> Signal<InternalStarsStatus, RequestStarsSubscriptionsError> in
|
||||
return account.postbox.transaction { transaction -> InternalStarsStatus in
|
||||
switch result {
|
||||
case let .starsStatus(_, balance, balanceNanos, subscriptions, subscriptionsNextOffset, subscriptionsMissingBalance, _, _, chats, users):
|
||||
let _ = balanceNanos
|
||||
case let .starsStatus(_, balance, subscriptions, subscriptionsNextOffset, subscriptionsMissingBalance, _, _, chats, users):
|
||||
let peers = AccumulatedPeers(chats: chats, users: users)
|
||||
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: peers)
|
||||
|
||||
@ -384,8 +443,8 @@ private func _internal_requestStarsSubscriptions(account: Account, peerId: Engin
|
||||
}
|
||||
}
|
||||
return InternalStarsStatus(
|
||||
balance: balance,
|
||||
subscriptionsMissingBalance: subscriptionsMissingBalance,
|
||||
balance: StarsAmount(apiAmount: balance),
|
||||
subscriptionsMissingBalance: subscriptionsMissingBalance.flatMap { StarsAmount(value: $0, nanos: 0) },
|
||||
subscriptions: parsedSubscriptions,
|
||||
nextSubscriptionsOffset: subscriptionsNextOffset,
|
||||
transactions: [],
|
||||
@ -464,7 +523,7 @@ private final class StarsContextImpl {
|
||||
}))
|
||||
}
|
||||
|
||||
func add(balance: Int64, addTransaction: Bool) {
|
||||
func add(balance: StarsAmount, addTransaction: Bool) {
|
||||
guard let state = self._state else {
|
||||
return
|
||||
}
|
||||
@ -473,10 +532,10 @@ private final class StarsContextImpl {
|
||||
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), at: 0)
|
||||
}
|
||||
|
||||
self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: max(0, state.balance + balance), subscriptions: state.subscriptions, canLoadMoreSubscriptions: state.canLoadMoreSubscriptions, transactions: transactions, canLoadMoreTransactions: state.canLoadMoreTransactions, isLoading: state.isLoading))
|
||||
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))
|
||||
}
|
||||
|
||||
fileprivate func updateBalance(_ balance: Int64, transactions: [StarsContext.State.Transaction]?) {
|
||||
fileprivate func updateBalance(_ balance: StarsAmount, transactions: [StarsContext.State.Transaction]?) {
|
||||
guard let state = self._state else {
|
||||
return
|
||||
}
|
||||
@ -492,8 +551,11 @@ 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, starNanos, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipNumber):
|
||||
let _ = starNanos
|
||||
case let .starsTransaction(apiFlags, id, stars, date, transactionPeer, title, description, photo, transactionDate, transactionUrl, _, messageId, extendedMedia, subscriptionPeriod, giveawayPostId, starGift, floodskipNumber, starrefCommissionPermille, starrefPeer, starrefAmount):
|
||||
let _ = starrefCommissionPermille
|
||||
let _ = starrefPeer
|
||||
let _ = starrefAmount
|
||||
|
||||
let parsedPeer: StarsContext.State.Transaction.Peer
|
||||
var paidMessageId: MessageId?
|
||||
var giveawayMessageId: MessageId?
|
||||
@ -549,7 +611,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: 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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,7 +672,7 @@ public final class StarsContext {
|
||||
|
||||
public let flags: Flags
|
||||
public let id: String
|
||||
public let count: Int64
|
||||
public let count: StarsAmount
|
||||
public let date: Int32
|
||||
public let peer: Peer
|
||||
public let title: String?
|
||||
@ -628,7 +690,7 @@ public final class StarsContext {
|
||||
public init(
|
||||
flags: Flags,
|
||||
id: String,
|
||||
count: Int64,
|
||||
count: StarsAmount,
|
||||
date: Int32,
|
||||
peer: Peer,
|
||||
title: String?,
|
||||
@ -803,14 +865,14 @@ public final class StarsContext {
|
||||
}
|
||||
|
||||
public var flags: Flags
|
||||
public var balance: Int64
|
||||
public var balance: StarsAmount
|
||||
public var subscriptions: [Subscription]
|
||||
public var canLoadMoreSubscriptions: Bool
|
||||
public var transactions: [Transaction]
|
||||
public var canLoadMoreTransactions: Bool
|
||||
public var isLoading: Bool
|
||||
|
||||
init(flags: Flags, balance: Int64, subscriptions: [Subscription], canLoadMoreSubscriptions: Bool, transactions: [Transaction], canLoadMoreTransactions: Bool, isLoading: Bool) {
|
||||
init(flags: Flags, balance: StarsAmount, subscriptions: [Subscription], canLoadMoreSubscriptions: Bool, transactions: [Transaction], canLoadMoreTransactions: Bool, isLoading: Bool) {
|
||||
self.flags = flags
|
||||
self.balance = balance
|
||||
self.subscriptions = subscriptions
|
||||
@ -873,13 +935,13 @@ public final class StarsContext {
|
||||
return state
|
||||
}
|
||||
|
||||
public func add(balance: Int64, addTransaction: Bool = true) {
|
||||
public func add(balance: StarsAmount, addTransaction: Bool = true) {
|
||||
self.impl.with {
|
||||
$0.add(balance: balance, addTransaction: addTransaction)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func updateBalance(_ balance: Int64, transactions: [StarsContext.State.Transaction]?) {
|
||||
fileprivate func updateBalance(_ balance: StarsAmount, transactions: [StarsContext.State.Transaction]?) {
|
||||
self.impl.with {
|
||||
$0.updateBalance(balance, transactions: transactions)
|
||||
}
|
||||
@ -942,9 +1004,9 @@ private final class StarsTransactionsContextImpl {
|
||||
case .all:
|
||||
initialTransactions = currentTransactions
|
||||
case .incoming:
|
||||
initialTransactions = currentTransactions.filter { $0.count > 0 }
|
||||
initialTransactions = currentTransactions.filter { $0.count > StarsAmount.zero }
|
||||
case .outgoing:
|
||||
initialTransactions = currentTransactions.filter { $0.count < 0 }
|
||||
initialTransactions = currentTransactions.filter { $0.count < StarsAmount.zero }
|
||||
}
|
||||
|
||||
self._state = StarsTransactionsContext.State(transactions: initialTransactions, canLoadMore: true, isLoading: false)
|
||||
@ -962,9 +1024,9 @@ private final class StarsTransactionsContextImpl {
|
||||
case .all:
|
||||
filteredTransactions = currentTransactions
|
||||
case .incoming:
|
||||
filteredTransactions = currentTransactions.filter { $0.count > 0 }
|
||||
filteredTransactions = currentTransactions.filter { $0.count > StarsAmount.zero }
|
||||
case .outgoing:
|
||||
filteredTransactions = currentTransactions.filter { $0.count < 0 }
|
||||
filteredTransactions = currentTransactions.filter { $0.count < StarsAmount.zero }
|
||||
}
|
||||
|
||||
if !filteredTransactions.isEmpty && self._state.transactions.isEmpty && filteredTransactions != initialTransactions {
|
||||
@ -989,9 +1051,9 @@ private final class StarsTransactionsContextImpl {
|
||||
case .all:
|
||||
filteredTransactions = currentTransactions
|
||||
case .incoming:
|
||||
filteredTransactions = currentTransactions.filter { $0.count > 0 }
|
||||
filteredTransactions = currentTransactions.filter { $0.count > StarsAmount.zero }
|
||||
case .outgoing:
|
||||
filteredTransactions = currentTransactions.filter { $0.count < 0 }
|
||||
filteredTransactions = currentTransactions.filter { $0.count < StarsAmount.zero }
|
||||
}
|
||||
|
||||
if filteredTransactions != initialTransactions {
|
||||
@ -1161,7 +1223,7 @@ private final class StarsSubscriptionsContextImpl {
|
||||
let currentSubscriptions = starsContext?.currentState?.subscriptions ?? []
|
||||
let canLoadMore = starsContext?.currentState?.canLoadMoreSubscriptions ?? true
|
||||
|
||||
self._state = StarsSubscriptionsContext.State(balance: 0, subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false)
|
||||
self._state = StarsSubscriptionsContext.State(balance: StarsAmount.zero, subscriptions: currentSubscriptions, canLoadMore: canLoadMore, isLoading: false)
|
||||
self._statePromise.set(.single(self._state))
|
||||
|
||||
self.loadMore()
|
||||
@ -1194,7 +1256,7 @@ private final class StarsSubscriptionsContextImpl {
|
||||
self.nextOffset = status.nextSubscriptionsOffset
|
||||
|
||||
var updatedState = self._state
|
||||
updatedState.balance = status.subscriptionsMissingBalance ?? 0
|
||||
updatedState.balance = status.subscriptionsMissingBalance ?? StarsAmount.zero
|
||||
updatedState.subscriptions = nextOffset.isEmpty ? status.subscriptions : updatedState.subscriptions + status.subscriptions
|
||||
updatedState.isLoading = false
|
||||
updatedState.canLoadMore = self.nextOffset != nil
|
||||
@ -1257,12 +1319,12 @@ private final class StarsSubscriptionsContextImpl {
|
||||
|
||||
public final class StarsSubscriptionsContext {
|
||||
public struct State: Equatable {
|
||||
public var balance: Int64
|
||||
public var balance: StarsAmount
|
||||
public var subscriptions: [StarsContext.State.Subscription]
|
||||
public var canLoadMore: Bool
|
||||
public var isLoading: Bool
|
||||
|
||||
init(balance: Int64, subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) {
|
||||
init(balance: StarsAmount, subscriptions: [StarsContext.State.Subscription], canLoadMore: Bool, isLoading: Bool) {
|
||||
self.balance = balance
|
||||
self.subscriptions = subscriptions
|
||||
self.canLoadMore = canLoadMore
|
||||
@ -1449,7 +1511,7 @@ func _internal_getStarsTransaction(accountPeerId: PeerId, postbox: Postbox, netw
|
||||
}
|
||||
|> mapToSignal { result -> Signal<StarsContext.State.Transaction?, NoError> in
|
||||
return postbox.transaction { transaction -> StarsContext.State.Transaction? in
|
||||
guard let result, case let .starsStatus(_, _, _, _, _, _, transactions, _, chats, users) = result, let matchingTransaction = transactions?.first else {
|
||||
guard let result, case let .starsStatus(_, _, _, _, _, transactions, _, chats, users) = result, let matchingTransaction = transactions?.first else {
|
||||
return nil
|
||||
}
|
||||
let peers = AccumulatedPeers(chats: chats, users: users)
|
||||
@ -1465,12 +1527,13 @@ public struct StarsSubscriptionPricing: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case period
|
||||
case amount
|
||||
case starsAmount
|
||||
}
|
||||
|
||||
public let period: Int32
|
||||
public let amount: Int64
|
||||
public let amount: StarsAmount
|
||||
|
||||
public init(period: Int32, amount: Int64) {
|
||||
public init(period: Int32, amount: StarsAmount) {
|
||||
self.period = period
|
||||
self.amount = amount
|
||||
}
|
||||
@ -1479,14 +1542,19 @@ public struct StarsSubscriptionPricing: Codable, Equatable {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.period = try container.decode(Int32.self, forKey: .period)
|
||||
self.amount = try container.decode(Int64.self, forKey: .amount)
|
||||
|
||||
if let legacyAmount = try container.decodeIfPresent(Int64.self, forKey: .amount) {
|
||||
self.amount = StarsAmount(value: legacyAmount, nanos: 0)
|
||||
} else {
|
||||
self.amount = try container.decode(StarsAmount.self, forKey: .starsAmount)
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.period, forKey: .period)
|
||||
try container.encode(self.amount, forKey: .amount)
|
||||
try container.encode(self.amount, forKey: .starsAmount)
|
||||
}
|
||||
|
||||
public static let monthPeriod: Int32 = 2592000
|
||||
@ -1497,12 +1565,12 @@ extension StarsSubscriptionPricing {
|
||||
init(apiStarsSubscriptionPricing: Api.StarsSubscriptionPricing) {
|
||||
switch apiStarsSubscriptionPricing {
|
||||
case let .starsSubscriptionPricing(period, amount):
|
||||
self = .init(period: period, amount: amount)
|
||||
self = .init(period: period, amount: StarsAmount(value: amount, nanos: 0))
|
||||
}
|
||||
}
|
||||
|
||||
var apiStarsSubscriptionPricing: Api.StarsSubscriptionPricing {
|
||||
return .starsSubscriptionPricing(period: self.period, amount: self.amount)
|
||||
return .starsSubscriptionPricing(period: self.period, amount: self.amount.value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import PresentationStrings
|
||||
import TelegramCore
|
||||
|
||||
public func compactNumericCountString(_ count: Int, decimalSeparator: String = ".") -> String {
|
||||
if count >= 1000 * 1000 {
|
||||
@ -38,6 +39,28 @@ public func presentationStringsFormattedNumber(_ count: Int32, _ groupingSeparat
|
||||
}
|
||||
}
|
||||
|
||||
public func presentationStringsFormattedNumber(_ starsAmount: StarsAmount, _ groupingSeparator: String = "") -> String {
|
||||
if starsAmount.nanos == 0 {
|
||||
let count = Int32(starsAmount.value)
|
||||
let string = "\(count)"
|
||||
if groupingSeparator.isEmpty || abs(count) < 1000 {
|
||||
return string
|
||||
} else {
|
||||
var groupedString: String = ""
|
||||
for i in 0 ..< Int(ceil(Double(string.count) / 3.0)) {
|
||||
let index = string.count - Int(i + 1) * 3
|
||||
if !groupedString.isEmpty {
|
||||
groupedString = groupingSeparator + groupedString
|
||||
}
|
||||
groupedString = String(string[string.index(string.startIndex, offsetBy: max(0, index)) ..< string.index(string.startIndex, offsetBy: index + 3)]) + groupedString
|
||||
}
|
||||
return groupedString
|
||||
}
|
||||
} else {
|
||||
return starsAmount.stringValue
|
||||
}
|
||||
}
|
||||
|
||||
public func dayIntervalString(strings: PresentationStrings, days: Int32) -> String {
|
||||
return strings.MessageTimer_Days(max(0, days))
|
||||
}
|
||||
|
22
submodules/TelegramUI/Components/AlertComponent/BUILD
Normal file
22
submodules/TelegramUI/Components/AlertComponent/BUILD
Normal file
@ -0,0 +1,22 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "AlertComponent",
|
||||
module_name = "AlertComponent",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,395 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import AsyncDisplayKit
|
||||
|
||||
private let alertWidth: CGFloat = 270.0
|
||||
|
||||
public enum ComponentAlertActionType {
|
||||
case genericAction
|
||||
case defaultAction
|
||||
case destructiveAction
|
||||
case defaultDestructiveAction
|
||||
}
|
||||
|
||||
public struct ComponentAlertAction {
|
||||
public let type: ComponentAlertActionType
|
||||
public let title: String
|
||||
public let action: () -> Void
|
||||
|
||||
public init(type: ComponentAlertActionType, title: String, action: @escaping () -> Void) {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
}
|
||||
|
||||
public final class ComponentAlertContentActionNode: HighlightableButtonNode {
|
||||
private var theme: AlertControllerTheme
|
||||
public var action: ComponentAlertAction {
|
||||
didSet {
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
|
||||
public var highlightedUpdated: (Bool) -> Void = { _ in }
|
||||
|
||||
public init(theme: AlertControllerTheme, action: ComponentAlertAction) {
|
||||
self.theme = theme
|
||||
self.action = action
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.alpha = 0.0
|
||||
|
||||
super.init()
|
||||
|
||||
self.titleNode.maximumNumberOfLines = 2
|
||||
|
||||
self.highligthedChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(value, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.updateTheme(theme)
|
||||
}
|
||||
|
||||
public override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.pointerInteraction = PointerInteraction(node: self, style: .hover, willEnter: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(true, animated: false)
|
||||
}
|
||||
}, willExit: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.setHighlighted(false, animated: false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func performAction() {
|
||||
if self.actionEnabled {
|
||||
self.action.action()
|
||||
}
|
||||
}
|
||||
|
||||
public func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
self.highlightedUpdated(highlighted)
|
||||
if highlighted {
|
||||
if self.backgroundNode.supernode == nil {
|
||||
self.insertSubnode(self.backgroundNode, at: 0)
|
||||
}
|
||||
self.backgroundNode.alpha = 1.0
|
||||
} else {
|
||||
if animated {
|
||||
UIView.animate(withDuration: 0.3, animations: {
|
||||
self.backgroundNode.alpha = 0.0
|
||||
})
|
||||
} else {
|
||||
self.backgroundNode.alpha = 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
public var actionEnabled: Bool = true {
|
||||
didSet {
|
||||
self.isUserInteractionEnabled = self.actionEnabled
|
||||
self.updateTitle()
|
||||
}
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.theme = theme
|
||||
self.backgroundNode.backgroundColor = theme.highlightedItemColor
|
||||
self.updateTitle()
|
||||
}
|
||||
|
||||
private func updateTitle() {
|
||||
var font = Font.regular(theme.baseFontSize)
|
||||
var color: UIColor
|
||||
switch self.action.type {
|
||||
case .defaultAction, .genericAction:
|
||||
color = self.actionEnabled ? self.theme.accentColor : self.theme.disabledColor
|
||||
case .destructiveAction, .defaultDestructiveAction:
|
||||
color = self.actionEnabled ? self.theme.destructiveColor : self.theme.disabledColor
|
||||
}
|
||||
switch self.action.type {
|
||||
case .defaultAction, .defaultDestructiveAction:
|
||||
font = Font.semibold(theme.baseFontSize)
|
||||
case .destructiveAction, .genericAction:
|
||||
break
|
||||
}
|
||||
self.setAttributedTitle(NSAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center), for: [])
|
||||
self.accessibilityLabel = self.action.title
|
||||
self.accessibilityTraits = [.button]
|
||||
}
|
||||
|
||||
@objc func pressed() {
|
||||
self.action.action()
|
||||
}
|
||||
|
||||
override public func layout() {
|
||||
super.layout()
|
||||
|
||||
self.backgroundNode.frame = self.bounds
|
||||
}
|
||||
}
|
||||
|
||||
public enum ComponentAlertContentActionLayout {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
public final class ComponentAlertContentNode: AlertContentNode {
|
||||
private var theme: AlertControllerTheme
|
||||
private let actionLayout: ComponentAlertContentActionLayout
|
||||
|
||||
private let content: AnyComponent<Empty>
|
||||
private let contentView = ComponentView<Empty>()
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [ComponentAlertContentActionNode]
|
||||
private let actionVerticalSeparators: [ASDisplayNode]
|
||||
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private let _dismissOnOutsideTap: Bool
|
||||
override public var dismissOnOutsideTap: Bool {
|
||||
return self._dismissOnOutsideTap
|
||||
}
|
||||
|
||||
private var highlightedItemIndex: Int? = nil
|
||||
|
||||
public init(theme: AlertControllerTheme, content: AnyComponent<Empty>, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout, dismissOnOutsideTap: Bool) {
|
||||
self.theme = theme
|
||||
self.actionLayout = actionLayout
|
||||
self._dismissOnOutsideTap = dismissOnOutsideTap
|
||||
self.content = content
|
||||
|
||||
self.actionNodesSeparator = ASDisplayNode()
|
||||
self.actionNodesSeparator.isUserInteractionEnabled = false
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
|
||||
self.actionNodes = actions.map { action -> ComponentAlertContentActionNode in
|
||||
return ComponentAlertContentActionNode(theme: theme, action: action)
|
||||
}
|
||||
|
||||
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||
if actions.count > 1 {
|
||||
for _ in 0 ..< actions.count - 1 {
|
||||
let separatorNode = ASDisplayNode()
|
||||
separatorNode.isLayerBacked = true
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
actionVerticalSeparators.append(separatorNode)
|
||||
}
|
||||
}
|
||||
self.actionVerticalSeparators = actionVerticalSeparators
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.actionNodesSeparator)
|
||||
|
||||
var i = 0
|
||||
for actionNode in self.actionNodes {
|
||||
self.addSubnode(actionNode)
|
||||
|
||||
let index = i
|
||||
actionNode.highlightedUpdated = { [weak self] highlighted in
|
||||
if highlighted {
|
||||
self?.highlightedItemIndex = index
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
self.addSubnode(separatorNode)
|
||||
}
|
||||
}
|
||||
|
||||
func setHighlightedItemIndex(_ index: Int?, update: Bool = false) {
|
||||
self.highlightedItemIndex = index
|
||||
|
||||
if update {
|
||||
var i = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if i == index {
|
||||
actionNode.setHighlighted(true, animated: false)
|
||||
} else {
|
||||
actionNode.setHighlighted(false, animated: false)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func decreaseHighlightedIndex() {
|
||||
let currentHighlightedIndex = self.highlightedItemIndex ?? 0
|
||||
|
||||
self.setHighlightedItemIndex(max(0, currentHighlightedIndex - 1), update: true)
|
||||
}
|
||||
|
||||
override public func increaseHighlightedIndex() {
|
||||
let currentHighlightedIndex = self.highlightedItemIndex ?? -1
|
||||
|
||||
self.setHighlightedItemIndex(min(self.actionNodes.count - 1, currentHighlightedIndex + 1), update: true)
|
||||
}
|
||||
|
||||
override public func performHighlightedAction() {
|
||||
guard let highlightedItemIndex = self.highlightedItemIndex else {
|
||||
return
|
||||
}
|
||||
|
||||
var i = 0
|
||||
for itemNode in self.actionNodes {
|
||||
if i == highlightedItemIndex {
|
||||
itemNode.performAction()
|
||||
return
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.theme = theme
|
||||
|
||||
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||
for actionNode in self.actionNodes {
|
||||
actionNode.updateTheme(theme)
|
||||
}
|
||||
for separatorNode in self.actionVerticalSeparators {
|
||||
separatorNode.backgroundColor = theme.separatorColor
|
||||
}
|
||||
|
||||
if let size = self.validLayout {
|
||||
_ = self.updateLayout(size: size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
override public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
self.validLayout = size
|
||||
|
||||
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||
|
||||
var size = size
|
||||
size.width = min(size.width, alertWidth)
|
||||
|
||||
let contentSize = self.contentView.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: self.content,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - insets.left - insets.right, height: 10000.0)
|
||||
)
|
||||
|
||||
let actionButtonHeight: CGFloat = 44.0
|
||||
|
||||
var minActionsWidth: CGFloat = 0.0
|
||||
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||
let actionTitleInsets: CGFloat = 8.0
|
||||
|
||||
var effectiveActionLayout = self.actionLayout
|
||||
for actionNode in self.actionNodes {
|
||||
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||
if case .horizontal = effectiveActionLayout, actionTitleSize.height > actionButtonHeight * 0.6667 {
|
||||
effectiveActionLayout = .vertical
|
||||
}
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||
case .vertical:
|
||||
minActionsWidth = max(minActionsWidth, actionTitleSize.width + actionTitleInsets)
|
||||
}
|
||||
}
|
||||
|
||||
let resultSize: CGSize
|
||||
|
||||
var actionsHeight: CGFloat = 0.0
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionsHeight = actionButtonHeight
|
||||
case .vertical:
|
||||
actionsHeight = actionButtonHeight * CGFloat(self.actionNodes.count)
|
||||
}
|
||||
|
||||
let contentWidth = alertWidth - insets.left - insets.right
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - contentSize.width) / 2.0), y: insets.top), size: contentSize)
|
||||
if let contentComponentView = self.contentView.view {
|
||||
if contentComponentView.superview == nil {
|
||||
self.view.insertSubview(contentComponentView, belowSubview: self.actionNodesSeparator.view)
|
||||
transition.updateFrame(view: contentComponentView, frame: contentFrame)
|
||||
}
|
||||
}
|
||||
|
||||
resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: contentSize.height + actionsHeight + insets.top + insets.bottom)
|
||||
|
||||
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||
var separatorIndex = -1
|
||||
var nodeIndex = 0
|
||||
for actionNode in self.actionNodes {
|
||||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||
case .vertical:
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
if nodeIndex == self.actionNodes.count - 1 {
|
||||
currentActionWidth = resultSize.width - actionOffset
|
||||
} else {
|
||||
currentActionWidth = actionWidth
|
||||
}
|
||||
case .vertical:
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
switch effectiveActionLayout {
|
||||
case .horizontal:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += currentActionWidth
|
||||
case .vertical:
|
||||
actionNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||
actionOffset += actionButtonHeight
|
||||
}
|
||||
|
||||
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||
|
||||
nodeIndex += 1
|
||||
}
|
||||
|
||||
return resultSize
|
||||
}
|
||||
}
|
||||
|
||||
public func componentAlertController(theme: AlertControllerTheme, content: AnyComponent<Empty>, actions: [ComponentAlertAction], actionLayout: ComponentAlertContentActionLayout = .horizontal, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = AlertController(theme: theme, contentNode: ComponentAlertContentNode(theme: theme, content: content, actions: actions.map { action in
|
||||
return ComponentAlertAction(type: action.type, title: action.title, action: {
|
||||
dismissImpl?()
|
||||
action.action()
|
||||
})
|
||||
}, actionLayout: actionLayout, dismissOnOutsideTap: dismissOnOutsideTap))
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
return controller
|
||||
}
|
@ -114,6 +114,7 @@ public final class ButtonTextContentComponent: Component {
|
||||
public let text: String
|
||||
public let badge: Int
|
||||
public let textColor: UIColor
|
||||
public let fontSize: CGFloat
|
||||
public let badgeBackground: UIColor
|
||||
public let badgeForeground: UIColor
|
||||
public let badgeStyle: BadgeStyle
|
||||
@ -124,6 +125,7 @@ public final class ButtonTextContentComponent: Component {
|
||||
text: String,
|
||||
badge: Int,
|
||||
textColor: UIColor,
|
||||
fontSize: CGFloat = 17.0,
|
||||
badgeBackground: UIColor,
|
||||
badgeForeground: UIColor,
|
||||
badgeStyle: BadgeStyle = .round,
|
||||
@ -133,6 +135,7 @@ public final class ButtonTextContentComponent: Component {
|
||||
self.text = text
|
||||
self.badge = badge
|
||||
self.textColor = textColor
|
||||
self.fontSize = fontSize
|
||||
self.badgeBackground = badgeBackground
|
||||
self.badgeForeground = badgeForeground
|
||||
self.badgeStyle = badgeStyle
|
||||
@ -150,6 +153,9 @@ public final class ButtonTextContentComponent: Component {
|
||||
if lhs.textColor != rhs.textColor {
|
||||
return false
|
||||
}
|
||||
if lhs.fontSize != rhs.fontSize {
|
||||
return false
|
||||
}
|
||||
if lhs.badgeBackground != rhs.badgeBackground {
|
||||
return false
|
||||
}
|
||||
@ -202,7 +208,7 @@ public final class ButtonTextContentComponent: Component {
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
text: component.text,
|
||||
font: Font.semibold(17.0),
|
||||
font: Font.semibold(component.fontSize),
|
||||
color: component.textColor
|
||||
)),
|
||||
environment: {},
|
||||
|
@ -1283,14 +1283,14 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
photo.append(photoRepresentation)
|
||||
}
|
||||
let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil)
|
||||
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0)
|
||||
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount.value, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0)
|
||||
|
||||
inputData.set(.single(BotCheckoutController.InputData(
|
||||
form: BotPaymentForm(
|
||||
id: subscriptionFormId,
|
||||
canSaveCredentials: false,
|
||||
passwordMissing: false,
|
||||
invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: subscriptionPricing.amount)], tip: nil, termsInfo: nil, subscriptionPeriod: subscriptionPricing.period),
|
||||
invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: subscriptionPricing.amount.value)], tip: nil, termsInfo: nil, subscriptionPeriod: subscriptionPricing.period),
|
||||
paymentBotId: channel.id,
|
||||
providerId: nil,
|
||||
url: nil,
|
||||
|
@ -25,13 +25,13 @@ private final class BalanceComponent: CombinedComponent {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let balance: Int64?
|
||||
let balance: StarsAmount?
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
balance: Int64?
|
||||
balance: StarsAmount?
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
@ -76,7 +76,7 @@ private final class BalanceComponent: CombinedComponent {
|
||||
|
||||
let balanceText: String
|
||||
if let value = context.component.balance {
|
||||
balanceText = "\(value)"
|
||||
balanceText = "\(value.stringValue)"
|
||||
} else {
|
||||
balanceText = "..."
|
||||
}
|
||||
@ -822,7 +822,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
let myPeer: EnginePeer
|
||||
let messageId: EngineMessage.Id
|
||||
let maxAmount: Int
|
||||
let balance: Int64?
|
||||
let balance: StarsAmount?
|
||||
let currentSentAmount: Int?
|
||||
let topPeers: [ChatSendStarsScreen.TopPeer]
|
||||
let myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||
@ -834,7 +834,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
myPeer: EnginePeer,
|
||||
messageId: EngineMessage.Id,
|
||||
maxAmount: Int,
|
||||
balance: Int64?,
|
||||
balance: StarsAmount?,
|
||||
currentSentAmount: Int?,
|
||||
topPeers: [ChatSendStarsScreen.TopPeer],
|
||||
myTopPeer: ChatSendStarsScreen.TopPeer?,
|
||||
@ -1021,7 +1021,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
|
||||
private var topOffsetDistance: CGFloat?
|
||||
|
||||
private var balance: Int64?
|
||||
private var balance: StarsAmount?
|
||||
|
||||
private var amount: Amount = Amount(realValue: 1, maxRealValue: 1000, maxSliderValue: 1000, isLogarithmic: true)
|
||||
private var didChangeAmount: Bool = false
|
||||
@ -1953,7 +1953,7 @@ private final class ChatSendStarsScreenComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
if balance < self.amount.realValue {
|
||||
if balance < StarsAmount(value: Int64(self.amount.realValue), nanos: 0) {
|
||||
let _ = (component.context.engine.payments.starsTopUpOptions()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] options in
|
||||
@ -2102,7 +2102,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
||||
fileprivate let peer: EnginePeer
|
||||
fileprivate let myPeer: EnginePeer
|
||||
fileprivate let messageId: EngineMessage.Id
|
||||
fileprivate let balance: Int64?
|
||||
fileprivate let balance: StarsAmount?
|
||||
fileprivate let currentSentAmount: Int?
|
||||
fileprivate let topPeers: [ChatSendStarsScreen.TopPeer]
|
||||
fileprivate let myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||
@ -2111,7 +2111,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
||||
peer: EnginePeer,
|
||||
myPeer: EnginePeer,
|
||||
messageId: EngineMessage.Id,
|
||||
balance: Int64?,
|
||||
balance: StarsAmount?,
|
||||
currentSentAmount: Int?,
|
||||
topPeers: [ChatSendStarsScreen.TopPeer],
|
||||
myTopPeer: ChatSendStarsScreen.TopPeer?
|
||||
@ -2240,7 +2240,7 @@ public class ChatSendStarsScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
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<StarsAmount?, NoError>
|
||||
if let starsContext = context.starsContext {
|
||||
balance = starsContext.state
|
||||
|> map { state in
|
||||
|
@ -580,7 +580,7 @@ final class GiftOptionsScreenComponent: Component {
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: presentationStringsFormattedNumber(Int32(self.starsState?.balance ?? 0), environment.dateTimeFormat.groupingSeparator),
|
||||
string: presentationStringsFormattedNumber(self.starsState?.balance ?? StarsAmount.zero, environment.dateTimeFormat.groupingSeparator),
|
||||
font: Font.semibold(14.0),
|
||||
textColor: environment.theme.actionSheet.primaryTextColor
|
||||
)),
|
||||
|
@ -391,7 +391,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
})
|
||||
}
|
||||
|
||||
if starsState.balance < starGift.price {
|
||||
if starsState.balance < StarsAmount(value: starGift.price, nanos: 0) {
|
||||
let _ = (self.optionsPromise.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
@ -405,7 +405,7 @@ final class GiftSetupScreenComponent: Component {
|
||||
options: options ?? [],
|
||||
purpose: .starGift(peerId: component.peerId, requiredStars: starGift.price),
|
||||
completion: { [weak starsContext] stars in
|
||||
starsContext?.add(balance: stars)
|
||||
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
Queue.mainQueue().after(0.1) {
|
||||
proceed()
|
||||
}
|
||||
|
@ -363,6 +363,10 @@ public final class ListSectionComponent: Component {
|
||||
public final class View: UIView {
|
||||
private let contentView: ListSectionContentView
|
||||
|
||||
public var contentViewImpl: UIView {
|
||||
return self.contentView
|
||||
}
|
||||
|
||||
private var header: ComponentView<Empty>?
|
||||
private var footer: ComponentView<Empty>?
|
||||
|
||||
|
@ -34,6 +34,10 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/TelegramUI/Components/Stories/PeerListItemComponent",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
"//submodules/TelegramUI/Components/ToastComponent",
|
||||
"//submodules/AvatarNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -25,6 +25,7 @@ import PeerListItemComponent
|
||||
import TelegramStringFormatting
|
||||
import ContextUI
|
||||
import BalancedTextComponent
|
||||
import AlertComponent
|
||||
|
||||
private func textForTimeout(value: Int32) -> String {
|
||||
if value < 3600 {
|
||||
@ -114,6 +115,8 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
||||
|
||||
private var suggestedStarBotList: TelegramSuggestedStarRefBotList?
|
||||
private var suggestedStarBotListDisposable: Disposable?
|
||||
private var suggestedSortMode: TelegramSuggestedStarRefBotList.SortMode = .date
|
||||
private var isSuggestedSortModeUpdating: Bool = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.scrollView = UIScrollView()
|
||||
@ -160,24 +163,43 @@ final class AffiliateProgramSetupScreenComponent: Component {
|
||||
}
|
||||
|
||||
private func requestApplyProgram() {
|
||||
guard let component = self.component else {
|
||||
guard let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
|
||||
let programPermille: Int32 = Int32(self.commissionPermille)
|
||||
let programDuration: Int32? = self.durationValue == Int(Int32.max) ? nil : Int32(self.durationValue)
|
||||
|
||||
let commissionTitle: String = "\(programPermille / 10)%"
|
||||
let durationTitle: String
|
||||
if let durationMonths = programDuration {
|
||||
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (24 * 60 * 60))
|
||||
} else {
|
||||
durationTitle = "Lifetime"
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
self.environment?.controller()?.present(standardTextAlertController(
|
||||
theme: AlertControllerTheme(presentationData: presentationData),
|
||||
self.environment?.controller()?.present(tableAlert(
|
||||
theme: presentationData.theme,
|
||||
title: "Warning",
|
||||
text: "This change is irreversible. You won't be able to reduce commission or duration. You can only increase these parameters or end the program, which will disable all previously shared referral links.",
|
||||
table: TableComponent(theme: environment.theme, items: [
|
||||
TableComponent.Item(id: 0, title: "Commission", component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: commissionTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor))
|
||||
))),
|
||||
TableComponent.Item(id: 1, title: "Duration", component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: durationTitle, font: Font.regular(17.0), textColor: environment.theme.actionSheet.primaryTextColor))
|
||||
)))
|
||||
]),
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Start", action: { [weak self] in
|
||||
ComponentAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
ComponentAlertAction(type: .defaultAction, title: "Start", action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.applyProgram()
|
||||
})
|
||||
],
|
||||
actionLayout: .horizontal
|
||||
]
|
||||
), in: .window(.root))
|
||||
}
|
||||
|
||||
@ -263,11 +285,19 @@ If you end your affiliate program:
|
||||
program: nil
|
||||
)
|
||||
|> deliverOnMainQueue).startStrict(completed: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, let component = self.component, let controller = self.environment?.controller() else {
|
||||
return
|
||||
}
|
||||
self.isApplying = false
|
||||
self.environment?.controller()?.dismiss()
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
|
||||
if let navigationController = controller.navigationController, let index = navigationController.viewControllers.firstIndex(where: { $0 === controller }), index != 0 {
|
||||
if let previousController = navigationController.viewControllers[index - 1] as? ViewController {
|
||||
previousController.present(UndoOverlayController(presentationData: presentationData, content: .universal(animation: "anim_link_broken", scale: 0.065, colors: [:], title: "Affiliate Program Ended", text: "Participating affiliates have been notified. All referral links will be disabled in 24 hours.", customUndoText: nil, timeout: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
}
|
||||
controller.dismiss()
|
||||
})
|
||||
|
||||
self.state?.updated(transition: .immediate)
|
||||
@ -311,6 +341,121 @@ If you end your affiliate program:
|
||||
}
|
||||
}
|
||||
|
||||
private func openConnectedBot(bot: TelegramConnectedStarRefBotList.Item) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: component.initialContent.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] targetPeer in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let targetPeer else {
|
||||
return
|
||||
}
|
||||
|
||||
self.environment?.controller()?.push(JoinAffiliateProgramScreen(
|
||||
context: component.context,
|
||||
sourcePeer: bot.peer,
|
||||
commissionPermille: bot.commissionPermille,
|
||||
programDuration: bot.durationMonths,
|
||||
mode: .active(JoinAffiliateProgramScreen.Active(
|
||||
targetPeer: targetPeer,
|
||||
link: bot.url,
|
||||
userCount: Int(bot.participants),
|
||||
copyLink: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
UIPasteboard.general.string = bot.url
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
self.environment?.controller()?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: "Link copied to clipboard", text: "Share this link and earn **\(bot.commissionPermille / 10)%** of what people who use it spend in **\(bot.peer.compactDisplayTitle)**!"), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||
}
|
||||
))
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
private func leaveProgram(bot: TelegramConnectedStarRefBotList.Item) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.context.engine.peers.removeConnectedStarRefBot(id: component.initialContent.peerId, link: bot.url)
|
||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let connectedStarBotList = self.connectedStarBotList {
|
||||
var updatedItems = connectedStarBotList.items
|
||||
if let index = updatedItems.firstIndex(where: { $0.peer.id == bot.peer.id }) {
|
||||
updatedItems.remove(at: index)
|
||||
}
|
||||
self.connectedStarBotList = TelegramConnectedStarRefBotList(
|
||||
items: updatedItems,
|
||||
totalCount: connectedStarBotList.totalCount + 1
|
||||
)
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func openSortModeMenu(sourceView: UIView) {
|
||||
guard let component = self.component, let environment = self.environment, let controller = environment.controller() else {
|
||||
return
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
let availableModes: [(TelegramSuggestedStarRefBotList.SortMode, String)] = [
|
||||
(.date, "Date"),
|
||||
(.commission, "Commission")
|
||||
]
|
||||
for (mode, title) in availableModes {
|
||||
let isSelected = mode == self.suggestedSortMode
|
||||
items.append(.action(ContextMenuActionItem(text: title, icon: { theme in
|
||||
if isSelected {
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.actionSheet.primaryTextColor)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if self.suggestedSortMode != mode {
|
||||
self.suggestedSortMode = mode
|
||||
self.isSuggestedSortModeUpdating = true
|
||||
self.state?.updated(transition: .immediate)
|
||||
|
||||
self.suggestedStarBotListDisposable?.dispose()
|
||||
self.suggestedStarBotListDisposable = (component.context.engine.peers.requestSuggestedStarRefBots(
|
||||
id: component.initialContent.peerId,
|
||||
orderByCommission: self.suggestedSortMode == .commission,
|
||||
offset: nil,
|
||||
limit: 100)
|
||||
|> deliverOnMainQueue).startStrict(next: { [weak self] list in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.suggestedStarBotList = list
|
||||
self.isSuggestedSortModeUpdating = false
|
||||
self.state?.updated(transition: .immediate)
|
||||
})
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
let contextController = ContextController(presentationData: presentationData, source: .reference(HeaderContextReferenceContentSource(controller: controller, sourceView: sourceView)), items: .single(ContextController.Items(id: AnyHashable(0), content: .list(items))), gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
func update(component: AffiliateProgramSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
@ -324,7 +469,7 @@ If you end your affiliate program:
|
||||
(12, "1y", "1 YEAR"),
|
||||
(2 * 12, "2y", "2 YEARS"),
|
||||
(3 * 12, "3y", "3 YEARS"),
|
||||
(Int32.max, "∞", "INDEFINITELY")
|
||||
(Int32.max, "∞", "LIFETIME")
|
||||
]
|
||||
|
||||
let environment = environment[EnvironmentType.self].value
|
||||
@ -342,36 +487,60 @@ If you end your affiliate program:
|
||||
switch component.initialContent.mode {
|
||||
case let .editProgram(editProgram):
|
||||
if let currentRefProgram = editProgram.currentRefProgram {
|
||||
self.commissionPermille = Int(currentRefProgram.commissionPermille)
|
||||
let commissionPercentValue = CGFloat(self.commissionPermille) / 1000.0
|
||||
self.commissionSliderValue = (commissionPercentValue - 0.01) / (0.9 - 0.01)
|
||||
|
||||
self.durationValue = Int(currentRefProgram.durationMonths ?? Int32.max)
|
||||
|
||||
self.commissionMinPermille = Int(currentRefProgram.commissionPermille)
|
||||
self.durationMinValue = Int(currentRefProgram.durationMonths ?? Int32.max)
|
||||
|
||||
self.currentProgram = currentRefProgram
|
||||
|
||||
var ignoreCurrentProgram = false
|
||||
if let endDate = currentRefProgram.endDate {
|
||||
self.programEndTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let remainingTime: Int32 = max(0, endDate - timestamp)
|
||||
if remainingTime <= 0 {
|
||||
self.currentProgram = nil
|
||||
self.programEndTimer?.invalidate()
|
||||
self.programEndTimer = nil
|
||||
}
|
||||
|
||||
self.state?.updated(transition: .immediate)
|
||||
})
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let remainingTime: Int32 = max(0, endDate - timestamp)
|
||||
if remainingTime <= 0 {
|
||||
ignoreCurrentProgram = true
|
||||
}
|
||||
}
|
||||
|
||||
if !ignoreCurrentProgram {
|
||||
self.commissionPermille = Int(currentRefProgram.commissionPermille)
|
||||
let commissionPercentValue = CGFloat(self.commissionPermille) / 1000.0
|
||||
self.commissionSliderValue = (commissionPercentValue - 0.01) / (0.9 - 0.01)
|
||||
|
||||
self.durationValue = Int(currentRefProgram.durationMonths ?? Int32.max)
|
||||
|
||||
self.commissionMinPermille = Int(currentRefProgram.commissionPermille)
|
||||
self.durationMinValue = Int(currentRefProgram.durationMonths ?? Int32.max)
|
||||
|
||||
self.currentProgram = currentRefProgram
|
||||
|
||||
if let endDate = currentRefProgram.endDate {
|
||||
self.programEndTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let remainingTime: Int32 = max(0, endDate - timestamp)
|
||||
if remainingTime <= 0 {
|
||||
self.currentProgram = nil
|
||||
self.programEndTimer?.invalidate()
|
||||
self.programEndTimer = nil
|
||||
|
||||
self.commissionSliderValue = 0.0
|
||||
self.commissionPermille = 10
|
||||
self.commissionMinPermille = 10
|
||||
|
||||
self.durationValue = 0
|
||||
self.durationMinValue = 0
|
||||
}
|
||||
|
||||
self.state?.updated(transition: .immediate)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self.commissionPermille = 10
|
||||
self.commissionSliderValue = 0.0
|
||||
self.commissionMinPermille = 10
|
||||
self.durationValue = 10
|
||||
}
|
||||
} else {
|
||||
self.commissionPermille = 10
|
||||
self.commissionSliderValue = 0.0
|
||||
self.commissionMinPermille = 10
|
||||
self.durationValue = 10
|
||||
}
|
||||
@ -919,6 +1088,8 @@ If you end your affiliate program:
|
||||
let timestamp = Int32(Date().timeIntervalSince1970)
|
||||
let remainingTime: Int32 = max(0, endDate - timestamp)
|
||||
buttonText = textForTimeout(value: remainingTime)
|
||||
} else if self.currentProgram != nil {
|
||||
buttonText = "Update Affiliate Program"
|
||||
} else {
|
||||
buttonText = "Start Affiliate Program"
|
||||
}
|
||||
@ -996,7 +1167,7 @@ If you end your affiliate program:
|
||||
} else {
|
||||
durationTitle = "Lifetime"
|
||||
}
|
||||
let subtitle = "\(item.commissionPermille / 10)%, \(durationTitle)"
|
||||
let commissionTitle = "\(item.commissionPermille / 10)%"
|
||||
|
||||
let itemContextAction: (EnginePeer, ContextExtractedContentContainingView, ContextGesture?) -> Void = { [weak self] peer, sourceView, gesture in
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
@ -1065,27 +1236,10 @@ If you end your affiliate program:
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: {
|
||||
guard let self, let component = self.component else {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.context.engine.peers.removeConnectedStarRefBot(id: component.initialContent.peerId, link: item.url)
|
||||
|> deliverOnMainQueue).startStandalone(completed: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let connectedStarBotList = self.connectedStarBotList {
|
||||
var updatedItems = connectedStarBotList.items
|
||||
if let index = updatedItems.firstIndex(where: { $0.peer.id == peer.id }) {
|
||||
updatedItems.remove(at: index)
|
||||
}
|
||||
self.connectedStarBotList = TelegramConnectedStarRefBotList(
|
||||
items: updatedItems,
|
||||
totalCount: connectedStarBotList.totalCount + 1
|
||||
)
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
self.leaveProgram(bot: item)
|
||||
})
|
||||
})))
|
||||
|
||||
@ -1108,21 +1262,43 @@ If you end your affiliate program:
|
||||
style: .generic,
|
||||
sideInset: 0.0,
|
||||
title: item.peer.compactDisplayTitle,
|
||||
avatarComponent: AnyComponent(PeerBadgeAvatarComponent(
|
||||
context: component.context,
|
||||
peer: item.peer,
|
||||
theme: environment.theme,
|
||||
hasBadge: true
|
||||
)),
|
||||
peer: item.peer,
|
||||
subtitle: PeerListItemComponent.Subtitle(text: subtitle, color: .neutral),
|
||||
subtitle: nil,
|
||||
subtitleComponent: AnyComponent(AffiliatePeerSubtitleComponent(
|
||||
theme: environment.theme,
|
||||
percentText: commissionTitle,
|
||||
text: durationTitle
|
||||
)),
|
||||
subtitleAccessory: .none,
|
||||
presence: nil,
|
||||
rightAccessory: .none,
|
||||
rightAccessory: .disclosure,
|
||||
selectionState: .none,
|
||||
hasNext: false,
|
||||
extractedTheme: PeerListItemComponent.ExtractedTheme(
|
||||
inset: 2.0,
|
||||
background: environment.theme.list.itemBlocksBackgroundColor
|
||||
),
|
||||
action: { peer, _, itemView in
|
||||
itemContextAction(peer, itemView.extractedContainerView, nil)
|
||||
insets: UIEdgeInsets(top: -1.0, left: 0.0, bottom: -1.0, right: 0.0),
|
||||
action: { [weak self] peer, _, itemView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openConnectedBot(bot: item)
|
||||
},
|
||||
inlineActions: nil,
|
||||
inlineActions: PeerListItemComponent.InlineActionsState(actions: [
|
||||
PeerListItemComponent.InlineAction(id: 0, title: "Leave", color: .destructive, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.leaveProgram(bot: item)
|
||||
})
|
||||
]),
|
||||
contextAction: { peer, sourceView, gesture in
|
||||
itemContextAction(peer, sourceView, gesture)
|
||||
}
|
||||
@ -1135,7 +1311,7 @@ If you end your affiliate program:
|
||||
theme: environment.theme,
|
||||
header: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "PROGRAMS",
|
||||
string: "MY PROGRAMS",
|
||||
font: Font.regular(13.0),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
@ -1168,60 +1344,13 @@ If you end your affiliate program:
|
||||
do {
|
||||
var suggestedSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
for item in suggestedStarBotListItems {
|
||||
let commissionTitle = "\(item.commissionPermille / 10)%"
|
||||
let durationTitle: String
|
||||
if let durationMonths = item.durationMonths {
|
||||
durationTitle = timeIntervalString(strings: environment.strings, value: durationMonths * (24 * 60 * 60))
|
||||
} else {
|
||||
durationTitle = "Lifetime"
|
||||
}
|
||||
let subtitle = "\(item.commissionPermille / 10)%, \(durationTitle)"
|
||||
|
||||
let itemContextAction: (EnginePeer, ContextExtractedContentContainingView, ContextGesture?) -> Void = { [weak self] peer, sourceView, gesture in
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
|
||||
var itemList: [ContextMenuItem] = []
|
||||
|
||||
itemList.append(.action(ContextMenuActionItem(text: "Join", textColor: .primary, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Add"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
c?.dismiss(completion: {
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
let _ = (component.context.engine.peers.connectStarRefBot(id: component.initialContent.peerId, botId: peer.id)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let connectedStarBotList = self.connectedStarBotList {
|
||||
var updatedItems = connectedStarBotList.items
|
||||
if !updatedItems.contains(where: { $0.peer.id == peer.id }) {
|
||||
updatedItems.insert(result, at: 0)
|
||||
}
|
||||
self.connectedStarBotList = TelegramConnectedStarRefBotList(
|
||||
items: updatedItems,
|
||||
totalCount: connectedStarBotList.totalCount + 1
|
||||
)
|
||||
self.state?.updated(transition: .immediate)
|
||||
}
|
||||
})
|
||||
})
|
||||
})))
|
||||
|
||||
let items = ContextController.Items(content: .list(itemList))
|
||||
|
||||
let controller = ContextController(
|
||||
presentationData: presentationData,
|
||||
source: .extracted(ListContextExtractedContentSource(contentView: sourceView)),
|
||||
items: .single(items),
|
||||
recognizer: nil,
|
||||
gesture: gesture
|
||||
)
|
||||
environment.controller()?.presentInGlobalOverlay(controller, with: nil)
|
||||
}
|
||||
|
||||
suggestedSectionItems.append(AnyComponentWithIdentity(id: item.peer.id, component: AnyComponent(PeerListItemComponent(
|
||||
context: component.context,
|
||||
@ -1230,24 +1359,83 @@ If you end your affiliate program:
|
||||
style: .generic,
|
||||
sideInset: 0.0,
|
||||
title: item.peer.compactDisplayTitle,
|
||||
avatarComponent: AnyComponent(PeerBadgeAvatarComponent(
|
||||
context: component.context,
|
||||
peer: item.peer,
|
||||
theme: environment.theme,
|
||||
hasBadge: false
|
||||
)),
|
||||
peer: item.peer,
|
||||
subtitle: PeerListItemComponent.Subtitle(text: subtitle, color: .neutral),
|
||||
subtitle: nil,
|
||||
subtitleComponent: AnyComponent(AffiliatePeerSubtitleComponent(
|
||||
theme: environment.theme,
|
||||
percentText: commissionTitle,
|
||||
text: durationTitle
|
||||
)),
|
||||
subtitleAccessory: .none,
|
||||
presence: nil,
|
||||
rightAccessory: .none,
|
||||
rightAccessory: .disclosure,
|
||||
selectionState: .none,
|
||||
hasNext: false,
|
||||
extractedTheme: PeerListItemComponent.ExtractedTheme(
|
||||
inset: 2.0,
|
||||
background: environment.theme.list.itemBlocksBackgroundColor
|
||||
),
|
||||
action: { peer, _, itemView in
|
||||
itemContextAction(peer, itemView.extractedContainerView, nil)
|
||||
insets: UIEdgeInsets(top: -1.0, left: 0.0, bottom: -1.0, right: 0.0),
|
||||
action: { [weak self] peer, _, itemView in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (component.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: item.peer.id),
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: component.initialContent.peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] botPeer, targetPeer in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
guard let botPeer, let targetPeer else {
|
||||
return
|
||||
}
|
||||
self.environment?.controller()?.push(JoinAffiliateProgramScreen(
|
||||
context: component.context,
|
||||
sourcePeer: botPeer,
|
||||
commissionPermille: item.commissionPermille,
|
||||
programDuration: item.durationMonths,
|
||||
mode: .join(JoinAffiliateProgramScreen.Join(
|
||||
initialTargetPeer: targetPeer,
|
||||
canSelectTargetPeer: false,
|
||||
completion: { [weak self] _ in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
let _ = (component.context.engine.peers.connectStarRefBot(id: component.initialContent.peerId, botId: peer.id)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let connectedStarBotList = self.connectedStarBotList {
|
||||
var updatedItems = connectedStarBotList.items
|
||||
if !updatedItems.contains(where: { $0.peer.id == peer.id }) {
|
||||
updatedItems.insert(result, at: 0)
|
||||
}
|
||||
self.connectedStarBotList = TelegramConnectedStarRefBotList(
|
||||
items: updatedItems,
|
||||
totalCount: connectedStarBotList.totalCount + 1
|
||||
)
|
||||
self.state?.updated(transition: .immediate)
|
||||
|
||||
self.openConnectedBot(bot: result)
|
||||
}
|
||||
})
|
||||
}
|
||||
))
|
||||
))
|
||||
})
|
||||
},
|
||||
inlineActions: nil,
|
||||
contextAction: { peer, sourceView, gesture in
|
||||
itemContextAction(peer, sourceView, gesture)
|
||||
}
|
||||
contextAction: nil
|
||||
))))
|
||||
}
|
||||
|
||||
@ -1255,14 +1443,27 @@ If you end your affiliate program:
|
||||
transition: transition,
|
||||
component: AnyComponent(ListSectionComponent(
|
||||
theme: environment.theme,
|
||||
header: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "SUGGESTED PROGRAMS",
|
||||
font: Font.regular(13.0),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
header: AnyComponent(HStack([
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "PROGRAMS",
|
||||
font: Font.regular(13.0),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 0
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(BotSectionSortButtonComponent(
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
sortMode: self.suggestedSortMode,
|
||||
action: { [weak self] sourceView in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.openSortModeMenu(sourceView: sourceView)
|
||||
}
|
||||
)))
|
||||
], spacing: 4.0, alignment: .alternatingLeftRight)),
|
||||
footer: nil,
|
||||
items: suggestedSectionItems,
|
||||
displaySeparators: true
|
||||
@ -1271,8 +1472,9 @@ If you end your affiliate program:
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
|
||||
)
|
||||
let suggestedProgramsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: suggestedProgramsSectionSize)
|
||||
if let suggestedProgramsSectionView = self.suggestedProgramsSection.view {
|
||||
if let suggestedProgramsSectionView = self.suggestedProgramsSection.view as? ListSectionComponent.View {
|
||||
if suggestedProgramsSectionView.superview == nil {
|
||||
suggestedProgramsSectionView.contentViewImpl.layer.allowsGroupOpacity = true
|
||||
self.scrollView.addSubview(suggestedProgramsSectionView)
|
||||
}
|
||||
transition.setFrame(view: suggestedProgramsSectionView, frame: suggestedProgramsSectionFrame)
|
||||
@ -1281,6 +1483,9 @@ If you end your affiliate program:
|
||||
} else {
|
||||
suggestedProgramsSectionView.isHidden = true
|
||||
}
|
||||
|
||||
suggestedProgramsSectionView.contentViewImpl.alpha = self.isSuggestedSortModeUpdating ? 0.6 : 1.0
|
||||
suggestedProgramsSectionView.contentViewImpl.isUserInteractionEnabled = !self.isSuggestedSortModeUpdating
|
||||
}
|
||||
if !suggestedStarBotListItems.isEmpty {
|
||||
contentHeight += suggestedProgramsSectionSize.height
|
||||
@ -1440,3 +1645,17 @@ private final class ListContextExtractedContentSource: ContextExtractedContentSo
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||
private let controller: ViewController
|
||||
private let sourceView: UIView
|
||||
|
||||
init(controller: ViewController, sourceView: UIView) {
|
||||
self.controller = controller
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,246 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
import AlertComponent
|
||||
|
||||
final class TableComponent: CombinedComponent {
|
||||
class Item: Equatable {
|
||||
public let id: AnyHashable
|
||||
public let title: String
|
||||
public let component: AnyComponent<Empty>
|
||||
public let insets: UIEdgeInsets?
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String, component: AnyComponent<Empty>, insets: UIEdgeInsets? = nil) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.component = component
|
||||
self.insets = insets
|
||||
}
|
||||
|
||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let theme: PresentationTheme
|
||||
private let items: [Item]
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item]) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedBorderImage: (UIImage, PresentationTheme)?
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
public static var body: Body {
|
||||
let leftColumnBackground = Child(Rectangle.self)
|
||||
let verticalBorder = Child(Rectangle.self)
|
||||
let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let outerBorder = Child(Image.self)
|
||||
|
||||
return { context in
|
||||
let verticalPadding: CGFloat = 11.0
|
||||
let horizontalPadding: CGFloat = 12.0
|
||||
let borderWidth: CGFloat = 1.0
|
||||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
var updatedTitleChildren: [_UpdatedChildComponent] = []
|
||||
var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = []
|
||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = titleChildren[item.id].update(
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: item.title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
updatedTitleChildren.append(titleChild)
|
||||
|
||||
if titleChild.size.width > leftColumnWidth {
|
||||
leftColumnWidth = titleChild.size.width
|
||||
}
|
||||
}
|
||||
|
||||
leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0)
|
||||
let rightColumnWidth = context.availableSize.width - leftColumnWidth
|
||||
|
||||
var i = 0
|
||||
var rowHeights: [Int: CGFloat] = [:]
|
||||
var totalHeight: CGFloat = 0.0
|
||||
|
||||
for item in context.component.items {
|
||||
let titleChild = updatedTitleChildren[i]
|
||||
|
||||
let insets: UIEdgeInsets
|
||||
if let customInsets = item.insets {
|
||||
insets = customInsets
|
||||
} else {
|
||||
insets = UIEdgeInsets(top: 0.0, left: horizontalPadding, bottom: 0.0, right: horizontalPadding)
|
||||
}
|
||||
let valueChild = valueChildren[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: rightColumnWidth - insets.left - insets.right, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedValueChildren.append((valueChild, insets))
|
||||
|
||||
let rowHeight = max(40.0, max(titleChild.size.height, valueChild.size.height) + verticalPadding * 2.0)
|
||||
rowHeights[i] = rowHeight
|
||||
totalHeight += rowHeight
|
||||
|
||||
if i < context.component.items.count - 1 {
|
||||
let borderChild = borderChildren[item.id].update(
|
||||
component: AnyComponent(Rectangle(color: borderColor)),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: borderWidth),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedBorderChildren.append(borderChild)
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
let leftColumnBackground = leftColumnBackground.update(
|
||||
component: Rectangle(color: context.component.theme.list.itemInputField.backgroundColor),
|
||||
availableSize: CGSize(width: leftColumnWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
leftColumnBackground
|
||||
.position(CGPoint(x: leftColumnWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let borderImage: UIImage
|
||||
if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme {
|
||||
borderImage = currentImage
|
||||
} else {
|
||||
let borderRadius: CGFloat = 5.0
|
||||
borderImage = generateImage(CGSize(width: 16.0, height: 16.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil)
|
||||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(borderColor.cgColor)
|
||||
context.setLineWidth(borderWidth)
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})!.stretchableImage(withLeftCapWidth: 5, topCapHeight: 5)
|
||||
context.state.cachedBorderImage = (borderImage, context.component.theme)
|
||||
}
|
||||
|
||||
let outerBorder = outerBorder.update(
|
||||
component: Image(image: borderImage),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(outerBorder
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let verticalBorder = verticalBorder.update(
|
||||
component: Rectangle(color: borderColor),
|
||||
availableSize: CGSize(width: borderWidth, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
verticalBorder
|
||||
.position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
i = 0
|
||||
var originY: CGFloat = 0.0
|
||||
for (titleChild, (valueChild, valueInsets)) in zip(updatedTitleChildren, updatedValueChildren) {
|
||||
let rowHeight = rowHeights[i] ?? 0.0
|
||||
|
||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
||||
let valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + valueInsets.left, y: originY + verticalPadding), size: valueChild.size)
|
||||
|
||||
context.add(titleChild
|
||||
.position(titleFrame.center)
|
||||
)
|
||||
|
||||
context.add(valueChild
|
||||
.position(valueFrame.center)
|
||||
)
|
||||
|
||||
if i < updatedBorderChildren.count {
|
||||
let borderChild = updatedBorderChildren[i]
|
||||
context.add(borderChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
originY += rowHeight
|
||||
i += 1
|
||||
}
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: totalHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableAlert(theme: PresentationTheme, title: String, text: String, table: TableComponent, actions: [ComponentAlertAction]) -> ViewController {
|
||||
let content: AnyComponent<Empty> = AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: title, font: Font.semibold(17.0), textColor: theme.actionSheet.primaryTextColor)),
|
||||
horizontalAlignment: .center
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: text, font: Font.regular(17.0), textColor: theme.actionSheet.primaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 2, component: AnyComponent(table)),
|
||||
], spacing: 10.0))
|
||||
return componentAlertController(
|
||||
theme: AlertControllerTheme(presentationTheme: theme, fontSize: .regular),
|
||||
content: content,
|
||||
actions: actions,
|
||||
actionLayout: .horizontal
|
||||
)
|
||||
}
|
@ -993,8 +993,8 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p
|
||||
}))
|
||||
if let starsState = data.starsState {
|
||||
let balanceText: String
|
||||
if starsState.balance > 0 {
|
||||
balanceText = presentationStringsFormattedNumber(Int32(starsState.balance), presentationData.dateTimeFormat.groupingSeparator)
|
||||
if starsState.balance > StarsAmount.zero {
|
||||
balanceText = presentationStringsFormattedNumber(starsState.balance, presentationData.dateTimeFormat.groupingSeparator)
|
||||
} else {
|
||||
balanceText = ""
|
||||
}
|
||||
@ -1514,10 +1514,10 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
let revenueBalance = data.revenueStatsState?.balances.currentBalance ?? 0
|
||||
let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue ?? 0
|
||||
|
||||
let starsBalance = data.starsRevenueStatsState?.balances.currentBalance ?? 0
|
||||
let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue ?? 0
|
||||
let starsBalance = data.starsRevenueStatsState?.balances.currentBalance ?? StarsAmount.zero
|
||||
let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue ?? StarsAmount.zero
|
||||
|
||||
if overallRevenueBalance > 0 || overallStarsBalance > 0 {
|
||||
if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero {
|
||||
items[.balances]!.append(PeerInfoScreenHeaderItem(id: 20, text: presentationData.strings.PeerInfo_BotBalance_Title))
|
||||
if overallRevenueBalance > 0 {
|
||||
let string = "*\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
@ -1531,8 +1531,8 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
}))
|
||||
}
|
||||
|
||||
if overallStarsBalance > 0 {
|
||||
let string = "*\(presentationStringsFormattedNumber(Int32(starsBalance), presentationData.dateTimeFormat.groupingSeparator))"
|
||||
if overallStarsBalance > StarsAmount.zero {
|
||||
let string = "*\(presentationStringsFormattedNumber(starsBalance, presentationData.dateTimeFormat.groupingSeparator))"
|
||||
let attributedString = NSMutableAttributedString(string: string, font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
if let range = attributedString.string.range(of: "*") {
|
||||
attributedString.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: NSRange(range, in: attributedString.string))
|
||||
@ -1765,21 +1765,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese
|
||||
|
||||
if cachedData.flags.contains(.canViewRevenue) || cachedData.flags.contains(.canViewStarsRevenue) {
|
||||
let revenueBalance = data.revenueStatsState?.balances.currentBalance ?? 0
|
||||
let starsBalance = data.starsRevenueStatsState?.balances.currentBalance ?? 0
|
||||
let starsBalance = data.starsRevenueStatsState?.balances.currentBalance ?? StarsAmount.zero
|
||||
|
||||
let overallRevenueBalance = data.revenueStatsState?.balances.overallRevenue ?? 0
|
||||
let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue ?? 0
|
||||
let overallStarsBalance = data.starsRevenueStatsState?.balances.overallRevenue ?? StarsAmount.zero
|
||||
|
||||
if overallRevenueBalance > 0 || overallStarsBalance > 0 {
|
||||
if overallRevenueBalance > 0 || overallStarsBalance > StarsAmount.zero {
|
||||
var string = ""
|
||||
if overallRevenueBalance > 0 {
|
||||
string.append("#\(formatTonAmountText(revenueBalance, dateTimeFormat: presentationData.dateTimeFormat))")
|
||||
}
|
||||
if overallStarsBalance > 0 {
|
||||
if overallStarsBalance > StarsAmount.zero {
|
||||
if !string.isEmpty {
|
||||
string.append(" ")
|
||||
}
|
||||
string.append("*\(presentationStringsFormattedNumber(Int32(starsBalance), presentationData.dateTimeFormat.groupingSeparator))")
|
||||
string.append("*\(presentationStringsFormattedNumber(starsBalance, presentationData.dateTimeFormat.groupingSeparator))")
|
||||
}
|
||||
let attributedString = NSMutableAttributedString(string: string, font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize), textColor: presentationData.theme.list.itemSecondaryTextColor)
|
||||
if let range = attributedString.string.range(of: "#") {
|
||||
|
@ -200,16 +200,16 @@ public final class PlainButtonComponent: Component {
|
||||
transition: component.animateContents ? transition : transition.withAnimation(.none),
|
||||
component: component.content,
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
containerSize: CGSize(width: availableSize.width - component.contentInsets.left - component.contentInsets.right, height: availableSize.height - component.contentInsets.top - component.contentInsets.bottom)
|
||||
)
|
||||
|
||||
var size = contentSize
|
||||
size.width += component.contentInsets.left + component.contentInsets.right
|
||||
size.height += component.contentInsets.top + component.contentInsets.bottom
|
||||
if let minSize = component.minSize {
|
||||
size.width = max(size.width, minSize.width)
|
||||
size.height = max(size.height, minSize.height)
|
||||
}
|
||||
size.width += component.contentInsets.left + component.contentInsets.right
|
||||
size.height += component.contentInsets.top + component.contentInsets.bottom
|
||||
|
||||
if let contentView = self.content.view {
|
||||
var contentTransition = transition
|
||||
|
@ -77,7 +77,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
|
||||
let context: AccountContext
|
||||
let externalState: ExternalState
|
||||
let containerSize: CGSize
|
||||
let balance: Int64?
|
||||
let balance: StarsAmount?
|
||||
let options: [Any]
|
||||
let purpose: StarsPurchasePurpose
|
||||
let selectedProductId: String?
|
||||
@ -93,7 +93,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
|
||||
context: AccountContext,
|
||||
externalState: ExternalState,
|
||||
containerSize: CGSize,
|
||||
balance: Int64?,
|
||||
balance: StarsAmount?,
|
||||
options: [Any],
|
||||
purpose: StarsPurchasePurpose,
|
||||
selectedProductId: String?,
|
||||
@ -297,17 +297,17 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
|
||||
if let products = state.products, let balance = context.component.balance {
|
||||
var minimumCount: Int64?
|
||||
var minimumCount: StarsAmount?
|
||||
if let requiredStars = context.component.purpose.requiredStars {
|
||||
if case .generic = context.component.purpose {
|
||||
minimumCount = requiredStars
|
||||
minimumCount = StarsAmount(value: requiredStars, nanos: 0)
|
||||
} else {
|
||||
minimumCount = requiredStars - balance
|
||||
minimumCount = StarsAmount(value: requiredStars, nanos: 0) - balance
|
||||
}
|
||||
}
|
||||
|
||||
for product in products {
|
||||
if let minimumCount, minimumCount > product.count && !(items.isEmpty && product.id == products.last?.id) {
|
||||
if let minimumCount, minimumCount > StarsAmount(value: product.count, nanos: 0) && !(items.isEmpty && product.id == products.last?.id) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -847,10 +847,11 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
let starsBalance: StarsAmount = state.starsState?.balance ?? StarsAmount.zero
|
||||
let balanceValue = balanceValue.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: presentationStringsFormattedNumber(Int32(state.starsState?.balance ?? 0), environment.dateTimeFormat.groupingSeparator),
|
||||
string: presentationStringsFormattedNumber(starsBalance, environment.dateTimeFormat.groupingSeparator),
|
||||
font: Font.semibold(14.0),
|
||||
textColor: environment.theme.actionSheet.primaryTextColor
|
||||
)),
|
||||
|
@ -207,7 +207,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
var statusText: String?
|
||||
var statusIsDestructive = false
|
||||
|
||||
let count: Int64
|
||||
let count: StarsAmount
|
||||
var countIsGeneric = false
|
||||
var countOnTop = false
|
||||
var transactionId: String?
|
||||
@ -243,14 +243,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
titleText = strings.Stars_Transaction_Giveaway_Boost_Stars(Int32(stars))
|
||||
descriptionText = ""
|
||||
boostsText = strings.Stars_Transaction_Giveaway_Boost_Boosts(boosts)
|
||||
count = stars
|
||||
count = StarsAmount(value: stars, nanos: 0)
|
||||
date = boost.date
|
||||
toPeer = state.peerMap[peerId]
|
||||
// toString = strings.Stars_Transaction_Giveaway_Boost_Subscribers(boost.quantity)
|
||||
giveawayMessageId = boost.giveawayMessageId
|
||||
isBoost = true
|
||||
case let .importer(peer, pricing, importer, usdRate):
|
||||
let usdValue = formatTonUsdValue(pricing.amount, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat)
|
||||
let usdValue = formatTonUsdValue(pricing.amount.value, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat)
|
||||
titleText = strings.Stars_Transaction_Subscription_Title
|
||||
descriptionText = strings.Stars_Transaction_Subscription_PerMonthUsd(usdValue).string
|
||||
count = pricing.amount
|
||||
@ -499,7 +499,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
case let .receipt(receipt):
|
||||
titleText = receipt.invoiceMedia.title
|
||||
descriptionText = receipt.invoiceMedia.description
|
||||
count = (receipt.invoice.prices.first?.amount ?? receipt.invoiceMedia.totalAmount) * -1
|
||||
count = StarsAmount(value: (receipt.invoice.prices.first?.amount ?? receipt.invoiceMedia.totalAmount) * -1, nanos: 0)
|
||||
transactionId = receipt.transactionId
|
||||
date = receipt.date
|
||||
if let peer = state.peerMap[receipt.botPaymentId] {
|
||||
@ -516,7 +516,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
if case let .giftStars(_, _, countValue, _, _, _) = action.action {
|
||||
titleText = incoming ? strings.Stars_Gift_Received_Title : strings.Stars_Gift_Sent_Title
|
||||
|
||||
count = countValue
|
||||
count = StarsAmount(value: countValue, nanos: 0)
|
||||
if !incoming {
|
||||
countIsGeneric = true
|
||||
}
|
||||
@ -530,7 +530,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
} else if case let .prizeStars(countValue, _, boostPeerId, _, giveawayMessageIdValue) = action.action {
|
||||
titleText = strings.Stars_Transaction_Giveaway_Title
|
||||
|
||||
count = countValue
|
||||
count = StarsAmount(value: countValue, nanos: 0)
|
||||
countOnTop = true
|
||||
transactionId = nil
|
||||
giveawayMessageId = giveawayMessageIdValue
|
||||
@ -561,7 +561,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
descriptionText = modifiedString
|
||||
}
|
||||
|
||||
let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.groupingSeparator)
|
||||
let absCount = StarsAmount(value: abs(count.value), nanos: abs(count.nanos))
|
||||
let formattedAmount = presentationStringsFormattedNumber(absCount, dateTimeFormat.groupingSeparator)
|
||||
let countColor: UIColor
|
||||
var countFont: UIFont = isSubscription || isSubscriber ? Font.regular(17.0) : Font.semibold(17.0)
|
||||
var countBackgroundColor: UIColor?
|
||||
@ -576,7 +577,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
} else if countIsGeneric {
|
||||
amountText = "\(formattedAmount)"
|
||||
countColor = theme.list.itemPrimaryTextColor
|
||||
} else if count < 0 {
|
||||
} else if count < StarsAmount.zero {
|
||||
amountText = "- \(formattedAmount)"
|
||||
countColor = theme.list.itemDestructiveColor
|
||||
} else {
|
||||
@ -602,7 +603,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let imageSubject: StarsImageComponent.Subject
|
||||
var imageIcon: StarsImageComponent.Icon?
|
||||
if isGift {
|
||||
imageSubject = .gift(count)
|
||||
imageSubject = .gift(count.value)
|
||||
} else if !media.isEmpty {
|
||||
imageSubject = .media(media)
|
||||
} else if let photo {
|
||||
@ -734,7 +735,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
} else if isSubscriber {
|
||||
title = strings.Stars_Transaction_Subscription_Subscriber
|
||||
} else {
|
||||
title = count < 0 || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
||||
title = count < StarsAmount.zero || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
||||
}
|
||||
tableItems.append(.init(
|
||||
id: "to",
|
||||
@ -788,7 +789,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
id: "prize",
|
||||
title: strings.Stars_Transaction_Giveaway_Prize,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Stars_Transaction_Giveaway_Stars(Int32(count)), font: tableFont, textColor: tableTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Stars_Transaction_Giveaway_Stars(Int32(count.value)), font: tableFont, textColor: tableTextColor)))
|
||||
)
|
||||
))
|
||||
|
||||
|
@ -10,12 +10,13 @@ import PresentationDataUtils
|
||||
import ButtonComponent
|
||||
import BundleIconComponent
|
||||
import TelegramStringFormatting
|
||||
import TelegramCore
|
||||
|
||||
final class StarsBalanceComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let count: Int64
|
||||
let count: StarsAmount
|
||||
let rate: Double?
|
||||
let actionTitle: String
|
||||
let actionAvailable: Bool
|
||||
@ -29,7 +30,7 @@ final class StarsBalanceComponent: Component {
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
count: Int64,
|
||||
count: StarsAmount,
|
||||
rate: Double?,
|
||||
actionTitle: String,
|
||||
actionAvailable: Bool,
|
||||
@ -139,7 +140,7 @@ final class StarsBalanceComponent: Component {
|
||||
let sideInset: CGFloat = 16.0
|
||||
var contentHeight: CGFloat = sideInset
|
||||
|
||||
let balanceString = presentationStringsFormattedNumber(Int32(component.count), component.dateTimeFormat.groupingSeparator)
|
||||
let balanceString = presentationStringsFormattedNumber(component.count, component.dateTimeFormat.groupingSeparator)
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
@ -168,7 +169,7 @@ final class StarsBalanceComponent: Component {
|
||||
|
||||
let subtitleText: String
|
||||
if let rate = component.rate {
|
||||
subtitleText = "≈\(formatTonUsdValue(component.count, divide: false, rate: rate, dateTimeFormat: component.dateTimeFormat))"
|
||||
subtitleText = "≈\(formatTonUsdValue(component.count.value, divide: false, rate: rate, dateTimeFormat: component.dateTimeFormat))"
|
||||
} else {
|
||||
subtitleText = component.strings.Stars_Intro_YourBalance
|
||||
}
|
||||
|
@ -8,19 +8,20 @@ import MultilineTextComponent
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import TelegramStringFormatting
|
||||
import TelegramCore
|
||||
|
||||
final class StarsOverviewItemComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let title: String
|
||||
let value: Int64
|
||||
let value: StarsAmount
|
||||
let rate: Double
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
title: String,
|
||||
value: Int64,
|
||||
value: StarsAmount,
|
||||
rate: Double
|
||||
) {
|
||||
self.theme = theme
|
||||
@ -80,8 +81,8 @@ final class StarsOverviewItemComponent: Component {
|
||||
valueOffset += icon.size.width
|
||||
}
|
||||
|
||||
let valueString = presentationStringsFormattedNumber(Int32(component.value), component.dateTimeFormat.groupingSeparator)
|
||||
let usdValueString = formatTonUsdValue(component.value, divide: false, rate: component.rate, dateTimeFormat: component.dateTimeFormat)
|
||||
let valueString = presentationStringsFormattedNumber(component.value, component.dateTimeFormat.groupingSeparator)
|
||||
let usdValueString = formatTonUsdValue(component.value.value, divide: false, rate: component.rate, dateTimeFormat: component.dateTimeFormat)
|
||||
|
||||
let valueSize = self.value.update(
|
||||
transition: .immediate,
|
||||
|
@ -519,21 +519,21 @@ final class StarsStatisticsScreenComponent: Component {
|
||||
theme: environment.theme,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
title: strings.Stars_BotRevenue_Proceeds_Available,
|
||||
value: starsState?.balances.availableBalance ?? 0,
|
||||
value: starsState?.balances.availableBalance ?? StarsAmount.zero,
|
||||
rate: starsState?.usdRate ?? 0.0
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(StarsOverviewItemComponent(
|
||||
theme: environment.theme,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
title: strings.Stars_BotRevenue_Proceeds_Current,
|
||||
value: starsState?.balances.currentBalance ?? 0,
|
||||
value: starsState?.balances.currentBalance ?? StarsAmount.zero,
|
||||
rate: starsState?.usdRate ?? 0.0
|
||||
))),
|
||||
AnyComponentWithIdentity(id: 2, component: AnyComponent(StarsOverviewItemComponent(
|
||||
theme: environment.theme,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
title: strings.Stars_BotRevenue_Proceeds_Total,
|
||||
value: starsState?.balances.overallRevenue ?? 0,
|
||||
value: starsState?.balances.overallRevenue ?? StarsAmount.zero,
|
||||
rate: starsState?.usdRate ?? 0.0
|
||||
)))
|
||||
],
|
||||
@ -602,7 +602,7 @@ final class StarsStatisticsScreenComponent: Component {
|
||||
theme: environment.theme,
|
||||
strings: strings,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
count: self.starsState?.balances.availableBalance ?? 0,
|
||||
count: self.starsState?.balances.availableBalance ?? StarsAmount.zero,
|
||||
rate: self.starsState?.usdRate ?? 0,
|
||||
actionTitle: strings.Stars_BotRevenue_Withdraw_Withdraw,
|
||||
actionAvailable: true,
|
||||
|
@ -20,7 +20,7 @@ import LottieComponent
|
||||
|
||||
private extension StarsContext.State.Transaction {
|
||||
var extendedId: String {
|
||||
if self.count > 0 {
|
||||
if self.count > StarsAmount.zero {
|
||||
return "\(id)_in"
|
||||
} else {
|
||||
return "\(id)_out"
|
||||
@ -304,7 +304,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
case let .peer(peer):
|
||||
if let starGift = item.starGift {
|
||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||
itemSubtitle = item.count > 0 ? environment.strings.Stars_Intro_Transaction_ConvertedGift : environment.strings.Stars_Intro_Transaction_Gift
|
||||
itemSubtitle = item.count > StarsAmount.zero ? environment.strings.Stars_Intro_Transaction_ConvertedGift : environment.strings.Stars_Intro_Transaction_Gift
|
||||
itemFile = starGift.file
|
||||
} else if let _ = item.giveawayMessageId {
|
||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||
@ -344,7 +344,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Subtitle
|
||||
}
|
||||
} else {
|
||||
if item.count > 0 && !item.flags.contains(.isRefund) {
|
||||
if item.count > StarsAmount.zero && !item.flags.contains(.isRefund) {
|
||||
itemTitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Title
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Subtitle
|
||||
} else {
|
||||
@ -373,8 +373,9 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
let itemLabel: NSAttributedString
|
||||
let labelString: String
|
||||
|
||||
let formattedLabel = presentationStringsFormattedNumber(abs(Int32(item.count)), environment.dateTimeFormat.groupingSeparator)
|
||||
if item.count < 0 {
|
||||
let absCount = StarsAmount(value: abs(item.count.value), nanos: abs(item.count.nanos))
|
||||
let formattedLabel = presentationStringsFormattedNumber(absCount, environment.dateTimeFormat.groupingSeparator)
|
||||
if item.count < StarsAmount.zero {
|
||||
labelString = "- \(formattedLabel)"
|
||||
} else {
|
||||
labelString = "+ \(formattedLabel)"
|
||||
|
@ -127,7 +127,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
private var stateDisposable: Disposable?
|
||||
private var starsState: StarsContext.State?
|
||||
|
||||
private var previousBalance: Int64?
|
||||
private var previousBalance: StarsAmount?
|
||||
|
||||
private var subscriptionsStateDisposable: Disposable?
|
||||
private var subscriptionsState: StarsSubscriptionsContext.State?
|
||||
@ -527,7 +527,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: presentationStringsFormattedNumber(Int32(self.starsState?.balance ?? 0), environment.dateTimeFormat.groupingSeparator),
|
||||
string: presentationStringsFormattedNumber(self.starsState?.balance ?? StarsAmount.zero, environment.dateTimeFormat.groupingSeparator),
|
||||
font: Font.semibold(14.0),
|
||||
textColor: environment.theme.actionSheet.primaryTextColor
|
||||
)),
|
||||
@ -611,7 +611,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
dateTimeFormat: environment.dateTimeFormat,
|
||||
count: self.starsState?.balance ?? 0,
|
||||
count: self.starsState?.balance ?? StarsAmount.zero,
|
||||
rate: nil,
|
||||
actionTitle: environment.strings.Stars_Intro_Buy,
|
||||
actionAvailable: !premiumConfiguration.areStarsDisabled,
|
||||
@ -1091,7 +1091,7 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.starsContext.add(balance: stars)
|
||||
self.starsContext.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let resultController = UndoOverlayController(
|
||||
|
@ -80,7 +80,7 @@ private final class SheetContent: CombinedComponent {
|
||||
private(set) var chatPeer: EnginePeer?
|
||||
private(set) var authorPeer: EnginePeer?
|
||||
private var peerDisposable: Disposable?
|
||||
private(set) var balance: Int64?
|
||||
private(set) var balance: StarsAmount?
|
||||
private(set) var form: BotPaymentForm?
|
||||
private(set) var navigateToPeer: (EnginePeer) -> Void
|
||||
|
||||
@ -129,14 +129,14 @@ private final class SheetContent: CombinedComponent {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.balance = inputData?.0.balance ?? 0
|
||||
self.balance = inputData?.0.balance ?? StarsAmount.zero
|
||||
self.form = inputData?.1
|
||||
self.botPeer = inputData?.2
|
||||
self.chatPeer = chatPeer
|
||||
self.authorPeer = inputData?.3
|
||||
self.updated(transition: .immediate)
|
||||
|
||||
if self.optionsDisposable == nil, let balance = self.balance, balance < self.invoice.totalAmount {
|
||||
if self.optionsDisposable == nil, let balance = self.balance, balance < StarsAmount(value: self.invoice.totalAmount, nanos: 0) {
|
||||
self.optionsDisposable = (context.engine.payments.starsTopUpOptions()
|
||||
|> deliverOnMainQueue).start(next: { [weak self] options in
|
||||
guard let self else {
|
||||
@ -206,7 +206,7 @@ private final class SheetContent: CombinedComponent {
|
||||
})
|
||||
}
|
||||
|
||||
if balance < self.invoice.totalAmount {
|
||||
if balance < StarsAmount(value: self.invoice.totalAmount, nanos: 0) {
|
||||
if self.options.isEmpty {
|
||||
self.inProgress = true
|
||||
self.updated()
|
||||
@ -236,7 +236,7 @@ private final class SheetContent: CombinedComponent {
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
Queue.mainQueue().after(0.1, { [weak self] in
|
||||
if let self, let balance = self.balance, balance < self.invoice.totalAmount {
|
||||
if let self, let balance = self.balance, balance < StarsAmount(value: self.invoice.totalAmount, nanos: 0) {
|
||||
self.inProgress = false
|
||||
self.updated()
|
||||
|
||||
@ -272,7 +272,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let balanceValue = Child(MultilineTextComponent.self)
|
||||
let balanceIcon = Child(BundleIconComponent.self)
|
||||
let info = Child(BalancedTextComponent.self)
|
||||
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
let component = context.component
|
||||
@ -501,7 +501,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let balanceValue = balanceValue.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: presentationStringsFormattedNumber(Int32(state.balance ?? 0), environment.dateTimeFormat.groupingSeparator),
|
||||
string: presentationStringsFormattedNumber(state.balance ?? StarsAmount.zero, environment.dateTimeFormat.groupingSeparator),
|
||||
font: Font.semibold(16.0),
|
||||
textColor: textColor
|
||||
)),
|
||||
@ -586,7 +586,7 @@ private final class SheetContent: CombinedComponent {
|
||||
options: state?.options ?? [],
|
||||
purpose: purpose,
|
||||
completion: { [weak starsContext] stars in
|
||||
starsContext?.add(balance: stars)
|
||||
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
|
||||
Queue.mainQueue().after(0.1) {
|
||||
completion()
|
||||
}
|
||||
@ -650,7 +650,6 @@ private final class SheetContent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: contentSize.height + button.size.height / 2.0))
|
||||
)
|
||||
contentSize.height += button.size.height
|
||||
|
||||
if isSubscription {
|
||||
contentSize.height += 14.0
|
||||
|
||||
|
@ -102,8 +102,8 @@ private final class SheetContent: CombinedComponent {
|
||||
let amountPlaceholder: String
|
||||
let amountLabel: String?
|
||||
|
||||
let minAmount: Int64?
|
||||
let maxAmount: Int64?
|
||||
let minAmount: StarsAmount?
|
||||
let maxAmount: StarsAmount?
|
||||
|
||||
let configuration = StarsWithdrawConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
@ -113,7 +113,7 @@ private final class SheetContent: CombinedComponent {
|
||||
amountTitle = environment.strings.Stars_Withdraw_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_Withdraw_AmountPlaceholder
|
||||
|
||||
minAmount = configuration.minWithdrawAmount
|
||||
minAmount = configuration.minWithdrawAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
maxAmount = status.balances.availableBalance
|
||||
amountLabel = nil
|
||||
case .paidMedia:
|
||||
@ -121,13 +121,13 @@ private final class SheetContent: CombinedComponent {
|
||||
amountTitle = environment.strings.Stars_PaidContent_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_PaidContent_AmountPlaceholder
|
||||
|
||||
minAmount = 1
|
||||
maxAmount = configuration.maxPaidMediaAmount
|
||||
minAmount = StarsAmount(value: 1, nanos: 0)
|
||||
maxAmount = configuration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
|
||||
var usdRate = 0.012
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate, let amount = state.amount, amount > 0 {
|
||||
if let usdWithdrawRate = configuration.usdWithdrawRate, let amount = state.amount, amount > StarsAmount.zero {
|
||||
usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
amountLabel = "≈\(formatTonUsdValue(amount, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||
amountLabel = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||
} else {
|
||||
amountLabel = nil
|
||||
}
|
||||
@ -136,8 +136,8 @@ private final class SheetContent: CombinedComponent {
|
||||
amountTitle = environment.strings.Stars_SendStars_AmountTitle
|
||||
amountPlaceholder = environment.strings.Stars_SendStars_AmountPlaceholder
|
||||
|
||||
minAmount = 1
|
||||
maxAmount = configuration.maxPaidMediaAmount
|
||||
minAmount = StarsAmount(value: 1, nanos: 0)
|
||||
maxAmount = configuration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
amountLabel = nil
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ private final class SheetContent: CombinedComponent {
|
||||
contentSize.height += title.size.height
|
||||
contentSize.height += 40.0
|
||||
|
||||
let balance: Int64?
|
||||
let balance: StarsAmount?
|
||||
if case .reaction = component.mode {
|
||||
balance = state.balance
|
||||
} else if case let .withdraw(starsState) = component.mode {
|
||||
@ -177,7 +177,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let balanceValue = balanceValue.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: presentationStringsFormattedNumber(Int32(balance), environment.dateTimeFormat.groupingSeparator),
|
||||
string: presentationStringsFormattedNumber(balance, environment.dateTimeFormat.groupingSeparator),
|
||||
font: Font.semibold(16.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
@ -246,7 +246,6 @@ private final class SheetContent: CombinedComponent {
|
||||
default:
|
||||
amountFooter = nil
|
||||
}
|
||||
|
||||
let amountSection = amountSection.update(
|
||||
component: ListSectionComponent(
|
||||
theme: theme,
|
||||
@ -267,13 +266,13 @@ private final class SheetContent: CombinedComponent {
|
||||
textColor: theme.list.itemPrimaryTextColor,
|
||||
secondaryColor: theme.list.itemSecondaryTextColor,
|
||||
placeholderColor: theme.list.itemPlaceholderTextColor,
|
||||
value: state.amount,
|
||||
minValue: minAmount,
|
||||
maxValue: maxAmount,
|
||||
value: state.amount?.value,
|
||||
minValue: minAmount?.value,
|
||||
maxValue: maxAmount?.value,
|
||||
placeholderText: amountPlaceholder,
|
||||
labelText: amountLabel,
|
||||
amountUpdated: { [weak state] amount in
|
||||
state?.amount = amount
|
||||
state?.amount = amount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
state?.updated()
|
||||
},
|
||||
tag: amountTag
|
||||
@ -298,7 +297,7 @@ private final class SheetContent: CombinedComponent {
|
||||
if case .paidMedia = component.mode {
|
||||
buttonString = environment.strings.Stars_PaidContent_Create
|
||||
} else if let amount = state.amount {
|
||||
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(Int32(amount), environment.dateTimeFormat.groupingSeparator))"
|
||||
buttonString = "\(environment.strings.Stars_Withdraw_Withdraw) # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
|
||||
} else {
|
||||
buttonString = environment.strings.Stars_Withdraw_Withdraw
|
||||
}
|
||||
@ -326,14 +325,14 @@ private final class SheetContent: CombinedComponent {
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
||||
),
|
||||
isEnabled: (state.amount ?? 0) > 0,
|
||||
isEnabled: (state.amount ?? StarsAmount.zero) > StarsAmount.zero,
|
||||
displaysProgress: false,
|
||||
action: { [weak state] in
|
||||
if let controller = controller() as? StarsWithdrawScreen, let amount = state?.amount {
|
||||
if let minAmount, amount < minAmount {
|
||||
controller.presentMinAmountTooltip(minAmount)
|
||||
controller.presentMinAmountTooltip(minAmount.value)
|
||||
} else {
|
||||
controller.completion(amount)
|
||||
controller.completion(amount.value)
|
||||
controller.dismissAnimated()
|
||||
}
|
||||
}
|
||||
@ -360,9 +359,9 @@ private final class SheetContent: CombinedComponent {
|
||||
private let context: AccountContext
|
||||
private let mode: StarsWithdrawScreen.Mode
|
||||
|
||||
fileprivate var amount: Int64?
|
||||
fileprivate var amount: StarsAmount?
|
||||
|
||||
fileprivate var balance: Int64?
|
||||
fileprivate var balance: StarsAmount?
|
||||
private var stateDisposable: Disposable?
|
||||
|
||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||
@ -376,12 +375,12 @@ private final class SheetContent: CombinedComponent {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
|
||||
var amount: Int64?
|
||||
var amount: StarsAmount?
|
||||
switch mode {
|
||||
case let .withdraw(stats):
|
||||
amount = stats.balances.availableBalance
|
||||
case let .paidMedia(initialValue):
|
||||
amount = initialValue
|
||||
amount = initialValue.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
case .reaction:
|
||||
amount = nil
|
||||
}
|
||||
|
@ -28,7 +28,24 @@ private let readIconImage: UIImage? = generateTintedImage(image: UIImage(bundleI
|
||||
private let repostIconImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Stories/HeaderRepost"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let forwardIconImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Stories/HeaderForward"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let checkImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
private let disclosureImage: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
private func generateDisclosureImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 7.0, height: 12.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
|
||||
let lineWidth: CGFloat = 2.0
|
||||
context.setLineWidth(lineWidth)
|
||||
context.setLineJoin(.round)
|
||||
context.setLineCap(.round)
|
||||
|
||||
context.move(to: CGPoint(x: lineWidth * 0.5, y: lineWidth * 0.5))
|
||||
context.addLine(to: CGPoint(x: size.width - lineWidth * 0.5, y: size.height * 0.5))
|
||||
context.addLine(to: CGPoint(x: lineWidth * 0.5, y: size.height - lineWidth * 0.5))
|
||||
context.strokePath()
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
private let disclosureImage: UIImage? = generateDisclosureImage()
|
||||
|
||||
public final class PeerListItemComponent: Component {
|
||||
public final class TransitionHint {
|
||||
@ -214,6 +231,7 @@ public final class PeerListItemComponent: Component {
|
||||
let peer: EnginePeer?
|
||||
let storyStats: PeerStoryStats?
|
||||
let subtitle: Subtitle?
|
||||
let subtitleComponent: AnyComponent<Empty>?
|
||||
let subtitleAccessory: SubtitleAccessory
|
||||
let presence: EnginePeer.Presence?
|
||||
let rightAccessory: RightAccessory
|
||||
@ -226,6 +244,7 @@ public final class PeerListItemComponent: Component {
|
||||
let isEnabled: Bool
|
||||
let hasNext: Bool
|
||||
let extractedTheme: ExtractedTheme?
|
||||
let insets: UIEdgeInsets?
|
||||
let action: (EnginePeer, EngineMessage.Id?, PeerListItemComponent.View) -> Void
|
||||
let inlineActions: InlineActionsState?
|
||||
let contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)?
|
||||
@ -243,6 +262,7 @@ public final class PeerListItemComponent: Component {
|
||||
peer: EnginePeer?,
|
||||
storyStats: PeerStoryStats? = nil,
|
||||
subtitle: Subtitle?,
|
||||
subtitleComponent: AnyComponent<Empty>? = nil,
|
||||
subtitleAccessory: SubtitleAccessory,
|
||||
presence: EnginePeer.Presence?,
|
||||
rightAccessory: RightAccessory = .none,
|
||||
@ -255,6 +275,7 @@ public final class PeerListItemComponent: Component {
|
||||
isEnabled: Bool = true,
|
||||
hasNext: Bool,
|
||||
extractedTheme: ExtractedTheme? = nil,
|
||||
insets: UIEdgeInsets? = nil,
|
||||
action: @escaping (EnginePeer, EngineMessage.Id?, PeerListItemComponent.View) -> Void,
|
||||
inlineActions: InlineActionsState? = nil,
|
||||
contextAction: ((EnginePeer, ContextExtractedContentContainingView, ContextGesture) -> Void)? = nil,
|
||||
@ -271,6 +292,7 @@ public final class PeerListItemComponent: Component {
|
||||
self.peer = peer
|
||||
self.storyStats = storyStats
|
||||
self.subtitle = subtitle
|
||||
self.subtitleComponent = subtitleComponent
|
||||
self.subtitleAccessory = subtitleAccessory
|
||||
self.presence = presence
|
||||
self.rightAccessory = rightAccessory
|
||||
@ -283,6 +305,7 @@ public final class PeerListItemComponent: Component {
|
||||
self.isEnabled = isEnabled
|
||||
self.hasNext = hasNext
|
||||
self.extractedTheme = extractedTheme
|
||||
self.insets = insets
|
||||
self.action = action
|
||||
self.inlineActions = inlineActions
|
||||
self.contextAction = contextAction
|
||||
@ -323,6 +346,9 @@ public final class PeerListItemComponent: Component {
|
||||
if lhs.subtitle != rhs.subtitle {
|
||||
return false
|
||||
}
|
||||
if lhs.subtitleComponent != rhs.subtitleComponent {
|
||||
return false
|
||||
}
|
||||
if lhs.subtitleAccessory != rhs.subtitleAccessory {
|
||||
return false
|
||||
}
|
||||
@ -356,6 +382,12 @@ public final class PeerListItemComponent: Component {
|
||||
if lhs.hasNext != rhs.hasNext {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
if lhs.extractedTheme != rhs.extractedTheme {
|
||||
return false
|
||||
}
|
||||
if lhs.inlineActions != rhs.inlineActions {
|
||||
return false
|
||||
}
|
||||
@ -370,6 +402,7 @@ public final class PeerListItemComponent: Component {
|
||||
|
||||
private let title = ComponentView<Empty>()
|
||||
private var label = ComponentView<Empty>()
|
||||
private var subtitleView: ComponentView<Empty>?
|
||||
private let separatorLayer: SimpleLayer
|
||||
private var avatarNode: AvatarNode?
|
||||
private var avatarImageView: UIImageView?
|
||||
@ -378,6 +411,7 @@ public final class PeerListItemComponent: Component {
|
||||
|
||||
private var avatarComponentView: ComponentView<Empty>?
|
||||
|
||||
private var rightIconView: UIImageView?
|
||||
private var iconView: UIImageView?
|
||||
private var checkLayer: CheckLayer?
|
||||
private var rightAccessoryComponentView: ComponentView<Empty>?
|
||||
@ -665,25 +699,8 @@ public final class PeerListItemComponent: Component {
|
||||
contextInset = 0.0
|
||||
}
|
||||
|
||||
let height: CGFloat
|
||||
let titleFont: UIFont
|
||||
let subtitleFont: UIFont
|
||||
switch component.style {
|
||||
case .generic:
|
||||
titleFont = Font.semibold(17.0)
|
||||
subtitleFont = Font.regular(15.0)
|
||||
if labelData.0.isEmpty {
|
||||
height = 50.0
|
||||
} else {
|
||||
height = 60.0
|
||||
}
|
||||
case .compact:
|
||||
titleFont = Font.semibold(14.0)
|
||||
subtitleFont = Font.regular(14.0)
|
||||
height = 42.0
|
||||
}
|
||||
let verticalInset: CGFloat = component.insets?.top ?? 1.0
|
||||
|
||||
let verticalInset: CGFloat = 1.0
|
||||
var leftInset: CGFloat = 53.0 + component.sideInset
|
||||
if case .generic = component.style {
|
||||
leftInset += 9.0
|
||||
@ -696,6 +713,50 @@ public final class PeerListItemComponent: Component {
|
||||
rightInset += 40.0
|
||||
}
|
||||
|
||||
var subtitleComponentSize: CGSize?
|
||||
if let subtitleComponent = component.subtitleComponent {
|
||||
let subtitleView: ComponentView<Empty>
|
||||
if let current = self.subtitleView {
|
||||
subtitleView = current
|
||||
} else {
|
||||
subtitleView = ComponentView()
|
||||
self.subtitleView = subtitleView
|
||||
}
|
||||
subtitleComponentSize = subtitleView.update(
|
||||
transition: transition,
|
||||
component: subtitleComponent,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: 100.0)
|
||||
)
|
||||
} else if let subtitleView = self.subtitleView {
|
||||
self.subtitleView = nil
|
||||
subtitleView.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
var height: CGFloat
|
||||
let titleFont: UIFont
|
||||
let subtitleFont: UIFont
|
||||
switch component.style {
|
||||
case .generic:
|
||||
titleFont = Font.semibold(17.0)
|
||||
subtitleFont = Font.regular(15.0)
|
||||
if let subtitleComponentSize {
|
||||
height = 40.0 + subtitleComponentSize.height + verticalInset * 2.0
|
||||
} else if labelData.0.isEmpty {
|
||||
height = 48.0 + verticalInset * 2.0
|
||||
} else {
|
||||
height = 58.0 + verticalInset * 2.0
|
||||
}
|
||||
case .compact:
|
||||
titleFont = Font.semibold(14.0)
|
||||
subtitleFont = Font.regular(14.0)
|
||||
if let subtitleComponentSize {
|
||||
height = 20.0 + subtitleComponentSize.height + verticalInset * 2.0
|
||||
} else {
|
||||
height = 40.0 + verticalInset * 2.0
|
||||
}
|
||||
}
|
||||
|
||||
var rightAccessoryComponentSize: CGSize?
|
||||
if let rightAccessoryComponent = component.rightAccessoryComponent {
|
||||
var rightAccessoryComponentTransition = transition
|
||||
@ -911,9 +972,13 @@ public final class PeerListItemComponent: Component {
|
||||
|
||||
let availableTextWidth = availableSize.width - leftInset - rightInset
|
||||
var titleAvailableWidth = component.style == .compact ? availableTextWidth * 0.7 : availableSize.width - leftInset - rightInset
|
||||
if case .none = component.rightAccessory {
|
||||
} else {
|
||||
switch component.rightAccessory {
|
||||
case .disclosure:
|
||||
titleAvailableWidth -= 32.0
|
||||
case .check:
|
||||
titleAvailableWidth -= 20.0
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
|
||||
if statusIcon != nil {
|
||||
@ -971,7 +1036,9 @@ public final class PeerListItemComponent: Component {
|
||||
let titleSpacing: CGFloat = 2.0
|
||||
let titleVerticalOffset: CGFloat = 0.0
|
||||
let centralContentHeight: CGFloat
|
||||
if labelSize.height > 0.0, case .generic = component.style {
|
||||
if let subtitleComponentSize {
|
||||
centralContentHeight = titleSize.height + subtitleComponentSize.height + titleSpacing
|
||||
} else if labelSize.height > 0.0, case .generic = component.style {
|
||||
centralContentHeight = titleSize.height + labelSize.height + titleSpacing
|
||||
} else {
|
||||
centralContentHeight = titleSize.height
|
||||
@ -1086,7 +1153,6 @@ public final class PeerListItemComponent: Component {
|
||||
labelFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floor((height - verticalInset * 2.0 - centralContentHeight) / 2.0)), size: labelSize)
|
||||
}
|
||||
|
||||
|
||||
if labelView.superview == nil {
|
||||
labelView.isUserInteractionEnabled = false
|
||||
labelView.layer.anchorPoint = CGPoint()
|
||||
@ -1107,20 +1173,31 @@ public final class PeerListItemComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
if let subtitleComponentView = self.subtitleView?.view, let subtitleComponentSize {
|
||||
let subtitleFrame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.maxY + titleSpacing), size: subtitleComponentSize)
|
||||
|
||||
if subtitleComponentView.superview == nil {
|
||||
subtitleComponentView.isUserInteractionEnabled = false
|
||||
subtitleComponentView.layer.anchorPoint = CGPoint()
|
||||
self.containerButton.addSubview(subtitleComponentView)
|
||||
}
|
||||
transition.setFrame(view: subtitleComponentView, frame: subtitleFrame)
|
||||
}
|
||||
|
||||
let imageSize = CGSize(width: 22.0, height: 22.0)
|
||||
self.iconFrame = CGRect(origin: CGPoint(x: availableSize.width - (contextInset * 2.0 + 14.0 + component.sideInset) - imageSize.width, y: floor((height - verticalInset * 2.0 - imageSize.height) * 0.5)), size: imageSize)
|
||||
|
||||
if case .none = component.rightAccessory {
|
||||
if case .none = component.subtitleAccessory {
|
||||
if let iconView = self.iconView {
|
||||
self.iconView = nil
|
||||
iconView.removeFromSuperview()
|
||||
if let rightIconView = self.rightIconView {
|
||||
self.rightIconView = nil
|
||||
rightIconView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let iconView: UIImageView
|
||||
if let current = self.iconView {
|
||||
iconView = current
|
||||
let rightIconView: UIImageView
|
||||
if let current = self.rightIconView {
|
||||
rightIconView = current
|
||||
} else {
|
||||
var image: UIImage?
|
||||
var color: UIColor = component.theme.list.itemSecondaryTextColor
|
||||
@ -1130,17 +1207,26 @@ public final class PeerListItemComponent: Component {
|
||||
color = component.theme.list.itemAccentColor
|
||||
case .disclosure:
|
||||
image = disclosureImage
|
||||
color = component.theme.list.disclosureArrowColor
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
iconView = UIImageView(image: image)
|
||||
iconView.tintColor = color
|
||||
self.iconView = iconView
|
||||
self.containerButton.addSubview(iconView)
|
||||
rightIconView = UIImageView(image: image)
|
||||
rightIconView.tintColor = color
|
||||
self.rightIconView = rightIconView
|
||||
self.containerButton.addSubview(rightIconView)
|
||||
}
|
||||
|
||||
if let image = iconView.image {
|
||||
transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: availableSize.width - image.size.width, y: floor((height - verticalInset * 2.0 - image.size.width) / 2.0)), size: image.size))
|
||||
if let image = rightIconView.image {
|
||||
let iconFrame: CGRect
|
||||
switch component.rightAccessory {
|
||||
case .disclosure:
|
||||
iconFrame = CGRect(origin: CGPoint(x: availableSize.width - image.size.width - 16.0 - contextInset, y: floor((height - verticalInset * 2.0 - image.size.height) / 2.0)), size: image.size)
|
||||
default:
|
||||
iconFrame = CGRect(origin: CGPoint(x: availableSize.width - image.size.width, y: floor((height - verticalInset * 2.0 - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
transition.setFrame(view: rightIconView, frame: iconFrame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,19 @@ import ComponentDisplayAdapters
|
||||
public final class ToastContentComponent: Component {
|
||||
public let icon: AnyComponent<Empty>
|
||||
public let content: AnyComponent<Empty>
|
||||
public let insets: UIEdgeInsets
|
||||
public let iconSpacing: CGFloat
|
||||
|
||||
public init(
|
||||
icon: AnyComponent<Empty>,
|
||||
content: AnyComponent<Empty>
|
||||
content: AnyComponent<Empty>,
|
||||
insets: UIEdgeInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0),
|
||||
iconSpacing: CGFloat = 10.0
|
||||
) {
|
||||
self.icon = icon
|
||||
self.content = content
|
||||
self.insets = insets
|
||||
self.iconSpacing = iconSpacing
|
||||
}
|
||||
|
||||
public static func ==(lhs: ToastContentComponent, rhs: ToastContentComponent) -> Bool {
|
||||
@ -23,6 +29,12 @@ public final class ToastContentComponent: Component {
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
if lhs.iconSpacing != rhs.iconSpacing {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -54,10 +66,11 @@ public final class ToastContentComponent: Component {
|
||||
func update(component: ToastContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
||||
let leftInset: CGFloat = 9.0
|
||||
let rightInset: CGFloat = 6.0
|
||||
let verticalInset: CGFloat = 10.0
|
||||
let spacing: CGFloat = 9.0
|
||||
let leftInset: CGFloat = component.insets.left
|
||||
let rightInset: CGFloat = component.insets.right
|
||||
let topInset: CGFloat = component.insets.top
|
||||
let bottomInset: CGFloat = component.insets.bottom
|
||||
let spacing: CGFloat = component.iconSpacing
|
||||
|
||||
let iconSize = self.icon.update(
|
||||
transition: transition,
|
||||
@ -72,7 +85,7 @@ public final class ToastContentComponent: Component {
|
||||
containerSize: CGSize(width: availableSize.width - leftInset - rightInset - spacing - iconSize.width, height: availableSize.height)
|
||||
)
|
||||
|
||||
contentHeight += verticalInset * 2.0 + max(iconSize.height, contentSize.height)
|
||||
contentHeight += topInset + bottomInset + max(iconSize.height, contentSize.height)
|
||||
|
||||
if let iconView = self.icon.view {
|
||||
if iconView.superview == nil {
|
||||
@ -89,7 +102,7 @@ public final class ToastContentComponent: Component {
|
||||
|
||||
let size = CGSize(width: availableSize.width, height: contentHeight)
|
||||
self.backgroundView.updateColor(color: UIColor(white: 0.0, alpha: 0.7), transition: .immediate)
|
||||
self.backgroundView.update(size: size, cornerRadius: 10.0, transition: transition.containedViewLayoutTransition)
|
||||
self.backgroundView.update(size: size, cornerRadius: 14.0, transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
return size
|
||||
|
BIN
submodules/TelegramUI/Resources/Animations/anim_link_broken.tgs
Normal file
BIN
submodules/TelegramUI/Resources/Animations/anim_link_broken.tgs
Normal file
Binary file not shown.
@ -418,7 +418,7 @@ extension ChatControllerImpl {
|
||||
return
|
||||
}
|
||||
|
||||
if balance < 1 {
|
||||
if balance < StarsAmount(value: 1, nanos: 0) {
|
||||
controller?.dismiss(completion: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
|
@ -1755,7 +1755,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
if balance < 1 {
|
||||
if balance < StarsAmount(value: 1, nanos: 0) {
|
||||
let _ = (strongSelf.context.engine.payments.starsTopUpOptions()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] options in
|
||||
|
@ -301,14 +301,14 @@ func openResolvedUrlImpl(
|
||||
photo.append(photoRepresentation)
|
||||
}
|
||||
let channel = TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(0)), accessHash: .genericPublic(0), title: invite.title, username: nil, photo: photo, creationDate: 0, version: 0, participationStatus: .left, info: .broadcast(TelegramChannelBroadcastInfo(flags: [])), flags: [], restrictionInfo: nil, adminRights: nil, bannedRights: nil, defaultBannedRights: nil, usernames: [], storiesHidden: nil, nameColor: invite.nameColor, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, emojiStatus: nil, approximateBoostLevel: nil, subscriptionUntilDate: nil)
|
||||
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0)
|
||||
let invoice = TelegramMediaInvoice(title: "", description: "", photo: nil, receiptMessageId: nil, currency: "XTR", totalAmount: subscriptionPricing.amount.value, startParam: "", extendedMedia: nil, subscriptionPeriod: nil, flags: [], version: 0)
|
||||
|
||||
inputData.set(.single(BotCheckoutController.InputData(
|
||||
form: BotPaymentForm(
|
||||
id: subscriptionFormId,
|
||||
canSaveCredentials: false,
|
||||
passwordMissing: false,
|
||||
invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: subscriptionPricing.amount)], tip: nil, termsInfo: nil, subscriptionPeriod: subscriptionPricing.period),
|
||||
invoice: BotPaymentInvoice(isTest: false, requestedFields: [], currency: "XTR", prices: [BotPaymentPrice(label: "", amount: subscriptionPricing.amount.value)], tip: nil, termsInfo: nil, subscriptionPeriod: subscriptionPricing.period),
|
||||
paymentBotId: channel.id,
|
||||
providerId: nil,
|
||||
url: nil,
|
||||
@ -725,7 +725,7 @@ func openResolvedUrlImpl(
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
if let currentState = starsContext.currentState, currentState.balance >= amount {
|
||||
if let currentState = starsContext.currentState, currentState.balance >= StarsAmount(value: amount, nanos: 0) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
|
Loading…
x
Reference in New Issue
Block a user