mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
85207874a1
commit
54489f428a
@ -41,6 +41,14 @@ public let sheetComponentTag = GenericComponentViewTag()
|
||||
public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
public typealias EnvironmentType = (ChildEnvironmentType, SheetComponentEnvironment)
|
||||
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var contentHeight: CGFloat
|
||||
|
||||
public init() {
|
||||
self.contentHeight = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
public enum BackgroundColor: Equatable {
|
||||
public enum BlurStyle: Equatable {
|
||||
case light
|
||||
@ -54,17 +62,20 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
public let content: AnyComponent<ChildEnvironmentType>
|
||||
public let backgroundColor: BackgroundColor
|
||||
public let followContentSizeChanges: Bool
|
||||
public let externalState: ExternalState?
|
||||
public let animateOut: ActionSlot<Action<()>>
|
||||
|
||||
public init(
|
||||
content: AnyComponent<ChildEnvironmentType>,
|
||||
backgroundColor: BackgroundColor,
|
||||
followContentSizeChanges: Bool = false,
|
||||
externalState: ExternalState? = nil,
|
||||
animateOut: ActionSlot<Action<()>>
|
||||
) {
|
||||
self.content = content
|
||||
self.backgroundColor = backgroundColor
|
||||
self.followContentSizeChanges = followContentSizeChanges
|
||||
self.externalState = externalState
|
||||
self.animateOut = animateOut
|
||||
}
|
||||
|
||||
@ -327,6 +338,7 @@ public final class SheetComponent<ChildEnvironmentType: Equatable>: Component {
|
||||
},
|
||||
containerSize: containerSize
|
||||
)
|
||||
component.externalState?.contentHeight = contentSize.height
|
||||
|
||||
self.ignoreScrolling = true
|
||||
if let contentView = self.contentView.view {
|
||||
|
@ -591,7 +591,7 @@ class GiftOptionItemNode: ItemListRevealOptionsItemNode {
|
||||
let badgeSize = strongSelf.titleBadge.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BoostIconComponent(text: badge)
|
||||
BoostIconComponent(hasIcon: true, text: badge)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: params.width, height: 100.0)
|
||||
|
@ -10,6 +10,34 @@ import PresentationDataUtils
|
||||
|
||||
//TODO:localize
|
||||
|
||||
private struct BoostState {
|
||||
let level: Int32
|
||||
let currentLevelBoosts: Int32
|
||||
let nextLevelBoosts: Int32?
|
||||
let boosts: Int32
|
||||
|
||||
func displayData(peer: EnginePeer, isCurrent: Bool, myBoostCount: Int32, currentMyBoostCount: Int32, replacedBoosts: Int32? = nil) -> (subject: PremiumLimitScreen.Subject, count: Int32) {
|
||||
var currentLevel = self.level
|
||||
var nextLevelBoosts = self.nextLevelBoosts
|
||||
var currentLevelBoosts = self.currentLevelBoosts
|
||||
var boosts = self.boosts
|
||||
if let replacedBoosts {
|
||||
boosts = max(currentLevelBoosts, boosts - replacedBoosts)
|
||||
}
|
||||
|
||||
if currentMyBoostCount > 0 && self.boosts == currentLevelBoosts {
|
||||
currentLevel = max(0, currentLevel - 1)
|
||||
nextLevelBoosts = currentLevelBoosts
|
||||
currentLevelBoosts = max(0, currentLevelBoosts - 1)
|
||||
}
|
||||
|
||||
return (
|
||||
.storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount),
|
||||
boosts
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func PremiumBoostScreen(
|
||||
context: AccountContext,
|
||||
contentContext: Any?,
|
||||
@ -17,6 +45,7 @@ public func PremiumBoostScreen(
|
||||
isCurrent: Bool,
|
||||
status: ChannelBoostStatus?,
|
||||
myBoostStatus: MyBoostStatus?,
|
||||
replacedBoosts: (Int32, Int32)? = nil,
|
||||
forceDark: Bool,
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
presentController: @escaping (ViewController) -> Void,
|
||||
@ -35,6 +64,7 @@ public func PremiumBoostScreen(
|
||||
let isPremium = accountPeer.isPremium
|
||||
|
||||
var myBoostCount: Int32 = 0
|
||||
var currentMyBoostCount: Int32 = 0
|
||||
var availableBoosts: [MyBoostStatus.Boost] = []
|
||||
var occupiedBoosts: [MyBoostStatus.Boost] = []
|
||||
if let myBoostStatus {
|
||||
@ -51,25 +81,15 @@ public func PremiumBoostScreen(
|
||||
}
|
||||
}
|
||||
|
||||
var currentLevel = Int32(status.level)
|
||||
var currentLevelBoosts = Int32(status.currentLevelBoosts)
|
||||
var nextLevelBoosts = status.nextLevelBoosts.flatMap(Int32.init)
|
||||
|
||||
if myBoostCount > 0 && status.boosts == currentLevelBoosts {
|
||||
currentLevel = max(0, currentLevel - 1)
|
||||
nextLevelBoosts = currentLevelBoosts
|
||||
currentLevelBoosts = max(0, currentLevelBoosts - 1)
|
||||
}
|
||||
|
||||
let subject: PremiumLimitScreen.Subject = .storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount)
|
||||
let nextSubject = Promise<PremiumLimitScreen.Subject>()
|
||||
nextSubject.set(.single(.storiesChannelBoost(peer: peer, isCurrent: isCurrent, level: currentLevel, currentLevelBoosts: currentLevelBoosts, nextLevelBoosts: nextLevelBoosts, link: nil, myBoostCount: myBoostCount + 1)))
|
||||
|
||||
var nextCount = Int32(status.boosts + 1)
|
||||
let initialState = BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts))
|
||||
let updatedState = Promise<BoostState?>()
|
||||
updatedState.set(.single(BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))))
|
||||
|
||||
var updateImpl: (() -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = PremiumLimitScreen(context: context, subject: subject, count: Int32(status.boosts), forceDark: forceDark, action: {
|
||||
|
||||
let (initialSubject, initialCount) = initialState.displayData(peer: peer, isCurrent: isCurrent, myBoostCount: myBoostCount, currentMyBoostCount: 0, replacedBoosts: replacedBoosts?.0)
|
||||
let controller = PremiumLimitScreen(context: context, subject: initialSubject, count: initialCount, forceDark: forceDark, action: {
|
||||
let dismiss = false
|
||||
updateImpl?()
|
||||
return dismiss
|
||||
@ -78,33 +98,88 @@ public func PremiumBoostScreen(
|
||||
openPeer(peer)
|
||||
})
|
||||
pushController(controller)
|
||||
|
||||
if let (replacedBoosts, inChannels) = replacedBoosts {
|
||||
currentMyBoostCount += 1
|
||||
let (subject, count) = initialState.displayData(peer: peer, isCurrent: isCurrent, myBoostCount: myBoostCount, currentMyBoostCount: 1, replacedBoosts: nil)
|
||||
controller.updateSubject(subject, count: count)
|
||||
|
||||
Queue.mainQueue().after(0.3) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .image(image: generateTintedImage(image: UIImage(bundleImageName: "Premium/BoostReplaceIcon"), color: .white)!, title: nil, text: "\(replacedBoosts) boosts are reassigned from \(inChannels) other channel.", round: false, undoText: nil), elevatedLayout: false, position: .bottom, action: { _ in return true })
|
||||
controller.present(undoController, in: .current)
|
||||
}
|
||||
}
|
||||
|
||||
controller.disposed = {
|
||||
dismissed()
|
||||
}
|
||||
|
||||
var updating = false
|
||||
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
updateImpl = { [weak controller] in
|
||||
guard !updating else {
|
||||
return
|
||||
}
|
||||
if let _ = status.nextLevelBoosts {
|
||||
if let availableBoost = availableBoosts.first {
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot]).startStandalone()
|
||||
let _ = (nextSubject.get()
|
||||
currentMyBoostCount += 1
|
||||
myBoostCount += 1
|
||||
|
||||
updating = true
|
||||
let _ = (context.engine.peers.applyChannelBoost(peerId: peerId, slots: [availableBoost.slot])
|
||||
|> deliverOnMainQueue).startStandalone(completed: {
|
||||
updating = false
|
||||
|
||||
updatedState.set(context.engine.peers.getChannelBoostStatus(peerId: peerId)
|
||||
|> map { status in
|
||||
if let status {
|
||||
return BoostState(level: Int32(status.level), currentLevelBoosts: Int32(status.currentLevelBoosts), nextLevelBoosts: status.nextLevelBoosts.flatMap(Int32.init), boosts: Int32(status.boosts + 1))
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let _ = (updatedState.get()
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { nextSubject in
|
||||
controller?.updateSubject(nextSubject, count: nextCount)
|
||||
|> deliverOnMainQueue).startStandalone(next: { state in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
let (subject, count) = state.displayData(peer: peer, isCurrent: isCurrent, myBoostCount: myBoostCount, currentMyBoostCount: currentMyBoostCount)
|
||||
controller?.updateSubject(subject, count: count)
|
||||
})
|
||||
|
||||
availableBoosts.removeFirst()
|
||||
nextCount += 1
|
||||
} else if !occupiedBoosts.isEmpty, let myBoostStatus {
|
||||
var dismissReplaceImpl: (() -> Void)?
|
||||
let replaceController = ReplaceBoostScreen(context: context, peerId: peerId, myBoostStatus: myBoostStatus, replaceBoosts: { slots in
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone()
|
||||
var channelIds = Set<EnginePeer.Id>()
|
||||
for boost in myBoostStatus.boosts {
|
||||
if slots.contains(boost.slot) {
|
||||
if let peer = boost.peer {
|
||||
channelIds.insert(peer.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "\(slots.count) boosts are reassigned from 1 other channel.", timeout: nil, customUndoText: nil), elevatedLayout: true, position: .bottom, action: { _ in return true })
|
||||
presentController(undoController)
|
||||
let _ = context.engine.peers.applyChannelBoost(peerId: peerId, slots: slots).startStandalone(completed: {
|
||||
let _ = combineLatest(queue: Queue.mainQueue(),
|
||||
context.engine.peers.getChannelBoostStatus(peerId: peerId),
|
||||
context.engine.peers.getMyBoostStatus()
|
||||
).startStandalone(next: { boostStatus, myBoostStatus in
|
||||
dismissReplaceImpl?()
|
||||
PremiumBoostScreen(context: context, contentContext: contentContext, peerId: peerId, isCurrent: isCurrent, status: boostStatus, myBoostStatus: myBoostStatus, replacedBoosts: (Int32(slots.count), Int32(channelIds.count)), forceDark: forceDark, openPeer: openPeer, presentController: presentController, pushController: pushController, dismissed: dismissed)
|
||||
})
|
||||
})
|
||||
})
|
||||
dismissImpl?()
|
||||
pushController(replaceController)
|
||||
dismissReplaceImpl = { [weak replaceController] in
|
||||
replaceController?.dismiss(animated: true)
|
||||
}
|
||||
} else {
|
||||
if isPremium {
|
||||
let controller = textAlertController(
|
||||
|
@ -821,7 +821,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
let closeButton = Child(Button.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let text = Child(BalancedTextComponent.self)
|
||||
let alternateText = Child(BalancedTextComponent.self)
|
||||
let alternateText = Child(List<Empty>.self)
|
||||
let limit = Child(PremiumLimitDisplayComponent.self)
|
||||
let linkButton = Child(SolidRoundedButtonComponent.self)
|
||||
let button = Child(SolidRoundedButtonComponent.self)
|
||||
@ -881,6 +881,9 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
var peerShortcutChild: _UpdatedChildComponent?
|
||||
|
||||
var useAlternateText = false
|
||||
var alternateTitle = ""
|
||||
var alternateBadge: String?
|
||||
|
||||
var titleText = strings.Premium_LimitReached
|
||||
var actionButtonText: String?
|
||||
var actionButtonHasGloss = true
|
||||
@ -1159,7 +1162,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
state.myBoostCount = myBoostCount
|
||||
boostUpdated = true
|
||||
}
|
||||
useAlternateText = (myBoostCount % 2) != 0
|
||||
useAlternateText = myBoostCount > 0
|
||||
|
||||
iconName = "Premium/Boost"
|
||||
badgeText = "\(component.count)"
|
||||
@ -1217,8 +1220,10 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
premiumTitle = ""
|
||||
|
||||
if myBoostCount > 0 {
|
||||
let prefixString = isCurrent ? strings.ChannelBoost_YouBoostedChannelText(peer.compactDisplayTitle).string : strings.ChannelBoost_YouBoostedOtherChannelText
|
||||
|
||||
alternateTitle = isCurrent ? strings.ChannelBoost_YouBoostedChannelText(peer.compactDisplayTitle).string : strings.ChannelBoost_YouBoostedOtherChannelText
|
||||
if myBoostCount > 1 {
|
||||
alternateBadge = "X\(myBoostCount)"
|
||||
}
|
||||
let storiesString = strings.ChannelBoost_StoriesPerDay(level + 1)
|
||||
if let _ = remaining {
|
||||
actionButtonText = "Boost Again"
|
||||
@ -1246,8 +1251,6 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
} else {
|
||||
string = strings.ChannelBoost_BoostedChannelReachedLevel("\(level + 1)", storiesString).string
|
||||
}
|
||||
|
||||
string = "**\(prefixString)**\n\(string)"
|
||||
}
|
||||
|
||||
let progress: CGFloat
|
||||
@ -1299,15 +1302,42 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
var alternateTextChild: _UpdatedChildComponent?
|
||||
if useAlternateText {
|
||||
alternateTextChild = alternateText.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .markdown(text: string, attributes: markdownAttributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
component: List(
|
||||
[
|
||||
AnyComponentWithIdentity(
|
||||
id: "title",
|
||||
component: AnyComponent(
|
||||
BoostedTitleContent(text: NSAttributedString(string: alternateTitle, font: Font.semibold(15.0), textColor: textColor), badge: alternateBadge)
|
||||
)
|
||||
),
|
||||
AnyComponentWithIdentity(
|
||||
id: "text",
|
||||
component: AnyComponent(
|
||||
BalancedTextComponent(
|
||||
text: .markdown(text: string, attributes: markdownAttributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.1
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
centerAlignment: true
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
// alternateTextChild = alternateText.update(
|
||||
// component: BalancedTextComponent(
|
||||
// text: .markdown(text: string, attributes: markdownAttributes),
|
||||
// horizontalAlignment: .center,
|
||||
// maximumNumberOfLines: 0,
|
||||
// lineSpacing: 0.1
|
||||
// ),
|
||||
// availableSize: CGSize(width: context.availableSize.width - textSideInset * 2.0, height: context.availableSize.height),
|
||||
// transition: .immediate
|
||||
// )
|
||||
} else {
|
||||
textChild = text.update(
|
||||
component: BalancedTextComponent(
|
||||
@ -1417,7 +1447,7 @@ private final class LimitSheetContent: CombinedComponent {
|
||||
)
|
||||
buttonOffset += 66.0
|
||||
|
||||
let linkFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + ceil((textChild?.size ?? .zero).height / 2.0) + 24.0), size: linkButton.size)
|
||||
let linkFrame = CGRect(origin: CGPoint(x: sideInset, y: textOffset + (textChild?.size ?? .zero).height + 24.0), size: linkButton.size)
|
||||
context.add(linkButton
|
||||
.position(CGPoint(x: linkFrame.midX, y: linkFrame.midY))
|
||||
)
|
||||
@ -1637,6 +1667,8 @@ private final class LimitSheetComponent: CombinedComponent {
|
||||
let sheet = Child(SheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let sheetExternalState = SheetComponent<EnvironmentType>.ExternalState()
|
||||
|
||||
return { context in
|
||||
let environment = context.environment[EnvironmentType.self]
|
||||
|
||||
@ -1663,6 +1695,7 @@ private final class LimitSheetComponent: CombinedComponent {
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
externalState: sheetExternalState,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
@ -1695,6 +1728,22 @@ private final class LimitSheetComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
if let controller = controller(), !controller.automaticallyControlPresentationContextLayout {
|
||||
let layout = ContainerViewLayout(
|
||||
size: context.availableSize,
|
||||
metrics: environment.metrics,
|
||||
deviceMetrics: environment.deviceMetrics,
|
||||
intrinsicInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: max(environment.safeInsets.bottom, sheetExternalState.contentHeight), right: 0.0),
|
||||
safeInsets: UIEdgeInsets(top: 0.0, left: environment.safeInsets.left, bottom: 0.0, right: environment.safeInsets.right),
|
||||
additionalInsets: .zero,
|
||||
statusBarHeight: environment.statusBarHeight,
|
||||
inputHeight: nil,
|
||||
inputHeightIsInteractivellyChanging: false,
|
||||
inVoiceOver: false
|
||||
)
|
||||
controller.presentationContext.containerLayoutUpdated(layout, transition: context.transition.containedViewLayoutTransition)
|
||||
}
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
@ -1734,6 +1783,9 @@ public class PremiumLimitScreen: ViewControllerComponentContainer {
|
||||
}, openPeer: openPeer, openStats: openStats, openGift: openGift), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
||||
|
||||
self.navigationPresentation = .flatModal
|
||||
if case .storiesChannelBoost = subject {
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
}
|
||||
|
||||
self.wasDismissed = cancel
|
||||
|
||||
@ -1769,13 +1821,25 @@ public class PremiumLimitScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
public func updateSubject(_ subject: Subject, count: Int32) {
|
||||
let component = LimitSheetComponent(context: self.context, subject: subject, count: count, cancel: {}, action: {
|
||||
return true
|
||||
}, openPeer: self.openPeer, openStats: nil, openGift: nil)
|
||||
let component = LimitSheetComponent(
|
||||
context: self.context,
|
||||
subject: subject,
|
||||
count: count,
|
||||
cancel: {},
|
||||
action: { [weak self] in
|
||||
return self?.action?() ?? true
|
||||
},
|
||||
openPeer: self.openPeer,
|
||||
openStats: nil,
|
||||
openGift: nil
|
||||
)
|
||||
self.updateComponent(component: AnyComponent(component), transition: .easeInOut(duration: 0.2))
|
||||
|
||||
self.animateSuccess()
|
||||
}
|
||||
|
||||
public func animateSuccess() {
|
||||
self.hapticFeedback.impact()
|
||||
|
||||
self.view.addSubview(ConfettiView(frame: self.view.bounds))
|
||||
}
|
||||
}
|
||||
@ -1814,8 +1878,6 @@ private final class PeerShortcutComponent: Component {
|
||||
private let avatarNode: AvatarNode
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private let badge = ComponentView<Empty>()
|
||||
|
||||
private var component: PeerShortcutComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
@ -1869,29 +1931,6 @@ private final class PeerShortcutComponent: Component {
|
||||
view.frame = textFrame
|
||||
}
|
||||
|
||||
if let badge = component.badge {
|
||||
let badgeSize = self.badge.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BoostIconComponent(text: badge)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let view = self.badge.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
let badgeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(size.width - badgeSize.width / 2.0), y: -5.0), size: badgeSize)
|
||||
view.frame = badgeFrame
|
||||
}
|
||||
} else {
|
||||
if let view = self.badge.view {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
self.backgroundView.frame = CGRect(origin: .zero, size: size)
|
||||
|
||||
return size
|
||||
@ -1908,13 +1947,18 @@ private final class PeerShortcutComponent: Component {
|
||||
}
|
||||
|
||||
public final class BoostIconComponent: Component {
|
||||
let hasIcon: Bool
|
||||
let text: String
|
||||
|
||||
public init(text: String) {
|
||||
public init(hasIcon: Bool, text: String) {
|
||||
self.hasIcon = hasIcon
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public static func ==(lhs: BoostIconComponent, rhs: BoostIconComponent) -> Bool {
|
||||
if lhs.hasIcon != rhs.hasIcon {
|
||||
return false
|
||||
}
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
@ -1961,11 +2005,11 @@ public final class BoostIconComponent: Component {
|
||||
)
|
||||
|
||||
let spacing: CGFloat = 2.0
|
||||
var totalWidth = textSize.width + spacing
|
||||
var totalWidth = textSize.width
|
||||
var iconSize = CGSize()
|
||||
if let icon = self.badgeIcon.image {
|
||||
if let icon = self.badgeIcon.image, component.hasIcon {
|
||||
iconSize = CGSize(width: icon.size.width * 0.9, height: icon.size.height * 0.9)
|
||||
totalWidth += icon.size.width
|
||||
totalWidth += spacing + icon.size.width
|
||||
}
|
||||
|
||||
let size = CGSize(width: totalWidth + 8.0, height: 19.0)
|
||||
@ -1973,7 +2017,7 @@ public final class BoostIconComponent: Component {
|
||||
let iconFrame = CGRect(x: floorToScreenPixels((size.width - totalWidth) / 2.0 + 1.0), y: 4.0 + UIScreenPixel, width: iconSize.width, height: iconSize.height)
|
||||
self.badgeIcon.frame = iconFrame
|
||||
|
||||
let textFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + spacing, y: 4.0), size: textSize)
|
||||
let textFrame = CGRect(origin: CGPoint(x: component.hasIcon ? iconFrame.maxX + spacing : 5.0, y: 4.0), size: textSize)
|
||||
|
||||
if let view = self.badgeText.view {
|
||||
if view.superview == nil {
|
||||
@ -1997,3 +2041,64 @@ public final class BoostIconComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class BoostedTitleContent: CombinedComponent {
|
||||
let text: NSAttributedString
|
||||
let badge: String?
|
||||
|
||||
init(
|
||||
text: NSAttributedString,
|
||||
badge: String?
|
||||
) {
|
||||
self.text = text
|
||||
self.badge = badge
|
||||
}
|
||||
|
||||
static func ==(lhs: BoostedTitleContent, rhs: BoostedTitleContent) -> Bool {
|
||||
if lhs.text != rhs.text {
|
||||
return false
|
||||
}
|
||||
if lhs.badge != rhs.badge {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let text = Child(MultilineTextComponent.self)
|
||||
let badge = Child(BoostIconComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
||||
let height: CGFloat = 24.0
|
||||
var totalWidth: CGFloat = 0.0
|
||||
let text = text.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(component.text),
|
||||
horizontalAlignment: .center
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - 40.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
totalWidth += text.size.width
|
||||
context.add(text
|
||||
.position(CGPoint(x: text.size.width / 2.0, y: height / 2.0))
|
||||
)
|
||||
|
||||
if let badgeText = component.badge {
|
||||
let badge = badge.update(
|
||||
component: BoostIconComponent(hasIcon: false, text: badgeText),
|
||||
availableSize: CGSize(width: 24.0, height: 24.0),
|
||||
transition: .immediate
|
||||
)
|
||||
totalWidth += badge.size.width + 4.0
|
||||
context.add(badge
|
||||
.position(CGPoint(x: totalWidth - badge.size.width / 2.0, y: height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
return CGSize(width: totalWidth, height: height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +349,6 @@ public class ReplaceBoostScreen: ViewController {
|
||||
return
|
||||
}
|
||||
self.controller?.replaceBoosts?(self.selectedSlots)
|
||||
self.controller?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,7 +368,7 @@ public class ReplaceBoostScreen: ViewController {
|
||||
}
|
||||
|
||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if case .ended = recognizer.state, !self.footerView.inProgress {
|
||||
self.controller?.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
@ -950,10 +949,12 @@ private final class FooterView: UIView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private var currentLayout: (CGSize, UIEdgeInsets)?
|
||||
fileprivate var inProgress = false
|
||||
|
||||
private var currentLayout: (CGSize, UIEdgeInsets, PresentationTheme, Int32)?
|
||||
func update(size: CGSize, insets: UIEdgeInsets, theme: PresentationTheme, count: Int32) -> CGFloat {
|
||||
let hadLayout = self.currentLayout != nil
|
||||
self.currentLayout = (size, insets)
|
||||
self.currentLayout = (size, insets, theme, count)
|
||||
|
||||
self.backgroundNode.updateColor(color: theme.rootController.tabBar.backgroundColor, transition: .immediate)
|
||||
self.separatorView.backgroundColor = theme.rootController.tabBar.separatorColor
|
||||
@ -996,11 +997,15 @@ private final class FooterView: UIView {
|
||||
))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
displaysProgress: self.inProgress,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
guard let self, !self.inProgress else {
|
||||
return
|
||||
}
|
||||
self.inProgress = true
|
||||
if let (size, insets, theme, count) = self.currentLayout {
|
||||
let _ = self.update(size: size, insets: insets, theme: theme, count: count)
|
||||
}
|
||||
self.action()
|
||||
}
|
||||
)
|
||||
|
@ -109,7 +109,7 @@ final class EmojiPickerItemNode: ListViewItemNode {
|
||||
let insets: UIEdgeInsets
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
let contentSize = CGSize(width: params.width, height: 200.0)
|
||||
let contentSize = CGSize(width: params.width, height: params.availableHeight - 452.0)
|
||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: contentSize.height - 20.0), insets: insets)
|
||||
|
@ -189,7 +189,7 @@ private func peerNameColorScreenEntries(
|
||||
state: PeerNameColorScreenState,
|
||||
peer: EnginePeer?,
|
||||
isPremium: Bool,
|
||||
emojiContent: EmojiPagerContentComponent
|
||||
emojiContent: EmojiPagerContentComponent?
|
||||
) -> [PeerNameColorScreenEntry] {
|
||||
var entries: [PeerNameColorScreenEntry] = []
|
||||
|
||||
@ -255,8 +255,10 @@ private func peerNameColorScreenEntries(
|
||||
))
|
||||
entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account))
|
||||
|
||||
entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title))
|
||||
entries.append(.backgroundEmoji(emojiContent, nameColor.color))
|
||||
if let emojiContent {
|
||||
entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title))
|
||||
entries.append(.backgroundEmoji(emojiContent, nameColor.color))
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
@ -316,7 +318,7 @@ public func PeerNameColorScreen(
|
||||
statePromise.get(),
|
||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
)
|
||||
|> mapToSignal { state, peer in
|
||||
|> mapToSignal { state, peer -> Signal<EmojiPagerContentComponent?, NoError> in
|
||||
var selectedEmojiId: Int64?
|
||||
if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId {
|
||||
selectedEmojiId = updatedBackgroundEmojiId
|
||||
@ -351,6 +353,7 @@ public func PeerNameColorScreen(
|
||||
selectedItems: Set(selectedItems),
|
||||
backgroundIconColor: nameColor
|
||||
)
|
||||
|> map(Optional.init)
|
||||
}
|
||||
|
||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||
@ -359,7 +362,7 @@ public func PeerNameColorScreen(
|
||||
statePromise.get(),
|
||||
context.engine.stickers.availableReactions(),
|
||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)),
|
||||
emojiContent
|
||||
.single(nil) |> then(emojiContent)
|
||||
)
|
||||
|> deliverOnMainQueue
|
||||
|> map { presentationData, state, availableReactions, peer, emojiContent -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
@ -475,7 +478,7 @@ public func PeerNameColorScreen(
|
||||
}
|
||||
)
|
||||
|
||||
emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
|
||||
emojiContent?.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
|
||||
performItemAction: { _, item, _, _, _, _ in
|
||||
var selectedFileId: Int64?
|
||||
if let fileId = item.itemFile?.fileId.id {
|
||||
@ -565,7 +568,7 @@ public func PeerNameColorScreen(
|
||||
entries: entries,
|
||||
style: .blocks,
|
||||
footerItem: footerItem,
|
||||
animateChanges: true
|
||||
animateChanges: false
|
||||
)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Premium/BoostReplaceIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/BoostReplaceIcon.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "replacedboost_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
273
submodules/TelegramUI/Images.xcassets/Premium/BoostReplaceIcon.imageset/replacedboost_30.pdf
vendored
Normal file
273
submodules/TelegramUI/Images.xcassets/Premium/BoostReplaceIcon.imageset/replacedboost_30.pdf
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.877197 15.157974 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.552186 8.359201 m
|
||||
5.243867 8.359201 5.008486 8.634665 5.056572 8.939211 c
|
||||
5.727019 13.185373 l
|
||||
5.810456 13.713807 5.119398 13.988482 4.817303 13.546960 c
|
||||
0.088512 6.635651 l
|
||||
-0.139333 6.302646 0.099122 5.850563 0.502614 5.850563 c
|
||||
2.581887 5.850563 l
|
||||
2.890206 5.850563 3.125587 5.575099 3.077501 5.270554 c
|
||||
2.407054 1.024391 l
|
||||
2.323617 0.495958 3.014675 0.221282 3.316770 0.662805 c
|
||||
8.045561 7.574114 l
|
||||
8.273406 7.907119 8.034951 8.359201 7.631459 8.359201 c
|
||||
5.552186 8.359201 l
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 16.877197 1.157974 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.552186 8.359201 m
|
||||
5.243867 8.359201 5.008486 8.634665 5.056572 8.939211 c
|
||||
5.727019 13.185373 l
|
||||
5.810456 13.713807 5.119398 13.988482 4.817303 13.546960 c
|
||||
0.088512 6.635651 l
|
||||
-0.139333 6.302646 0.099122 5.850563 0.502614 5.850563 c
|
||||
2.581887 5.850563 l
|
||||
2.890206 5.850563 3.125587 5.575099 3.077501 5.270554 c
|
||||
2.407054 1.024391 l
|
||||
2.323617 0.495958 3.014675 0.221282 3.316770 0.662805 c
|
||||
8.045561 7.574114 l
|
||||
8.273406 7.907119 8.034951 8.359201 7.631459 8.359201 c
|
||||
5.552186 8.359201 l
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 20.000000 15.177917 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
6.586899 4.235184 m
|
||||
6.911034 4.559319 6.911034 5.084846 6.586899 5.408981 c
|
||||
6.262764 5.733116 5.737236 5.733116 5.413101 5.408981 c
|
||||
6.586899 4.235184 l
|
||||
h
|
||||
3.000000 1.822083 m
|
||||
2.413101 1.235184 l
|
||||
2.737236 0.911049 3.262764 0.911049 3.586899 1.235184 c
|
||||
3.000000 1.822083 l
|
||||
h
|
||||
0.586899 5.408981 m
|
||||
0.262763 5.733116 -0.262763 5.733116 -0.586899 5.408981 c
|
||||
-0.911034 5.084846 -0.911034 4.559319 -0.586899 4.235184 c
|
||||
0.586899 5.408981 l
|
||||
h
|
||||
5.413101 5.408981 m
|
||||
2.413101 2.408981 l
|
||||
3.586899 1.235184 l
|
||||
6.586899 4.235184 l
|
||||
5.413101 5.408981 l
|
||||
h
|
||||
3.586899 2.408981 m
|
||||
0.586899 5.408981 l
|
||||
-0.586899 4.235184 l
|
||||
2.413101 1.235184 l
|
||||
3.586899 2.408981 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 16.000000 15.339355 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 10.490644 m
|
||||
-0.458396 10.490644 -0.830000 10.119040 -0.830000 9.660645 c
|
||||
-0.830000 9.202249 -0.458396 8.830645 0.000000 8.830645 c
|
||||
0.000000 10.490644 l
|
||||
h
|
||||
6.170000 1.660645 m
|
||||
6.170000 1.202249 6.541604 0.830645 7.000000 0.830645 c
|
||||
7.458396 0.830645 7.830000 1.202249 7.830000 1.660645 c
|
||||
6.170000 1.660645 l
|
||||
h
|
||||
6.727516 8.295621 m
|
||||
7.467052 8.672433 l
|
||||
6.727516 8.295621 l
|
||||
h
|
||||
0.000000 8.830645 m
|
||||
3.000000 8.830645 l
|
||||
3.000000 10.490644 l
|
||||
0.000000 10.490644 l
|
||||
0.000000 8.830645 l
|
||||
h
|
||||
6.170000 5.660645 m
|
||||
6.170000 1.660645 l
|
||||
7.830000 1.660645 l
|
||||
7.830000 5.660645 l
|
||||
6.170000 5.660645 l
|
||||
h
|
||||
3.000000 8.830645 m
|
||||
3.713761 8.830645 4.199165 8.829999 4.574407 8.799340 c
|
||||
4.939959 8.769474 5.127283 8.715313 5.258164 8.648625 c
|
||||
6.011788 10.127696 l
|
||||
5.607891 10.333492 5.177792 10.415573 4.709584 10.453828 c
|
||||
4.251065 10.491290 3.686370 10.490644 3.000000 10.490644 c
|
||||
3.000000 8.830645 l
|
||||
h
|
||||
7.830000 5.660645 m
|
||||
7.830000 6.347014 7.830646 6.911709 7.793183 7.370228 c
|
||||
7.754929 7.838436 7.672848 8.268535 7.467052 8.672433 c
|
||||
5.987981 7.918809 l
|
||||
6.054668 7.787928 6.108829 7.600603 6.138696 7.235051 c
|
||||
6.169354 6.859809 6.170000 6.374406 6.170000 5.660645 c
|
||||
7.830000 5.660645 l
|
||||
h
|
||||
5.258164 8.648625 m
|
||||
5.572395 8.488517 5.827872 8.233040 5.987981 7.918809 c
|
||||
7.467052 8.672433 l
|
||||
7.147793 9.299013 6.638368 9.808438 6.011788 10.127696 c
|
||||
5.258164 8.648625 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
-1.000000 -0.000000 -0.000000 -1.000000 10.000000 14.822083 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
6.586899 4.235184 m
|
||||
6.911034 4.559319 6.911034 5.084846 6.586899 5.408981 c
|
||||
6.262764 5.733116 5.737236 5.733116 5.413101 5.408981 c
|
||||
6.586899 4.235184 l
|
||||
h
|
||||
3.000000 1.822083 m
|
||||
2.413101 1.235184 l
|
||||
2.737236 0.911049 3.262764 0.911049 3.586899 1.235184 c
|
||||
3.000000 1.822083 l
|
||||
h
|
||||
0.586899 5.408981 m
|
||||
0.262763 5.733116 -0.262763 5.733116 -0.586899 5.408981 c
|
||||
-0.911034 5.084846 -0.911034 4.559319 -0.586899 4.235184 c
|
||||
0.586899 5.408981 l
|
||||
h
|
||||
5.413101 5.408981 m
|
||||
2.413101 2.408981 l
|
||||
3.586899 1.235184 l
|
||||
6.586899 4.235184 l
|
||||
5.413101 5.408981 l
|
||||
h
|
||||
3.586899 2.408981 m
|
||||
0.586899 5.408981 l
|
||||
-0.586899 4.235184 l
|
||||
2.413101 1.235184 l
|
||||
3.586899 2.408981 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
q
|
||||
-1.000000 -0.000000 -0.000000 -1.000000 14.000000 13.660645 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
0.000000 9.490644 m
|
||||
-0.458396 9.490644 -0.830000 9.119040 -0.830000 8.660645 c
|
||||
-0.830000 8.202249 -0.458396 7.830645 0.000000 7.830645 c
|
||||
0.000000 9.490644 l
|
||||
h
|
||||
6.170000 1.660645 m
|
||||
6.170000 1.202248 6.541604 0.830645 7.000000 0.830645 c
|
||||
7.458396 0.830645 7.830000 1.202248 7.830000 1.660645 c
|
||||
6.170000 1.660645 l
|
||||
h
|
||||
6.727516 7.295621 m
|
||||
7.467052 7.672433 l
|
||||
6.727516 7.295621 l
|
||||
h
|
||||
0.000000 7.830645 m
|
||||
3.000000 7.830645 l
|
||||
3.000000 9.490644 l
|
||||
0.000000 9.490644 l
|
||||
0.000000 7.830645 l
|
||||
h
|
||||
6.170000 4.660645 m
|
||||
6.170000 1.660645 l
|
||||
7.830000 1.660645 l
|
||||
7.830000 4.660645 l
|
||||
6.170000 4.660645 l
|
||||
h
|
||||
3.000000 7.830645 m
|
||||
3.713761 7.830645 4.199165 7.829999 4.574407 7.799341 c
|
||||
4.939959 7.769474 5.127283 7.715313 5.258164 7.648625 c
|
||||
6.011788 9.127696 l
|
||||
5.607891 9.333492 5.177792 9.415573 4.709584 9.453828 c
|
||||
4.251065 9.491290 3.686370 9.490644 3.000000 9.490644 c
|
||||
3.000000 7.830645 l
|
||||
h
|
||||
7.830000 4.660645 m
|
||||
7.830000 5.347014 7.830646 5.911709 7.793183 6.370228 c
|
||||
7.754929 6.838436 7.672848 7.268535 7.467052 7.672433 c
|
||||
5.987981 6.918809 l
|
||||
6.054668 6.787928 6.108829 6.600603 6.138696 6.235051 c
|
||||
6.169354 5.859809 6.170000 5.374406 6.170000 4.660645 c
|
||||
7.830000 4.660645 l
|
||||
h
|
||||
5.258164 7.648625 m
|
||||
5.572395 7.488517 5.827872 7.233039 5.987981 6.918809 c
|
||||
7.467052 7.672433 l
|
||||
7.147793 8.299013 6.638368 8.808438 6.011788 9.127696 c
|
||||
5.258164 7.648625 l
|
||||
h
|
||||
f
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
5530
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000005620 00000 n
|
||||
0000005643 00000 n
|
||||
0000005816 00000 n
|
||||
0000005890 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
5949
|
||||
%%EOF
|
@ -1374,7 +1374,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
let undoButtonFrame = CGRect(origin: CGPoint(x: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - rightInset - buttonTextSize.width - 8.0 - leftMargin * 2.0, y: 0.0), size: CGSize(width: layout.safeInsets.right + rightInset + buttonTextSize.width + 8.0 + leftMargin, height: contentHeight))
|
||||
self.undoButtonNode.frame = undoButtonFrame
|
||||
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: undoButtonFrame.minX, height: contentHeight))
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.undoButtonNode.supernode == nil ? panelFrame.width : undoButtonFrame.minX, height: contentHeight))
|
||||
|
||||
var textContentHeight = textSize.height
|
||||
var textOffset: CGFloat = 0.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user