mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Temp
This commit is contained in:
parent
c954a55d2b
commit
c9fd9f2a16
@ -812,6 +812,9 @@ public protocol CollectibleItemInfoScreenInitialData: AnyObject {
|
|||||||
public protocol BusinessLinksSetupScreenInitialData: AnyObject {
|
public protocol BusinessLinksSetupScreenInitialData: AnyObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol AffiliateProgramSetupScreenInitialData: AnyObject {
|
||||||
|
}
|
||||||
|
|
||||||
public enum CollectibleItemInfoScreenSubject {
|
public enum CollectibleItemInfoScreenSubject {
|
||||||
case phoneNumber(String)
|
case phoneNumber(String)
|
||||||
case username(String)
|
case username(String)
|
||||||
@ -1052,6 +1055,9 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?)
|
func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?)
|
||||||
|
|
||||||
|
func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id) -> Signal<AffiliateProgramSetupScreenInitialData, NoError>
|
||||||
|
func makeAffiliateProgramSetupScreen(context: AccountContext, initialData: AffiliateProgramSetupScreenInitialData) -> ViewController
|
||||||
|
|
||||||
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
func makeDebugSettingsController(context: AccountContext?) -> ViewController?
|
||||||
|
|
||||||
func navigateToCurrentCall()
|
func navigateToCurrentCall()
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
public enum HStackAlignment {
|
||||||
|
case left
|
||||||
|
case alternatingLeftRight
|
||||||
|
}
|
||||||
|
|
||||||
public final class HStack<ChildEnvironment: Equatable>: CombinedComponent {
|
public final class HStack<ChildEnvironment: Equatable>: CombinedComponent {
|
||||||
public typealias EnvironmentType = ChildEnvironment
|
public typealias EnvironmentType = ChildEnvironment
|
||||||
|
|
||||||
private let items: [AnyComponentWithIdentity<ChildEnvironment>]
|
private let items: [AnyComponentWithIdentity<ChildEnvironment>]
|
||||||
private let spacing: CGFloat
|
private let spacing: CGFloat
|
||||||
|
private let alignment: HStackAlignment
|
||||||
|
|
||||||
public init(_ items: [AnyComponentWithIdentity<ChildEnvironment>], spacing: CGFloat) {
|
public init(_ items: [AnyComponentWithIdentity<ChildEnvironment>], spacing: CGFloat, alignment: HStackAlignment = .left) {
|
||||||
self.items = items
|
self.items = items
|
||||||
self.spacing = spacing
|
self.spacing = spacing
|
||||||
|
self.alignment = alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: HStack<ChildEnvironment>, rhs: HStack<ChildEnvironment>) -> Bool {
|
public static func ==(lhs: HStack<ChildEnvironment>, rhs: HStack<ChildEnvironment>) -> Bool {
|
||||||
@ -19,6 +26,9 @@ public final class HStack<ChildEnvironment: Equatable>: CombinedComponent {
|
|||||||
if lhs.spacing != rhs.spacing {
|
if lhs.spacing != rhs.spacing {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.alignment != rhs.alignment {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,21 +52,51 @@ public final class HStack<ChildEnvironment: Equatable>: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var size = CGSize(width: 0.0, height: 0.0)
|
var size = CGSize(width: 0.0, height: 0.0)
|
||||||
for child in updatedChildren {
|
switch context.component.alignment {
|
||||||
size.width += child.size.width
|
case .left:
|
||||||
size.height = max(size.height, child.size.height)
|
for child in updatedChildren {
|
||||||
}
|
size.width += child.size.width
|
||||||
size.width += context.component.spacing * CGFloat(updatedChildren.count - 1)
|
size.height = max(size.height, child.size.height)
|
||||||
|
}
|
||||||
|
size.width += context.component.spacing * CGFloat(updatedChildren.count - 1)
|
||||||
|
|
||||||
var nextX = 0.0
|
var nextX = 0.0
|
||||||
for child in updatedChildren {
|
for child in updatedChildren {
|
||||||
context.add(child
|
context.add(child
|
||||||
.position(child.size.centered(in: CGRect(origin: CGPoint(x: nextX, y: floor((size.height - child.size.height) * 0.5)), size: child.size)).center)
|
.position(child.size.centered(in: CGRect(origin: CGPoint(x: nextX, y: floor((size.height - child.size.height) * 0.5)), size: child.size)).center)
|
||||||
.appear(.default(scale: true, alpha: true))
|
.appear(.default(scale: true, alpha: true))
|
||||||
.disappear(.default(scale: true, alpha: true))
|
.disappear(.default(scale: true, alpha: true))
|
||||||
)
|
)
|
||||||
nextX += child.size.width
|
nextX += child.size.width
|
||||||
nextX += context.component.spacing
|
nextX += context.component.spacing
|
||||||
|
}
|
||||||
|
case .alternatingLeftRight:
|
||||||
|
size.width = context.availableSize.width
|
||||||
|
for child in updatedChildren {
|
||||||
|
size.height = max(size.height, child.size.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextLeftX = 0.0
|
||||||
|
var nextRightX = size.width
|
||||||
|
for i in 0 ..< updatedChildren.count {
|
||||||
|
let child = updatedChildren[i]
|
||||||
|
let childFrame: CGRect
|
||||||
|
if i % 2 == 0 {
|
||||||
|
childFrame = CGRect(origin: CGPoint(x: nextLeftX, y: floor((size.height - child.size.height) * 0.5)), size: child.size)
|
||||||
|
nextLeftX += child.size.width
|
||||||
|
nextLeftX += context.component.spacing
|
||||||
|
} else {
|
||||||
|
childFrame = CGRect(origin: CGPoint(x: nextRightX - child.size.width, y: floor((size.height - child.size.height) * 0.5)), size: child.size)
|
||||||
|
nextRightX -= child.size.width
|
||||||
|
nextRightX -= context.component.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
context.add(child
|
||||||
|
.position(child.size.centered(in: childFrame).center)
|
||||||
|
.appear(.default(scale: true, alpha: true))
|
||||||
|
.disappear(.default(scale: true, alpha: true))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return size
|
return size
|
||||||
|
@ -462,6 +462,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
|
"//submodules/TelegramUI/Components/Stars/StarsIntroScreen",
|
||||||
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
|
"//submodules/TelegramUI/Components/Gifts/GiftOptionsScreen",
|
||||||
"//submodules/TelegramUI/Components/ContentReportScreen",
|
"//submodules/TelegramUI/Components/ContentReportScreen",
|
||||||
|
"//submodules/TelegramUI/Components/PeerInfo/AffiliateProgramSetupScreen",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
"//build-system:ios_sim_arm64": [],
|
"//build-system:ios_sim_arm64": [],
|
||||||
|
@ -1363,42 +1363,44 @@ private final class ChatSendStarsScreenComponent: Component {
|
|||||||
let sliderSize = self.slider.update(
|
let sliderSize = self.slider.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
component: AnyComponent(SliderComponent(
|
component: AnyComponent(SliderComponent(
|
||||||
valueCount: self.amount.maxSliderValue + 1,
|
content: .discrete(SliderComponent.Discrete(
|
||||||
value: self.amount.sliderValue,
|
valueCount: self.amount.maxSliderValue + 1,
|
||||||
markPositions: false,
|
value: self.amount.sliderValue,
|
||||||
|
markPositions: false,
|
||||||
|
valueUpdated: { [weak self] value in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.amount = self.amount.withSliderValue(value)
|
||||||
|
self.didChangeAmount = true
|
||||||
|
|
||||||
|
self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint()))
|
||||||
|
|
||||||
|
let sliderValue = Float(value) / Float(component.maxAmount)
|
||||||
|
let currentTimestamp = CACurrentMediaTime()
|
||||||
|
|
||||||
|
if let previousTimestamp {
|
||||||
|
let deltaTime = currentTimestamp - previousTimestamp
|
||||||
|
let delta = sliderValue - self.previousSliderValue
|
||||||
|
let deltaValue = abs(sliderValue - self.previousSliderValue)
|
||||||
|
|
||||||
|
let speed = deltaValue / Float(deltaTime)
|
||||||
|
let newSpeed = max(0, min(65.0, speed * 70.0))
|
||||||
|
|
||||||
|
if newSpeed < 0.01 && deltaValue < 0.001 {
|
||||||
|
} else {
|
||||||
|
self.badgeStars.update(speed: newSpeed, delta: delta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.previousSliderValue = sliderValue
|
||||||
|
self.previousTimestamp = currentTimestamp
|
||||||
|
}
|
||||||
|
)),
|
||||||
trackBackgroundColor: .clear,
|
trackBackgroundColor: .clear,
|
||||||
trackForegroundColor: .clear,
|
trackForegroundColor: .clear,
|
||||||
knobSize: 26.0,
|
knobSize: 26.0,
|
||||||
knobColor: .white,
|
knobColor: .white,
|
||||||
valueUpdated: { [weak self] value in
|
|
||||||
guard let self, let component = self.component else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.amount = self.amount.withSliderValue(value)
|
|
||||||
self.didChangeAmount = true
|
|
||||||
|
|
||||||
self.state?.updated(transition: ComponentTransition(animation: .none).withUserData(IsAdjustingAmountHint()))
|
|
||||||
|
|
||||||
let sliderValue = Float(value) / Float(component.maxAmount)
|
|
||||||
let currentTimestamp = CACurrentMediaTime()
|
|
||||||
|
|
||||||
if let previousTimestamp {
|
|
||||||
let deltaTime = currentTimestamp - previousTimestamp
|
|
||||||
let delta = sliderValue - self.previousSliderValue
|
|
||||||
let deltaValue = abs(sliderValue - self.previousSliderValue)
|
|
||||||
|
|
||||||
let speed = deltaValue / Float(deltaTime)
|
|
||||||
let newSpeed = max(0, min(65.0, speed * 70.0))
|
|
||||||
|
|
||||||
if newSpeed < 0.01 && deltaValue < 0.001 {
|
|
||||||
} else {
|
|
||||||
self.badgeStars.update(speed: newSpeed, delta: delta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.previousSliderValue = sliderValue
|
|
||||||
self.previousTimestamp = currentTimestamp
|
|
||||||
},
|
|
||||||
isTrackingUpdated: { [weak self] isTracking in
|
isTrackingUpdated: { [weak self] isTracking in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
|
@ -9,43 +9,91 @@ import ListSectionComponent
|
|||||||
import SliderComponent
|
import SliderComponent
|
||||||
|
|
||||||
public final class ListItemSliderSelectorComponent: Component {
|
public final class ListItemSliderSelectorComponent: Component {
|
||||||
|
public final class Discrete: Equatable {
|
||||||
|
public let values: [String]
|
||||||
|
public let markPositions: Bool
|
||||||
|
public let selectedIndex: Int
|
||||||
|
public let title: String?
|
||||||
|
public let selectedIndexUpdated: (Int) -> Void
|
||||||
|
|
||||||
|
public init(values: [String], markPositions: Bool, selectedIndex: Int, title: String?, selectedIndexUpdated: @escaping (Int) -> Void) {
|
||||||
|
self.values = values
|
||||||
|
self.markPositions = markPositions
|
||||||
|
self.selectedIndex = selectedIndex
|
||||||
|
self.title = title
|
||||||
|
self.selectedIndexUpdated = selectedIndexUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Discrete, rhs: Discrete) -> Bool {
|
||||||
|
if lhs.values != rhs.values {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.markPositions != rhs.markPositions {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.selectedIndex != rhs.selectedIndex {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Continuous: Equatable {
|
||||||
|
public let value: CGFloat
|
||||||
|
public let lowerBoundTitle: String
|
||||||
|
public let upperBoundTitle: String
|
||||||
|
public let title: String
|
||||||
|
public let valueUpdated: (CGFloat) -> Void
|
||||||
|
|
||||||
|
public init(value: CGFloat, lowerBoundTitle: String, upperBoundTitle: String, title: String, valueUpdated: @escaping (CGFloat) -> Void) {
|
||||||
|
self.value = value
|
||||||
|
self.lowerBoundTitle = lowerBoundTitle
|
||||||
|
self.upperBoundTitle = upperBoundTitle
|
||||||
|
self.title = title
|
||||||
|
self.valueUpdated = valueUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Continuous, rhs: Continuous) -> Bool {
|
||||||
|
if lhs.value != rhs.value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.lowerBoundTitle != rhs.lowerBoundTitle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.upperBoundTitle != rhs.upperBoundTitle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.title != rhs.title {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Content: Equatable {
|
||||||
|
case discrete(Discrete)
|
||||||
|
case continuous(Continuous)
|
||||||
|
}
|
||||||
|
|
||||||
public let theme: PresentationTheme
|
public let theme: PresentationTheme
|
||||||
public let values: [String]
|
public let content: Content
|
||||||
public let markPositions: Bool
|
|
||||||
public let selectedIndex: Int
|
|
||||||
public let title: String?
|
|
||||||
public let selectedIndexUpdated: (Int) -> Void
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
values: [String],
|
content: Content
|
||||||
markPositions: Bool,
|
|
||||||
selectedIndex: Int,
|
|
||||||
title: String?,
|
|
||||||
selectedIndexUpdated: @escaping (Int) -> Void
|
|
||||||
) {
|
) {
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.values = values
|
self.content = content
|
||||||
self.markPositions = markPositions
|
|
||||||
self.selectedIndex = selectedIndex
|
|
||||||
self.title = title
|
|
||||||
self.selectedIndexUpdated = selectedIndexUpdated
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: ListItemSliderSelectorComponent, rhs: ListItemSliderSelectorComponent) -> Bool {
|
public static func ==(lhs: ListItemSliderSelectorComponent, rhs: ListItemSliderSelectorComponent) -> Bool {
|
||||||
if lhs.theme !== rhs.theme {
|
if lhs.theme !== rhs.theme {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.values != rhs.values {
|
if lhs.content != rhs.content {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.markPositions != rhs.markPositions {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.selectedIndex != rhs.selectedIndex {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.title != rhs.title {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -81,48 +129,90 @@ public final class ListItemSliderSelectorComponent: Component {
|
|||||||
let titleAreaWidth: CGFloat = availableSize.width - titleSideInset * 2.0
|
let titleAreaWidth: CGFloat = availableSize.width - titleSideInset * 2.0
|
||||||
|
|
||||||
var validIds: [Int] = []
|
var validIds: [Int] = []
|
||||||
for i in 0 ..< component.values.count {
|
var mainTitleValue: String?
|
||||||
if component.title != nil {
|
|
||||||
if i != 0 && i != component.values.count - 1 {
|
switch component.content {
|
||||||
continue
|
case let .discrete(discrete):
|
||||||
|
mainTitleValue = discrete.title
|
||||||
|
|
||||||
|
for i in 0 ..< discrete.values.count {
|
||||||
|
if discrete.title != nil {
|
||||||
|
if i != 0 && i != discrete.values.count - 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validIds.append(i)
|
||||||
|
|
||||||
|
var titleTransition = transition
|
||||||
|
let title: ComponentView<Empty>
|
||||||
|
if let current = self.titles[i] {
|
||||||
|
title = current
|
||||||
|
} else {
|
||||||
|
titleTransition = titleTransition.withAnimation(.none)
|
||||||
|
title = ComponentView()
|
||||||
|
self.titles[i] = title
|
||||||
|
}
|
||||||
|
let titleSize = title.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: discrete.values[i], font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||||
|
)
|
||||||
|
var titleFrame = CGRect(origin: CGPoint(x: titleSideInset - floor(titleSize.width * 0.5), y: 14.0), size: titleSize)
|
||||||
|
if discrete.values.count > 1 {
|
||||||
|
titleFrame.origin.x += floor(CGFloat(i) / CGFloat(discrete.values.count - 1) * titleAreaWidth)
|
||||||
|
}
|
||||||
|
if titleFrame.minX < titleClippingSideInset {
|
||||||
|
titleFrame.origin.x = titleSideInset
|
||||||
|
}
|
||||||
|
if titleFrame.maxX > availableSize.width - titleClippingSideInset {
|
||||||
|
titleFrame.origin.x = availableSize.width - titleClippingSideInset - titleSize.width
|
||||||
|
}
|
||||||
|
if let titleView = title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
self.addSubview(titleView)
|
||||||
|
}
|
||||||
|
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||||
|
titleTransition.setPosition(view: titleView, position: titleFrame.center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case let .continuous(continuous):
|
||||||
|
mainTitleValue = continuous.title
|
||||||
|
|
||||||
validIds.append(i)
|
for i in 0 ..< 2 {
|
||||||
|
validIds.append(i)
|
||||||
|
|
||||||
var titleTransition = transition
|
var titleTransition = transition
|
||||||
let title: ComponentView<Empty>
|
let title: ComponentView<Empty>
|
||||||
if let current = self.titles[i] {
|
if let current = self.titles[i] {
|
||||||
title = current
|
title = current
|
||||||
} else {
|
} else {
|
||||||
titleTransition = titleTransition.withAnimation(.none)
|
titleTransition = titleTransition.withAnimation(.none)
|
||||||
title = ComponentView()
|
title = ComponentView()
|
||||||
self.titles[i] = title
|
self.titles[i] = title
|
||||||
}
|
}
|
||||||
let titleSize = title.update(
|
let titleSize = title.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(MultilineTextComponent(
|
component: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(string: component.values[i], font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
text: .plain(NSAttributedString(string: i == 0 ? continuous.lowerBoundTitle : continuous.upperBoundTitle, font: Font.regular(13.0), textColor: component.theme.list.itemSecondaryTextColor))
|
||||||
)),
|
)),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||||
)
|
)
|
||||||
var titleFrame = CGRect(origin: CGPoint(x: titleSideInset - floor(titleSize.width * 0.5), y: 14.0), size: titleSize)
|
var titleFrame = CGRect(origin: CGPoint(x: titleSideInset, y: 14.0), size: titleSize)
|
||||||
if component.values.count > 1 {
|
if i == 1 {
|
||||||
titleFrame.origin.x += floor(CGFloat(i) / CGFloat(component.values.count - 1) * titleAreaWidth)
|
titleFrame.origin.x = availableSize.width - titleClippingSideInset - titleSize.width
|
||||||
}
|
}
|
||||||
if titleFrame.minX < titleClippingSideInset {
|
if let titleView = title.view {
|
||||||
titleFrame.origin.x = titleSideInset
|
if titleView.superview == nil {
|
||||||
}
|
self.addSubview(titleView)
|
||||||
if titleFrame.maxX > availableSize.width - titleClippingSideInset {
|
}
|
||||||
titleFrame.origin.x = availableSize.width - titleClippingSideInset - titleSize.width
|
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||||
}
|
titleTransition.setPosition(view: titleView, position: titleFrame.center)
|
||||||
if let titleView = title.view {
|
|
||||||
if titleView.superview == nil {
|
|
||||||
self.addSubview(titleView)
|
|
||||||
}
|
}
|
||||||
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
|
||||||
titleTransition.setPosition(view: titleView, position: titleFrame.center)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var removeIds: [Int] = []
|
var removeIds: [Int] = []
|
||||||
@ -136,7 +226,7 @@ public final class ListItemSliderSelectorComponent: Component {
|
|||||||
self.titles.removeValue(forKey: id)
|
self.titles.removeValue(forKey: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let title = component.title {
|
if let title = mainTitleValue {
|
||||||
let mainTitle: ComponentView<Empty>
|
let mainTitle: ComponentView<Empty>
|
||||||
var mainTitleTransition = transition
|
var mainTitleTransition = transition
|
||||||
if let current = self.mainTitle {
|
if let current = self.mainTitle {
|
||||||
@ -169,24 +259,50 @@ public final class ListItemSliderSelectorComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sliderSize = self.slider.update(
|
let sliderSize: CGSize
|
||||||
transition: transition,
|
switch component.content {
|
||||||
component: AnyComponent(SliderComponent(
|
case let .discrete(discrete):
|
||||||
valueCount: component.values.count,
|
sliderSize = self.slider.update(
|
||||||
value: component.selectedIndex,
|
transition: transition,
|
||||||
markPositions: component.markPositions,
|
component: AnyComponent(SliderComponent(
|
||||||
trackBackgroundColor: component.theme.list.controlSecondaryColor,
|
content: .discrete(SliderComponent.Discrete(
|
||||||
trackForegroundColor: component.theme.list.itemAccentColor,
|
valueCount: discrete.values.count,
|
||||||
valueUpdated: { [weak self] value in
|
value: discrete.selectedIndex,
|
||||||
guard let self, let component = self.component else {
|
markPositions: discrete.markPositions,
|
||||||
return
|
valueUpdated: { [weak self] value in
|
||||||
}
|
guard let self, let component = self.component, case let .discrete(discrete) = component.content else {
|
||||||
component.selectedIndexUpdated(value)
|
return
|
||||||
}
|
}
|
||||||
)),
|
discrete.selectedIndexUpdated(value)
|
||||||
environment: {},
|
})
|
||||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
),
|
||||||
)
|
trackBackgroundColor: component.theme.list.controlSecondaryColor,
|
||||||
|
trackForegroundColor: component.theme.list.itemAccentColor
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||||
|
)
|
||||||
|
case let .continuous(continuous):
|
||||||
|
sliderSize = self.slider.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(SliderComponent(
|
||||||
|
content: .continuous(SliderComponent.Continuous(
|
||||||
|
value: continuous.value,
|
||||||
|
valueUpdated: { [weak self] value in
|
||||||
|
guard let self, let component = self.component, case let .continuous(continuous) = component.content else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continuous.valueUpdated(value)
|
||||||
|
})
|
||||||
|
),
|
||||||
|
trackBackgroundColor: component.theme.list.controlSecondaryColor,
|
||||||
|
trackForegroundColor: component.theme.list.itemAccentColor
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let sliderFrame = CGRect(origin: CGPoint(x: sideInset, y: 36.0), size: sliderSize)
|
let sliderFrame = CGRect(origin: CGPoint(x: sideInset, y: 36.0), size: sliderSize)
|
||||||
if let sliderView = self.slider.view {
|
if let sliderView = self.slider.view {
|
||||||
if sliderView.superview == nil {
|
if sliderView.superview == nil {
|
||||||
|
@ -1281,20 +1281,22 @@ final class PeerAllowedReactionsScreenComponent: Component {
|
|||||||
items: [
|
items: [
|
||||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
values: reactionCountValueList.map { item in
|
content: .discrete(ListItemSliderSelectorComponent.Discrete(
|
||||||
return item
|
values: reactionCountValueList.map { item in
|
||||||
},
|
return item
|
||||||
markPositions: false,
|
},
|
||||||
selectedIndex: max(0, min(reactionCountValueList.count - 1, self.allowedReactionCount - 1)),
|
markPositions: false,
|
||||||
title: sliderTitle,
|
selectedIndex: max(0, min(reactionCountValueList.count - 1, self.allowedReactionCount - 1)),
|
||||||
selectedIndexUpdated: { [weak self] index in
|
title: sliderTitle,
|
||||||
guard let self else {
|
selectedIndexUpdated: { [weak self] index in
|
||||||
return
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let index = max(1, min(reactionCountValueList.count, index + 1))
|
||||||
|
self.allowedReactionCount = index
|
||||||
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
let index = max(1, min(reactionCountValueList.count, index + 1))
|
))
|
||||||
self.allowedReactionCount = index
|
|
||||||
self.state?.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
)))
|
)))
|
||||||
],
|
],
|
||||||
displaySeparators: false
|
displaySeparators: false
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "AffiliateProgramSetupScreen",
|
||||||
|
module_name = "AffiliateProgramSetupScreen",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
|
"//submodules/AsyncDisplayKit",
|
||||||
|
"//submodules/TelegramCore",
|
||||||
|
"//submodules/Postbox",
|
||||||
|
"//submodules/TelegramPresentationData",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
"//submodules/TelegramStringFormatting",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/AppBundle",
|
||||||
|
"//submodules/UndoUI",
|
||||||
|
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||||
|
"//submodules/Components/MultilineTextComponent",
|
||||||
|
"//submodules/Components/ViewControllerComponent",
|
||||||
|
"//submodules/Components/ComponentDisplayAdapters",
|
||||||
|
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||||
|
"//submodules/TelegramUI/Components/ListItemSliderSelectorComponent",
|
||||||
|
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||||
|
"//submodules/Components/BlurredBackgroundComponent",
|
||||||
|
"//submodules/Markdown",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,715 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import TelegramPresentationData
|
||||||
|
import ComponentFlow
|
||||||
|
import ComponentDisplayAdapters
|
||||||
|
import AppBundle
|
||||||
|
import ViewControllerComponent
|
||||||
|
import AccountContext
|
||||||
|
import TelegramCore
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import MultilineTextComponent
|
||||||
|
import ButtonComponent
|
||||||
|
import UndoUI
|
||||||
|
import BundleIconComponent
|
||||||
|
import ListSectionComponent
|
||||||
|
import ListItemSliderSelectorComponent
|
||||||
|
import ListActionItemComponent
|
||||||
|
import Markdown
|
||||||
|
import BlurredBackgroundComponent
|
||||||
|
|
||||||
|
final class AffiliateProgramSetupScreenComponent: Component {
|
||||||
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
|
|
||||||
|
let context: AccountContext
|
||||||
|
let initialContent: AffiliateProgramSetupScreen.Content
|
||||||
|
|
||||||
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
initialContent: AffiliateProgramSetupScreen.Content
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
self.initialContent = initialContent
|
||||||
|
}
|
||||||
|
|
||||||
|
static func ==(lhs: AffiliateProgramSetupScreenComponent, rhs: AffiliateProgramSetupScreenComponent) -> Bool {
|
||||||
|
if lhs.context !== rhs.context {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
final class View: UIView, UIScrollViewDelegate {
|
||||||
|
private let scrollView: UIScrollView
|
||||||
|
|
||||||
|
private let title = ComponentView<Empty>()
|
||||||
|
private let titleTransformContainer: UIView
|
||||||
|
private let subtitle = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let introBackground = ComponentView<Empty>()
|
||||||
|
private var introIconItems: [Int: ComponentView<Empty>] = [:]
|
||||||
|
private var introTitleItems: [Int: ComponentView<Empty>] = [:]
|
||||||
|
private var introTextItems: [Int: ComponentView<Empty>] = [:]
|
||||||
|
|
||||||
|
private let commissionSection = ComponentView<Empty>()
|
||||||
|
private let durationSection = ComponentView<Empty>()
|
||||||
|
private let existingProgramsSection = ComponentView<Empty>()
|
||||||
|
private let endProgramSection = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let bottomPanelSeparator = SimpleLayer()
|
||||||
|
private let bottomPanelBackground = ComponentView<Empty>()
|
||||||
|
private let bottomPanelButton = ComponentView<Empty>()
|
||||||
|
private let bottomPanelText = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private var isUpdating: Bool = false
|
||||||
|
|
||||||
|
private var component: AffiliateProgramSetupScreenComponent?
|
||||||
|
private(set) weak var state: EmptyComponentState?
|
||||||
|
private var environment: EnvironmentType?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.scrollView = UIScrollView()
|
||||||
|
self.scrollView.showsVerticalScrollIndicator = true
|
||||||
|
self.scrollView.showsHorizontalScrollIndicator = false
|
||||||
|
self.scrollView.scrollsToTop = false
|
||||||
|
self.scrollView.delaysContentTouches = false
|
||||||
|
self.scrollView.canCancelContentTouches = true
|
||||||
|
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||||
|
self.scrollView.alwaysBounceVertical = true
|
||||||
|
|
||||||
|
self.titleTransformContainer = UIView()
|
||||||
|
self.scrollView.addSubview(self.titleTransformContainer)
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.scrollView.delegate = self
|
||||||
|
self.addSubview(self.scrollView)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.bottomPanelSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollToTop() {
|
||||||
|
self.scrollView.setContentOffset(CGPoint(), animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func attemptNavigation(complete: @escaping () -> Void) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateScrolling(transition: ComponentTransition) {
|
||||||
|
let navigationAlphaDistance: CGFloat = 16.0
|
||||||
|
let navigationAlpha: CGFloat = max(0.0, min(1.0, self.scrollView.contentOffset.y / navigationAlphaDistance))
|
||||||
|
if let controller = self.environment?.controller(), let navigationBar = controller.navigationBar {
|
||||||
|
transition.setAlpha(layer: navigationBar.backgroundNode.layer, alpha: navigationAlpha)
|
||||||
|
transition.setAlpha(layer: navigationBar.stripeNode.layer, alpha: navigationAlpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: AffiliateProgramSetupScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||||
|
self.isUpdating = true
|
||||||
|
defer {
|
||||||
|
self.isUpdating = false
|
||||||
|
}
|
||||||
|
|
||||||
|
let environment = environment[EnvironmentType.self].value
|
||||||
|
let themeUpdated = self.environment?.theme !== environment.theme
|
||||||
|
self.environment = environment
|
||||||
|
|
||||||
|
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
|
||||||
|
|
||||||
|
if themeUpdated {
|
||||||
|
self.backgroundColor = environment.theme.list.blocksBackgroundColor
|
||||||
|
self.bottomPanelSeparator.backgroundColor = environment.theme.rootController.navigationBar.separatorColor.cgColor
|
||||||
|
}
|
||||||
|
|
||||||
|
self.component = component
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
let topInset: CGFloat = environment.navigationHeight + 87.0
|
||||||
|
let bottomInset: CGFloat = 8.0
|
||||||
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||||
|
let textSideInset: CGFloat = 16.0
|
||||||
|
let sectionSpacing: CGFloat = 24.0
|
||||||
|
|
||||||
|
var contentHeight: CGFloat = 0.0
|
||||||
|
contentHeight += topInset
|
||||||
|
|
||||||
|
let titleSize = self.title.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: "Affiliate Program", font: Font.bold(30.0), textColor: environment.theme.list.itemPrimaryTextColor))
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - textSideInset * 2.0, height: 1000.0)
|
||||||
|
)
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - titleSize.width) * 0.5), y: contentHeight), size: titleSize)
|
||||||
|
if let titleView = self.title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
self.titleTransformContainer.addSubview(titleView)
|
||||||
|
}
|
||||||
|
titleView.bounds = CGRect(origin: CGPoint(), size: titleFrame.size)
|
||||||
|
transition.setPosition(view: self.titleTransformContainer, position: titleFrame.center)
|
||||||
|
}
|
||||||
|
contentHeight += titleSize.height
|
||||||
|
contentHeight += 10.0
|
||||||
|
|
||||||
|
let subtitleSize = self.subtitle.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: "Reward those who help grow your userbase.", font: Font.regular(15.0), textColor: environment.theme.list.itemPrimaryTextColor)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - textSideInset * 2.0, height: 1000.0)
|
||||||
|
)
|
||||||
|
let subtitleFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - subtitleSize.width) * 0.5), y: contentHeight), size: subtitleSize)
|
||||||
|
if let subtitleView = self.subtitle.view {
|
||||||
|
if subtitleView.superview == nil {
|
||||||
|
self.scrollView.addSubview(subtitleView)
|
||||||
|
subtitleView.bounds = CGRect(origin: CGPoint(), size: subtitleFrame.size)
|
||||||
|
transition.setPosition(view: subtitleView, position: subtitleFrame.center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentHeight += subtitleSize.height
|
||||||
|
contentHeight += 24.0
|
||||||
|
|
||||||
|
let introItems: [(icon: String, title: String, text: String)] = [
|
||||||
|
(
|
||||||
|
"Chat/Context Menu/Smile",
|
||||||
|
"Share revenue with affiliates",
|
||||||
|
"Set the commission for revenue generated by users referred to you."
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Chat/Context Menu/Smile",
|
||||||
|
"Launch your affiliate program",
|
||||||
|
"Telegram will feature your program for millions of potential affiliates."
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Chat/Context Menu/Smile",
|
||||||
|
"Let affiliates promote you",
|
||||||
|
"Affiliates will share your referral link with their audience."
|
||||||
|
)
|
||||||
|
]
|
||||||
|
var introItemsHeight: CGFloat = 17.0
|
||||||
|
let introItemIconX: CGFloat = sideInset + 19.0
|
||||||
|
let introItemTextX: CGFloat = sideInset + 56.0
|
||||||
|
let introItemTextRightInset: CGFloat = sideInset + 10.0
|
||||||
|
let introItemSpacing: CGFloat = 22.0
|
||||||
|
for i in 0 ..< introItems.count {
|
||||||
|
if i != 0 {
|
||||||
|
introItemsHeight += introItemSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = introItems[i]
|
||||||
|
|
||||||
|
let itemIcon: ComponentView<Empty>
|
||||||
|
let itemTitle: ComponentView<Empty>
|
||||||
|
let itemText: ComponentView<Empty>
|
||||||
|
|
||||||
|
if let current = self.introIconItems[i] {
|
||||||
|
itemIcon = current
|
||||||
|
} else {
|
||||||
|
itemIcon = ComponentView()
|
||||||
|
self.introIconItems[i] = itemIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
if let current = self.introTitleItems[i] {
|
||||||
|
itemTitle = current
|
||||||
|
} else {
|
||||||
|
itemTitle = ComponentView()
|
||||||
|
self.introTitleItems[i] = itemTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
if let current = self.introTextItems[i] {
|
||||||
|
itemText = current
|
||||||
|
} else {
|
||||||
|
itemText = ComponentView()
|
||||||
|
self.introTextItems[i] = itemText
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconSize = itemIcon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(BundleIconComponent(
|
||||||
|
name: item.icon,
|
||||||
|
tintColor: environment.theme.list.itemAccentColor
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||||
|
)
|
||||||
|
let titleSize = itemTitle.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: item.title, font: Font.semibold(15.0), textColor: environment.theme.list.itemPrimaryTextColor)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - introItemTextRightInset - introItemTextX, height: 1000.0)
|
||||||
|
)
|
||||||
|
let textSize = itemText.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(string: item.text, font: Font.regular(15.0), textColor: environment.theme.list.itemSecondaryTextColor)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - introItemTextRightInset - introItemTextX, height: 1000.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let itemIconFrame = CGRect(origin: CGPoint(x: introItemIconX, y: contentHeight + introItemsHeight + 8.0), size: iconSize)
|
||||||
|
let itemTitleFrame = CGRect(origin: CGPoint(x: introItemTextX, y: contentHeight + introItemsHeight), size: titleSize)
|
||||||
|
let itemTextFrame = CGRect(origin: CGPoint(x: introItemTextX, y: itemTitleFrame.maxY + 5.0), size: textSize)
|
||||||
|
|
||||||
|
if let itemIconView = itemIcon.view {
|
||||||
|
if itemIconView.superview == nil {
|
||||||
|
self.scrollView.addSubview(itemIconView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: itemIconView, frame: itemIconFrame)
|
||||||
|
}
|
||||||
|
if let itemTitleView = itemTitle.view {
|
||||||
|
if itemTitleView.superview == nil {
|
||||||
|
itemTitleView.layer.anchorPoint = CGPoint()
|
||||||
|
self.scrollView.addSubview(itemTitleView)
|
||||||
|
}
|
||||||
|
transition.setPosition(view: itemTitleView, position: itemTitleFrame.origin)
|
||||||
|
itemTitleView.bounds = CGRect(origin: CGPoint(), size: itemTitleFrame.size)
|
||||||
|
}
|
||||||
|
if let itemTextView = itemText.view {
|
||||||
|
if itemTextView.superview == nil {
|
||||||
|
itemTextView.layer.anchorPoint = CGPoint()
|
||||||
|
self.scrollView.addSubview(itemTextView)
|
||||||
|
}
|
||||||
|
transition.setPosition(view: itemTextView, position: itemTextFrame.origin)
|
||||||
|
itemTextView.bounds = CGRect(origin: CGPoint(), size: itemTextFrame.size)
|
||||||
|
}
|
||||||
|
introItemsHeight = itemTextFrame.maxY - contentHeight
|
||||||
|
}
|
||||||
|
introItemsHeight += 19.0
|
||||||
|
|
||||||
|
let introBackgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: CGSize(width: availableSize.width - sideInset * 2.0, height: introItemsHeight))
|
||||||
|
let _ = self.introBackground.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(FilledRoundedRectangleComponent(
|
||||||
|
color: environment.theme.list.itemBlocksBackgroundColor,
|
||||||
|
cornerRadius: .value(5.0),
|
||||||
|
smoothCorners: true
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: introBackgroundFrame.size
|
||||||
|
)
|
||||||
|
if let introBackgroundView = self.introBackground.view {
|
||||||
|
if introBackgroundView.superview == nil, let firstIconItemView = self.introIconItems[0]?.view {
|
||||||
|
self.scrollView.insertSubview(introBackgroundView, belowSubview: firstIconItemView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: introBackgroundView, frame: introBackgroundFrame)
|
||||||
|
}
|
||||||
|
contentHeight += introItemsHeight
|
||||||
|
contentHeight += sectionSpacing + 6.0
|
||||||
|
|
||||||
|
let commissionSectionSize = self.commissionSection.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(ListSectionComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
header: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "COMMISSION",
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "Define the percentage of star revenue your affiliates earn for referring users to your bot.",
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
items: [
|
||||||
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
content: .continuous(ListItemSliderSelectorComponent.Continuous(
|
||||||
|
value: 0.0,
|
||||||
|
lowerBoundTitle: "1%",
|
||||||
|
upperBoundTitle: "90%",
|
||||||
|
title: "1%",
|
||||||
|
valueUpdated: { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = self
|
||||||
|
}
|
||||||
|
))
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
displaySeparators: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
|
||||||
|
)
|
||||||
|
let commissionSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: commissionSectionSize)
|
||||||
|
if let commissionSectionView = self.commissionSection.view {
|
||||||
|
if commissionSectionView.superview == nil {
|
||||||
|
self.scrollView.addSubview(commissionSectionView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: commissionSectionView, frame: commissionSectionFrame)
|
||||||
|
}
|
||||||
|
contentHeight += commissionSectionSize.height
|
||||||
|
contentHeight += sectionSpacing + 12.0
|
||||||
|
|
||||||
|
let durationItems: [(months: Int32, title: String, selectedTitle: String)] = [
|
||||||
|
(1, "1m", "1 MONTH"),
|
||||||
|
(3, "3m", "3 MONTHS"),
|
||||||
|
(6, "6m", "6 MONTHS"),
|
||||||
|
(12, "1y", "1 YEAR"),
|
||||||
|
(2 * 12, "2y", "2 YEARS"),
|
||||||
|
(3 * 12, "3y", "3 YEARS"),
|
||||||
|
(Int32.max, "∞", "INDEFINITELY")
|
||||||
|
]
|
||||||
|
let durationSectionSize = self.durationSection.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(ListSectionComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
header: AnyComponent(HStack([
|
||||||
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "DURATION",
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
))),
|
||||||
|
AnyComponentWithIdentity(id: 1, component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: durationItems[0].selectedTitle,
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)))
|
||||||
|
], spacing: 4.0, alignment: .alternatingLeftRight)),
|
||||||
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "Set the duration for which affiliates will earn commissions from referred users.",
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
items: [
|
||||||
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
content: .discrete(ListItemSliderSelectorComponent.Discrete(
|
||||||
|
values: durationItems.map(\.title),
|
||||||
|
markPositions: true,
|
||||||
|
selectedIndex: 0,
|
||||||
|
title: nil,
|
||||||
|
selectedIndexUpdated: { [weak self] value in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = self
|
||||||
|
}
|
||||||
|
))
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
displaySeparators: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
|
||||||
|
)
|
||||||
|
let durationSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: durationSectionSize)
|
||||||
|
if let durationSectionView = self.durationSection.view {
|
||||||
|
if durationSectionView.superview == nil {
|
||||||
|
self.scrollView.addSubview(durationSectionView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: durationSectionView, frame: durationSectionFrame)
|
||||||
|
}
|
||||||
|
contentHeight += durationSectionSize.height
|
||||||
|
contentHeight += sectionSpacing + 12.0
|
||||||
|
|
||||||
|
let existingProgramsSectionSize = self.existingProgramsSection.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(ListSectionComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
header: nil,
|
||||||
|
footer: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "Explore what other mini apps offer.",
|
||||||
|
font: Font.regular(13.0),
|
||||||
|
textColor: environment.theme.list.freeTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
items: [
|
||||||
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
title: AnyComponent(VStack([
|
||||||
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "View Existing Programs",
|
||||||
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 1
|
||||||
|
))),
|
||||||
|
], alignment: .left, spacing: 2.0)),
|
||||||
|
accessory: .arrow,
|
||||||
|
action: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
displaySeparators: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
|
||||||
|
)
|
||||||
|
let existingProgramsSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: existingProgramsSectionSize)
|
||||||
|
if let existingProgramsSectionView = self.existingProgramsSection.view {
|
||||||
|
if existingProgramsSectionView.superview == nil {
|
||||||
|
self.scrollView.addSubview(existingProgramsSectionView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: existingProgramsSectionView, frame: existingProgramsSectionFrame)
|
||||||
|
}
|
||||||
|
contentHeight += existingProgramsSectionSize.height
|
||||||
|
contentHeight += sectionSpacing + 12.0
|
||||||
|
|
||||||
|
let endProgramSectionSize = self.endProgramSection.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(ListSectionComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
header: nil,
|
||||||
|
footer: nil,
|
||||||
|
items: [
|
||||||
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
|
||||||
|
theme: environment.theme,
|
||||||
|
title: AnyComponent(VStack([
|
||||||
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .plain(NSAttributedString(
|
||||||
|
string: "End Affiliate Program",
|
||||||
|
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
|
||||||
|
textColor: environment.theme.list.itemDestructiveColor
|
||||||
|
)),
|
||||||
|
maximumNumberOfLines: 1
|
||||||
|
))),
|
||||||
|
], alignment: .center, spacing: 2.0)),
|
||||||
|
accessory: nil,
|
||||||
|
action: { [weak self] _ in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self
|
||||||
|
}
|
||||||
|
)))
|
||||||
|
],
|
||||||
|
displaySeparators: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 10000.0)
|
||||||
|
)
|
||||||
|
let endProgramSectionFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: endProgramSectionSize)
|
||||||
|
if let endProgramSectionView = self.endProgramSection.view {
|
||||||
|
if endProgramSectionView.superview == nil {
|
||||||
|
self.scrollView.addSubview(endProgramSectionView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: endProgramSectionView, frame: endProgramSectionFrame)
|
||||||
|
}
|
||||||
|
contentHeight += endProgramSectionSize.height
|
||||||
|
contentHeight += sectionSpacing
|
||||||
|
|
||||||
|
contentHeight += bottomInset
|
||||||
|
|
||||||
|
let bottomPanelTextSize = self.bottomPanelText.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(MultilineTextComponent(
|
||||||
|
text: .markdown(
|
||||||
|
text: "By creating an affiliate program, you afree to the [terms and conditions](https://telegram.org/terms) of Affiliate Programs.",
|
||||||
|
attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.itemSecondaryTextColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
maximumNumberOfLines: 0
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 1000.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let bottomPanelButtonInsets = UIEdgeInsets(top: 10.0, left: sideInset, bottom: 10.0, right: sideInset)
|
||||||
|
|
||||||
|
let bottomPanelButtonSize = self.bottomPanelButton.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(Text(text: "Start Affiliate Program", font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor))),
|
||||||
|
isEnabled: true,
|
||||||
|
allowActionWhenDisabled: true,
|
||||||
|
displaysProgress: false,
|
||||||
|
action: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = self
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: availableSize.width - bottomPanelButtonInsets.left - bottomPanelButtonInsets.right, height: 50.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
let bottomPanelHeight: CGFloat = bottomPanelButtonInsets.top + bottomPanelButtonSize.height + bottomPanelButtonInsets.bottom + bottomPanelTextSize.height + 8.0 + environment.safeInsets.bottom
|
||||||
|
let bottomPanelFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - bottomPanelHeight), size: CGSize(width: availableSize.width, height: bottomPanelHeight))
|
||||||
|
|
||||||
|
let _ = self.bottomPanelBackground.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(BlurredBackgroundComponent(
|
||||||
|
color: environment.theme.rootController.navigationBar.blurredBackgroundColor
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: bottomPanelFrame.size
|
||||||
|
)
|
||||||
|
|
||||||
|
if let bottomPanelBackgroundView = self.bottomPanelBackground.view {
|
||||||
|
if bottomPanelBackgroundView.superview == nil {
|
||||||
|
self.addSubview(bottomPanelBackgroundView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: bottomPanelBackgroundView, frame: bottomPanelFrame)
|
||||||
|
}
|
||||||
|
transition.setFrame(layer: self.bottomPanelSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: bottomPanelFrame.minY - UIScreenPixel), size: CGSize(width: availableSize.width, height: UIScreenPixel)))
|
||||||
|
|
||||||
|
let bottomPanelButtonFrame = CGRect(origin: CGPoint(x: bottomPanelFrame.minX + bottomPanelButtonInsets.left, y: bottomPanelFrame.minY + bottomPanelButtonInsets.top), size: bottomPanelButtonSize)
|
||||||
|
if let bottomPanelButtonView = self.bottomPanelButton.view {
|
||||||
|
if bottomPanelButtonView.superview == nil {
|
||||||
|
self.addSubview(bottomPanelButtonView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: bottomPanelButtonView, frame: bottomPanelButtonFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
let bottomPanelTextFrame = CGRect(origin: CGPoint(x: bottomPanelFrame.minX + floor((bottomPanelFrame.width - bottomPanelTextSize.width) * 0.5), y: bottomPanelButtonFrame.maxY + bottomPanelButtonInsets.bottom), size: bottomPanelTextSize)
|
||||||
|
if let bottomPanelTextView = self.bottomPanelText.view {
|
||||||
|
if bottomPanelTextView.superview == nil {
|
||||||
|
self.addSubview(bottomPanelTextView)
|
||||||
|
}
|
||||||
|
transition.setPosition(view: bottomPanelTextView, position: bottomPanelTextFrame.center)
|
||||||
|
bottomPanelTextView.bounds = CGRect(origin: CGPoint(), size: bottomPanelTextFrame.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentHeight += bottomPanelFrame.height
|
||||||
|
|
||||||
|
let contentSize = CGSize(width: availableSize.width, height: contentHeight)
|
||||||
|
if self.scrollView.frame != CGRect(origin: CGPoint(), size: availableSize) {
|
||||||
|
self.scrollView.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||||
|
}
|
||||||
|
if self.scrollView.contentSize != contentSize {
|
||||||
|
self.scrollView.contentSize = contentSize
|
||||||
|
}
|
||||||
|
let scrollInsets = UIEdgeInsets(top: environment.navigationHeight, left: 0.0, bottom: environment.safeInsets.bottom, right: 0.0)
|
||||||
|
if self.scrollView.scrollIndicatorInsets != scrollInsets {
|
||||||
|
self.scrollView.scrollIndicatorInsets = scrollInsets
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateScrolling(transition: transition)
|
||||||
|
|
||||||
|
return availableSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeView() -> View {
|
||||||
|
return View()
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AffiliateProgramSetupScreen: ViewControllerComponentContainer {
|
||||||
|
public final class Content: AffiliateProgramSetupScreenInitialData {
|
||||||
|
let peerId: EnginePeer.Id
|
||||||
|
|
||||||
|
init(
|
||||||
|
peerId: EnginePeer.Id
|
||||||
|
) {
|
||||||
|
self.peerId = peerId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let context: AccountContext
|
||||||
|
private var isDismissed: Bool = false
|
||||||
|
|
||||||
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
initialContent: Content
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
super.init(context: context, component: AffiliateProgramSetupScreenComponent(
|
||||||
|
context: context,
|
||||||
|
initialContent: initialContent
|
||||||
|
), navigationBarAppearance: .default, theme: .default)
|
||||||
|
|
||||||
|
self.scrollToTop = { [weak self] in
|
||||||
|
guard let self, let componentView = self.node.hostView.componentView as? AffiliateProgramSetupScreenComponent.View else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
componentView.scrollToTop()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attemptNavigation = { [weak self] complete in
|
||||||
|
guard let self, let componentView = self.node.hostView.componentView as? AffiliateProgramSetupScreenComponent.View else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return componentView.attemptNavigation(complete: complete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancelPressed() {
|
||||||
|
self.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func content(context: AccountContext, peerId: EnginePeer.Id) -> Signal<AffiliateProgramSetupScreenInitialData, NoError> {
|
||||||
|
return .single(Content(
|
||||||
|
peerId: peerId
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -607,6 +607,7 @@ private final class PeerInfoInteraction {
|
|||||||
let openWorkingHoursContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
let openWorkingHoursContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||||
let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
let openBusinessLocationContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||||
let openBirthdayContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
let openBirthdayContextMenu: (ASDisplayNode, ContextGesture?) -> Void
|
||||||
|
let editingOpenAffiliateProgram: () -> Void
|
||||||
let getController: () -> ViewController?
|
let getController: () -> ViewController?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -675,6 +676,7 @@ private final class PeerInfoInteraction {
|
|||||||
openWorkingHoursContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
openWorkingHoursContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||||
openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
openBusinessLocationContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||||
openBirthdayContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
openBirthdayContextMenu: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||||
|
editingOpenAffiliateProgram: @escaping () -> Void,
|
||||||
getController: @escaping () -> ViewController?
|
getController: @escaping () -> ViewController?
|
||||||
) {
|
) {
|
||||||
self.openUsername = openUsername
|
self.openUsername = openUsername
|
||||||
@ -742,6 +744,7 @@ private final class PeerInfoInteraction {
|
|||||||
self.openWorkingHoursContextMenu = openWorkingHoursContextMenu
|
self.openWorkingHoursContextMenu = openWorkingHoursContextMenu
|
||||||
self.openBusinessLocationContextMenu = openBusinessLocationContextMenu
|
self.openBusinessLocationContextMenu = openBusinessLocationContextMenu
|
||||||
self.openBirthdayContextMenu = openBirthdayContextMenu
|
self.openBirthdayContextMenu = openBirthdayContextMenu
|
||||||
|
self.editingOpenAffiliateProgram = editingOpenAffiliateProgram
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1906,6 +1909,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
|||||||
let ItemInfo = 3
|
let ItemInfo = 3
|
||||||
let ItemDelete = 4
|
let ItemDelete = 4
|
||||||
let ItemUsername = 5
|
let ItemUsername = 5
|
||||||
|
let ItemAffiliateProgram = 6
|
||||||
|
|
||||||
let ItemIntro = 7
|
let ItemIntro = 7
|
||||||
let ItemCommands = 8
|
let ItemCommands = 8
|
||||||
@ -1916,6 +1920,10 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
|
|||||||
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: {
|
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemUsername, label: .text("@\(user.addressName ?? "")"), text: presentationData.strings.PeerInfo_Bot_Username, icon: PresentationResourcesSettings.bot, action: {
|
||||||
interaction.editingOpenPublicLinkSetup()
|
interaction.editingOpenPublicLinkSetup()
|
||||||
}))
|
}))
|
||||||
|
//TODO:localize
|
||||||
|
items[.peerDataSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemAffiliateProgram, label: .text("Off"), additionalBadgeLabel: presentationData.strings.Settings_New, text: "Affiliate Program", icon: PresentationResourcesSettings.bot, action: {
|
||||||
|
interaction.editingOpenAffiliateProgram()
|
||||||
|
}))
|
||||||
|
|
||||||
items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: {
|
items[.peerSettings]!.append(PeerInfoScreenActionItem(id: ItemIntro, text: presentationData.strings.PeerInfo_Bot_EditIntro, icon: UIImage(bundleImageName: "Peer Info/BotIntro"), action: {
|
||||||
interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive)))
|
interaction.openPeerMention("botfather", .withBotStartPayload(ChatControllerInitialBotStart(payload: "\(user.addressName ?? "")-intro", behavior: .interactive)))
|
||||||
@ -3012,6 +3020,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.openBirthdayContextMenu(node: node, gesture: gesture)
|
self.openBirthdayContextMenu(node: node, gesture: gesture)
|
||||||
|
}, editingOpenAffiliateProgram: { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.editingOpenAffiliateProgram()
|
||||||
},
|
},
|
||||||
getController: { [weak self] in
|
getController: { [weak self] in
|
||||||
return self?.controller
|
return self?.controller
|
||||||
@ -8548,6 +8561,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func editingOpenAffiliateProgram() {
|
||||||
|
if let peer = self.data?.peer as? TelegramUser, peer.botInfo != nil {
|
||||||
|
let _ = (self.context.sharedContext.makeAffiliateProgramSetupScreenInitialData(context: self.context, peerId: peer.id)
|
||||||
|
|> deliverOnMainQueue).startStandalone(next: { [weak self] initialData in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = self.context.sharedContext.makeAffiliateProgramSetupScreen(context: self.context, initialData: initialData)
|
||||||
|
self.controller?.push(controller)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func editingOpenNameColorSetup() {
|
private func editingOpenNameColorSetup() {
|
||||||
if self.peerId == self.context.account.peerId {
|
if self.peerId == self.context.account.peerId {
|
||||||
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account)
|
let controller = PeerNameColorScreen(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, subject: .account)
|
||||||
|
@ -1461,20 +1461,22 @@ final class AutomaticBusinessMessageSetupScreenComponent: Component {
|
|||||||
items: [
|
items: [
|
||||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListItemSliderSelectorComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
values: valueList.map { item in
|
content: .discrete(ListItemSliderSelectorComponent.Discrete(
|
||||||
return environment.strings.MessageTimer_Days(Int32(item))
|
values: valueList.map { item in
|
||||||
},
|
return environment.strings.MessageTimer_Days(Int32(item))
|
||||||
markPositions: true,
|
},
|
||||||
selectedIndex: selectedInactivityIndex,
|
markPositions: true,
|
||||||
title: nil,
|
selectedIndex: selectedInactivityIndex,
|
||||||
selectedIndexUpdated: { [weak self] index in
|
title: nil,
|
||||||
guard let self else {
|
selectedIndexUpdated: { [weak self] index in
|
||||||
return
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let index = max(0, min(valueList.count - 1, index))
|
||||||
|
self.inactivityDays = valueList[index]
|
||||||
|
self.state?.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
let index = max(0, min(valueList.count - 1, index))
|
))
|
||||||
self.inactivityDays = valueList[index]
|
|
||||||
self.state?.updated(transition: .immediate)
|
|
||||||
}
|
|
||||||
)))
|
)))
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
|
@ -7,46 +7,80 @@ import LegacyComponents
|
|||||||
import ComponentFlow
|
import ComponentFlow
|
||||||
|
|
||||||
public final class SliderComponent: Component {
|
public final class SliderComponent: Component {
|
||||||
public let valueCount: Int
|
public final class Discrete: Equatable {
|
||||||
public let value: Int
|
public let valueCount: Int
|
||||||
public let markPositions: Bool
|
public let value: Int
|
||||||
|
public let markPositions: Bool
|
||||||
|
public let valueUpdated: (Int) -> Void
|
||||||
|
|
||||||
|
public init(valueCount: Int, value: Int, markPositions: Bool, valueUpdated: @escaping (Int) -> Void) {
|
||||||
|
self.valueCount = valueCount
|
||||||
|
self.value = value
|
||||||
|
self.markPositions = markPositions
|
||||||
|
self.valueUpdated = valueUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Discrete, rhs: Discrete) -> Bool {
|
||||||
|
if lhs.valueCount != rhs.valueCount {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.value != rhs.value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.markPositions != rhs.markPositions {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Continuous: Equatable {
|
||||||
|
public let value: CGFloat
|
||||||
|
public let valueUpdated: (CGFloat) -> Void
|
||||||
|
|
||||||
|
public init(value: CGFloat, valueUpdated: @escaping (CGFloat) -> Void) {
|
||||||
|
self.value = value
|
||||||
|
self.valueUpdated = valueUpdated
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: Continuous, rhs: Continuous) -> Bool {
|
||||||
|
if lhs.value != rhs.value {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Content: Equatable {
|
||||||
|
case discrete(Discrete)
|
||||||
|
case continuous(Continuous)
|
||||||
|
}
|
||||||
|
|
||||||
|
public let content: Content
|
||||||
public let trackBackgroundColor: UIColor
|
public let trackBackgroundColor: UIColor
|
||||||
public let trackForegroundColor: UIColor
|
public let trackForegroundColor: UIColor
|
||||||
public let knobSize: CGFloat?
|
public let knobSize: CGFloat?
|
||||||
public let knobColor: UIColor?
|
public let knobColor: UIColor?
|
||||||
public let valueUpdated: (Int) -> Void
|
|
||||||
public let isTrackingUpdated: ((Bool) -> Void)?
|
public let isTrackingUpdated: ((Bool) -> Void)?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
valueCount: Int,
|
content: Content,
|
||||||
value: Int,
|
|
||||||
markPositions: Bool,
|
|
||||||
trackBackgroundColor: UIColor,
|
trackBackgroundColor: UIColor,
|
||||||
trackForegroundColor: UIColor,
|
trackForegroundColor: UIColor,
|
||||||
knobSize: CGFloat? = nil,
|
knobSize: CGFloat? = nil,
|
||||||
knobColor: UIColor? = nil,
|
knobColor: UIColor? = nil,
|
||||||
valueUpdated: @escaping (Int) -> Void,
|
|
||||||
isTrackingUpdated: ((Bool) -> Void)? = nil
|
isTrackingUpdated: ((Bool) -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
self.valueCount = valueCount
|
self.content = content
|
||||||
self.value = value
|
|
||||||
self.markPositions = markPositions
|
|
||||||
self.trackBackgroundColor = trackBackgroundColor
|
self.trackBackgroundColor = trackBackgroundColor
|
||||||
self.trackForegroundColor = trackForegroundColor
|
self.trackForegroundColor = trackForegroundColor
|
||||||
self.knobSize = knobSize
|
self.knobSize = knobSize
|
||||||
self.knobColor = knobColor
|
self.knobColor = knobColor
|
||||||
self.valueUpdated = valueUpdated
|
|
||||||
self.isTrackingUpdated = isTrackingUpdated
|
self.isTrackingUpdated = isTrackingUpdated
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: SliderComponent, rhs: SliderComponent) -> Bool {
|
public static func ==(lhs: SliderComponent, rhs: SliderComponent) -> Bool {
|
||||||
if lhs.valueCount != rhs.valueCount {
|
if lhs.content != rhs.content {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.value != rhs.value {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs.markPositions != rhs.markPositions {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.trackBackgroundColor != rhs.trackBackgroundColor {
|
if lhs.trackBackgroundColor != rhs.trackBackgroundColor {
|
||||||
@ -122,10 +156,16 @@ public final class SliderComponent: Component {
|
|||||||
sliderView.minimumValue = 0.0
|
sliderView.minimumValue = 0.0
|
||||||
sliderView.startValue = 0.0
|
sliderView.startValue = 0.0
|
||||||
sliderView.disablesInteractiveTransitionGestureRecognizer = true
|
sliderView.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
sliderView.maximumValue = CGFloat(component.valueCount - 1)
|
|
||||||
sliderView.positionsCount = component.valueCount
|
switch component.content {
|
||||||
sliderView.useLinesForPositions = true
|
case let .discrete(discrete):
|
||||||
sliderView.markPositions = component.markPositions
|
sliderView.maximumValue = CGFloat(discrete.valueCount - 1)
|
||||||
|
sliderView.positionsCount = discrete.valueCount
|
||||||
|
sliderView.useLinesForPositions = true
|
||||||
|
sliderView.markPositions = discrete.markPositions
|
||||||
|
case .continuous:
|
||||||
|
sliderView.maximumValue = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
sliderView.backgroundColor = nil
|
sliderView.backgroundColor = nil
|
||||||
sliderView.isOpaque = false
|
sliderView.isOpaque = false
|
||||||
@ -162,7 +202,12 @@ public final class SliderComponent: Component {
|
|||||||
self.sliderView = sliderView
|
self.sliderView = sliderView
|
||||||
self.addSubview(sliderView)
|
self.addSubview(sliderView)
|
||||||
}
|
}
|
||||||
sliderView.value = CGFloat(component.value)
|
switch component.content {
|
||||||
|
case let .discrete(discrete):
|
||||||
|
sliderView.value = CGFloat(discrete.value)
|
||||||
|
case let .continuous(continuous):
|
||||||
|
sliderView.value = continuous.value
|
||||||
|
}
|
||||||
sliderView.interactionBegan = {
|
sliderView.interactionBegan = {
|
||||||
internalIsTrackingUpdated?(true)
|
internalIsTrackingUpdated?(true)
|
||||||
}
|
}
|
||||||
@ -180,7 +225,12 @@ public final class SliderComponent: Component {
|
|||||||
guard let component = self.component, let sliderView = self.sliderView else {
|
guard let component = self.component, let sliderView = self.sliderView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
component.valueUpdated(Int(sliderView.value))
|
switch component.content {
|
||||||
|
case let .discrete(discrete):
|
||||||
|
discrete.valueUpdated(Int(sliderView.value))
|
||||||
|
case let .continuous(continuous):
|
||||||
|
continuous.valueUpdated(sliderView.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ import GiftOptionsScreen
|
|||||||
import GiftViewScreen
|
import GiftViewScreen
|
||||||
import StarsIntroScreen
|
import StarsIntroScreen
|
||||||
import ContentReportScreen
|
import ContentReportScreen
|
||||||
|
import AffiliateProgramSetupScreen
|
||||||
|
|
||||||
private final class AccountUserInterfaceInUseContext {
|
private final class AccountUserInterfaceInUseContext {
|
||||||
let subscribers = Bag<(Bool) -> Void>()
|
let subscribers = Bag<(Bool) -> Void>()
|
||||||
@ -2828,6 +2829,14 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) {
|
public func openWebApp(context: AccountContext, parentController: ViewController, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, botPeer: EnginePeer, chatPeer: EnginePeer?, threadId: Int64?, buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource, skipTermsOfService: Bool, payload: String?) {
|
||||||
openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload)
|
openWebAppImpl(context: context, parentController: parentController, updatedPresentationData: updatedPresentationData, botPeer: botPeer, chatPeer: chatPeer, threadId: threadId, buttonText: buttonText, url: url, simple: simple, source: source, skipTermsOfService: skipTermsOfService, payload: payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeAffiliateProgramSetupScreenInitialData(context: AccountContext, peerId: EnginePeer.Id) -> Signal<AffiliateProgramSetupScreenInitialData, NoError> {
|
||||||
|
return AffiliateProgramSetupScreen.content(context: context, peerId: peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeAffiliateProgramSetupScreen(context: AccountContext, initialData: AffiliateProgramSetupScreenInitialData) -> ViewController {
|
||||||
|
return AffiliateProgramSetupScreen(context: context, initialContent: initialData as! AffiliateProgramSetupScreen.Content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user