mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various fixes
This commit is contained in:
parent
f8c872db55
commit
874fac0c63
@ -14301,3 +14301,16 @@ Sorry for the inconvenience.";
|
||||
|
||||
"FrozenAccount.Violation.TextNew" = "Your account was frozen for breaking Telegram's [Terms and Conditions]().";
|
||||
"FrozenAccount.Violation.TextNew_URL" = "https://telegram.org/tos";
|
||||
|
||||
"Stars.Purchase.BuyStarGiftInfo" = "Buy Stars to acquire a unique collectible.";
|
||||
|
||||
"Stars.Purchase.EnoughStars" = "You have enough stars at the moment.";
|
||||
"Stars.Purchase.BuyAnyway" = "Buy Anyway";
|
||||
|
||||
"Gift.Buy.Confirm.Title" = "Confirm Payment";
|
||||
"Gift.Buy.Confirm.Text" = "Do you really want to buy **%1$@** for %2$@?";
|
||||
"Gift.Buy.Confirm.GiftText" = "Do you really want to buy **%1$@** for %2$@ and gift it to **%3$@**?";
|
||||
"Gift.Buy.Confirm.Text.Stars_1" = "**%@** Star";
|
||||
"Gift.Buy.Confirm.Text.Stars_any" = "**%@** Stars";
|
||||
"Gift.Buy.Confirm.BuyFor_1" = "Buy for %@ Star";
|
||||
"Gift.Buy.Confirm.BuyFor_any" = "Buy for %@ Stars";
|
||||
|
@ -322,6 +322,7 @@ public enum ResolvedUrl {
|
||||
case premiumMultiGift(reference: String?)
|
||||
case collectible(gift: StarGift.UniqueGift?)
|
||||
case messageLink(link: TelegramResolvedMessageLink?)
|
||||
case stars
|
||||
}
|
||||
|
||||
public enum ResolveUrlResult {
|
||||
|
@ -141,6 +141,7 @@ public enum StarsPurchasePurpose: Equatable {
|
||||
case upgradeStarGift(requiredStars: Int64)
|
||||
case transferStarGift(requiredStars: Int64)
|
||||
case sendMessage(peerId: EnginePeer.Id, requiredStars: Int64)
|
||||
case buyStarGift(requiredStars: Int64)
|
||||
}
|
||||
|
||||
public struct PremiumConfiguration {
|
||||
|
@ -167,10 +167,12 @@ public func searchCountries(items: [((String, String), String, [Int])], query: S
|
||||
let componentsOne = item.0.0.components(separatedBy: " ")
|
||||
let abbrOne = componentsOne.compactMap { $0.first.flatMap { String($0) } }.reduce(into: String(), { $0.append(contentsOf: $1) }).replacingOccurrences(of: "&", with: "")
|
||||
|
||||
let componentsTwo = item.0.0.components(separatedBy: " ")
|
||||
let componentsTwo = item.0.1.components(separatedBy: " ")
|
||||
let abbrTwo = componentsTwo.compactMap { $0.first.flatMap { String($0) } }.reduce(into: String(), { $0.append(contentsOf: $1) }).replacingOccurrences(of: "&", with: "")
|
||||
|
||||
let string = "\(item.0.0) \((item.0.1)) \(item.1) \(abbrOne) \(abbrTwo)"
|
||||
let phoneCodes = item.2.map { "\($0)" }.joined(separator: " ")
|
||||
|
||||
let string = "\(item.0.0) \((item.0.1)) \(item.1) \(abbrOne) \(abbrTwo) \(phoneCodes)"
|
||||
let tokens = stringTokens(string)
|
||||
if matchStringTokens(tokens, with: queryTokens) {
|
||||
for code in item.2 {
|
||||
|
@ -1441,6 +1441,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
break
|
||||
case .messageLink:
|
||||
break
|
||||
case .stars:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
@ -955,6 +955,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
titleTransition = .immediate
|
||||
}
|
||||
|
||||
let statusSpacing: CGFloat = 3.0
|
||||
let titleSideInset: CGFloat = 6.0
|
||||
var titleFrame: CGRect
|
||||
if size.height > 40.0 {
|
||||
@ -966,7 +967,12 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
var titleSize = self.titleTextNode.updateLayout(size: CGSize(width: clearBounds.width - leftIconWidth - credibilityIconWidth - verifiedIconWidth - statusIconWidth - rightIconWidth - titleSideInset * 2.0, height: size.height), insets: titleInsets, animated: titleTransition.isAnimated)
|
||||
titleSize.width += credibilityIconWidth
|
||||
titleSize.width += verifiedIconWidth
|
||||
titleSize.width += statusIconWidth
|
||||
if statusIconWidth > 0.0 {
|
||||
titleSize.width += statusIconWidth
|
||||
if credibilityIconWidth > 0.0 {
|
||||
titleSize.width += statusSpacing
|
||||
}
|
||||
}
|
||||
|
||||
let activitySize = self.activityNode.updateLayout(CGSize(width: clearBounds.size.width - titleSideInset * 2.0, height: clearBounds.size.height), alignment: .center)
|
||||
let titleInfoSpacing: CGFloat = 0.0
|
||||
@ -1006,6 +1012,9 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
|
||||
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
|
||||
nextIconX -= titleCredibilitySize.width
|
||||
if credibilityIconWidth > 0.0 {
|
||||
nextIconX -= statusSpacing
|
||||
}
|
||||
|
||||
self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize)
|
||||
nextIconX -= titleStatusSize.width
|
||||
|
@ -240,9 +240,20 @@ final class GiftStoreScreenComponent: Component {
|
||||
} else {
|
||||
mainController = controller
|
||||
}
|
||||
|
||||
let allSubjects: [GiftViewScreen.Subject] = (self.effectiveGifts ?? []).compactMap { gift in
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
return .uniqueGift(uniqueGift, state.peerId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
let index = self.effectiveGifts?.firstIndex(where: { $0 == .unique(uniqueGift) }) ?? 0
|
||||
|
||||
let giftController = GiftViewScreen(
|
||||
context: component.context,
|
||||
subject: .uniqueGift(uniqueGift, state.peerId),
|
||||
allSubjects: allSubjects,
|
||||
index: index,
|
||||
buyGift: { slug, peerId in
|
||||
return self.state?.starGiftsContext.buyStarGift(slug: slug, peerId: peerId) ?? .complete()
|
||||
},
|
||||
|
@ -0,0 +1,241 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import ComponentFlow
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import ViewControllerComponent
|
||||
import AccountContext
|
||||
|
||||
final class GiftPagerComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
||||
public final class Item: Equatable {
|
||||
let id: AnyHashable
|
||||
let subject: GiftViewScreen.Subject
|
||||
|
||||
public init(id: AnyHashable, subject: GiftViewScreen.Subject) {
|
||||
self.id = id
|
||||
self.subject = subject
|
||||
}
|
||||
|
||||
public static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
let context: AccountContext
|
||||
let items: [Item]
|
||||
let index: Int
|
||||
let itemSpacing: CGFloat
|
||||
let updated: (CGFloat, Int) -> Void
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
items: [Item],
|
||||
index: Int = 0,
|
||||
itemSpacing: CGFloat = 0.0,
|
||||
updated: @escaping (CGFloat, Int) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.items = items
|
||||
self.index = index
|
||||
self.itemSpacing = itemSpacing
|
||||
self.updated = updated
|
||||
}
|
||||
|
||||
public static func ==(lhs: GiftPagerComponent, rhs: GiftPagerComponent) -> Bool {
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
if lhs.index != rhs.index {
|
||||
return false
|
||||
}
|
||||
if lhs.itemSpacing != rhs.itemSpacing {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView, UIScrollViewDelegate {
|
||||
private let scrollView: UIScrollView
|
||||
private var itemViews: [AnyHashable: ComponentHostView<EnvironmentType>] = [:]
|
||||
|
||||
private var component: GiftPagerComponent?
|
||||
private var environment: Environment<EnvironmentType>?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.scrollView = UIScrollView(frame: frame)
|
||||
self.scrollView.isPagingEnabled = true
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.showsVerticalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceHorizontal = false
|
||||
self.scrollView.bounces = false
|
||||
self.scrollView.layer.cornerRadius = 10.0
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.scrollView.delegate = self
|
||||
|
||||
self.addSubview(self.scrollView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private var isSwiping: Bool = false
|
||||
private var lastScrollTime: TimeInterval = 0
|
||||
private let swipeInactiveThreshold: TimeInterval = 0.5
|
||||
|
||||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
self.isSwiping = true
|
||||
self.lastScrollTime = CACurrentMediaTime()
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
self.isSwiping = false
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
self.isSwiping = false
|
||||
}
|
||||
|
||||
private var ignoreContentOffsetChange = false
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
guard let component = self.component, let environment = self.environment, !self.ignoreContentOffsetChange && !self.isUpdating else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.isSwiping {
|
||||
self.lastScrollTime = CACurrentMediaTime()
|
||||
}
|
||||
|
||||
self.ignoreContentOffsetChange = true
|
||||
let _ = self.update(component: component, availableSize: self.bounds.size, environment: environment, transition: .immediate)
|
||||
component.updated(self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width), component.items.count)
|
||||
self.ignoreContentOffsetChange = false
|
||||
}
|
||||
|
||||
private var isUpdating = true
|
||||
func update(component: GiftPagerComponent, availableSize: CGSize, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
var validIds: [AnyHashable] = []
|
||||
|
||||
self.component = component
|
||||
self.environment = environment
|
||||
|
||||
let firstTime = self.itemViews.isEmpty
|
||||
|
||||
let itemWidth = availableSize.width
|
||||
let totalWidth = itemWidth * CGFloat(component.items.count) + component.itemSpacing * CGFloat(max(0, component.items.count - 1))
|
||||
|
||||
let contentSize = CGSize(width: totalWidth, height: availableSize.height)
|
||||
if self.scrollView.contentSize != contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
let scrollFrame = CGRect(origin: .zero, size: availableSize)
|
||||
if self.scrollView.frame != scrollFrame {
|
||||
self.scrollView.frame = scrollFrame
|
||||
}
|
||||
|
||||
if firstTime {
|
||||
let initialOffset = CGFloat(component.index) * (itemWidth + component.itemSpacing)
|
||||
self.scrollView.contentOffset = CGPoint(x: initialOffset, y: 0.0)
|
||||
|
||||
var position: CGFloat
|
||||
if self.scrollView.contentSize.width > self.scrollView.frame.width {
|
||||
position = self.scrollView.contentOffset.x / (self.scrollView.contentSize.width - self.scrollView.frame.width)
|
||||
} else {
|
||||
position = 0.0
|
||||
}
|
||||
component.updated(position, component.items.count)
|
||||
}
|
||||
let viewportCenter = self.scrollView.contentOffset.x + availableSize.width * 0.5
|
||||
|
||||
let currentTime = CACurrentMediaTime()
|
||||
let isSwipingActive = self.isSwiping || (currentTime - self.lastScrollTime < self.swipeInactiveThreshold)
|
||||
|
||||
var i = 0
|
||||
for item in component.items {
|
||||
let itemOriginX = (itemWidth + component.itemSpacing) * CGFloat(i)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: itemOriginX, y: 0.0), size: CGSize(width: itemWidth, height: availableSize.height))
|
||||
|
||||
let centerDelta = itemFrame.midX - viewportCenter
|
||||
let position = centerDelta / (availableSize.width * 0.75)
|
||||
|
||||
i += 1
|
||||
|
||||
if !isSwipingActive && abs(position) > 0.5 {
|
||||
continue
|
||||
} else if isSwipingActive && abs(position) > 1.5 {
|
||||
continue
|
||||
}
|
||||
|
||||
validIds.append(item.id)
|
||||
|
||||
let itemView: ComponentHostView<EnvironmentType>
|
||||
var itemTransition = transition
|
||||
|
||||
if let current = self.itemViews[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = transition.withAnimation(.none)
|
||||
itemView = ComponentHostView<EnvironmentType>()
|
||||
self.itemViews[item.id] = itemView
|
||||
|
||||
self.scrollView.addSubview(itemView)
|
||||
}
|
||||
|
||||
let environment = environment[EnvironmentType.self]
|
||||
|
||||
let _ = itemView.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(GiftViewSheetComponent(
|
||||
context: component.context,
|
||||
subject: item.subject
|
||||
)),
|
||||
environment: { environment },
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
itemView.frame = itemFrame
|
||||
}
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,278 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import ComponentFlow
|
||||
import Display
|
||||
import TelegramPresentationData
|
||||
import MultilineTextComponent
|
||||
|
||||
final class TableComponent: CombinedComponent {
|
||||
class Item: Equatable {
|
||||
public let id: AnyHashable
|
||||
public let title: String?
|
||||
public let hasBackground: Bool
|
||||
public let component: AnyComponent<Empty>
|
||||
public let insets: UIEdgeInsets?
|
||||
|
||||
public init<IdType: Hashable>(id: IdType, title: String?, hasBackground: Bool = false, component: AnyComponent<Empty>, insets: UIEdgeInsets? = nil) {
|
||||
self.id = AnyHashable(id)
|
||||
self.title = title
|
||||
self.hasBackground = hasBackground
|
||||
self.component = component
|
||||
self.insets = insets
|
||||
}
|
||||
|
||||
public static func == (lhs: Item, rhs: Item) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.hasBackground != rhs.hasBackground {
|
||||
return false
|
||||
}
|
||||
if lhs.component != rhs.component {
|
||||
return false
|
||||
}
|
||||
if lhs.insets != rhs.insets {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let theme: PresentationTheme
|
||||
private let items: [Item]
|
||||
|
||||
public init(theme: PresentationTheme, items: [Item]) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
}
|
||||
|
||||
public static func ==(lhs: TableComponent, rhs: TableComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class State: ComponentState {
|
||||
var cachedBorderImage: (UIImage, PresentationTheme)?
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
return State()
|
||||
}
|
||||
|
||||
public static var body: Body {
|
||||
let leftColumnBackground = Child(Rectangle.self)
|
||||
let lastBackground = Child(Rectangle.self)
|
||||
let verticalBorder = Child(Rectangle.self)
|
||||
let titleChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let valueChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let borderChildren = ChildMap(environment: Empty.self, keyedBy: AnyHashable.self)
|
||||
let outerBorder = Child(Image.self)
|
||||
|
||||
return { context in
|
||||
let verticalPadding: CGFloat = 11.0
|
||||
let horizontalPadding: CGFloat = 12.0
|
||||
let borderWidth: CGFloat = 1.0
|
||||
|
||||
let backgroundColor = context.component.theme.actionSheet.opaqueItemBackgroundColor
|
||||
let borderColor = backgroundColor.mixedWith(context.component.theme.list.itemBlocksSeparatorColor, alpha: 0.6)
|
||||
let secondaryBackgroundColor = context.component.theme.overallDarkAppearance ? context.component.theme.list.itemModalBlocksBackgroundColor : context.component.theme.list.itemInputField.backgroundColor
|
||||
|
||||
var leftColumnWidth: CGFloat = 0.0
|
||||
|
||||
var updatedTitleChildren: [Int: _UpdatedChildComponent] = [:]
|
||||
var updatedValueChildren: [(_UpdatedChildComponent, UIEdgeInsets)] = []
|
||||
var updatedBorderChildren: [_UpdatedChildComponent] = []
|
||||
|
||||
var i = 0
|
||||
for item in context.component.items {
|
||||
guard let title = item.title else {
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
let titleChild = titleChildren[item.id].update(
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: title, font: Font.regular(15.0), textColor: context.component.theme.list.itemPrimaryTextColor))
|
||||
)),
|
||||
availableSize: context.availableSize,
|
||||
transition: context.transition
|
||||
)
|
||||
updatedTitleChildren[i] = titleChild
|
||||
|
||||
if titleChild.size.width > leftColumnWidth {
|
||||
leftColumnWidth = titleChild.size.width
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
leftColumnWidth = max(100.0, leftColumnWidth + horizontalPadding * 2.0)
|
||||
let rightColumnWidth = context.availableSize.width - leftColumnWidth
|
||||
|
||||
i = 0
|
||||
var rowHeights: [Int: CGFloat] = [:]
|
||||
var totalHeight: CGFloat = 0.0
|
||||
var innerTotalHeight: CGFloat = 0.0
|
||||
var hasLastBackground = false
|
||||
|
||||
for item in context.component.items {
|
||||
let insets: UIEdgeInsets
|
||||
if let customInsets = item.insets {
|
||||
insets = customInsets
|
||||
} else {
|
||||
insets = UIEdgeInsets(top: 0.0, left: horizontalPadding, bottom: 0.0, right: horizontalPadding)
|
||||
}
|
||||
|
||||
var titleHeight: CGFloat = 0.0
|
||||
if let titleChild = updatedTitleChildren[i] {
|
||||
titleHeight = titleChild.size.height
|
||||
}
|
||||
|
||||
let availableValueWidth: CGFloat
|
||||
if titleHeight > 0.0 {
|
||||
availableValueWidth = rightColumnWidth
|
||||
} else {
|
||||
availableValueWidth = context.availableSize.width
|
||||
}
|
||||
|
||||
let valueChild = valueChildren[item.id].update(
|
||||
component: item.component,
|
||||
availableSize: CGSize(width: availableValueWidth - insets.left - insets.right, height: context.availableSize.height),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedValueChildren.append((valueChild, insets))
|
||||
|
||||
let rowHeight = max(40.0, max(titleHeight, valueChild.size.height) + verticalPadding * 2.0)
|
||||
rowHeights[i] = rowHeight
|
||||
totalHeight += rowHeight
|
||||
if titleHeight > 0.0 {
|
||||
innerTotalHeight += rowHeight
|
||||
}
|
||||
|
||||
if i < context.component.items.count - 1 {
|
||||
let borderChild = borderChildren[item.id].update(
|
||||
component: AnyComponent(Rectangle(color: borderColor)),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: borderWidth),
|
||||
transition: context.transition
|
||||
)
|
||||
updatedBorderChildren.append(borderChild)
|
||||
}
|
||||
|
||||
if item.hasBackground {
|
||||
hasLastBackground = true
|
||||
}
|
||||
|
||||
i += 1
|
||||
}
|
||||
|
||||
if hasLastBackground {
|
||||
let lastRowHeight = rowHeights[i - 1] ?? 0
|
||||
let lastBackground = lastBackground.update(
|
||||
component: Rectangle(color: secondaryBackgroundColor),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: lastRowHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
lastBackground
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight - lastRowHeight / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
let leftColumnBackground = leftColumnBackground.update(
|
||||
component: Rectangle(color: secondaryBackgroundColor),
|
||||
availableSize: CGSize(width: leftColumnWidth, height: innerTotalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
leftColumnBackground
|
||||
.position(CGPoint(x: leftColumnWidth / 2.0, y: innerTotalHeight / 2.0))
|
||||
)
|
||||
|
||||
let borderImage: UIImage
|
||||
if let (currentImage, theme) = context.state.cachedBorderImage, theme === context.component.theme {
|
||||
borderImage = currentImage
|
||||
} else {
|
||||
let borderRadius: CGFloat = 10.0
|
||||
borderImage = generateImage(CGSize(width: 24.0, height: 24.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: .zero, size: size)
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
let path = CGPath(roundedRect: bounds.insetBy(dx: borderWidth / 2.0, dy: borderWidth / 2.0), cornerWidth: borderRadius, cornerHeight: borderRadius, transform: nil)
|
||||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
context.setBlendMode(.normal)
|
||||
context.setStrokeColor(borderColor.cgColor)
|
||||
context.setLineWidth(borderWidth)
|
||||
context.addPath(path)
|
||||
context.strokePath()
|
||||
})!.stretchableImage(withLeftCapWidth: 10, topCapHeight: 10)
|
||||
context.state.cachedBorderImage = (borderImage, context.component.theme)
|
||||
}
|
||||
|
||||
let outerBorder = outerBorder.update(
|
||||
component: Image(image: borderImage),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: totalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(outerBorder
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: totalHeight / 2.0))
|
||||
)
|
||||
|
||||
let verticalBorder = verticalBorder.update(
|
||||
component: Rectangle(color: borderColor),
|
||||
availableSize: CGSize(width: borderWidth, height: innerTotalHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(
|
||||
verticalBorder
|
||||
.position(CGPoint(x: leftColumnWidth - borderWidth / 2.0, y: innerTotalHeight / 2.0))
|
||||
)
|
||||
|
||||
i = 0
|
||||
var originY: CGFloat = 0.0
|
||||
for (valueChild, valueInsets) in updatedValueChildren {
|
||||
let rowHeight = rowHeights[i] ?? 0.0
|
||||
|
||||
let valueFrame: CGRect
|
||||
if let titleChild = updatedTitleChildren[i] {
|
||||
let titleFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: titleChild.size)
|
||||
context.add(titleChild
|
||||
.position(titleFrame.center)
|
||||
)
|
||||
valueFrame = CGRect(origin: CGPoint(x: leftColumnWidth + valueInsets.left, y: originY + verticalPadding), size: valueChild.size)
|
||||
} else {
|
||||
if hasLastBackground {
|
||||
valueFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((context.availableSize.width - valueChild.size.width) / 2.0), y: originY + verticalPadding), size: valueChild.size)
|
||||
} else {
|
||||
valueFrame = CGRect(origin: CGPoint(x: horizontalPadding, y: originY + verticalPadding), size: valueChild.size)
|
||||
}
|
||||
}
|
||||
|
||||
context.add(valueChild
|
||||
.position(valueFrame.center)
|
||||
)
|
||||
|
||||
if i < updatedBorderChildren.count {
|
||||
let borderChild = updatedBorderChildren[i]
|
||||
context.add(borderChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + rowHeight - borderWidth / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
originY += rowHeight
|
||||
i += 1
|
||||
}
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: totalHeight)
|
||||
}
|
||||
}
|
||||
}
|
@ -825,6 +825,14 @@ public final class MediaScrubberComponent: Component {
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
} else {
|
||||
for (_ , trackView) in self.trackViews {
|
||||
trackView.updateTrimEdges(
|
||||
left: leftHandleFrame.minX,
|
||||
right: rightHandleFrame.maxX,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let isDraggingTracks = self.trackViews.values.contains(where: { $0.isDragging })
|
||||
@ -863,7 +871,6 @@ public final class MediaScrubberComponent: Component {
|
||||
|
||||
transition.setFrame(view: self.cursorImageView, frame: CGRect(origin: .zero, size: self.cursorView.frame.size))
|
||||
|
||||
|
||||
if let (coverPosition, coverImage) = component.cover {
|
||||
let imageSize = CGSize(width: 36.0, height: 36.0)
|
||||
var animateFrame = false
|
||||
@ -964,6 +971,7 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
fileprivate let audioIconView: UIImageView
|
||||
fileprivate let audioTitle = ComponentView<Empty>()
|
||||
|
||||
fileprivate let segmentsContainerView = UIView()
|
||||
fileprivate var segmentTitles: [Int32: ComponentView<Empty>] = [:]
|
||||
fileprivate var segmentLayers: [Int32: SimpleLayer] = [:]
|
||||
|
||||
@ -1037,7 +1045,10 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
self.clippingView.addSubview(self.scrollView)
|
||||
self.scrollView.addSubview(self.containerView)
|
||||
self.backgroundView.addSubview(self.vibrancyView)
|
||||
|
||||
|
||||
self.segmentsContainerView.clipsToBounds = true
|
||||
self.segmentsContainerView.isUserInteractionEnabled = false
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
|
||||
self.addGestureRecognizer(tapGesture)
|
||||
|
||||
@ -1133,6 +1144,25 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
}
|
||||
}
|
||||
|
||||
private var leftTrimEdge: CGFloat?
|
||||
private var rightTrimEdge: CGFloat?
|
||||
func updateTrimEdges(
|
||||
left: CGFloat,
|
||||
right: CGFloat,
|
||||
transition: ComponentTransition
|
||||
) {
|
||||
self.leftTrimEdge = left
|
||||
self.rightTrimEdge = right
|
||||
|
||||
if let params = self.params {
|
||||
self.updateSegmentContainer(
|
||||
scrubberSize: CGSize(width: params.availableSize.width, height: trackHeight),
|
||||
availableSize: params.availableSize,
|
||||
transition: transition
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateThumbnailContainers(
|
||||
scrubberSize: CGSize,
|
||||
availableSize: CGSize,
|
||||
@ -1146,6 +1176,17 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
transition.setBounds(view: self.videoOpaqueFramesContainer, bounds: CGRect(origin: CGPoint(x: containerLeftEdge, y: 0.0), size: CGSize(width: containerRightEdge - containerLeftEdge, height: scrubberSize.height)))
|
||||
}
|
||||
|
||||
private func updateSegmentContainer(
|
||||
scrubberSize: CGSize,
|
||||
availableSize: CGSize,
|
||||
transition: ComponentTransition
|
||||
) {
|
||||
let containerLeftEdge: CGFloat = self.leftTrimEdge ?? 0.0
|
||||
let containerRightEdge: CGFloat = self.rightTrimEdge ?? availableSize.width
|
||||
|
||||
transition.setFrame(view: self.segmentsContainerView, frame: CGRect(origin: CGPoint(x: containerLeftEdge, y: 0.0), size: CGSize(width: containerRightEdge - containerLeftEdge - 2.0, height: scrubberSize.height)))
|
||||
}
|
||||
|
||||
func update(
|
||||
context: AccountContext,
|
||||
style: MediaScrubberComponent.Style,
|
||||
@ -1281,6 +1322,7 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
if self.videoTransparentFramesContainer.superview == nil {
|
||||
self.containerView.addSubview(self.videoTransparentFramesContainer)
|
||||
self.containerView.addSubview(self.videoOpaqueFramesContainer)
|
||||
self.containerView.addSubview(self.segmentsContainerView)
|
||||
}
|
||||
var previousFramesUpdateTimestamp: Double?
|
||||
if let previousParams, case let .video(_, previousFramesUpdateTimestampValue) = previousParams.track.content {
|
||||
@ -1333,6 +1375,12 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
transition: transition
|
||||
)
|
||||
|
||||
self.updateSegmentContainer(
|
||||
scrubberSize: scrubberSize,
|
||||
availableSize: availableSize,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
var frameAspectRatio = 0.66
|
||||
if let image = frames.first, image.size.height > 0.0 {
|
||||
frameAspectRatio = max(0.66, image.size.width / image.size.height)
|
||||
@ -1488,9 +1536,8 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
self.backgroundView.update(size: containerFrame.size, transition: transition.containedViewLayoutTransition)
|
||||
transition.setFrame(view: self.vibrancyView, frame: CGRect(origin: .zero, size: containerFrame.size))
|
||||
transition.setFrame(view: self.vibrancyContainer, frame: CGRect(origin: .zero, size: containerFrame.size))
|
||||
|
||||
|
||||
var segmentCount = 0
|
||||
var segmentOrigin: CGFloat = 0.0
|
||||
var segmentWidth: CGFloat = 0.0
|
||||
if let segmentDuration {
|
||||
if duration > segmentDuration {
|
||||
@ -1499,17 +1546,15 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
segmentWidth = floorToScreenPixels(containerFrame.width * fraction)
|
||||
}
|
||||
if let trimRange = track.trimRange {
|
||||
if trimRange.lowerBound > 0.0 {
|
||||
let fraction = trimRange.lowerBound / duration
|
||||
segmentOrigin = floorToScreenPixels(containerFrame.width * fraction)
|
||||
}
|
||||
let actualSegmentCount = Int(ceil((trimRange.upperBound - trimRange.lowerBound) / segmentDuration)) - 1
|
||||
segmentCount = min(actualSegmentCount, segmentCount)
|
||||
}
|
||||
}
|
||||
|
||||
let displaySegmentLabels = segmentWidth >= 30.0
|
||||
|
||||
var validIds = Set<Int32>()
|
||||
var segmentFrame = CGRect(x: segmentOrigin + segmentWidth, y: 0.0, width: 1.0, height: containerFrame.size.height)
|
||||
var segmentFrame = CGRect(x: segmentWidth, y: 0.0, width: 1.0, height: containerFrame.size.height)
|
||||
for i in 0 ..< min(segmentCount, 2) {
|
||||
let id = Int32(i)
|
||||
validIds.insert(id)
|
||||
@ -1530,7 +1575,7 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
self.segmentLayers[id] = segmentLayer
|
||||
self.segmentTitles[id] = segmentTitle
|
||||
|
||||
self.containerView.layer.addSublayer(segmentLayer)
|
||||
self.segmentsContainerView.layer.addSublayer(segmentLayer)
|
||||
}
|
||||
|
||||
transition.setFrame(layer: segmentLayer, frame: segmentFrame)
|
||||
@ -1546,8 +1591,9 @@ private class TrackView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelega
|
||||
containerSize: containerFrame.size
|
||||
)
|
||||
if let view = segmentTitle.view {
|
||||
view.alpha = displaySegmentLabels ? 1.0 : 0.0
|
||||
if view.superview == nil {
|
||||
self.containerView.addSubview(view)
|
||||
self.segmentsContainerView.addSubview(view)
|
||||
}
|
||||
segmentTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: segmentFrame.maxX + 2.0, y: 2.0), size: segmentTitleSize))
|
||||
}
|
||||
|
@ -571,10 +571,15 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let allSubjects: [GiftViewScreen.Subject] = (self.starsProducts ?? []).map { .profileGift(self.peerId, $0) }
|
||||
let index = self.starsProducts?.firstIndex(where: { $0 == product }) ?? 0
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = GiftViewScreen(
|
||||
context: self.context,
|
||||
subject: .profileGift(self.peerId, product),
|
||||
allSubjects: allSubjects,
|
||||
index: index,
|
||||
updateSavedToProfile: { [weak self] reference, added in
|
||||
guard let self else {
|
||||
return
|
||||
|
@ -249,6 +249,8 @@ private final class StarsPurchaseScreenContentComponent: CombinedComponent {
|
||||
} else {
|
||||
textString = strings.Stars_Purchase_SendGroupMessageInfo(component.peers.first?.value.compactDisplayTitle ?? "").string
|
||||
}
|
||||
case .buyStarGift:
|
||||
textString = strings.Stars_Purchase_BuyStarGiftInfo
|
||||
}
|
||||
|
||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: accentColor), linkAttribute: { contents in
|
||||
@ -830,7 +832,7 @@ private final class StarsPurchaseScreenComponent: CombinedComponent {
|
||||
titleText = strings.Stars_Purchase_GetStars
|
||||
case .gift:
|
||||
titleText = strings.Stars_Purchase_GiftStars
|
||||
case let .topUp(requiredStars, _), let .transfer(_, requiredStars), let .reactions(_, requiredStars), let .subscription(_, requiredStars, _), let .unlockMedia(requiredStars), let .starGift(_, requiredStars), let .upgradeStarGift(requiredStars), let .transferStarGift(requiredStars), let .sendMessage(_, requiredStars):
|
||||
case let .topUp(requiredStars, _), let .transfer(_, requiredStars), let .reactions(_, requiredStars), let .subscription(_, requiredStars, _), let .unlockMedia(requiredStars), let .starGift(_, requiredStars), let .upgradeStarGift(requiredStars), let .transferStarGift(requiredStars), let .sendMessage(_, requiredStars), let .buyStarGift(requiredStars):
|
||||
titleText = strings.Stars_Purchase_StarsNeeded(Int32(requiredStars))
|
||||
}
|
||||
|
||||
@ -1280,6 +1282,8 @@ private extension StarsPurchasePurpose {
|
||||
return requiredStars
|
||||
case let .sendMessage(_, requiredStars):
|
||||
return requiredStars
|
||||
case let .buyStarGift(requiredStars):
|
||||
return requiredStars
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ private final class SheetContent: CombinedComponent {
|
||||
let closeButton = Child(Button.self)
|
||||
let title = Child(Text.self)
|
||||
let amountSection = Child(ListSectionComponent.self)
|
||||
let amountAdditionalLabel = Child(MultilineTextComponent.self)
|
||||
let button = Child(ButtonComponent.self)
|
||||
let balanceTitle = Child(MultilineTextComponent.self)
|
||||
let balanceValue = Child(MultilineTextComponent.self)
|
||||
@ -100,7 +101,8 @@ private final class SheetContent: CombinedComponent {
|
||||
let titleString: String
|
||||
let amountTitle: String
|
||||
let amountPlaceholder: String
|
||||
let amountLabel: String?
|
||||
var amountLabel: String?
|
||||
var amountRightLabel: String?
|
||||
|
||||
let minAmount: StarsAmount?
|
||||
let maxAmount: StarsAmount?
|
||||
@ -116,7 +118,6 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = withdrawConfiguration.minWithdrawAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
maxAmount = status.balances.availableBalance
|
||||
amountLabel = nil
|
||||
case .accountWithdraw:
|
||||
titleString = environment.strings.Stars_Withdraw_Title
|
||||
amountTitle = environment.strings.Stars_Withdraw_AmountTitle
|
||||
@ -124,7 +125,6 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = withdrawConfiguration.minWithdrawAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
maxAmount = state.balance
|
||||
amountLabel = nil
|
||||
case .paidMedia:
|
||||
titleString = environment.strings.Stars_PaidContent_Title
|
||||
amountTitle = environment.strings.Stars_PaidContent_AmountTitle
|
||||
@ -136,8 +136,6 @@ private final class SheetContent: CombinedComponent {
|
||||
if let usdWithdrawRate = withdrawConfiguration.usdWithdrawRate, let amount = state.amount, amount > StarsAmount.zero {
|
||||
let usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
amountLabel = "≈\(formatTonUsdValue(amount.value, divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||
} else {
|
||||
amountLabel = nil
|
||||
}
|
||||
case .reaction:
|
||||
titleString = environment.strings.Stars_SendStars_Title
|
||||
@ -146,7 +144,6 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = StarsAmount(value: 1, nanos: 0)
|
||||
maxAmount = withdrawConfiguration.maxPaidMediaAmount.flatMap { StarsAmount(value: $0, nanos: 0) }
|
||||
amountLabel = nil
|
||||
case let .starGiftResell(update):
|
||||
titleString = update ? environment.strings.Stars_SellGift_EditTitle : environment.strings.Stars_SellGift_Title
|
||||
amountTitle = environment.strings.Stars_SellGift_AmountTitle
|
||||
@ -154,7 +151,6 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMinAmount, nanos: 0)
|
||||
maxAmount = StarsAmount(value: resaleConfiguration.starGiftResaleMaxAmount, nanos: 0)
|
||||
amountLabel = nil
|
||||
case let .paidMessages(_, minAmountValue, _, kind):
|
||||
//TODO:localize
|
||||
switch kind {
|
||||
@ -168,7 +164,6 @@ private final class SheetContent: CombinedComponent {
|
||||
|
||||
minAmount = StarsAmount(value: minAmountValue, nanos: 0)
|
||||
maxAmount = StarsAmount(value: resaleConfiguration.paidMessageMaxAmount, nanos: 0)
|
||||
amountLabel = nil
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
@ -287,6 +282,11 @@ private final class SheetContent: CombinedComponent {
|
||||
let starsValue = Int32(floor(Float(value) * Float(resaleConfiguration.paidMessageCommissionPermille) / 1000.0))
|
||||
let starsString = environment.strings.Stars_SellGift_AmountInfo_Stars(starsValue)
|
||||
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo(starsString).string, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||
|
||||
if let usdWithdrawRate = withdrawConfiguration.usdWithdrawRate {
|
||||
let usdRate = Double(usdWithdrawRate) / 1000.0 / 100.0
|
||||
amountRightLabel = "≈\(formatTonUsdValue(Int64(starsValue), divide: false, rate: usdRate, dateTimeFormat: environment.dateTimeFormat))"
|
||||
}
|
||||
} else {
|
||||
amountInfoString = NSAttributedString(attributedString: parseMarkdownIntoAttributedString(environment.strings.Stars_SellGift_AmountInfo("\(resaleConfiguration.paidMessageCommissionPermille / 10)%").string, attributes: amountMarkdownAttributes, textAlignment: .natural))
|
||||
}
|
||||
@ -355,8 +355,17 @@ private final class SheetContent: CombinedComponent {
|
||||
.cornerRadius(10.0)
|
||||
)
|
||||
contentSize.height += amountSection.size.height
|
||||
if let amountRightLabel {
|
||||
let amountAdditionalLabel = amountAdditionalLabel.update(
|
||||
component: MultilineTextComponent(text: .plain(NSAttributedString(string: amountRightLabel, font: amountFont, textColor: amountTextColor))),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(amountAdditionalLabel
|
||||
.position(CGPoint(x: context.availableSize.width - amountAdditionalLabel.size.width / 2.0 - sideInset - 16.0, y: contentSize.height - amountAdditionalLabel.size.height / 2.0)))
|
||||
}
|
||||
contentSize.height += 32.0
|
||||
|
||||
|
||||
let buttonString: String
|
||||
if case .paidMedia = component.mode {
|
||||
buttonString = environment.strings.Stars_PaidContent_Create
|
||||
|
@ -802,7 +802,6 @@ func openResolvedUrlImpl(
|
||||
}
|
||||
if let currentState = starsContext.currentState, currentState.balance >= StarsAmount(value: amount, nanos: 0) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
//TODO:localize
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .universal(
|
||||
@ -810,8 +809,8 @@ func openResolvedUrlImpl(
|
||||
scale: 0.066,
|
||||
colors: [:],
|
||||
title: nil,
|
||||
text: "You have enough stars at the moment.",
|
||||
customUndoText: "Buy Anyway",
|
||||
text: presentationData.strings.Stars_Purchase_EnoughStars,
|
||||
customUndoText: presentationData.strings.Stars_Purchase_BuyAnyway,
|
||||
timeout: nil
|
||||
),
|
||||
elevatedLayout: true,
|
||||
@ -826,6 +825,12 @@ func openResolvedUrlImpl(
|
||||
proceed()
|
||||
}
|
||||
}
|
||||
case .stars:
|
||||
dismissInput()
|
||||
let controller = context.sharedContext.makeStarsIntroScreen(context: context)
|
||||
if let navigationController = navigationController {
|
||||
navigationController.pushViewController(controller, animated: true)
|
||||
}
|
||||
case let .joinVoiceChat(peerId, invite):
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
|
@ -1016,7 +1016,9 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if parsedUrl.host == "importStickers" {
|
||||
if parsedUrl.host == "stars" {
|
||||
handleResolvedUrl(.stars)
|
||||
} else if parsedUrl.host == "importStickers" {
|
||||
handleResolvedUrl(.importStickers)
|
||||
} else if parsedUrl.host == "settings" {
|
||||
if let path = parsedUrl.pathComponents.last {
|
||||
|
Loading…
x
Reference in New Issue
Block a user