Skip to content

Commit 99192d8

Browse files
committed
Merge branch 'beta' into release
# Conflicts: # Telegram-Mac/ChatController.swift # Telegram-Mac/Info.plist # Telegram.xcodeproj/project.pbxproj # TelegramShare/Info.plist
2 parents 67b5f6e + 48bbb6a commit 99192d8

File tree

148 files changed

+8030
-1268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+8030
-1268
lines changed

Telegram-Mac/AccountContext.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ extension AppConfiguration {
230230
return defaultValue
231231
}
232232
}
233+
func getGeneralValue64(_ key: String, orElse defaultValue: Int64) -> Int64 {
234+
if let value = self.data?[key] as? Double {
235+
return Int64(value)
236+
} else {
237+
return defaultValue
238+
}
239+
}
240+
func getGeneralValueDouble(_ key: String, orElse defaultValue: Double) -> Double {
241+
if let value = self.data?[key] as? Double {
242+
return Double(value)
243+
} else {
244+
return defaultValue
245+
}
246+
}
233247
func getStringValue(_ key: String, orElse defaultValue: String) -> String {
234248
if let value = self.data?[key] as? String {
235249
return value
@@ -440,13 +454,23 @@ final class AccountContext {
440454
let networkStatusManager: NetworkStatusManager
441455
let inAppPurchaseManager: InAppPurchaseManager
442456
let starsContext: StarsContext
457+
let tonContext: StarsContext
443458
let starsSubscriptionsContext: StarsSubscriptionsContext
444459
let currentCountriesConfiguration: Atomic<CountriesConfiguration> = Atomic(value: CountriesConfiguration(countries: loadCountryCodes()))
445460
private(set) var contentConfig: ContentSettingsConfiguration = .default
446461
private let _countriesConfiguration = Promise<CountriesConfiguration>()
447462
var countriesConfiguration: Signal<CountriesConfiguration, NoError> {
448463
return self._countriesConfiguration.get()
449464
}
465+
466+
func currencyContext(_ currency: CurrencyAmount.Currency) -> StarsContext {
467+
switch currency {
468+
case .stars:
469+
return starsContext
470+
case .ton:
471+
return tonContext
472+
}
473+
}
450474

451475
#endif
452476
private(set) var timeDifference:TimeInterval = 0
@@ -692,6 +716,7 @@ final class AccountContext {
692716
self.reactions = Reactions(engine)
693717
self.dockControl = DockControl(engine, accountManager: sharedContext.accountManager)
694718
self.starsContext = engine.payments.peerStarsContext()
719+
self.tonContext = engine.payments.peerTonContext()
695720
self.starsSubscriptionsContext = engine.payments.peerStarsSubscriptionsContext(starsContext: self.starsContext)
696721

697722
_ = self.engine.payments.keepStarGiftsUpdated().start()

Telegram-Mac/AccountViewController.swift

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,14 @@ fileprivate final class AccountInfoArguments {
7171
let openPremium:(Bool)->Void
7272
let giftPremium:()->Void
7373
let stars:()->Void
74+
let ton:()->Void
7475
let addAccount:([AccountWithInfo])->Void
7576
let setStatus:(Control, TelegramUser)->Void
7677
let runStatusPopover:()->Void
7778
let set2Fa:(TwoStepVeriticationAccessConfiguration?)->Void
7879
let openStory:(StoryInitialIndex?)->Void
7980
let openWebBot:(AttachMenuBot)->Void
80-
init(context: AccountContext, storyList: PeerStoryListContext, presentController:@escaping(ViewController, Bool)->Void, openFaq: @escaping()->Void, ask:@escaping()->Void, openUpdateApp: @escaping() -> Void, openPremium:@escaping(Bool)->Void, giftPremium:@escaping()->Void, addAccount:@escaping([AccountWithInfo])->Void, setStatus:@escaping(Control, TelegramUser)->Void, runStatusPopover:@escaping()->Void, set2Fa:@escaping(TwoStepVeriticationAccessConfiguration?)->Void, openStory:@escaping(StoryInitialIndex?)->Void, openWebBot:@escaping(AttachMenuBot)->Void, stars:@escaping()->Void) {
81+
init(context: AccountContext, storyList: PeerStoryListContext, presentController:@escaping(ViewController, Bool)->Void, openFaq: @escaping()->Void, ask:@escaping()->Void, openUpdateApp: @escaping() -> Void, openPremium:@escaping(Bool)->Void, giftPremium:@escaping()->Void, addAccount:@escaping([AccountWithInfo])->Void, setStatus:@escaping(Control, TelegramUser)->Void, runStatusPopover:@escaping()->Void, set2Fa:@escaping(TwoStepVeriticationAccessConfiguration?)->Void, openStory:@escaping(StoryInitialIndex?)->Void, openWebBot:@escaping(AttachMenuBot)->Void, stars:@escaping()->Void, ton:@escaping()->Void) {
8182
self.context = context
8283
self.storyList = storyList
8384
self.presentController = presentController
@@ -93,6 +94,7 @@ fileprivate final class AccountInfoArguments {
9394
self.openStory = openStory
9495
self.openWebBot = openWebBot
9596
self.stars = stars
97+
self.ton = ton
9698
}
9799
}
98100

@@ -146,7 +148,8 @@ private enum AccountInfoEntry : TableItemListNodeEntry {
146148
case premium(index: Int, viewType: GeneralViewType)
147149
case business(index: Int, viewType: GeneralViewType)
148150
case giftPremium(index: Int, viewType: GeneralViewType)
149-
case stars(index: Int, count: Int64, viewType: GeneralViewType)
151+
case stars(index: Int, count: StarsAmount, viewType: GeneralViewType)
152+
case ton(index: Int, count: StarsAmount, viewType: GeneralViewType)
150153
case about(index: Int, viewType: GeneralViewType)
151154
case faq(index: Int, viewType: GeneralViewType)
152155
case ask(index: Int, viewType: GeneralViewType)
@@ -201,14 +204,16 @@ private enum AccountInfoEntry : TableItemListNodeEntry {
201204
return .index(20)
202205
case .stars:
203206
return .index(21)
204-
case .faq:
207+
case .ton:
205208
return .index(22)
206-
case .ask:
209+
case .faq:
207210
return .index(23)
208-
case .about:
211+
case .ask:
209212
return .index(24)
213+
case .about:
214+
return .index(25)
210215
case let .attach(index, _, _):
211-
return .index(25 + index)
216+
return .index(26 + index)
212217
case let .whiteSpace(index, _):
213218
return .index(1000 + index)
214219
}
@@ -264,6 +269,8 @@ private enum AccountInfoEntry : TableItemListNodeEntry {
264269
return index
265270
case let .stars(index, _, _):
266271
return index
272+
case let .ton(index, _, _):
273+
return index
267274
case let .faq(index, _):
268275
return index
269276
case let .ask(index, _):
@@ -420,7 +427,9 @@ private enum AccountInfoEntry : TableItemListNodeEntry {
420427
case let .giftPremium(_, viewType):
421428
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: strings().accountSettingsSendGift, icon: theme.icons.settingsGiftPremium, activeIcon: theme.icons.settingsGiftPremium, type: .next, viewType: viewType, action: arguments.giftPremium, border:[BorderType.Right], inset:NSEdgeInsets(left: 12, right: 12))
422429
case let .stars(_, stars, viewType):
423-
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: strings().accountSettingsStars, icon: theme.icons.settingsStars, activeIcon: theme.icons.settingsStars, type: .nextContext(stars > 0 ? "\(stars)" : ""), viewType: viewType, action: arguments.stars, border:[BorderType.Right], inset:NSEdgeInsets(left: 12, right: 12))
430+
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: strings().accountSettingsStars, icon: theme.icons.settingsStars, activeIcon: theme.icons.settingsStars, type: .nextContext(stars.value > 0 ? "\(stars.stringValue)" : ""), viewType: viewType, action: arguments.stars, border:[BorderType.Right], inset:NSEdgeInsets(left: 12, right: 12))
431+
case let .ton(_, ton, viewType):
432+
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: strings().accountSettingsTon, icon: theme.icons.settingsWallet, activeIcon: theme.icons.settingsWalletActive, type: .nextContext(ton.value > 0 ? "\(ton.string(.ton))" : ""), viewType: viewType, action: arguments.ton, border:[BorderType.Right], inset:NSEdgeInsets(left: 12, right: 12))
424433
case let .faq(_, viewType):
425434
return GeneralInteractedRowItem(initialSize, stableId: stableId, name: strings().accountSettingsFAQ, icon: theme.icons.settingsFaq, activeIcon: theme.icons.settingsFaqActive, type: .next, viewType: viewType, action: arguments.openFaq, border:[BorderType.Right], inset:NSEdgeInsets(left: 12, right: 12))
426435
case let .ask(_, viewType):
@@ -474,7 +483,7 @@ private enum AccountInfoEntry : TableItemListNodeEntry {
474483
}
475484

476485

477-
private func accountInfoEntries(peerView:PeerView, context: AccountContext, accounts: [AccountWithInfo], language: TelegramLocalization, privacySettings: AccountPrivacySettings?, webSessions: WebSessionsContextState, proxySettings: (ProxySettings, ConnectionStatus), passportVisible: Bool, appUpdateState: Any?, hasFilters: Bool, sessionsCount: Int, unAuthStatus: UNUserNotifications.AuthorizationStatus, has2fa: Bool, twoStepConfiguration: TwoStepVeriticationAccessConfiguration?, storyStats: EngineStorySubscriptions?, attachMenuBots: [AttachMenuBot], stars: StarsContext.State?) -> [AccountInfoEntry] {
486+
private func accountInfoEntries(peerView:PeerView, context: AccountContext, accounts: [AccountWithInfo], language: TelegramLocalization, privacySettings: AccountPrivacySettings?, webSessions: WebSessionsContextState, proxySettings: (ProxySettings, ConnectionStatus), passportVisible: Bool, appUpdateState: Any?, hasFilters: Bool, sessionsCount: Int, unAuthStatus: UNUserNotifications.AuthorizationStatus, has2fa: Bool, twoStepConfiguration: TwoStepVeriticationAccessConfiguration?, storyStats: EngineStorySubscriptions?, attachMenuBots: [AttachMenuBot], stars: StarsContext.State?, ton: StarsContext.State?) -> [AccountInfoEntry] {
478487
var entries:[AccountInfoEntry] = []
479488

480489
var index:Int = 0
@@ -635,8 +644,13 @@ private func accountInfoEntries(peerView:PeerView, context: AccountContext, acco
635644

636645
let stars_purchase_blocked = context.appConfiguration.getBoolValue("stars_purchase_blocked", orElse: true)
637646

638-
if !stars_purchase_blocked, let stars, stars.balance.value > 0 || !stars.transactions.isEmpty {
639-
entries.append(.stars(index: index, count: stars.balance.value, viewType: .singleItem))
647+
if !stars_purchase_blocked, let stars {
648+
entries.append(.stars(index: index, count: stars.balance, viewType: .singleItem))
649+
index += 1
650+
}
651+
652+
if let ton, ton.balance.value > 0 || !ton.transactions.isEmpty {
653+
entries.append(.ton(index: index, count: ton.balance, viewType: .singleItem))
640654
index += 1
641655
}
642656

@@ -997,6 +1011,8 @@ class AccountViewController : TelegramGenericViewController<AccountControllerVie
9971011
openWebBot(bot, context: context)
9981012
}, stars: {
9991013
showModal(with: Star_ListScreen(context: context, source: .account), for: context.window)
1014+
}, ton: {
1015+
showModal(with: Star_ListScreen(context: context, currency: .ton, source: .account), for: context.window)
10001016
})
10011017

10021018
self.arguments = arguments
@@ -1057,8 +1073,8 @@ class AccountViewController : TelegramGenericViewController<AccountControllerVie
10571073
}))
10581074

10591075

1060-
let apply = combineLatest(queue: prepareQueue, context.account.viewTracker.peerView(context.account.peerId), context.sharedContext.activeAccountsWithInfo, appearanceSignal, settings.get(), appUpdateState, hasFilters.get(), sessionsCount, UNUserNotifications.recurrentAuthorizationStatus(context), twoStep, storyStats, acceptBots.get(), context.starsContext.state) |> map { peerView, accounts, appearance, settings, appUpdateState, hasFilters, sessionsCount, unAuthStatus, twoStepConfiguration, storyStats, attachMenuBots, stars -> TableUpdateTransition in
1061-
let entries = accountInfoEntries(peerView: peerView, context: context, accounts: accounts.accounts, language: appearance.language, privacySettings: settings.0, webSessions: settings.1, proxySettings: settings.2, passportVisible: settings.3.0, appUpdateState: appUpdateState, hasFilters: hasFilters, sessionsCount: sessionsCount, unAuthStatus: unAuthStatus, has2fa: settings.3.1, twoStepConfiguration: twoStepConfiguration, storyStats: storyStats, attachMenuBots: attachMenuBots, stars: stars).map {AppearanceWrapperEntry(entry: $0, appearance: appearance)}
1076+
let apply = combineLatest(queue: prepareQueue, context.account.viewTracker.peerView(context.account.peerId), context.sharedContext.activeAccountsWithInfo, appearanceSignal, settings.get(), appUpdateState, hasFilters.get(), sessionsCount, UNUserNotifications.recurrentAuthorizationStatus(context), twoStep, storyStats, acceptBots.get(), context.starsContext.state, context.tonContext.state) |> map { peerView, accounts, appearance, settings, appUpdateState, hasFilters, sessionsCount, unAuthStatus, twoStepConfiguration, storyStats, attachMenuBots, stars, ton -> TableUpdateTransition in
1077+
let entries = accountInfoEntries(peerView: peerView, context: context, accounts: accounts.accounts, language: appearance.language, privacySettings: settings.0, webSessions: settings.1, proxySettings: settings.2, passportVisible: settings.3.0, appUpdateState: appUpdateState, hasFilters: hasFilters, sessionsCount: sessionsCount, unAuthStatus: unAuthStatus, has2fa: settings.3.1, twoStepConfiguration: twoStepConfiguration, storyStats: storyStats, attachMenuBots: attachMenuBots, stars: stars, ton: ton).map {AppearanceWrapperEntry(entry: $0, appearance: appearance)}
10621078
var size = atomicSize.modify {$0}
10631079
size.width = max(size.width, 280)
10641080
return prepareEntries(left: previous.swap(entries), right: entries, arguments: arguments, initialSize: size)
@@ -1192,6 +1208,9 @@ class AccountViewController : TelegramGenericViewController<AccountControllerVie
11921208
}
11931209
return .invokeNext
11941210
}, with: self, for: .Escape, priority:.low)
1211+
1212+
context.starsContext.load(force: true)
1213+
context.tonContext.load(force: true)
11951214
}
11961215

11971216
override func viewWillDisappear(_ animated: Bool) {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//
2+
// AddTonBalanceController.swift
3+
// Telegram
4+
//
5+
// Created by Mikhail Filimonov on 27.06.2025.
6+
// Copyright © 2025 Telegram. All rights reserved.
7+
//
8+
9+
10+
import Cocoa
11+
import TGUIKit
12+
import SwiftSignalKit
13+
import CurrencyFormat
14+
private final class Arguments {
15+
let context: AccountContext
16+
init(context: AccountContext) {
17+
self.context = context
18+
}
19+
}
20+
21+
private struct State : Equatable {
22+
var tonAmount: Int64
23+
}
24+
25+
26+
private func entries(_ state: State, arguments: Arguments) -> [InputDataEntry] {
27+
var entries:[InputDataEntry] = []
28+
29+
var sectionId:Int32 = 0
30+
var index: Int32 = 0
31+
32+
// entries.append(.sectionId(sectionId, type: .normal))
33+
// sectionId += 1
34+
35+
36+
37+
entries.append(.custom(sectionId: sectionId, index: index, value: .none, identifier: .init("header"), equatable: .init(state), comparable: nil, item: { initialSize, stableId in
38+
let attr = NSMutableAttributedString()
39+
let formattedAmount = formatCurrencyAmount(state.tonAmount, currency: TON).prettyCurrencyNumberUsd
40+
attr.append(string: strings().fragmentTonAddFundsHeaderAmount(formattedAmount), color: theme.colors.text, font: .medium(18))
41+
attr.append(string: "\n\n")
42+
attr.append(string: strings().fragmentTonAddFundsHeaderInfo, color: theme.colors.text, font: .normal(.text))
43+
return AnimatedStickerHeaderItem(initialSize, stableId: stableId, context: arguments.context, sticker: LocalAnimatedSticker.diamond, text: attr, bgColor: theme.colors.listBackground)
44+
}))
45+
46+
// entries
47+
48+
entries.append(.sectionId(sectionId, type: .normal))
49+
sectionId += 1
50+
51+
return entries
52+
}
53+
54+
func AddTonBalanceController(context: AccountContext, tonAmount: Int64) -> InputDataModalController {
55+
56+
let actionsDisposable = DisposableSet()
57+
58+
let initialState = State(tonAmount: tonAmount)
59+
60+
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
61+
let stateValue = Atomic(value: initialState)
62+
let updateState: ((State) -> State) -> Void = { f in
63+
statePromise.set(stateValue.modify (f))
64+
}
65+
66+
var getController:(()->ViewController?)? = nil
67+
var close:(()->Void)? = nil
68+
69+
var window:Window {
70+
get {
71+
return bestWindow(context, getController?())
72+
}
73+
}
74+
75+
let arguments = Arguments(context: context)
76+
77+
let signal = statePromise.get() |> deliverOnPrepareQueue |> map { state in
78+
return InputDataSignalValue(entries: entries(state, arguments: arguments))
79+
}
80+
81+
let controller = InputDataController(dataSignal: signal, title: strings().fragmentTonAddFundsTitle)
82+
83+
getController = { [weak controller] in
84+
return controller
85+
}
86+
87+
controller.onDeinit = {
88+
actionsDisposable.dispose()
89+
}
90+
91+
controller.validateData = { _ in
92+
close?()
93+
execute(inapp: .external(link: strings().fragmentTonAddFundsLink, false))
94+
return .none
95+
}
96+
97+
let modalInteractions = ModalInteractions(
98+
acceptTitle: strings().fragmentTonAddFundsAction,
99+
accept: { [weak controller] in
100+
_ = controller?.returnKeyAction()
101+
},
102+
singleButton: true
103+
)
104+
105+
106+
let modalController = InputDataModalController(controller, modalInteractions: modalInteractions)
107+
108+
controller.leftModalHeader = ModalHeaderData(image: theme.icons.modalClose, handler: { [weak modalController] in
109+
modalController?.close()
110+
})
111+
112+
close = { [weak modalController] in
113+
modalController?.close()
114+
}
115+
116+
return modalController
117+
118+
}
119+

0 commit comments

Comments
 (0)