diff --git a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD new file mode 100644 index 0000000000..4c9aad1501 --- /dev/null +++ b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/BUILD @@ -0,0 +1,29 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "BalanceNeededScreen", + module_name = "BalanceNeededScreen", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/ComponentFlow", + "//submodules/TelegramPresentationData", + "//submodules/SSignalKit/SwiftSignalKit", + "//submodules/AccountContext", + "//submodules/Components/MultilineTextComponent", + "//submodules/Components/BalancedTextComponent", + "//submodules/Components/BundleIconComponent", + "//submodules/Components/ViewControllerComponent", + "//submodules/Components/SheetComponent", + "//submodules/TelegramUI/Components/ButtonComponent", + "//submodules/TelegramUI/Components/LottieComponent", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift new file mode 100644 index 0000000000..c8df11eb3b --- /dev/null +++ b/submodules/TelegramUI/Components/Stars/BalanceNeededScreen/Sources/BalanceNeededScreen.swift @@ -0,0 +1,361 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import ViewControllerComponent +import AccountContext +import SheetComponent +import ButtonComponent +import LottieComponent +import MultilineTextComponent +import BalancedTextComponent +import Markdown +import TelegramStringFormatting +import BundleIconComponent + +private final class BalanceNeededSheetContentComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let action: () -> Void + let dismiss: () -> Void + + init( + action: @escaping () -> Void, + dismiss: @escaping () -> Void + ) { + self.action = action + self.dismiss = dismiss + } + + static func ==(lhs: BalanceNeededSheetContentComponent, rhs: BalanceNeededSheetContentComponent) -> Bool { + return true + } + + final class View: UIView { + private let icon = ComponentView() + private let title = ComponentView() + private let text = ComponentView() + private let button = ComponentView() + + private var cancelButton: ComponentView? + + private var component: BalanceNeededSheetContentComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: BalanceNeededSheetContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + self.state = state + + let environment = environment[EnvironmentType.self].value + + let sideInset: CGFloat = 16.0 + + let cancelButton: ComponentView + if let current = self.cancelButton { + cancelButton = current + } else { + cancelButton = ComponentView() + self.cancelButton = cancelButton + } + let cancelButtonSize = cancelButton.update( + transition: transition, + component: AnyComponent(Button( + content: AnyComponent(Text(text: environment.strings.Common_Cancel, font: Font.regular(17.0), color: environment.theme.list.itemAccentColor)), + action: { [weak self] in + guard let self, let component = self.component else { + return + } + component.dismiss() + } + ).minSize(CGSize(width: 8.0, height: 44.0))), + environment: {}, + containerSize: CGSize(width: 200.0, height: 100.0) + ) + if let cancelButtonView = cancelButton.view { + if cancelButtonView.superview == nil { + self.addSubview(cancelButtonView) + } + transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: 16.0, y: 6.0), size: cancelButtonSize)) + } + + var contentHeight: CGFloat = 0.0 + contentHeight += 32.0 + + let iconSize = self.icon.update( + transition: transition, + component: AnyComponent(LottieComponent( + content: LottieComponent.AppBundleContent(name: "StoryUpgradeSheet"), + color: nil, + startingPosition: .begin, + size: CGSize(width: 100.0, height: 100.0) + )), + environment: {}, + containerSize: CGSize(width: 100.0, height: 100.0) + ) + if let iconView = self.icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + transition.setFrame(view: iconView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - iconSize.width) * 0.5), y: 42.0), size: iconSize)) + } + + contentHeight += 138.0 + + let titleSize = self.title.update( + transition: transition, + component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: environment.strings.Story_UpgradeQuality_Title, font: Font.semibold(20.0), textColor: environment.theme.list.itemPrimaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + if let titleView = self.title.view { + if titleView.superview == nil { + self.addSubview(titleView) + } + transition.setFrame(view: titleView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: contentHeight), size: titleSize)) + } + contentHeight += titleSize.height + contentHeight += 14.0 + + let textSize = self.text.update( + transition: transition, + component: AnyComponent(BalancedTextComponent( + text: .plain(NSAttributedString(string: environment.strings.Story_UpgradeQuality_Text, font: Font.regular(14.0), textColor: environment.theme.list.itemSecondaryTextColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 0, + lineSpacing: 0.18 + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0) + ) + if let textView = self.text.view { + if textView.superview == nil { + self.addSubview(textView) + } + transition.setFrame(view: textView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - textSize.width) * 0.5), y: contentHeight), size: textSize)) + } + contentHeight += textSize.height + contentHeight += 12.0 + + contentHeight += 32.0 + + let buttonSize = self.button.update( + transition: transition, + component: AnyComponent(ButtonComponent( + background: ButtonComponent.Background( + color: environment.theme.list.itemCheckColors.fillColor, + foreground: environment.theme.list.itemCheckColors.foregroundColor, + pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8) + ), + content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(MultilineTextComponent( + text: .plain(NSAttributedString(string: "Add Funds via Fragment")) + ))), + isEnabled: true, + allowActionWhenDisabled: true, + displaysProgress: false, + action: { [weak self] in + guard let self, let component = self.component else { + return + } + + component.action() + } + )), + environment: {}, + containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0) + ) + let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: buttonSize) + if let buttonView = self.button.view { + if buttonView.superview == nil { + self.addSubview(buttonView) + } + transition.setFrame(view: buttonView, frame: buttonFrame) + } + contentHeight += buttonSize.height + + if environment.safeInsets.bottom.isZero { + contentHeight += 16.0 + } else { + contentHeight += environment.safeInsets.bottom + 14.0 + } + + return CGSize(width: availableSize.width, height: contentHeight) + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +private final class BalanceNeededScreenComponent: Component { + typealias EnvironmentType = ViewControllerComponentContainer.Environment + + let context: AccountContext + let buttonAction: (() -> Void)? + + init( + context: AccountContext, + buttonAction: (() -> Void)? + ) { + self.context = context + self.buttonAction = buttonAction + } + + static func ==(lhs: BalanceNeededScreenComponent, rhs: BalanceNeededScreenComponent) -> Bool { + if lhs.context !== rhs.context { + return false + } + return true + } + + final class View: UIView { + private let sheet = ComponentView<(ViewControllerComponentContainer.Environment, SheetComponentEnvironment)>() + private let sheetAnimateOut = ActionSlot>() + + private var component: BalanceNeededScreenComponent? + private var environment: EnvironmentType? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: BalanceNeededScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.component = component + + let environment = environment[ViewControllerComponentContainer.Environment.self].value + self.environment = environment + + let sheetEnvironment = SheetComponentEnvironment( + isDisplaying: environment.isVisible, + isCentered: environment.metrics.widthClass == .regular, + hasInputHeight: !environment.inputHeight.isZero, + regularMetricsSize: CGSize(width: 430.0, height: 900.0), + dismiss: { [weak self] _ in + guard let self, let environment = self.environment else { + return + } + self.sheetAnimateOut.invoke(Action { _ in + if let controller = environment.controller() { + controller.dismiss(completion: nil) + } + }) + } + ) + let _ = self.sheet.update( + transition: transition, + component: AnyComponent(SheetComponent( + content: AnyComponent(BalanceNeededSheetContentComponent( + action: { [weak self] in + guard let self else { + return + } + self.sheetAnimateOut.invoke(Action { [weak self] _ in + if let controller = environment.controller() { + controller.dismiss(completion: nil) + } + + guard let self else { + return + } + self.component?.buttonAction?() + }) + }, + dismiss: { + self.sheetAnimateOut.invoke(Action { _ in + if let controller = environment.controller() { + controller.dismiss(completion: nil) + } + }) + } + )), + backgroundColor: .color(environment.theme.overallDarkAppearance ? environment.theme.list.itemBlocksBackgroundColor : environment.theme.list.blocksBackgroundColor), + animateOut: self.sheetAnimateOut + )), + environment: { + environment + sheetEnvironment + }, + containerSize: availableSize + ) + if let sheetView = self.sheet.view { + if sheetView.superview == nil { + self.addSubview(sheetView) + } + transition.setFrame(view: sheetView, frame: CGRect(origin: CGPoint(), size: availableSize)) + } + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} + +public class BalanceNeededScreen: ViewControllerComponentContainer { + public init( + context: AccountContext, + buttonAction: (() -> Void)? = nil + ) { + super.init(context: context, component: BalanceNeededScreenComponent( + context: context, + buttonAction: buttonAction + ), navigationBarAppearance: .none) + + self.statusBar.statusBarStyle = .Ignore + self.navigationPresentation = .flatModal + self.blocksBackgroundWhenInOverlay = true + } + + required public init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + } + + override public func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.view.disablesInteractiveModalDismiss = true + } + + override public func dismiss(completion: (() -> Void)? = nil) { + super.dismiss(completion: { + completion?() + }) + self.wasDismissed?() + } +} diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD index b29361fb08..c55b3c36f6 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/BUILD @@ -39,6 +39,7 @@ swift_library( "//submodules/TelegramUI/Components/PeerManagement/OwnershipTransferController", "//submodules/TelegramUI/Components/ChatScheduleTimeController", "//submodules/TelegramUI/Components/TabSelectorComponent", + "//submodules/TelegramUI/Components/Stars/BalanceNeededScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 7933167ced..804082356c 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -25,6 +25,7 @@ import ListActionItemComponent import ChatScheduleTimeController import TabSelectorComponent import PresentationDataUtils +import BalanceNeededScreen private let amountTag = GenericComponentViewTag() @@ -788,11 +789,9 @@ private final class SheetContent: CombinedComponent { } case .ton: if let balance = state.tonBalance, amount > balance { - //TODO:localize - let presentationData = state.context.sharedContext.currentPresentationData.with { $0 } - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: "Not enough TON", actions: [ - TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {}) - ]), in: .window(.root)) + controller.push(BalanceNeededScreen( + context: state.context + )) return } }