Merge commit '4d6810c5c001d818a50922d4de129725dbb38776'

This commit is contained in:
Isaac 2024-05-25 18:06:00 +04:00
commit ce84ce52b0
39 changed files with 546 additions and 259 deletions

View File

@ -12231,10 +12231,27 @@ Sorry for the inconvenience.";
"Stars.Intro.Title" = "Telegram Stars";
"Stars.Intro.Description" = "Buy Stars to unlock content and services in miniapps on Telegram.";
"Stars.Intro.Balance" = "Balance";
"Stars.Intro.YourBalance" = "your balance";
"Stars.Intro.Buy" = "Buy More Stars";
"Stars.Intro.AllTransactions" = "All Transactions";
"Stars.Intro.Incoming" = "Incoming";
"Stars.Intro.Outgoing" = "Outgoing";
"Stars.Intro.Transaction.AppleTopUp.Title" = "Stars Top-Up";
"Stars.Intro.Transaction.AppleTopUp.Subtitle" = "via App Store";
"Stars.Intro.Transaction.GoogleTopUp.Title" = "Stars Top-Up";
"Stars.Intro.Transaction.GoogleTopUp.Subtitle" = "via Play Market";
"Stars.Intro.Transaction.PremiumBotTopUp.Title" = "Stars Top-Up";
"Stars.Intro.Transaction.PremiumBotTopUp.Subtitle" = "via Premium Bot";
"Stars.Intro.Transaction.FragmentTopUp.Title" = "Stars Top-Up";
"Stars.Intro.Transaction.FragmentTopUp.Subtitle" = "via Fragment";
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
"Stars.Intro.PurchasedTitle" = "Stars Acquired";
"Stars.Intro.PurchasedText" = "**%@** added to your balance.";
"Stars.Intro.PurchasedText.Stars_1" = "%@ Star";
"Stars.Intro.PurchasedText.Stars_any" = "%@ Stars";
"Stars.Purchase.GetStars" = "Get Stars";
"Stars.Purchase.GetStarsInfo" = "Choose how many Stars you would like to buy.";
@ -12250,6 +12267,7 @@ Sorry for the inconvenience.";
"Stars.Purchase.Info" = "By proceeding and purchasing Stars, you agree with [Terms and Conditions]().";
"Stars.Purchase.Terms_URL" = "https://telegram.org/tos";
"Stars.Transaction.Via" = "Via";
"Stars.Transaction.To" = "To";
"Stars.Transaction.Id" = "Transaction ID";
"Stars.Transaction.Date" = "Date";
@ -12257,6 +12275,16 @@ Sorry for the inconvenience.";
"Stars.Transaction.Terms_URL" = "https://telegram.org/tos";
"Stars.Transaction.CopiedId" = "Transaction ID copied to clipboard.";
"Stars.Transaction.AppleTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.AppleTopUp.Subtitle" = "App Store";
"Stars.Transaction.GoogleTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.GoogleTopUp.Subtitle" = "Play Market";
"Stars.Transaction.PremiumBotTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.PremiumBotTopUp.Subtitle" = "Premium Bot";
"Stars.Transaction.FragmentTopUp.Title" = "Stars Top-Up";
"Stars.Transaction.FragmentTopUp.Subtitle" = "Fragment";
"Stars.Transaction.Unsupported.Title" = "Unsupported";
"Stars.Transfer.Title" = "Confirm Your Purchase";
"Stars.Transfer.Info" = "Do you want to buy **%1$@** in **%2$@** for **%3$@**?";
"Stars.Transfer.Info.Stars_1" = "%@ Star";

View File

@ -1127,6 +1127,7 @@ public protocol AccountContext: AnyObject {
var wallpaperUploadManager: WallpaperUploadManager? { get }
var watchManager: WatchManager? { get }
var inAppPurchaseManager: InAppPurchaseManager? { get }
var starsContext: StarsContext? { get }
var currentLimitsConfiguration: Atomic<LimitsConfiguration> { get }
var currentContentSettings: Atomic<ContentSettings> { get }

View File

@ -299,9 +299,11 @@ public struct ChatControllerInitialBotAppStart {
public enum ChatControllerInteractionNavigateToPeer {
public struct InfoParams {
public let switchToRecommendedChannels: Bool
public let ignoreInSavedMessages: Bool
public init(switchToRecommendedChannels: Bool) {
public init(switchToRecommendedChannels: Bool = false, ignoreInSavedMessages: Bool = false) {
self.switchToRecommendedChannels = switchToRecommendedChannels
self.ignoreInSavedMessages = ignoreInSavedMessages
}
}

View File

@ -13,6 +13,7 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/PeerPresenceStatusManager:PeerPresenceStatusManager",

View File

@ -2,6 +2,7 @@ import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import TelegramPresentationData
@ -60,7 +61,7 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable {
}
}
public func composedDisplayTitle(context: AccountContext, strings: PresentationStrings) -> String {
public func composedDisplayTitle(context: AccountContext?, strings: PresentationStrings) -> String {
switch self {
case let .personName(firstName, lastName, phone):
if !firstName.isEmpty {
@ -72,7 +73,11 @@ public enum ItemListAvatarAndNameInfoItemName: Equatable {
} else if !lastName.isEmpty {
return lastName
} else if !phone.isEmpty {
return formatPhoneNumber(context: context, number: "+\(phone)")
if let context {
return formatPhoneNumber(context: context, number: "+\(phone)")
} else {
return "+\(phone)"
}
} else {
return strings.User_DeletedAccount
}
@ -127,7 +132,18 @@ public enum ItemListAvatarAndNameInfoItemMode {
}
public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
let accountContext: AccountContext
public enum ItemContext {
case accountContext(AccountContext)
case other(accountPeerId: EnginePeer.Id, postbox: Postbox, network: Network)
var accountContext: AccountContext? {
if case let .accountContext(accountContext) = self {
return accountContext
}
return nil
}
}
let itemContext: ItemContext
let presentationData: ItemListPresentationData
let dateTimeFormat: PresentationDateTimeFormat
let mode: ItemListAvatarAndNameInfoItemMode
@ -150,8 +166,8 @@ public class ItemListAvatarAndNameInfoItem: ListViewItem, ItemListItem {
public let selectable: Bool
public init(accountContext: AccountContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: EnginePeer?, presence: EnginePeer.Presence?, label: String? = nil, memberCount: Int?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, editingNameCompleted: @escaping () -> Void = {}, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
self.accountContext = accountContext
public init(itemContext: ItemContext, presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, mode: ItemListAvatarAndNameInfoItemMode, peer: EnginePeer?, presence: EnginePeer.Presence?, label: String? = nil, memberCount: Int?, state: ItemListAvatarAndNameInfoItemState, sectionId: ItemListSectionId, style: ItemListAvatarAndNameInfoItemStyle, editingNameUpdated: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, editingNameCompleted: @escaping () -> Void = {}, avatarTapped: @escaping () -> Void, context: ItemListAvatarAndNameInfoItemContext? = nil, updatingImage: ItemListAvatarAndNameInfoItemUpdatingAvatar? = nil, call: (() -> Void)? = nil, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, tag: ItemListItemTag? = nil) {
self.itemContext = itemContext
self.presentationData = presentationData
self.dateTimeFormat = dateTimeFormat
self.mode = mode
@ -358,7 +374,12 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
updatedTheme = item.presentationData.theme
}
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.accountContext.currentAppConfiguration.with { $0 })
let premiumConfiguration: PremiumConfiguration
if case let .accountContext(context) = item.itemContext {
premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
} else {
premiumConfiguration = .defaultValue
}
var credibilityIconImage: UIImage?
var credibilityIconOffset: CGFloat = 4.0
@ -395,7 +416,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
nameMaximumNumberOfLines = 2
}
let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(context: item.accountContext, strings: item.presentationData.strings), font: nameFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: nameMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
let (nameNodeLayout, nameNodeApply) = layoutNameNode(TextNodeLayoutArguments(attributedString: NSAttributedString(string: displayTitle.composedDisplayTitle(context: item.itemContext.accountContext, strings: item.presentationData.strings), font: nameFont, textColor: item.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: nameMaximumNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: baseWidth - 20 - 94.0 - (item.call != nil ? 36.0 : 0.0) - additionalTitleInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
var statusText: String = ""
let statusColor: UIColor
@ -404,7 +425,11 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
switch item.mode {
case .settings:
if let phone = peer.phone, !phone.isEmpty {
statusText += formatPhoneNumber(context: item.accountContext, number: phone)
if let accountContext = item.itemContext.accountContext {
statusText += formatPhoneNumber(context: accountContext, number: phone)
} else {
statusText += formatPhoneNumber(phone)
}
}
if let username = peer.addressName, !username.isEmpty {
if !statusText.isEmpty {
@ -669,7 +694,13 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
overrideImage = .deletedIcon
}
strongSelf.avatarNode.setPeer(context: item.accountContext, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
switch item.itemContext {
case let .accountContext(context):
strongSelf.avatarNode.setPeer(context: context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
case let .other(accountPeerId, postbox, network):
strongSelf.avatarNode.setPeer(accountPeerId: accountPeerId, postbox: postbox, network: network, contentSettings: .default, theme: item.presentationData.theme, peer: peer, authorOfMessage: nil, overrideImage: overrideImage, emptyColor: ignoreEmpty ? nil : item.presentationData.theme.list.mediaPlaceholderColor, synchronousLoad: synchronousLoads)
}
}
let avatarFrame = CGRect(origin: CGPoint(x: params.leftInset + 15.0, y: floor((layout.contentSize.height - 66.0) / 2.0)), size: CGSize(width: 66.0, height: 66.0))

View File

@ -346,7 +346,7 @@ private enum ChannelAdminEntry: ItemListNodeEntry {
let arguments = arguments as! ChannelAdminControllerArguments
switch self {
case let .info(_, _, dateTimeFormat, peer, presence):
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
}, avatarTapped: {
})
case let .rankTitle(_, text, count, limit):

View File

@ -215,7 +215,7 @@ private enum ChannelBannedMemberEntry: ItemListNodeEntry {
let arguments = arguments as! ChannelBannedMemberControllerArguments
switch self {
case let .info(_, _, dateTimeFormat, peer, presence):
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, memberCount: nil, state: ItemListAvatarAndNameInfoItemState(), sectionId: self.section, style: .blocks(withTopInset: true, withExtendedBottomInset: false), editingNameUpdated: { _ in
}, avatarTapped: {
})
case let .rightsHeader(_, text):

View File

@ -35,7 +35,7 @@ private enum DeviceContactInfoAction {
}
private final class DeviceContactInfoControllerArguments {
let accountContext: AccountContext?
let context: ShareControllerAccountContext
let isPlain: Bool
let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void
let updatePhone: (Int64, String) -> Void
@ -52,8 +52,8 @@ private final class DeviceContactInfoControllerArguments {
let updateShareViaException: (Bool) -> Void
let openAvatar: (EnginePeer) -> Void
init(accountContext: AccountContext?, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) {
self.accountContext = accountContext
init(context: ShareControllerAccountContext, isPlain: Bool, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, updatePhone: @escaping (Int64, String) -> Void, updatePhoneLabel: @escaping (Int64, String) -> Void, deletePhone: @escaping (Int64) -> Void, setPhoneIdWithRevealedOptions: @escaping (Int64?, Int64?) -> Void, addPhoneNumber: @escaping () -> Void, performAction: @escaping (DeviceContactInfoAction) -> Void, toggleSelection: @escaping (DeviceContactInfoDataId) -> Void, callPhone: @escaping (String) -> Void, openUrl: @escaping (String) -> Void, openAddress: @escaping (DeviceContactAddressData) -> Void, displayCopyContextMenu: @escaping (DeviceContactInfoEntryTag, String) -> Void, updateShareViaException: @escaping (Bool) -> Void, openAvatar: @escaping (EnginePeer) -> Void) {
self.context = context
self.isPlain = isPlain
self.updateEditingName = updateEditingName
self.updatePhone = updatePhone
@ -406,10 +406,13 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry {
let arguments = arguments as! DeviceContactInfoControllerArguments
switch self {
case let .info(_, _, _, dateTimeFormat, peer, state, jobSummary, _, hiddenAvatar):
guard let accountContext = arguments.accountContext else {
fatalError()
let itemContext: ItemListAvatarAndNameInfoItem.ItemContext
if let context = arguments.context as? ShareControllerAppAccountContext {
itemContext = .accountContext(context.context)
} else {
itemContext = .other(accountPeerId: arguments.context.accountPeerId, postbox: arguments.context.stateManager.postbox, network: arguments.context.stateManager.network)
}
return ItemListAvatarAndNameInfoItem(accountContext: accountContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in
return ItemListAvatarAndNameInfoItem(itemContext: itemContext, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, memberCount: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in
arguments.updateEditingName(editingName)
}, avatarTapped: {
if peer.smallProfileImage != nil {
@ -945,13 +948,7 @@ public func deviceContactInfoController(context: ShareControllerAccountContext,
shareViaException = shareViaExceptionValue
}
let accountContext: AccountContext?
if let context = context as? ShareControllerAppAccountContext {
accountContext = context.context
} else {
accountContext = nil
}
let arguments = DeviceContactInfoControllerArguments(accountContext: accountContext, isPlain: !isShare, updateEditingName: { editingName in
let arguments = DeviceContactInfoControllerArguments(context: context, isPlain: !isShare, updateEditingName: { editingName in
updateState { state in
var state = state
if let _ = state.editingState {

View File

@ -5,17 +5,6 @@ import TelegramCore
private let phoneNumberUtil = NBPhoneNumberUtil()
public func enhancePhoneNumberWithCodeFromNumber(_ phoneNumber: String, otherPhoneNumber: String, configuration: CountriesConfiguration) -> String {
guard let (_, code) = lookupCountryIdByNumber(otherPhoneNumber, configuration: configuration) else {
return phoneNumber
}
var cleanNumber = cleanPhoneNumber(phoneNumber)
while cleanNumber.hasPrefix("0") {
cleanNumber.removeFirst()
}
return "+\(code)\(cleanNumber)"
}
public func cleanPhoneNumber(_ text: String, removePlus: Bool = false) -> String {
var result = ""
for c in text {

View File

@ -167,7 +167,7 @@ private enum SaveIncomingMediaEntry: ItemListNodeEntry {
switch self {
case let .peer(peer, presence):
return ItemListAvatarAndNameInfoItem(
accountContext: arguments.context,
itemContext: .accountContext(arguments.context),
presentationData: presentationData,
dateTimeFormat: PresentationDateTimeFormat(),
mode: .generic,

View File

@ -72,10 +72,10 @@ struct InternalStarsStatus {
let nextOffset: String?
}
func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset: String?) -> Signal<InternalStarsStatus?, NoError> {
func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset: String?) -> Signal<InternalStarsStatus, NoError> {
return account.postbox.transaction { transaction -> Peer? in
return transaction.getPeer(peerId)
} |> mapToSignal { peer -> Signal<InternalStarsStatus?, NoError> in
} |> mapToSignal { peer -> Signal<InternalStarsStatus, NoError> in
guard let peer, let inputPeer = apiInputPeer(peer) else {
return .never()
}
@ -88,15 +88,9 @@ func _internal_requestStarsState(account: Account, peerId: EnginePeer.Id, offset
}
return signal
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.payments.StarsStatus?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<InternalStarsStatus?, NoError> in
guard let result else {
return .single(nil)
}
return account.postbox.transaction { transaction -> InternalStarsStatus? in
|> retryRequest
|> mapToSignal { result -> Signal<InternalStarsStatus, NoError> in
return account.postbox.transaction { transaction -> InternalStarsStatus in
switch result {
case let .starsStatus(_, balance, history, nextOffset, chats, users):
let peers = AccumulatedPeers(chats: chats, users: users)
@ -119,13 +113,7 @@ private final class StarsContextImpl {
private let account: Account
private let peerId: EnginePeer.Id
private var _state: StarsContext.State? {
didSet {
if self._state != oldValue {
self._statePromise.set(.single(self._state))
}
}
}
private var _state: StarsContext.State?
private let _statePromise = Promise<StarsContext.State?>()
var state: Signal<StarsContext.State?, NoError> {
return self._statePromise.get()
@ -151,7 +139,7 @@ private final class StarsContextImpl {
guard let self, let state = self._state, let balance = balances[peerId] else {
return
}
self._state = StarsContext.State(balance: balance, transactions: state.transactions, canLoadMore: nextOffset != nil, isLoading: false)
self.updateState(StarsContext.State(flags: [], balance: balance, transactions: state.transactions, canLoadMore: nextOffset != nil, isLoading: false))
self.load()
})
}
@ -168,26 +156,20 @@ private final class StarsContextImpl {
self.disposable.set((_internal_requestStarsState(account: self.account, peerId: self.peerId, offset: nil)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
if let status {
self._state = StarsContext.State(balance: status.balance, transactions: status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false)
self.nextOffset = status.nextOffset
self.loadMore()
} else {
self._state = nil
}
self.updateState(StarsContext.State(flags: [], balance: status.balance, transactions: status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false))
self.nextOffset = status.nextOffset
}
}))
}
func add(balance: Int64) {
if var state = self._state {
var transactions = state.transactions
transactions.insert(.init(id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil), at: 0)
state.balance = state.balance + balance
self._state = state
guard let state = self._state else {
return
}
var transactions = state.transactions
transactions.insert(.init(id: "\(arc4random())", count: balance, date: Int32(Date().timeIntervalSince1970), peer: .appStore, title: nil, description: nil, photo: nil), at: 0)
self.updateState(StarsContext.State(flags: [.isPendingBalance], balance: state.balance + balance, transactions: transactions, canLoadMore: state.canLoadMore, isLoading: state.isLoading))
}
func loadMore() {
@ -202,15 +184,16 @@ private final class StarsContextImpl {
self.disposable.set((_internal_requestStarsState(account: self.account, peerId: self.peerId, offset: nextOffset)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let self {
if let status {
self._state = StarsContext.State(balance: status.balance, transactions: currentState.transactions + status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false)
self.nextOffset = status.nextOffset
} else {
self.nextOffset = nil
}
self.updateState(StarsContext.State(flags: [], balance: status.balance, transactions: currentState.transactions + status.transactions, canLoadMore: status.nextOffset != nil, isLoading: false))
self.nextOffset = status.nextOffset
}
}))
}
private func updateState(_ state: StarsContext.State) {
self._state = state
self._statePromise.set(.single(state))
}
}
private extension StarsContext.State.Transaction {
@ -279,11 +262,24 @@ public final class StarsContext {
}
}
public struct Flags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let isPendingBalance = Flags(rawValue: 1 << 0)
}
public var flags: Flags
public var balance: Int64
public var transactions: [Transaction]
public var canLoadMore: Bool
public var isLoading: Bool
init(balance: Int64, transactions: [Transaction], canLoadMore: Bool, isLoading: Bool) {
init(flags: Flags, balance: Int64, transactions: [Transaction], canLoadMore: Bool, isLoading: Bool) {
self.flags = flags
self.balance = balance
self.transactions = transactions
self.canLoadMore = canLoadMore
@ -291,6 +287,9 @@ public final class StarsContext {
}
public static func == (lhs: State, rhs: State) -> Bool {
if lhs.flags != rhs.flags {
return true
}
if lhs.balance != rhs.balance {
return false
}
@ -327,6 +326,12 @@ public final class StarsContext {
}
}
public func load() {
self.impl.with {
$0.load()
}
}
public func loadMore() {
self.impl.with {
$0.loadMore()

View File

@ -73,17 +73,7 @@ public extension TelegramEngine {
public func peerStarsContext(peerId: EnginePeer.Id) -> StarsContext {
return StarsContext(account: self.account, peerId: peerId)
}
public func peerStarsState(peerId: EnginePeer.Id) -> Signal<StarsContext.State?, NoError> {
return _internal_requestStarsState(account: self.account, peerId: peerId, offset: nil)
|> map { state -> StarsContext.State? in
guard let state else {
return nil
}
return StarsContext.State(balance: state.balance, transactions: state.transactions, canLoadMore: false, isLoading: false)
}
}
public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
return _internal_sendStarsPaymentForm(account: self.account, formId: formId, source: source)
}

View File

@ -847,8 +847,8 @@ private let starImage: UIImage? = {
generateImage(CGSize(width: 32.0, height: 32.0), contextGenerator: { size, context in
context.clear(CGRect(origin: .zero, size: size))
if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: .white), let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 2.0, dy: 2.0), byTiling: false)
if let image = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white), let cgImage = image.cgImage {
context.draw(cgImage, in: CGRect(origin: .zero, size: size).insetBy(dx: 4.0, dy: 4.0), byTiling: false)
}
})?.withRenderingMode(.alwaysTemplate)
}()

View File

@ -11931,7 +11931,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
}
if isSettings {
self.starsContext = context.engine.payments.peerStarsContext(peerId: context.account.peerId)
self.starsContext = context.starsContext
} else {
self.starsContext = nil
}

View File

@ -305,7 +305,7 @@ public final class PremiumStarComponent: Component {
]
for name in names {
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first {
if let node = scene.rootNode.childNode(withName: name, recursively: false), let particleSystem = node.particleSystems?.first, color.rgb != 0x6a94ff {
particleSystem.particleColor = color
particleSystem.particleColorVariation = SCNVector4Make(0, 0, 0, 0)
}

View File

@ -53,6 +53,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
let context: AccountContext
let externalState: ExternalState
let containerSize: CGSize
let balance: Int64?
let options: [StarsTopUpOption]
let peerId: EnginePeer.Id?
let requiredStars: Int64?
@ -67,6 +68,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
context: AccountContext,
externalState: ExternalState,
containerSize: CGSize,
balance: Int64?,
options: [StarsTopUpOption],
peerId: EnginePeer.Id?,
requiredStars: Int64?,
@ -80,6 +82,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
self.context = context
self.externalState = externalState
self.containerSize = containerSize
self.balance = balance
self.options = options
self.peerId = peerId
self.requiredStars = requiredStars
@ -129,9 +132,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
var peer: EnginePeer?
private var disposable: Disposable?
var cachedChevronImage: (UIImage, PresentationTheme)?
init(
context: AccountContext,
peerId: EnginePeer.Id?
@ -302,14 +303,20 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
var i = 0
var items: [AnyComponentWithIdentity<Empty>] = []
if let products = state.products {
if let products = state.products, let balance = context.component.balance {
var minimumCount: Int64?
if let requiredStars = context.component.requiredStars {
minimumCount = requiredStars - balance
}
for product in products {
if let requiredStars = context.component.requiredStars, requiredStars > product.option.count {
if let minimumCount, minimumCount > product.option.count {
continue
}
if !context.component.expanded && !initialValues.contains(product.option.count) {
if let _ = minimumCount, items.isEmpty {
} else if !context.component.expanded && !initialValues.contains(product.option.count) {
continue
}
@ -553,8 +560,6 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
private var disposable: Disposable?
private var paymentDisposable = MetaDisposable()
var cachedChevronImage: (UIImage, PresentationTheme)?
init(
context: AccountContext,
starsContext: StarsContext,
@ -696,7 +701,9 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
let topPanel = Child(BlurredBackgroundComponent.self)
let topSeparator = Child(Rectangle.self)
let title = Child(MultilineTextComponent.self)
let balanceText = Child(MultilineTextComponent.self)
let balanceTitle = Child(MultilineTextComponent.self)
let balanceValue = Child(MultilineTextComponent.self)
let balanceIcon = Child(BundleIconComponent.self)
let scrollAction = ActionSlot<CGPoint?>()
@ -765,32 +772,33 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
transition: context.transition
)
let textColor = environment.theme.list.itemPrimaryTextColor
let accentColor = UIColor(rgb: 0x597cf5)
let textFont = Font.regular(14.0)
let boldTextFont = Font.bold(14.0)
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: accentColor), linkAttribute: { _ in
return nil
})
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: UIColor(rgb: 0xf09903))!, environment.theme)
}
let balanceAttributedString = parseMarkdownIntoAttributedString(" \(strings.Stars_Purchase_Balance)\n # **\(state.starsState?.balance ?? 0)**", attributes: markdownAttributes, textAlignment: .right).mutableCopy() as! NSMutableAttributedString
if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = state.cachedChevronImage?.0 {
balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: balanceAttributedString.string))
}
let balanceText = balanceText.update(
let balanceTitle = balanceTitle.update(
component: MultilineTextComponent(
text: .plain(balanceAttributedString),
horizontalAlignment: .left,
maximumNumberOfLines: 0
text: .plain(NSAttributedString(
string: environment.strings.Stars_Purchase_Balance,
font: Font.regular(14.0),
textColor: environment.theme.actionSheet.primaryTextColor
)),
maximumNumberOfLines: 1
),
availableSize: CGSize(width: 200, height: context.availableSize.height),
availableSize: context.availableSize,
transition: .immediate
)
let balanceValue = balanceValue.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: presentationStringsFormattedNumber(Int32(state.starsState?.balance ?? 0), environment.dateTimeFormat.decimalSeparator),
font: Font.semibold(14.0),
textColor: environment.theme.actionSheet.primaryTextColor
)),
maximumNumberOfLines: 1
),
availableSize: context.availableSize,
transition: .immediate
)
let balanceIcon = balanceIcon.update(
component: BundleIconComponent(name: "Premium/Stars/StarSmall", tintColor: nil),
availableSize: context.availableSize,
transition: .immediate
)
@ -800,6 +808,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
context: context.component.context,
externalState: contentExternalState,
containerSize: context.availableSize,
balance: state.starsState?.balance,
options: context.component.options,
peerId: context.component.peerId,
requiredStars: context.component.requiredStars,
@ -890,8 +899,16 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
.opacity(titleAlpha)
)
context.add(balanceText
.position(CGPoint(x: context.availableSize.width - 16.0 - balanceText.size.width / 2.0, y: 28.0))
let navigationHeight = environment.navigationHeight - environment.statusBarHeight
let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - balanceTitle.size.height - balanceValue.size.height) / 2.0
context.add(balanceTitle
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0))
)
context.add(balanceValue
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0))
)
context.add(balanceIcon
.position(CGPoint(x: context.availableSize.width - 16.0 - environment.safeInsets.right - balanceValue.size.width - balanceIcon.size.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel))
)
return context.availableSize
@ -1059,13 +1076,13 @@ func generateStarsIcon(count: Int) -> UIImage {
var originX = floorToScreenPixels((size.width - totalWidth) / 2.0)
let mainImage = UIImage(bundleImageName: "Premium/Stars/Star")
let mainImage = UIImage(bundleImageName: "Premium/Stars/StarLarge")
if let cgImage = mainImage?.cgImage, let partCGImage = partImage.cgImage {
context.draw(cgImage, in: CGRect(origin: CGPoint(x: originX, y: 0.0), size: imageSize), byTiling: false)
originX += spacing
originX += spacing + UIScreenPixel
for _ in 0 ..< count - 1 {
context.draw(partCGImage, in: CGRect(origin: CGPoint(x: originX, y: UIScreenPixel), size: imageSize), byTiling: false)
context.draw(partCGImage, in: CGRect(origin: CGPoint(x: originX, y: -UIScreenPixel), size: imageSize).insetBy(dx: -1.0 + UIScreenPixel, dy: -1.0 + UIScreenPixel), byTiling: false)
originX += spacing
}
}

View File

@ -52,7 +52,7 @@ final class StarsBalanceComponent: Component {
override init(frame: CGRect) {
super.init(frame: frame)
self.icon.image = UIImage(bundleImageName: "Premium/Stars/StarLarge")
self.icon.image = UIImage(bundleImageName: "Premium/Stars/BalanceStar")
self.addSubview(self.icon)
}
@ -107,7 +107,7 @@ final class StarsBalanceComponent: Component {
transition: .immediate,
component: AnyComponent(
MultilineTextComponent(
text: .plain(NSAttributedString(string: "your balance", font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)),
text: .plain(NSAttributedString(string: component.strings.Stars_Intro_YourBalance, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)),
horizontalAlignment: .center
)
),
@ -126,7 +126,7 @@ final class StarsBalanceComponent: Component {
transition: .immediate,
component: AnyComponent(
SolidRoundedButtonComponent(
title: "Buy More Stars",
title: component.strings.Stars_Intro_Buy,
theme: SolidRoundedButtonComponent.Theme(theme: component.theme),
height: 50.0,
cornerRadius: 11.0,

View File

@ -118,7 +118,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let closeButton = Child(Button.self)
let title = Child(MultilineTextComponent.self)
let star = Child(GiftAvatarComponent.self)
let description = Child(BalancedTextComponent.self)
let amount = Child(BalancedTextComponent.self)
let amountStar = Child(BundleIconComponent.self)
let description = Child(MultilineTextComponent.self)
let table = Child(TableComponent.self)
let additional = Child(BalancedTextComponent.self)
let button = Child(SolidRoundedButtonComponent.self)
@ -157,6 +159,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
)
let titleText: String
let amountText: String
let descriptionText: String
let additionalText: String
let buttonText: String
@ -164,6 +167,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
let count: Int64
let transactionId: String?
let date: Int32
let via: String?
let toPeer: EnginePeer?
let photo: TelegramMediaWebFile?
@ -173,17 +177,24 @@ private final class StarsTransactionSheetContent: CombinedComponent {
switch transaction.peer {
case let .peer(peer):
titleText = transaction.title ?? peer.compactDisplayTitle
via = nil
case .appStore:
titleText = "In-App Purchase"
titleText = strings.Stars_Transaction_AppleTopUp_Title
via = strings.Stars_Transaction_AppleTopUp_Subtitle
case .playMarket:
titleText = "Play Market"
titleText = strings.Stars_Transaction_GoogleTopUp_Title
via = strings.Stars_Transaction_GoogleTopUp_Subtitle
case .premiumBot:
titleText = "Premium Bot"
titleText = strings.Stars_Transaction_PremiumBotTopUp_Title
via = strings.Stars_Transaction_PremiumBotTopUp_Subtitle
case .fragment:
titleText = "Fragment"
titleText = strings.Stars_Transaction_FragmentTopUp_Title
via = strings.Stars_Transaction_FragmentTopUp_Subtitle
case .unsupported:
titleText = "Unsupported"
titleText = strings.Stars_Transaction_Unsupported_Title
via = nil
}
descriptionText = transaction.description ?? ""
count = transaction.count
transactionId = transaction.id
@ -196,7 +207,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
photo = transaction.photo
case let .receipt(receipt):
titleText = receipt.invoiceMedia.title
descriptionText = receipt.invoiceMedia.description
count = (receipt.invoice.prices.first?.amount ?? receipt.invoiceMedia.totalAmount) * -1
via = nil
transactionId = receipt.transactionId
date = receipt.date
if let peer = state.peerMap[receipt.botPaymentId] {
@ -207,22 +220,15 @@ private final class StarsTransactionSheetContent: CombinedComponent {
photo = receipt.invoiceMedia.photo
}
let formattedAmount = presentationStringsFormattedNumber(abs(Int32(count)), dateTimeFormat.decimalSeparator)
if count < 0 {
amountText = "- \(formattedAmount)"
} else {
amountText = "+ \(formattedAmount)"
}
additionalText = strings.Stars_Transaction_Terms
buttonText = strings.Common_OK
if count < 0 {
descriptionText = " - \(count * -1) # "
} else {
descriptionText = " + \(count) # "
}
let descriptionAttributedText = NSMutableAttributedString(string: descriptionText, font: Font.semibold(18.0), textColor: descriptionText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor)
if let range = descriptionAttributedText.string.range(of: "#"), let chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: UIColor(rgb: 0xf09903)) {
descriptionAttributedText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: descriptionAttributedText.string))
descriptionAttributedText.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: descriptionAttributedText.string))
descriptionAttributedText.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: descriptionAttributedText.string))
}
let title = title.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
@ -254,9 +260,10 @@ private final class StarsTransactionSheetContent: CombinedComponent {
transition: .immediate
)
let description = description.update(
let amountAttributedText = NSMutableAttributedString(string: amountText, font: Font.semibold(17.0), textColor: amountText.hasPrefix("-") ? theme.list.itemDestructiveColor : theme.list.itemDisclosureActions.constructive.fillColor)
let amount = amount.update(
component: BalancedTextComponent(
text: .plain(descriptionAttributedText),
text: .plain(amountAttributedText),
horizontalAlignment: .center,
maximumNumberOfLines: 0,
lineSpacing: 0.2
@ -264,7 +271,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
transition: .immediate
)
let amountStar = amountStar.update(
component: BundleIconComponent(
name: "Premium/Stars/StarMedium",
tintColor: nil
),
availableSize: context.availableSize,
transition: .immediate
)
let tableFont = Font.regular(15.0)
let tableTextColor = theme.list.itemPrimaryTextColor
let tableLinkColor = theme.list.itemAccentColor
@ -294,6 +310,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
)
)
))
} else if let via {
tableItems.append(.init(
id: "via",
title: strings.Stars_Transaction_Via,
component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: via, font: tableFont, textColor: tableTextColor)))
)
))
}
if let transactionId {
@ -393,10 +417,35 @@ private final class StarsTransactionSheetContent: CombinedComponent {
var originY: CGFloat = 0.0
originY += star.size.height - 23.0
context.add(description
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0))
if !descriptionText.isEmpty {
let description = description.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: descriptionText,
font: Font.regular(15.0),
textColor: theme.actionSheet.primaryTextColor,
paragraphAlignment: .center
)),
horizontalAlignment: .center,
maximumNumberOfLines: 3
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
context.add(description
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0))
)
originY += description.size.height + 10.0
}
context.add(amount
.position(CGPoint(x: context.availableSize.width / 2.0 - 10.0, y: originY + amount.size.height / 2.0))
)
originY += description.size.height + 20.0
context.add(amountStar
.position(CGPoint(x: context.availableSize.width / 2.0 + amount.size.width / 2.0 + amountStar.size.width / 2.0 - 7.0, y: originY + amountStar.size.height / 2.0))
)
originY += amount.size.height + 20.0
context.add(table
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
@ -1054,7 +1103,6 @@ private final class TransactionCellComponent: Component {
let size = CGSize(width: textSize.width + spacing + buttonSize.width, height: textSize.height)
let buttonFrame = CGRect(origin: CGPoint(x: textSize.width + spacing, y: floorToScreenPixels((size.height - buttonSize.height) / 2.0)), size: buttonSize)
if let buttonView = self.button.view {
if buttonView.superview == nil {
@ -1063,7 +1111,7 @@ private final class TransactionCellComponent: Component {
transition.setFrame(view: buttonView, frame: buttonFrame)
}
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize)
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + 1.0), size: textSize)
if let textView = self.text.view {
if textView.superview == nil {
self.addSubview(textView)

View File

@ -203,7 +203,7 @@ final class StarsTransactionsListPanelComponent: Component {
separatorView = UIView()
self.separatorViews[id] = separatorView
self.addSubview(separatorView)
self.scrollView.addSubview(separatorView)
}
separatorView.backgroundColor = environment.theme.list.itemBlocksSeparatorColor
@ -213,33 +213,38 @@ final class StarsTransactionsListPanelComponent: Component {
let itemTitle: String
let itemSubtitle: String?
let itemDate: String
let itemLabel: NSAttributedString
switch item.transaction.peer {
case let .peer(peer):
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
itemSubtitle = item.transaction.title
itemLabel = NSAttributedString(string: "- \(item.transaction.count * -1)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDestructiveColor)
case .appStore:
itemTitle = "Stars Top-Up"
itemSubtitle = "via App Store"
itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemTitle = environment.strings.Stars_Intro_Transaction_AppleTopUp_Title
itemSubtitle = environment.strings.Stars_Intro_Transaction_AppleTopUp_Subtitle
case .playMarket:
itemTitle = "Stars Top-Up"
itemSubtitle = "via Play Market"
itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemTitle = environment.strings.Stars_Intro_Transaction_GoogleTopUp_Title
itemSubtitle = environment.strings.Stars_Intro_Transaction_GoogleTopUp_Subtitle
case .fragment:
itemTitle = "Stars Top-Up"
itemSubtitle = "via Fragment"
itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemTitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Title
itemSubtitle = environment.strings.Stars_Intro_Transaction_FragmentTopUp_Subtitle
case .premiumBot:
itemTitle = "Stars Top-Up"
itemSubtitle = "via Premium Bot"
itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemTitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Title
itemSubtitle = environment.strings.Stars_Intro_Transaction_PremiumBotTopUp_Subtitle
case .unsupported:
itemTitle = "Unsupported"
itemTitle = environment.strings.Stars_Intro_Transaction_Unsupported_Title
itemSubtitle = nil
itemLabel = NSAttributedString(string: "+ \(item.transaction.count)", font: Font.medium(fontBaseDisplaySize), textColor: environment.theme.list.itemDisclosureActions.constructive.fillColor)
}
let itemLabel: NSAttributedString
let labelString: String
let formattedLabel = presentationStringsFormattedNumber(abs(Int32(item.transaction.count)), environment.dateTimeFormat.decimalSeparator)
if item.transaction.count < 0 {
labelString = "- \(formattedLabel)"
} else {
labelString = "+ \(formattedLabel)"
}
itemLabel = NSAttributedString(string: labelString, font: Font.medium(fontBaseDisplaySize), textColor: labelString.hasPrefix("-") ? environment.theme.list.itemDestructiveColor : environment.theme.list.itemDisclosureActions.constructive.fillColor)
itemDate = stringForMediumCompactDate(timestamp: item.transaction.date, strings: environment.strings, dateTimeFormat: environment.dateTimeFormat)
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
@ -280,7 +285,7 @@ final class StarsTransactionsListPanelComponent: Component {
component: AnyComponent(ListActionItemComponent(
theme: environment.theme,
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0),
contentInsets: UIEdgeInsets(top: 9.0, left: environment.containerInsets.left, bottom: 8.0, right: environment.containerInsets.right),
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(AvatarComponent(context: component.context, theme: environment.theme, peer: item.transaction.peer))), false),
icon: nil,
accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(LabelComponent(text: itemLabel))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
@ -301,7 +306,7 @@ final class StarsTransactionsListPanelComponent: Component {
}
itemTransition.setFrame(view: itemComponentView, frame: itemFrame)
}
let sideInset: CGFloat = 60.0
let sideInset: CGFloat = 60.0 + environment.containerInsets.left
itemTransition.setFrame(view: separatorView, frame: CGRect(x: sideInset, y: itemFrame.maxY, width: itemFrame.width - sideInset, height: UIScreenPixel))
}
}
@ -577,7 +582,7 @@ private final class LabelComponent: CombinedComponent {
let iconSize = CGSize(width: 20.0, height: 20.0)
let icon = icon.update(
component: BundleIconComponent(
name: "Premium/Stars/Star",
name: "Premium/Stars/StarLarge",
tintColor: nil
),
availableSize: iconSize,
@ -592,7 +597,7 @@ private final class LabelComponent: CombinedComponent {
.position(CGPoint(x: text.size.width / 2.0, y: size.height / 2.0))
)
context.add(icon
.position(CGPoint(x: totalWidth - iconSize.width / 2.0, y: size.height / 2.0))
.position(CGPoint(x: totalWidth - iconSize.width / 2.0, y: size.height / 2.0 - UIScreenPixel))
)
return size
}

View File

@ -623,7 +623,9 @@ final class StarsTransactionsPanelContainerComponent: Component {
}
}
let _ = self.header.update(
let sideInset: CGFloat = 16.0
let condensedPanelWidth: CGFloat = availableSize.width - sideInset * 2.0
let headerSize = self.header.update(
transition: transition,
component: AnyComponent(StarsTransactionsHeaderComponent(
theme: component.theme,
@ -648,13 +650,13 @@ final class StarsTransactionsPanelContainerComponent: Component {
}
)),
environment: {},
containerSize: topPanelFrame.size
containerSize: CGSize(width: condensedPanelWidth, height: topPanelFrame.size.height)
)
if let headerView = self.header.view {
if headerView.superview == nil {
self.addSubview(headerView)
}
transition.setFrame(view: headerView, frame: topPanelFrame)
transition.setFrame(view: headerView, frame: CGRect(origin: topPanelFrame.origin.offsetBy(dx: sideInset, dy: 0.0), size: headerSize))
}
let childEnvironment = StarsTransactionsPanelEnvironment(

View File

@ -15,7 +15,9 @@ import BalancedTextComponent
import Markdown
import PremiumStarComponent
import ListSectionComponent
import BundleIconComponent
import TextFormat
import UndoUI
final class StarsTransactionsScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -90,7 +92,9 @@ final class StarsTransactionsScreenComponent: Component {
private let balanceView = ComponentView<Empty>()
private let topBalanceView = ComponentView<Empty>()
private let topBalanceTitleView = ComponentView<Empty>()
private let topBalanceValueView = ComponentView<Empty>()
private let topBalanceIconView = ComponentView<Empty>()
private let panelContainer = ComponentView<StarsTransactionsPanelContainerEnvironment>()
@ -241,8 +245,15 @@ final class StarsTransactionsScreenComponent: Component {
panelContainerView.updateNavigationMergeFactor(value: 1.0 - expansionDistanceFactor, transition: transition)
}
if let topBalanceView = self.topBalanceView.view {
topBalanceView.alpha = 1.0 - expansionDistanceFactor
let topBalanceAlpha = 1.0 - expansionDistanceFactor
if let view = self.topBalanceTitleView.view {
view.alpha = topBalanceAlpha
}
if let view = self.topBalanceValueView.view {
view.alpha = topBalanceAlpha
}
if let view = self.topBalanceIconView.view {
view.alpha = topBalanceAlpha
}
}
@ -408,36 +419,67 @@ final class StarsTransactionsScreenComponent: Component {
starTransition.setBounds(view: titleView, bounds: CGRect(origin: .zero, size: titleSize))
}
let textFont = Font.regular(14.0)
let boldTextFont = Font.semibold(14.0)
let textColor = environment.theme.actionSheet.primaryTextColor
let linkColor = environment.theme.actionSheet.controlAccentColor
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
return (TelegramTextAttributes.URL, contents)
})
let balanceAttributedString = parseMarkdownIntoAttributedString(" \(environment.strings.Stars_Intro_Balance)\n # **\(self.starsState?.balance ?? 0)**", attributes: markdownAttributes, textAlignment: .right).mutableCopy() as! NSMutableAttributedString
if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: UIColor(rgb: 0xf09903)) {
balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: balanceAttributedString.string))
}
let topBalanceSize = self.topBalanceView.update(
let topBalanceTitleSize = self.topBalanceTitleView.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(balanceAttributedString),
horizontalAlignment: .right,
maximumNumberOfLines: 0,
lineSpacing: 0.1
text: .plain(NSAttributedString(
string: environment.strings.Stars_Intro_Balance,
font: Font.regular(14.0),
textColor: environment.theme.actionSheet.primaryTextColor
)),
maximumNumberOfLines: 1
)),
environment: {},
containerSize: CGSize(width: 120.0, height: 100.0)
)
if let topBalanceView = self.topBalanceView.view {
if topBalanceView.superview == nil {
topBalanceView.alpha = 0.0
self.addSubview(topBalanceView)
let topBalanceValueSize = self.topBalanceValueView.update(
transition: .immediate,
component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: presentationStringsFormattedNumber(Int32(self.starsState?.balance ?? 0), environment.dateTimeFormat.decimalSeparator),
font: Font.semibold(14.0),
textColor: environment.theme.actionSheet.primaryTextColor
)),
maximumNumberOfLines: 1
)),
environment: {},
containerSize: CGSize(width: 120.0, height: 100.0)
)
let topBalanceIconSize = self.topBalanceIconView.update(
transition: .immediate,
component: AnyComponent(BundleIconComponent(name: "Premium/Stars/StarSmall", tintColor: nil)),
environment: {},
containerSize: availableSize
)
let navigationHeight = environment.navigationHeight - environment.statusBarHeight
let topBalanceOriginY = environment.statusBarHeight + (navigationHeight - topBalanceTitleSize.height - topBalanceValueSize.height) / 2.0
let topBalanceTitleFrame = CGRect(origin: CGPoint(x: availableSize.width - topBalanceTitleSize.width - 16.0 - environment.safeInsets.right, y: topBalanceOriginY), size: topBalanceTitleSize)
if let topBalanceTitleView = self.topBalanceTitleView.view {
if topBalanceTitleView.superview == nil {
topBalanceTitleView.alpha = 0.0
self.addSubview(topBalanceTitleView)
}
starTransition.setFrame(view: topBalanceView, frame: CGRect(origin: CGPoint(x: availableSize.width - topBalanceSize.width - 16.0, y: 56.0), size: topBalanceSize))
starTransition.setFrame(view: topBalanceTitleView, frame: topBalanceTitleFrame)
}
let topBalanceValueFrame = CGRect(origin: CGPoint(x: availableSize.width - topBalanceValueSize.width - 16.0 - environment.safeInsets.right, y: topBalanceTitleFrame.maxY), size: topBalanceValueSize)
if let topBalanceValueView = self.topBalanceValueView.view {
if topBalanceValueView.superview == nil {
topBalanceValueView.alpha = 0.0
self.addSubview(topBalanceValueView)
}
starTransition.setFrame(view: topBalanceValueView, frame: topBalanceValueFrame)
}
let topBalanceIconFrame = CGRect(origin: CGPoint(x: topBalanceValueFrame.minX - topBalanceIconSize.width - 2.0, y: floorToScreenPixels(topBalanceValueFrame.midY - topBalanceIconSize.height / 2.0) - UIScreenPixel), size: topBalanceIconSize)
if let topBalanceIconView = self.topBalanceIconView.view {
if topBalanceIconView.superview == nil {
topBalanceIconView.alpha = 0.0
self.addSubview(topBalanceIconView)
}
starTransition.setFrame(view: topBalanceIconView, frame: topBalanceIconFrame)
}
contentHeight += 181.0
@ -678,10 +720,26 @@ public final class StarsTransactionsScreen: ViewControllerComponentContainer {
return
}
self.starsContext.add(balance: stars)
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/StarMedium")!,
title: presentationData.strings.Stars_Intro_PurchasedTitle,
text: presentationData.strings.Stars_Intro_PurchasedText(presentationData.strings.Stars_Intro_PurchasedText_Stars(Int32(stars))).string,
round: false,
undoText: nil
),
elevatedLayout: false,
action: { _ in return true})
self.present(resultController, in: .window(.root))
})
self.push(controller)
})
}
self.starsContext.load()
}
required public init(coder aDecoder: NSCoder) {

View File

@ -11,11 +11,12 @@ import ViewControllerComponent
import SheetComponent
import BalancedTextComponent
import MultilineTextComponent
import BundleIconComponent
import ButtonComponent
import PremiumStarComponent
import ItemListUI
import UndoUI
import AccountContext
import PremiumStarComponent
import ButtonComponent
private final class SheetContent: CombinedComponent {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -55,10 +56,10 @@ private final class SheetContent: CombinedComponent {
final class State: ComponentState {
var cachedCloseImage: (UIImage, PresentationTheme)?
var cachedChevronImage: (UIImage, PresentationTheme)?
var cachedStarImage: (UIImage, PresentationTheme)?
private let context: AccountContext
private let starsContext: StarsContext
private let source: BotPaymentInvoiceSource
private let invoice: TelegramMediaInvoice
@ -67,18 +68,27 @@ private final class SheetContent: CombinedComponent {
private(set) var balance: Int64?
private(set) var form: BotPaymentForm?
private var stateDisposable: Disposable?
private var optionsDisposable: Disposable?
private(set) var options: [StarsTopUpOption] = []
private(set) var options: [StarsTopUpOption] = [] {
didSet {
self.optionsPromise.set(self.options)
}
}
private let optionsPromise = ValuePromise<[StarsTopUpOption]?>(nil)
var inProgress = false
init(
context: AccountContext,
starsContext: StarsContext,
source: BotPaymentInvoiceSource,
invoice: TelegramMediaInvoice,
inputData: Signal<(StarsContext.State, BotPaymentForm, EnginePeer?)?, NoError>
) {
self.context = context
self.starsContext = starsContext
self.source = source
self.invoice = invoice
@ -94,7 +104,7 @@ private final class SheetContent: CombinedComponent {
self.peer = inputData?.2
self.updated(transition: .immediate)
if self.optionsDisposable != nil {
if self.optionsDisposable == nil, let balance = self.balance, balance < self.invoice.totalAmount {
self.optionsDisposable = (context.engine.payments.starsTopUpOptions()
|> deliverOnMainQueue).start(next: { [weak self] options in
guard let self else {
@ -104,19 +114,32 @@ private final class SheetContent: CombinedComponent {
})
}
})
self.stateDisposable = (starsContext.state
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let self else {
return
}
self.balance = state?.balance
self.updated(transition: .immediate)
})
}
deinit {
self.peerDisposable?.dispose()
self.stateDisposable?.dispose()
self.optionsDisposable?.dispose()
}
func buy(requestTopUp: (@escaping () -> Void) -> Void, completion: @escaping () -> Void) {
func buy(requestTopUp: @escaping (@escaping () -> Void) -> Void, completion: @escaping () -> Void) {
guard let form, let balance else {
return
}
let action = {
let action = { [weak self] in
guard let self else {
return
}
self.inProgress = true
self.updated()
@ -127,8 +150,38 @@ private final class SheetContent: CombinedComponent {
}
if balance < self.invoice.totalAmount {
requestTopUp({
action()
if self.options.isEmpty {
self.inProgress = true
self.updated()
}
let _ = (self.optionsPromise.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak self] _ in
if let self {
self.inProgress = false
self.updated()
requestTopUp({ [weak self] in
guard let self else {
return
}
self.inProgress = true
self.updated()
let _ = (self.starsContext.state
|> filter { state in
if let state {
return !state.flags.contains(.isPendingBalance)
}
return false
}
|> take(1)
|> deliverOnMainQueue).start(next: { _ in
action()
})
})
}
})
} else {
action()
@ -137,7 +190,7 @@ private final class SheetContent: CombinedComponent {
}
func makeState() -> State {
return State(context: self.context, source: self.source, invoice: self.invoice, inputData: self.inputData)
return State(context: self.context, starsContext: self.starsContext, source: self.source, invoice: self.invoice, inputData: self.inputData)
}
static var body: Body {
@ -146,8 +199,10 @@ private final class SheetContent: CombinedComponent {
let closeButton = Child(Button.self)
let title = Child(Text.self)
let text = Child(BalancedTextComponent.self)
let balanceText = Child(MultilineTextComponent.self)
let button = Child(ButtonComponent.self)
let balanceTitle = Child(MultilineTextComponent.self)
let balanceValue = Child(MultilineTextComponent.self)
let balanceIcon = Child(BundleIconComponent.self)
return { context in
let environment = context.environment[EnvironmentType.self]
@ -261,31 +316,47 @@ private final class SheetContent: CombinedComponent {
contentSize.height += text.size.height
contentSize.height += 28.0
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== theme {
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Premium/Stars/Star"), color: UIColor(rgb: 0xf09903))!, theme)
}
let balanceAttributedString = NSMutableAttributedString(string: strings.Stars_Transfer_Balance, font: Font.regular(14.0), textColor: textColor)
balanceAttributedString.append(NSMutableAttributedString(string: "\n # \(state.balance ?? 0)", font: Font.semibold(16.0), textColor: textColor))
if let range = balanceAttributedString.string.range(of: "#"), let chevronImage = state.cachedChevronImage?.0 {
balanceAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.foregroundColor, value: UIColor(rgb: 0xf09903), range: NSRange(range, in: balanceAttributedString.string))
balanceAttributedString.addAttribute(.baselineOffset, value: 2.0, range: NSRange(range, in: balanceAttributedString.string))
}
let balanceText = balanceText.update(
let balanceTitle = balanceTitle.update(
component: MultilineTextComponent(
text: .plain(balanceAttributedString),
horizontalAlignment: .left,
maximumNumberOfLines: 0,
lineSpacing: 0.25
text: .plain(NSAttributedString(
string: environment.strings.Stars_Transfer_Balance,
font: Font.regular(14.0),
textColor: textColor
)),
maximumNumberOfLines: 1
),
availableSize: CGSize(width: constrainedTitleWidth, height: context.availableSize.height),
availableSize: context.availableSize,
transition: .immediate
)
context.add(balanceText
.position(CGPoint(x: 16.0 + balanceText.size.width / 2.0, y: 31.0))
let balanceValue = balanceValue.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: presentationStringsFormattedNumber(Int32(state.balance ?? 0), environment.dateTimeFormat.decimalSeparator),
font: Font.semibold(16.0),
textColor: textColor
)),
maximumNumberOfLines: 1
),
availableSize: context.availableSize,
transition: .immediate
)
let balanceIcon = balanceIcon.update(
component: BundleIconComponent(name: "Premium/Stars/StarMedium", tintColor: nil),
availableSize: context.availableSize,
transition: .immediate
)
let topBalanceOriginY = 11.0
context.add(balanceTitle
.position(CGPoint(x: 16.0 + environment.safeInsets.left + balanceTitle.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height / 2.0))
)
context.add(balanceIcon
.position(CGPoint(x: 16.0 + environment.safeInsets.left + balanceIcon.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0 - UIScreenPixel))
)
context.add(balanceValue
.position(CGPoint(x: 16.0 + environment.safeInsets.left + balanceIcon.size.width + 3.0 + balanceValue.size.width / 2.0, y: topBalanceOriginY + balanceTitle.size.height + balanceValue.size.height / 2.0))
)
if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme {
state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: .white)!, theme)
}
@ -327,7 +398,9 @@ private final class SheetContent: CombinedComponent {
requiredStars: invoice.totalAmount,
completion: { [weak starsContext] stars in
starsContext?.add(balance: stars)
completion()
Queue.mainQueue().after(0.1) {
completion()
}
}
)
controller?.push(purchaseController)
@ -336,7 +409,7 @@ private final class SheetContent: CombinedComponent {
let resultController = UndoOverlayController(
presentationData: presentationData,
content: .image(
image: UIImage(bundleImageName: "Premium/Stars/Star")!,
image: UIImage(bundleImageName: "Premium/Stars/StarMedium")!,
title: presentationData.strings.Stars_Transfer_PurchasedTitle,
text: presentationData.strings.Stars_Transfer_PurchasedText(invoice.title, botTitle, presentationData.strings.Stars_Transfer_Purchased_Stars(Int32(invoice.totalAmount))).string,
round: false,
@ -347,6 +420,8 @@ private final class SheetContent: CombinedComponent {
controller?.present(resultController, in: .window(.root))
controller?.dismissAnimated()
starsContext.load()
})
}
),
@ -491,6 +566,8 @@ public final class StarsTransferScreen: ViewControllerComponentContainer {
)
self.navigationPresentation = .flatModal
starsContext.load()
}
required public init(coder aDecoder: NSCoder) {

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "transactionstar_20 (2).pdf",
"filename" : "balancestar_48 (2).pdf",
"idiom" : "universal"
}
],

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "balancestar_48 (2).pdf",
"filename" : "Star20.pdf",
"idiom" : "universal"
}
],

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "star_18.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "star_16 (2).pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -127,6 +127,7 @@ public final class AccountContextImpl: AccountContext {
public let wallpaperUploadManager: WallpaperUploadManager?
private let themeUpdateManager: ThemeUpdateManager?
public let inAppPurchaseManager: InAppPurchaseManager?
public let starsContext: StarsContext?
public let peerChannelMemberCategoriesContextsManager = PeerChannelMemberCategoriesContextsManager()
@ -294,11 +295,13 @@ public final class AccountContextImpl: AccountContext {
self.themeUpdateManager = ThemeUpdateManagerImpl(sharedContext: sharedContext, account: account)
self.inAppPurchaseManager = InAppPurchaseManager(engine: self.engine)
self.starsContext = self.engine.payments.peerStarsContext(peerId: account.peerId)
} else {
self.prefetchManager = nil
self.wallpaperUploadManager = nil
self.themeUpdateManager = nil
self.inAppPurchaseManager = nil
self.starsContext = nil
}
if let locationManager = self.sharedContextImpl.locationManager, sharedContext.applicationBindings.isMainApp && !temp {

View File

@ -131,8 +131,14 @@ extension ChatControllerImpl {
switch navigation {
case let .info(params):
var recommendedChannels = false
if let params, params.switchToRecommendedChannels {
recommendedChannels = true
if let params {
if params.switchToRecommendedChannels {
recommendedChannels = true
}
if params.ignoreInSavedMessages && currentPeerId == self.context.account.peerId {
self.playShakeAnimation()
return
}
}
self.navigationButtonAction(.openChatInfo(expandAvatar: expandAvatar, recommendedChannels: recommendedChannels))
case let .chat(textInputState, _, _):

View File

@ -45,7 +45,13 @@ extension ChatControllerImpl: MFMessageComposeViewControllerDelegate {
source = .location(ChatMessageContextLocationContentSource(controller: self, location: messageNode.view.convert(messageNode.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y)))
} else {
source = .extracted(ChatMessagePhoneContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode))
// source = .extracted(ChatMessageContextExtractedContentSource(chatController: self, chatNode: self.chatDisplayNode, engine: self.context.engine, message: message, selectAll: false))
}
let phoneNumber: String
if let peer, case let .user(user) = peer, let phone = user.phone {
phoneNumber = "+\(phone)"
} else {
phoneNumber = number
}
var items: [ContextMenuItem] = []
@ -55,7 +61,7 @@ extension ChatControllerImpl: MFMessageComposeViewControllerDelegate {
return
}
let basicData = DeviceContactBasicData(firstName: "", lastName: "", phoneNumbers: [
DeviceContactPhoneNumberData(label: "", value: number)
DeviceContactPhoneNumberData(label: "", value: phoneNumber)
])
let contactData = DeviceContactExtendedData(basicData: basicData, middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: "")
@ -121,7 +127,7 @@ extension ChatControllerImpl: MFMessageComposeViewControllerDelegate {
guard let self else {
return
}
self.openUrl("tel:\(number)", concealed: false)
self.openUrl("tel:\(phoneNumber)", concealed: false)
}))
)
}
@ -158,7 +164,7 @@ extension ChatControllerImpl: MFMessageComposeViewControllerDelegate {
guard let self else {
return
}
self.openPeer(peer: peer, navigation: .info(nil), fromMessage: nil)
self.openPeer(peer: peer, navigation: .info(ChatControllerInteractionNavigateToPeer.InfoParams(ignoreInSavedMessages: true)), fromMessage: nil)
}))
)
} else {

View File

@ -2941,8 +2941,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
return .single(nil)
})
if invoice.currency == "XTR" {
let starsContext = strongSelf.context.engine.payments.peerStarsContext(peerId: strongSelf.context.account.peerId)
if invoice.currency == "XTR", let starsContext = strongSelf.context.starsContext {
let starsInputData = combineLatest(
inputData.get(),
starsContext.state

View File

@ -191,7 +191,7 @@ private enum CreateChannelEntry: ItemListNodeEntry {
let arguments = arguments as! CreateChannelArguments
switch self {
case let .channelInfo(_, _, dateTimeFormat, peer, state, avatar):
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in
arguments.updateEditingName(editingName)
}, editingNameCompleted: {
arguments.focusOnDescription()

View File

@ -318,7 +318,7 @@ private enum CreateGroupEntry: ItemListNodeEntry {
let arguments = arguments as! CreateGroupArguments
switch self {
case let .groupInfo(_, _, dateTimeFormat, peer, state, avatar):
return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in
return ItemListAvatarAndNameInfoItem(itemContext: .accountContext(arguments.context), presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .editSettings, peer: peer.flatMap(EnginePeer.init), presence: nil, memberCount: nil, state: state, sectionId: ItemListSectionId(self.section), style: .blocks(withTopInset: false, withExtendedBottomInset: false), editingNameUpdated: { editingName in
arguments.updateEditingName(editingName)
}, editingNameCompleted: {
arguments.done()

View File

@ -824,8 +824,7 @@ func openResolvedUrlImpl(
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
return .single(nil)
})
if invoice.currency == "XTR" {
let starsContext = context.engine.payments.peerStarsContext(peerId: context.account.peerId)
if invoice.currency == "XTR", let starsContext = context.starsContext {
let starsInputData = combineLatest(
inputData.get(),
starsContext.state

View File

@ -864,8 +864,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|> `catch` { _ -> Signal<BotCheckoutController.InputData?, NoError> in
return .single(nil)
})
if invoice.currency == "XTR" {
let starsContext = strongSelf.context.engine.payments.peerStarsContext(peerId: strongSelf.context.account.peerId)
if invoice.currency == "XTR", let starsContext = strongSelf.context.starsContext {
let starsInputData = combineLatest(
inputData.get(),
starsContext.state