diff --git a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift index 177571d2b1..e5f16c1201 100644 --- a/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift +++ b/submodules/SettingsUI/Sources/Data and Storage/DataAndStorageSettingsController.swift @@ -35,9 +35,8 @@ private final class DataAndStorageControllerArguments { let openBrowserSelection: () -> Void let openIntents: () -> Void let toggleEnableSensitiveContent: (Bool) -> Void - let presentSensitiveContentConfirmation: () -> Void - init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void, presentSensitiveContentConfirmation: @escaping () -> Void) { + init(openStorageUsage: @escaping () -> Void, openNetworkUsage: @escaping () -> Void, openProxy: @escaping () -> Void, openAutomaticDownloadConnectionType: @escaping (AutomaticDownloadConnectionType) -> Void, resetAutomaticDownload: @escaping () -> Void, toggleVoiceUseLessData: @escaping (Bool) -> Void, openSaveIncoming: @escaping (AutomaticSaveIncomingPeerType) -> Void, toggleSaveEditedPhotos: @escaping (Bool) -> Void, togglePauseMusicOnRecording: @escaping (Bool) -> Void, toggleRaiseToListen: @escaping (Bool) -> Void, toggleDownloadInBackground: @escaping (Bool) -> Void, openBrowserSelection: @escaping () -> Void, openIntents: @escaping () -> Void, toggleEnableSensitiveContent: @escaping (Bool) -> Void) { self.openStorageUsage = openStorageUsage self.openNetworkUsage = openNetworkUsage self.openProxy = openProxy @@ -52,7 +51,6 @@ private final class DataAndStorageControllerArguments { self.openBrowserSelection = openBrowserSelection self.openIntents = openIntents self.toggleEnableSensitiveContent = toggleEnableSensitiveContent - self.presentSensitiveContentConfirmation = presentSensitiveContentConfirmation } } @@ -112,7 +110,6 @@ private enum DataAndStorageEntry: ItemListNodeEntry { case connectionHeader(PresentationTheme, String) case connectionProxy(PresentationTheme, String, String) case enableSensitiveContent(String, Bool) - case enableSensitiveContentInfo(String) var section: ItemListSectionId { switch self { @@ -130,7 +127,7 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return DataAndStorageSection.other.rawValue case .connectionHeader, .connectionProxy: return DataAndStorageSection.connection.rawValue - case .enableSensitiveContent, .enableSensitiveContentInfo: + case .enableSensitiveContent: return DataAndStorageSection.enableSensitiveContent.rawValue } } @@ -177,14 +174,12 @@ private enum DataAndStorageEntry: ItemListNodeEntry { return 34 case .raiseToListenInfo: return 35 - case .enableSensitiveContent: - return 36 - case .enableSensitiveContentInfo: - return 37 case .connectionHeader: - return 38 + return 36 case .connectionProxy: - return 39 + return 37 + case .enableSensitiveContent: + return 38 } } @@ -328,12 +323,6 @@ private enum DataAndStorageEntry: ItemListNodeEntry { } else { return false } - case let .enableSensitiveContentInfo(text): - if case .enableSensitiveContentInfo(text) = rhs { - return true - } else { - return false - } } } @@ -432,15 +421,9 @@ private enum DataAndStorageEntry: ItemListNodeEntry { arguments.openProxy() }) case let .enableSensitiveContent(text, value): - return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, enableInteractiveChanges: value, sectionId: self.section, style: .blocks, updated: { updatedValue in - if value { - arguments.toggleEnableSensitiveContent(updatedValue) - } else { - arguments.presentSensitiveContentConfirmation() - } + return ItemListSwitchItem(presentationData: presentationData, title: text, value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.toggleEnableSensitiveContent(value) }, tag: nil) - case let .enableSensitiveContentInfo(text): - return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) } } } @@ -644,14 +627,6 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat entries.append(.raiseToListen(presentationData.theme, presentationData.strings.Settings_RaiseToListen, data.mediaInputSettings.enableRaiseToSpeak)) entries.append(.raiseToListenInfo(presentationData.theme, presentationData.strings.Settings_RaiseToListenInfo)) - -#if DEBUG - if let contentSettingsConfiguration = contentSettingsConfiguration { //, contentSettingsConfiguration.canAdjustSensitiveContent { - entries.append(.enableSensitiveContent("Show 18+ Content", contentSettingsConfiguration.sensitiveContentEnabled)) - entries.append(.enableSensitiveContentInfo("Do not hide media that contain content suitable only for adults.")) - } -#endif - let proxyValue: String if let proxySettings = data.proxySettings, let activeServer = proxySettings.activeServer, proxySettings.enabled { switch activeServer.connection { @@ -665,7 +640,13 @@ private func dataAndStorageControllerEntries(state: DataAndStorageControllerStat } entries.append(.connectionHeader(presentationData.theme, presentationData.strings.ChatSettings_ConnectionType_Title.uppercased())) entries.append(.connectionProxy(presentationData.theme, presentationData.strings.SocksProxySetup_Title, proxyValue)) - + + #if DEBUG + if let contentSettingsConfiguration = contentSettingsConfiguration, contentSettingsConfiguration.canAdjustSensitiveContent { + entries.append(.enableSensitiveContent("Display Sensitive Content", contentSettingsConfiguration.sensitiveContentEnabled)) + } + #endif + return entries } @@ -798,8 +779,6 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da return DataAndStorageData(automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, generatedMediaStoreSettings: generatedMediaStoreSettings, mediaInputSettings: mediaInputSettings, voiceCallSettings: voiceCallSettings, proxySettings: proxySettings) }) - var enableSensitiveContentImpl: (() -> Void)? - let arguments = DataAndStorageControllerArguments(openStorageUsage: { pushControllerImpl?(StorageUsageScreen(context: context, makeStorageUsageExceptionsScreen: { category in return storageUsageExceptionsScreen(context: context, category: category) @@ -903,22 +882,8 @@ public func dataAndStorageController(context: AccountContext, focusOnItemTag: Da } }) updateSensitiveContentDisposable.set(updateRemoteContentSettingsConfiguration(postbox: context.account.postbox, network: context.account.network, sensitiveContentEnabled: value).start()) - }, presentSensitiveContentConfirmation: { - let controller = textAlertController(context: context, title: "18+ Content", text: "Confirm that you are over 18 years old and update your settings to see potentially explicit and sensitive content.", actions: [ - TextAlertAction(type: .genericAction, title: "Cancel", action: { - - }), - TextAlertAction(type: .defaultAction, title: "Confirm", action: { - enableSensitiveContentImpl?() - }) - ]) - presentControllerImpl?(controller, nil) }) - enableSensitiveContentImpl = { - arguments.toggleEnableSensitiveContent(true) - } - let preferencesKey: PostboxViewKey = .preferences(keys: Set([ApplicationSpecificPreferencesKeys.mediaAutoSaveSettings])) let preferences = context.account.postbox.combinedView(keys: [preferencesKey]) |> map { views -> MediaAutoSaveSettings in diff --git a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift index 5eb583a75b..bcc1890cbd 100644 --- a/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsPurchaseScreen/Sources/StarsPurchaseScreen.swift @@ -129,9 +129,7 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent { var peer: EnginePeer? private var disposable: Disposable? - - var cachedChevronImage: (UIImage, PresentationTheme)? - + init( context: AccountContext, peerId: EnginePeer.Id? @@ -553,8 +551,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 +692,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() @@ -765,32 +763,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 ) @@ -890,8 +889,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 diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift index 9e15b901bf..a484bfaab4 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsListPanelComponent.swift @@ -285,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))), @@ -306,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)) } } diff --git a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift index c65b493a29..a8ea6c7deb 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransactionsScreen/Sources/StarsTransactionsScreen.swift @@ -426,7 +426,6 @@ final class StarsTransactionsScreenComponent: Component { font: Font.regular(14.0), textColor: environment.theme.actionSheet.primaryTextColor )), - horizontalAlignment: .right, maximumNumberOfLines: 1 )), environment: {}, @@ -441,7 +440,6 @@ final class StarsTransactionsScreenComponent: Component { font: Font.semibold(14.0), textColor: environment.theme.actionSheet.primaryTextColor )), - horizontalAlignment: .right, maximumNumberOfLines: 1 )), environment: {}, @@ -456,7 +454,7 @@ final class StarsTransactionsScreenComponent: Component { 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, y: topBalanceOriginY), size: topBalanceTitleSize) + 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 @@ -465,7 +463,7 @@ final class StarsTransactionsScreenComponent: Component { starTransition.setFrame(view: topBalanceTitleView, frame: topBalanceTitleFrame) } - let topBalanceValueFrame = CGRect(origin: CGPoint(x: availableSize.width - topBalanceValueSize.width - 16.0, y: topBalanceTitleFrame.maxY), size: topBalanceValueSize) + 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 diff --git a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift index 186639df2d..80b86126df 100644 --- a/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsTransferScreen/Sources/StarsTransferScreen.swift @@ -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,7 +56,6 @@ 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 @@ -68,7 +68,12 @@ private final class SheetContent: CombinedComponent { private(set) var form: BotPaymentForm? 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 @@ -94,7 +99,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 { @@ -111,12 +116,15 @@ private final class SheetContent: CombinedComponent { 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 +135,21 @@ 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({ + action() + }) }) } else { action() @@ -146,8 +167,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,32 +284,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/StarLarge"), color: UIColor(rgb: 0xf09903))!, theme) - } - - let balanceValue = presentationStringsFormattedNumber(Int32(state.balance ?? 0), environment.dateTimeFormat.decimalSeparator) - let balanceAttributedString = NSMutableAttributedString(string: strings.Stars_Transfer_Balance, font: Font.regular(14.0), textColor: textColor) - balanceAttributedString.append(NSMutableAttributedString(string: "\n # \(balanceValue)", 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) }