mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
609c22582a
15 changed files with 406 additions and 180 deletions
|
|
@ -15700,6 +15700,11 @@ Error: %8$@";
|
|||
"Gift.Craft.Unavailable.Title" = "Try Later";
|
||||
"Gift.Craft.Unavailable.Text" = "You will be able to craft this gift on %@.";
|
||||
|
||||
"Gift.Craft.Select.Title" = "Select Gifts";
|
||||
"Gift.Craft.Select.YourGifts" = "Your Gifts";
|
||||
"Gift.Craft.Select.SaleGifts" = "Suitable Gifts On Sale";
|
||||
"Gift.Craft.Select.NoGiftsFromCollection" = "You don't have other gifts\nfrom this collection";
|
||||
|
||||
"Gift.Attribute.Rare" = "rare";
|
||||
"Gift.Attribute.Legendary" = "legendary";
|
||||
"Gift.Attribute.Epic" = "epic";
|
||||
|
|
|
|||
|
|
@ -1462,7 +1462,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, crafted: Bool, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController
|
||||
func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController
|
||||
func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
||||
func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift, profileGiftsContext: ProfileGiftsContext?) -> ViewController
|
||||
func makeGiftDemoScreen(context: AccountContext) -> ViewController
|
||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||
func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void, requestSelectMessages: ((String, Data, String?) -> Void)?)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
public let screenSize: CGSize
|
||||
public let regularMetricsSize: CGSize?
|
||||
public let dismiss: (Bool) -> Void
|
||||
public let boundsUpdated: ActionSlot<CGRect>
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
|
|
@ -30,7 +31,8 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
isCentered: Bool,
|
||||
screenSize: CGSize,
|
||||
regularMetricsSize: CGSize?,
|
||||
dismiss: @escaping (Bool) -> Void
|
||||
dismiss: @escaping (Bool) -> Void,
|
||||
boundsUpdated: ActionSlot<CGRect> = ActionSlot<CGRect>()
|
||||
) {
|
||||
self.theme = theme
|
||||
self.statusBarHeight = statusBarHeight
|
||||
|
|
@ -42,6 +44,7 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
self.screenSize = screenSize
|
||||
self.regularMetricsSize = regularMetricsSize
|
||||
self.dismiss = dismiss
|
||||
self.boundsUpdated = boundsUpdated
|
||||
}
|
||||
|
||||
public static func ==(lhs: ResizableSheetComponentEnvironment, rhs: ResizableSheetComponentEnvironment) -> Bool {
|
||||
|
|
@ -179,7 +182,7 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
}
|
||||
}
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView, UIGestureRecognizerDelegate {
|
||||
public final class Tag {
|
||||
public init() {
|
||||
}
|
||||
|
|
@ -213,6 +216,10 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
private let backgroundHandleView: UIImageView
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
private var isDismissingInteractively: Bool = false
|
||||
private var dismissTranslation: CGFloat = 0.0
|
||||
private var dismissStartTranslation: CGFloat?
|
||||
private var dismissPanGesture: UIPanGestureRecognizer?
|
||||
|
||||
private var component: ResizableSheetComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
|
@ -284,6 +291,12 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
self.containerView.addSubview(self.bottomContainer)
|
||||
|
||||
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
|
||||
let dismissPanGesture = UIPanGestureRecognizer(target: self, action: #selector(self.dismissPanGesture(_:)))
|
||||
dismissPanGesture.maximumNumberOfTouches = 1
|
||||
dismissPanGesture.delegate = self
|
||||
self.addGestureRecognizer(dismissPanGesture)
|
||||
self.dismissPanGesture = dismissPanGesture
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -314,6 +327,26 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
return result
|
||||
}
|
||||
|
||||
override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer === self.dismissPanGesture {
|
||||
let pan = gestureRecognizer as! UIPanGestureRecognizer
|
||||
let velocity = pan.velocity(in: self)
|
||||
if abs(velocity.y) <= abs(velocity.x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer === self.dismissPanGesture {
|
||||
if otherGestureRecognizer === self.scrollView.panGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.dismissAnimated()
|
||||
|
|
@ -328,6 +361,79 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
environment.dismiss(true)
|
||||
}
|
||||
|
||||
private func updateDismissTranslation(_ translation: CGFloat) {
|
||||
self.dismissTranslation = translation
|
||||
self.updateScrolling(transition: .immediate)
|
||||
|
||||
let maxAlphaDistance = max(1.0, self.bounds.height * 0.9)
|
||||
let alpha = 1.0 - min(1.0, translation / maxAlphaDistance)
|
||||
self.dimView.alpha = alpha
|
||||
}
|
||||
|
||||
private func resetDismissTranslation(animated: Bool) {
|
||||
self.dismissTranslation = 0.0
|
||||
if animated {
|
||||
let transition: ComponentTransition = .easeInOut(duration: 0.2)
|
||||
transition.setAlpha(view: self.dimView, alpha: 1.0)
|
||||
self.updateScrolling(transition: transition)
|
||||
} else {
|
||||
self.dimView.alpha = 1.0
|
||||
self.updateScrolling(transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dismissPanGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let translation = recognizer.translation(in: self)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.dismissStartTranslation = nil
|
||||
case .changed:
|
||||
let shouldStartDismiss = self.scrollView.contentOffset.y <= 0.0 && translation.y > 0.0
|
||||
if shouldStartDismiss {
|
||||
if !self.isDismissingInteractively {
|
||||
self.isDismissingInteractively = true
|
||||
self.dismissStartTranslation = translation.y
|
||||
self.scrollView.isScrollEnabled = false
|
||||
}
|
||||
|
||||
let start = self.dismissStartTranslation ?? translation.y
|
||||
let dismissOffset = max(0.0, translation.y - start)
|
||||
self.scrollView.contentOffset = .zero
|
||||
self.updateDismissTranslation(dismissOffset)
|
||||
} else if self.isDismissingInteractively {
|
||||
let start = self.dismissStartTranslation ?? translation.y
|
||||
let dismissOffset = max(0.0, translation.y - start)
|
||||
self.updateDismissTranslation(dismissOffset)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if self.isDismissingInteractively {
|
||||
let velocityY = recognizer.velocity(in: self).y
|
||||
let currentOffset = self.dismissTranslation
|
||||
let threshold = min(180.0, self.bounds.height * 0.25)
|
||||
let shouldDismiss = currentOffset > threshold || velocityY > 1000.0
|
||||
|
||||
self.isDismissingInteractively = false
|
||||
self.scrollView.isScrollEnabled = !component.isFullscreen
|
||||
|
||||
if shouldDismiss {
|
||||
let animateOffset = self.bounds.height - self.backgroundLayer.frame.minY
|
||||
let initialVelocity = animateOffset > 0.0 ? max(0.0, velocityY) / animateOffset : 0.0
|
||||
self.animateOut(initialVelocity: initialVelocity, completion: { [weak self] in
|
||||
self?.environment?.dismiss(false)
|
||||
})
|
||||
} else {
|
||||
self.resetDismissTranslation(animated: true)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func updateScrolling(transition: ComponentTransition) {
|
||||
guard let itemLayout = self.itemLayout, let component = self.component else {
|
||||
return
|
||||
|
|
@ -356,6 +462,7 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
var containerTransform = CATransform3DIdentity
|
||||
containerTransform = CATransform3DTranslate(containerTransform, 0.0, scaledTranslation, 0.0)
|
||||
containerTransform = CATransform3DScale(containerTransform, scale, scale, scale)
|
||||
containerTransform = CATransform3DTranslate(containerTransform, 0.0, self.dismissTranslation, 0.0)
|
||||
transition.setTransform(view: self.containerView, transform: containerTransform)
|
||||
transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: scaledCornerRadius)
|
||||
|
||||
|
|
@ -363,8 +470,10 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: .zero, size: self.scrollView.bounds.size))
|
||||
self.scrollView.isScrollEnabled = false
|
||||
} else {
|
||||
self.scrollView.isScrollEnabled = true
|
||||
self.scrollView.isScrollEnabled = !self.isDismissingInteractively
|
||||
}
|
||||
|
||||
self.environment?.boundsUpdated.invoke(self.scrollView.bounds)
|
||||
}
|
||||
|
||||
private var didPlayAppearanceAnimation = false
|
||||
|
|
@ -379,16 +488,28 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
self.bottomContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
func animateOut(initialVelocity: CGFloat? = nil, completion: @escaping () -> Void) {
|
||||
let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY
|
||||
|
||||
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.bottomContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.dimView.layer.animateAlpha(from: self.dimView.alpha, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
if let initialVelocity = initialVelocity {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.35, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||
|
||||
transition.updatePosition(layer: self.scrollContentClippingView.layer, position: CGPoint(x: self.scrollContentClippingView.layer.position.x, y: self.scrollContentClippingView.layer.position.y + animateOffset), completion: { _ in
|
||||
completion()
|
||||
})
|
||||
transition.updatePosition(layer: self.backgroundLayer, position: CGPoint(x: self.backgroundLayer.position.x, y: self.backgroundLayer.position.y + animateOffset))
|
||||
transition.updatePosition(layer: self.navigationBarContainer.layer, position: CGPoint(x: self.navigationBarContainer.layer.position.x, y: self.navigationBarContainer.layer.position.y + animateOffset))
|
||||
transition.updatePosition(layer: self.bottomContainer.layer, position: CGPoint(x: self.bottomContainer.layer.position.x, y: self.bottomContainer.layer.position.y + animateOffset))
|
||||
} else {
|
||||
let duration: Double = 0.25
|
||||
self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.bottomContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: ResizableSheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
|
|
|
|||
|
|
@ -811,7 +811,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let title: String
|
||||
public let number: Int32
|
||||
public let slug: String
|
||||
public let owner: Owner
|
||||
public let owner: Owner?
|
||||
public let attributes: [Attribute]
|
||||
public let availability: Availability
|
||||
public let giftAddress: String?
|
||||
|
|
@ -828,7 +828,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let minOfferStars: Int64?
|
||||
public let craftChancePermille: Int32?
|
||||
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?, craftChancePermille: Int32?) {
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner?, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?, craftChancePermille: Int32?) {
|
||||
self.id = id
|
||||
self.giftId = giftId
|
||||
self.title = title
|
||||
|
|
@ -866,7 +866,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
} else if let ownerName = try container.decodeIfPresent(String.self, forKey: .ownerName) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
self.owner = .name("Unknown")
|
||||
self.owner = nil
|
||||
}
|
||||
self.attributes = try container.decode([UniqueGift.Attribute].self, forKey: .attributes)
|
||||
self.availability = try container.decode(UniqueGift.Availability.self, forKey: .availability)
|
||||
|
|
@ -904,7 +904,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
} else if let ownerName = decoder.decodeOptionalStringForKey(CodingKeys.ownerName.rawValue) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
self.owner = .name("Unknown")
|
||||
self.owner = nil
|
||||
}
|
||||
self.attributes = (try? decoder.decodeObjectArrayWithCustomDecoderForKey(CodingKeys.attributes.rawValue, decoder: { UniqueGift.Attribute(decoder: $0) })) ?? []
|
||||
self.availability = decoder.decodeObjectForKey(CodingKeys.availability.rawValue, decoder: { UniqueGift.Availability(decoder: $0) }) as! UniqueGift.Availability
|
||||
|
|
@ -943,6 +943,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
try container.encode(name, forKey: .ownerName)
|
||||
case let .address(address):
|
||||
try container.encode(address, forKey: .ownerAddress)
|
||||
default:
|
||||
break
|
||||
}
|
||||
try container.encode(self.attributes, forKey: .attributes)
|
||||
try container.encode(self.availability, forKey: .availability)
|
||||
|
|
@ -974,6 +976,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
encoder.encodeString(name, forKey: CodingKeys.ownerName.rawValue)
|
||||
case let .address(address):
|
||||
encoder.encodeString(address, forKey: CodingKeys.ownerAddress.rawValue)
|
||||
default:
|
||||
break
|
||||
}
|
||||
encoder.encodeObjectArray(self.attributes, forKey: CodingKeys.attributes.rawValue)
|
||||
encoder.encodeObject(self.availability, forKey: CodingKeys.availability.rawValue)
|
||||
|
|
@ -1296,7 +1300,7 @@ extension StarGift {
|
|||
))
|
||||
case let .starGiftUnique(starGiftUniqueData):
|
||||
let (apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, apiResellAmount, releasedBy, valueAmount, valueCurrency, valueUsdAmount, themePeer, peerColor, hostPeerId, minOfferStars, craftChancePermille) = (starGiftUniqueData.flags, starGiftUniqueData.id, starGiftUniqueData.giftId, starGiftUniqueData.title, starGiftUniqueData.slug, starGiftUniqueData.num, starGiftUniqueData.ownerId, starGiftUniqueData.ownerName, starGiftUniqueData.ownerAddress, starGiftUniqueData.attributes, starGiftUniqueData.availabilityIssued, starGiftUniqueData.availabilityTotal, starGiftUniqueData.giftAddress, starGiftUniqueData.resellAmount, starGiftUniqueData.releasedBy, starGiftUniqueData.valueAmount, starGiftUniqueData.valueCurrency, starGiftUniqueData.valueUsdAmount, starGiftUniqueData.themePeer, starGiftUniqueData.peerColor, starGiftUniqueData.hostId, starGiftUniqueData.offerMinStars, starGiftUniqueData.craftChancePermille)
|
||||
let owner: StarGift.UniqueGift.Owner
|
||||
let owner: StarGift.UniqueGift.Owner?
|
||||
if let ownerAddress {
|
||||
owner = .address(ownerAddress)
|
||||
} else if let ownerId = ownerPeerId?.peerId {
|
||||
|
|
@ -1304,7 +1308,7 @@ extension StarGift {
|
|||
} else if let ownerName {
|
||||
owner = .name(ownerName)
|
||||
} else {
|
||||
return nil
|
||||
owner = nil
|
||||
}
|
||||
let resellAmounts = apiResellAmount?.compactMap { CurrencyAmount(apiAmount: $0) }
|
||||
var flags = StarGift.UniqueGift.Flags()
|
||||
|
|
@ -1335,8 +1339,7 @@ extension StarGift {
|
|||
number: num,
|
||||
slug: slug,
|
||||
owner: owner,
|
||||
attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0)
|
||||
},
|
||||
attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) },
|
||||
availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal),
|
||||
giftAddress: giftAddress,
|
||||
resellAmounts: resellAmounts,
|
||||
|
|
@ -1351,7 +1354,7 @@ extension StarGift {
|
|||
hostPeerId: hostPeerId?.peerId,
|
||||
minOfferStars: minOfferStars.flatMap { Int64($0) },
|
||||
craftChancePermille: craftChancePermille
|
||||
))
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2410,12 +2413,16 @@ private final class ProfileGiftsContextImpl {
|
|||
}
|
||||
|
||||
func removeStarGifts(references: [StarGiftReference]) {
|
||||
self.gifts.removeAll(where: {
|
||||
if let reference = $0.reference {
|
||||
return references.contains(reference)
|
||||
} else {
|
||||
return false
|
||||
self.gifts.removeAll(where: { gift in
|
||||
if let reference = gift.reference, references.contains(reference) {
|
||||
return true
|
||||
}
|
||||
for reference in references {
|
||||
if case let .slug(slug) = reference, case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
self.pushState()
|
||||
|
||||
|
|
@ -2430,13 +2437,17 @@ private final class ProfileGiftsContextImpl {
|
|||
} else {
|
||||
updatedGifts = []
|
||||
}
|
||||
updatedGifts = updatedGifts.filter { gift in
|
||||
if let reference = gift.reference {
|
||||
return !references.contains(reference)
|
||||
} else {
|
||||
updatedGifts.removeAll(where: { gift in
|
||||
if let reference = gift.reference, references.contains(reference) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for reference in references {
|
||||
if case let .slug(slug) = reference, case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
updatedCount -= Int32(references.count)
|
||||
if let entry = CodableEntry(CachedProfileGifts(gifts: updatedGifts, count: updatedCount, notificationsEnabled: nil)) {
|
||||
transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry)
|
||||
|
|
|
|||
|
|
@ -1780,7 +1780,27 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
rawString = strings.Conversation_EmojiStake_WonYou(value).string
|
||||
}
|
||||
} else {
|
||||
let compactPeerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||
var compactPeerName = ""
|
||||
if let authorSignature = message.forwardInfo?.authorSignature {
|
||||
compactPeerName = authorSignature
|
||||
} else if let author = message.forwardInfo?.author {
|
||||
compactPeerName = EnginePeer(author).compactDisplayTitle
|
||||
} else if let source = message.forwardInfo?.source {
|
||||
compactPeerName = EnginePeer(source).compactDisplayTitle
|
||||
} else {
|
||||
if let author = message.author, case .user = author {
|
||||
compactPeerName = author.compactDisplayTitle
|
||||
} else {
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
||||
compactPeerName = attribute.signature
|
||||
}
|
||||
}
|
||||
if compactPeerName.isEmpty {
|
||||
compactPeerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if value == 1, let tonAmount = dice.tonAmount {
|
||||
let value = formatTonAmountText(tonAmount, dateTimeFormat: dateTimeFormat)
|
||||
rawString = strings.Conversation_EmojiStake_Lost(compactPeerName, value).string
|
||||
|
|
|
|||
|
|
@ -149,8 +149,7 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess
|
|||
dayText = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").string
|
||||
}
|
||||
dateText = strings.Message_FullDateFormat(dayText, stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat)).string
|
||||
}
|
||||
else if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
||||
} else if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
||||
dateText = strings.Message_ImportedDateFormat(dateStringForDay(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: forwardInfo.date), stringForMessageTimestamp(timestamp: forwardInfo.date, dateTimeFormat: dateTimeFormat), dateText).string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -270,7 +270,6 @@ private final class CraftGiftPageContent: Component {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
//let isFirstTime = self.craftState == nil
|
||||
self.craftState = state
|
||||
|
||||
var items: [GiftItem] = []
|
||||
|
|
@ -1118,9 +1117,14 @@ private final class CraftGiftPageContent: Component {
|
|||
self.state?.updated(transition: .easeInOut(duration: 0.5))
|
||||
},
|
||||
finished: { [weak self] view in
|
||||
guard let self, let component = self.component, let environment = self.environment, let controller = environment.controller() else {
|
||||
guard let self, let component = self.component, let environment = self.environment, let controller = environment.controller() as? GiftCraftScreen else {
|
||||
return
|
||||
}
|
||||
var references: [StarGiftReference] = []
|
||||
for gift in selectedGifts.values {
|
||||
references.append(gift.reference)
|
||||
}
|
||||
controller.profileGiftsContext?.removeStarGifts(references: references)
|
||||
if let _ = view {
|
||||
if case let .gift(gift) = component.result {
|
||||
let giftController = GiftViewScreen(context: component.context, subject: .profileGift(component.context.account.peerId, gift))
|
||||
|
|
@ -1129,12 +1133,12 @@ private final class CraftGiftPageContent: Component {
|
|||
|
||||
navigationController.view.addSubview(ConfettiView(frame: navigationController.view.bounds))
|
||||
}
|
||||
|
||||
controller.profileGiftsContext?.insertStarGifts(gifts: [gift])
|
||||
}
|
||||
controller.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false, completion: { _ in
|
||||
controller.dismiss()
|
||||
})
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -1241,7 +1245,7 @@ private final class CraftGiftPageContent: Component {
|
|||
}
|
||||
|
||||
|
||||
self.infoBackground.backgroundColor = environment.theme.list.plainBackgroundColor.cgColor
|
||||
self.infoBackground.backgroundColor = environment.theme.list.modalPlainBackgroundColor.cgColor
|
||||
|
||||
let infoBackgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 80.0), size: CGSize(width: availableSize.width, height: 1000.0))
|
||||
transition.setFrame(layer: self.infoBackground, frame: infoBackgroundFrame)
|
||||
|
|
@ -1691,8 +1695,8 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
selectedGiftIds: Set(),
|
||||
starsTopUpOptions: state.starsTopUpOptionsPromise.get(),
|
||||
selectGift: { item in
|
||||
let craftController = GiftCraftScreen(context: component.context, gift: item.gift)
|
||||
if let controller = controller() as? GiftCraftScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||
let craftController = GiftCraftScreen(context: component.context, gift: item.gift, profileGiftsContext: controller.profileGiftsContext)
|
||||
controller.dismissAnimated()
|
||||
navigationController.pushViewController(craftController)
|
||||
}
|
||||
|
|
@ -1770,7 +1774,7 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
)
|
||||
),
|
||||
backgroundColor: .color(backgroundColor),
|
||||
isFullscreen: false, //state.isCrafting,
|
||||
isFullscreen: false,
|
||||
animateOut: animateOut
|
||||
),
|
||||
environment: {
|
||||
|
|
@ -1805,12 +1809,16 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
|
||||
|
||||
public class GiftCraftScreen: ViewControllerComponentContainer {
|
||||
fileprivate weak var profileGiftsContext: ProfileGiftsContext?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
gift: StarGift.UniqueGift
|
||||
gift: StarGift.UniqueGift,
|
||||
profileGiftsContext: ProfileGiftsContext?
|
||||
) {
|
||||
let craftContext = CraftGiftsContext(account: context.account, giftId: gift.giftId)
|
||||
self.profileGiftsContext = profileGiftsContext
|
||||
|
||||
let craftContext = CraftGiftsContext(account: context.account, giftId: gift.giftId)
|
||||
super.init(
|
||||
context: context,
|
||||
component: SheetContainerComponent(
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ final class SelectGiftPageContent: Component {
|
|||
let starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>
|
||||
let selectGift: (GiftItem) -> Void
|
||||
let dismiss: () -> Void
|
||||
let boundsUpdated: ActionSlot<CGRect>
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
|
|
@ -45,7 +46,8 @@ final class SelectGiftPageContent: Component {
|
|||
selectedGiftIds: Set<Int64>,
|
||||
starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>,
|
||||
selectGift: @escaping (GiftItem) -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
dismiss: @escaping () -> Void,
|
||||
boundsUpdated: ActionSlot<CGRect>
|
||||
) {
|
||||
self.context = context
|
||||
self.craftContext = craftContext
|
||||
|
|
@ -56,6 +58,7 @@ final class SelectGiftPageContent: Component {
|
|||
self.starsTopUpOptions = starsTopUpOptions
|
||||
self.selectGift = selectGift
|
||||
self.dismiss = dismiss
|
||||
self.boundsUpdated = boundsUpdated
|
||||
}
|
||||
|
||||
static func ==(lhs: SelectGiftPageContent, rhs: SelectGiftPageContent) -> Bool {
|
||||
|
|
@ -86,6 +89,9 @@ final class SelectGiftPageContent: Component {
|
|||
private var availableGifts: [GiftItem] = []
|
||||
private var giftMap: [Int64: GiftItem] = [:]
|
||||
|
||||
private var availableSize: CGSize?
|
||||
private var currentBounds: CGRect?
|
||||
|
||||
private var component: SelectGiftPageContent?
|
||||
private weak var state: EmptyComponentState?
|
||||
private var environment: ViewControllerComponentContainer.Environment?
|
||||
|
|
@ -107,93 +113,16 @@ final class SelectGiftPageContent: Component {
|
|||
deinit {
|
||||
self.craftStateDisposable?.dispose()
|
||||
}
|
||||
|
||||
func update(component: SelectGiftPageContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
|
||||
func updateScrolling(interactive: Bool, transition: ComponentTransition) -> CGFloat {
|
||||
guard let bounds = self.currentBounds, let availableSize = self.availableSize, let component = self.component, let environment = self.environment else {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
if self.component == nil {
|
||||
let initialGiftItem = GiftItem(
|
||||
gift: component.gift,
|
||||
reference: .slug(slug: component.gift.slug)
|
||||
)
|
||||
self.availableGifts = [
|
||||
initialGiftItem
|
||||
]
|
||||
self.giftMap = [initialGiftItem.gift.id: initialGiftItem]
|
||||
|
||||
self.craftStateDisposable = (component.craftContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
//let isFirstTime = self.craftState == nil
|
||||
self.craftState = state
|
||||
|
||||
var items: [GiftItem] = []
|
||||
var map: [Int64: GiftItem] = [:]
|
||||
var foundInitial = false
|
||||
for gift in state.gifts {
|
||||
guard let reference = gift.reference, case let .unique(uniqueGift) = gift.gift else {
|
||||
continue
|
||||
}
|
||||
let giftItem = GiftItem(
|
||||
gift: uniqueGift,
|
||||
reference: reference
|
||||
)
|
||||
map[uniqueGift.id] = giftItem
|
||||
if uniqueGift.id == component.gift.id {
|
||||
foundInitial = true
|
||||
} else {
|
||||
if component.selectedGiftIds.contains(uniqueGift.id) {
|
||||
continue
|
||||
}
|
||||
items.append(giftItem)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundInitial {
|
||||
map[initialGiftItem.gift.id] = initialGiftItem
|
||||
}
|
||||
self.availableGifts = items
|
||||
self.giftMap = map
|
||||
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
})
|
||||
}
|
||||
let visibleBounds = bounds.insetBy(dx: 0.0, dy: -10.0)
|
||||
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
var contentHeight: CGFloat = 88.0 + 32.0
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
self.environment = environment
|
||||
|
||||
self.backgroundColor = environment.theme.actionSheet.opaqueItemBackgroundColor
|
||||
|
||||
var contentHeight: CGFloat = 88.0
|
||||
|
||||
let myGiftsTitleSize = self.myGiftsTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Your Gifts".uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
let myGiftsTitleFrame = CGRect(origin: CGPoint(x: 26.0, y: contentHeight), size: myGiftsTitleSize)
|
||||
if let myGiftsTitleView = self.myGiftsTitle.view {
|
||||
if myGiftsTitleView.superview == nil {
|
||||
self.addSubview(myGiftsTitleView)
|
||||
}
|
||||
transition.setFrame(view: myGiftsTitleView, frame: myGiftsTitleFrame)
|
||||
}
|
||||
|
||||
contentHeight += 32.0
|
||||
|
||||
let itemSpacing: CGFloat = 10.0
|
||||
let itemSideInset = 16.0
|
||||
let itemsInRow: Int
|
||||
|
|
@ -208,8 +137,7 @@ final class SelectGiftPageContent: Component {
|
|||
}
|
||||
let itemWidth = (availableSize.width - itemSideInset * 2.0 - itemSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||
let itemSize = CGSize(width: itemWidth, height: itemWidth)
|
||||
|
||||
|
||||
|
||||
var isLoading = false
|
||||
if self.availableGifts.isEmpty, case .loading = (self.craftState?.dataState ?? .loading) {
|
||||
isLoading = true
|
||||
|
|
@ -229,10 +157,10 @@ final class SelectGiftPageContent: Component {
|
|||
var itemsHeight: CGFloat = 0.0
|
||||
var validIds: [AnyHashable] = []
|
||||
for gift in self.availableGifts {
|
||||
let isVisible = "".isEmpty
|
||||
// if visibleBounds.intersects(itemFrame) {
|
||||
// isVisible = true
|
||||
// }
|
||||
var isVisible = false
|
||||
if visibleBounds.intersects(itemFrame) {
|
||||
isVisible = true
|
||||
}
|
||||
if isVisible {
|
||||
let itemId = AnyHashable(gift.gift.id)
|
||||
validIds.append(itemId)
|
||||
|
|
@ -256,8 +184,6 @@ final class SelectGiftPageContent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
let badge: String? = gift.gift.craftChancePermille.flatMap { "+\($0 / 10)%" }
|
||||
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(
|
||||
|
|
@ -269,7 +195,7 @@ final class SelectGiftPageContent: Component {
|
|||
peer: nil,
|
||||
subject: .uniqueGift(gift: gift.gift, price: nil),
|
||||
ribbon: GiftItemComponent.Ribbon(text: ribbonText, font: .monospaced, color: ribbonColor, outline: nil),
|
||||
badge: badge,
|
||||
badge: gift.gift.craftChancePermille.flatMap { "+\($0 / 10)%" },
|
||||
resellPrice: nil,
|
||||
isHidden: false,
|
||||
isSelected: false,
|
||||
|
|
@ -339,7 +265,7 @@ final class SelectGiftPageContent: Component {
|
|||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "You don't have other gifts\nfrom this collection", font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor)),
|
||||
text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_NoGiftsFromCollection, font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.1
|
||||
|
|
@ -362,10 +288,115 @@ final class SelectGiftPageContent: Component {
|
|||
contentHeight += 24.0
|
||||
}
|
||||
|
||||
if let storeGiftsView = self.storeGifts.view as? GiftStoreContentComponent.View {
|
||||
storeGiftsView.updateScrolling(bounds: bounds.offsetBy(dx: 0.0, dy: -contentHeight), interactive: interactive, transition: .immediate)
|
||||
}
|
||||
|
||||
return contentHeight
|
||||
}
|
||||
|
||||
func update(component: SelectGiftPageContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
|
||||
self.availableSize = availableSize
|
||||
if self.component == nil {
|
||||
self.currentBounds = CGRect(origin: .zero, size: availableSize)
|
||||
|
||||
component.boundsUpdated.connect { [weak self] bounds in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.currentBounds = bounds
|
||||
let _ = self.updateScrolling(interactive: true, transition: .immediate)
|
||||
}
|
||||
|
||||
let initialGiftItem = GiftItem(
|
||||
gift: component.gift,
|
||||
reference: .slug(slug: component.gift.slug)
|
||||
)
|
||||
self.availableGifts = [
|
||||
initialGiftItem
|
||||
]
|
||||
self.giftMap = [initialGiftItem.gift.id: initialGiftItem]
|
||||
|
||||
self.craftStateDisposable = (component.craftContext.state
|
||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.craftState = state
|
||||
|
||||
var items: [GiftItem] = []
|
||||
var map: [Int64: GiftItem] = [:]
|
||||
var foundInitial = false
|
||||
for gift in state.gifts {
|
||||
guard let reference = gift.reference, case let .unique(uniqueGift) = gift.gift else {
|
||||
continue
|
||||
}
|
||||
let giftItem = GiftItem(
|
||||
gift: uniqueGift,
|
||||
reference: reference
|
||||
)
|
||||
map[uniqueGift.id] = giftItem
|
||||
if uniqueGift.id == component.gift.id {
|
||||
foundInitial = true
|
||||
} else {
|
||||
if component.selectedGiftIds.contains(uniqueGift.id) {
|
||||
continue
|
||||
}
|
||||
items.append(giftItem)
|
||||
}
|
||||
}
|
||||
|
||||
if !foundInitial {
|
||||
map[initialGiftItem.gift.id] = initialGiftItem
|
||||
}
|
||||
self.availableGifts = items
|
||||
self.giftMap = map
|
||||
|
||||
self.state?.updated(transition: .spring(duration: 0.4))
|
||||
})
|
||||
}
|
||||
|
||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
|
||||
self.component = component
|
||||
self.state = state
|
||||
self.environment = environment
|
||||
|
||||
self.backgroundColor = environment.theme.actionSheet.opaqueItemBackgroundColor
|
||||
|
||||
var contentHeight: CGFloat = 88.0
|
||||
|
||||
let myGiftsTitleSize = self.myGiftsTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_YourGifts.uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
let myGiftsTitleFrame = CGRect(origin: CGPoint(x: 26.0, y: contentHeight), size: myGiftsTitleSize)
|
||||
if let myGiftsTitleView = self.myGiftsTitle.view {
|
||||
if myGiftsTitleView.superview == nil {
|
||||
self.addSubview(myGiftsTitleView)
|
||||
}
|
||||
transition.setFrame(view: myGiftsTitleView, frame: myGiftsTitleFrame)
|
||||
}
|
||||
|
||||
contentHeight += 32.0
|
||||
|
||||
contentHeight = self.updateScrolling(interactive: false, transition: transition)
|
||||
|
||||
let storeGiftsTitleSize = self.storeGiftsTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "SUITABLE GIFTS ON SALE".uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_SaleGifts.uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -497,6 +528,8 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
static var body: Body {
|
||||
let sheet = Child(ResizableSheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let boundsUpdated = ActionSlot<CGRect>()
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
|
@ -536,11 +569,12 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
selectGift: component.selectGift,
|
||||
dismiss: {
|
||||
dismiss(true)
|
||||
}
|
||||
},
|
||||
boundsUpdated: boundsUpdated
|
||||
)
|
||||
),
|
||||
titleItem: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Select Gifts", font: Font.semibold(17.0), textColor: environment.theme.actionSheet.primaryTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_Title, font: Font.semibold(17.0), textColor: environment.theme.actionSheet.primaryTextColor)))
|
||||
),
|
||||
leftItem: AnyComponent(
|
||||
GlassBarButtonComponent(
|
||||
|
|
@ -579,7 +613,8 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
dismiss(animated)
|
||||
}
|
||||
},
|
||||
boundsUpdated: boundsUpdated
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
|
|
|
|||
|
|
@ -1134,7 +1134,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
|
||||
let craftScreen = self.context.sharedContext.makeGiftCraftScreen(
|
||||
context: self.context,
|
||||
gift: gift
|
||||
gift: gift,
|
||||
profileGiftsContext: controller.profileGiftsContext
|
||||
)
|
||||
navigationController.pushViewController(craftScreen)
|
||||
}
|
||||
|
|
@ -1510,7 +1511,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
}
|
||||
|
||||
if case let .unique(gift) = arguments.gift, let resellAmount = gift.resellAmounts?.first, resellAmount.amount.value > 0 {
|
||||
if arguments.reference != nil || gift.owner.peerId == context.account.peerId {
|
||||
if arguments.reference != nil || gift.owner?.peerId == self.context.account.peerId {
|
||||
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_ChangePrice, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/PriceTag"), color: theme.contextMenu.primaryColor)
|
||||
}, action: { [weak self] c, _ in
|
||||
|
|
@ -2482,6 +2483,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
var isMyHostedUniqueGift = false
|
||||
var releasedByPeer: EnginePeer?
|
||||
var canGiftUpgrade = false
|
||||
var isDismantled = false
|
||||
|
||||
if case let .soldOutGift(gift) = subject {
|
||||
animationFile = gift.file
|
||||
|
|
@ -2554,7 +2556,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
}
|
||||
|
||||
if let number = arguments.giftNumber, let title = genericGift?.title {
|
||||
titleString = "\(title) #\(formatCollectibleNumber(number, dateTimeFormat: environment.dateTimeFormat))"
|
||||
titleString = "\(title) **#\(formatCollectibleNumber(number, dateTimeFormat: environment.dateTimeFormat))**"
|
||||
} else if isSelfGift {
|
||||
titleString = strings.Gift_View_Self_Title
|
||||
} else {
|
||||
|
|
@ -2570,6 +2572,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
titleString = ""
|
||||
}
|
||||
|
||||
if let uniqueGift, uniqueGift.owner == nil {
|
||||
isDismantled = true
|
||||
}
|
||||
|
||||
if !canUpgrade, let gift = state.starGiftsMap[giftId], let _ = gift.upgradeStars {
|
||||
canUpgrade = true
|
||||
}
|
||||
|
|
@ -3252,17 +3258,17 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
|
||||
let titleFont = Font.bold(20.0)
|
||||
let smallTitleFont = Font.bold(15.0)
|
||||
let numberFont: UIFont
|
||||
if let number = context.component.subject.arguments?.giftNumber, number < 1000 {
|
||||
numberFont = titleFont
|
||||
} else {
|
||||
numberFont = smallTitleFont
|
||||
}
|
||||
let titleAttributedString: NSAttributedString
|
||||
if let uniqueGift {
|
||||
let numberFont = uniqueGift.number < 1000 ? titleFont : smallTitleFont
|
||||
if let _ = uniqueGift {
|
||||
titleAttributedString = parseMarkdownIntoAttributedString(titleString, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: .white), bold: MarkdownAttributeSet(font: numberFont, textColor: vibrantColor), link: MarkdownAttributeSet(font: titleFont, textColor: .white), linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
} else {
|
||||
titleAttributedString = NSAttributedString(
|
||||
string: titleString,
|
||||
font: titleFont,
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
paragraphAlignment: .center
|
||||
)
|
||||
titleAttributedString = parseMarkdownIntoAttributedString(titleString, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: numberFont, textColor: theme.actionSheet.secondaryTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
|
|
@ -3763,6 +3769,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
)
|
||||
)
|
||||
))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let peerId = uniqueGift.hostPeerId, let peer = state.peerMap[peerId] {
|
||||
|
|
@ -4331,7 +4339,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
)
|
||||
), at: hasOriginalInfo ? tableItems.count - 1 : tableItems.count)
|
||||
|
||||
if let valueAmount = uniqueGift.valueAmount, let valueCurrency = uniqueGift.valueCurrency {
|
||||
if let valueAmount = uniqueGift.valueAmount, let valueCurrency = uniqueGift.valueCurrency, !isDismantled {
|
||||
tableItems.insert(.init(
|
||||
id: "fiatValue",
|
||||
title: strings.Gift_Unique_Value,
|
||||
|
|
@ -4544,7 +4552,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
if let controller = controller() as? GiftViewScreen, controller.openChatTheme != nil {
|
||||
isChatTheme = true
|
||||
}
|
||||
if ((incoming && !converted && !upgraded) || exported || selling || isChatTheme) && (!showUpgradePreview && !showWearPreview) {
|
||||
if ((incoming && !converted && !upgraded) || exported || selling || isChatTheme) && (!showUpgradePreview && !showWearPreview && !isDismantled) {
|
||||
let textFont = Font.regular(13.0)
|
||||
let textColor = theme.list.itemSecondaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
|
|
@ -4653,6 +4661,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
)
|
||||
|
||||
let buttonChild: _UpdatedChildComponent
|
||||
if state.canSkip {
|
||||
buttonChild = button.update(
|
||||
|
|
@ -5026,7 +5035,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else if incoming && !converted && !savedToProfile {
|
||||
} else if incoming && !converted && !savedToProfile && !isDismantled {
|
||||
let buttonTitle = isChannelGift ? strings.Gift_View_Display_Channel : strings.Gift_View_Display
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
|
|
@ -5245,7 +5254,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
))
|
||||
|
||||
var rightControlItems: [GlassControlGroupComponent.Item] = []
|
||||
if uniqueGift != nil && !showWearPreview {
|
||||
if uniqueGift != nil && !showWearPreview && !isDismantled {
|
||||
if let _ = component.subject.arguments?.canCraftDate {
|
||||
var canCraft = false
|
||||
if let data = component.context.currentAppConfiguration.with({ $0 }).data {
|
||||
|
|
@ -5597,7 +5606,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
if case let .unique(uniqueGift) = gift.gift {
|
||||
resellAmounts = uniqueGift.resellAmounts
|
||||
}
|
||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.debugDisplayTitle, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash, gift.upgradeSeparate, gift.dropOriginalDetailsStars, nil, gift.number, gift.canCraftAt)
|
||||
|
||||
var number: Int32?
|
||||
if case let .unique(uniqueGift) = gift.gift {
|
||||
number = uniqueGift.number
|
||||
} else if let numberValue = gift.number {
|
||||
number = numberValue
|
||||
}
|
||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.debugDisplayTitle, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash, gift.upgradeSeparate, gift.dropOriginalDetailsStars, nil, number, gift.canCraftAt)
|
||||
case .soldOutGift:
|
||||
return nil
|
||||
case .upgradePreview:
|
||||
|
|
@ -5639,6 +5655,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
|
||||
fileprivate let balanceOverlay = ComponentView<Empty>()
|
||||
|
||||
fileprivate let profileGiftsContext: ProfileGiftsContext?
|
||||
fileprivate let updateSavedToProfile: ((StarGiftReference, Bool) -> Void)?
|
||||
fileprivate let convertToStars: ((StarGiftReference) -> Void)?
|
||||
fileprivate let dropOriginalDetails: ((StarGiftReference) -> Signal<Never, DropStarGiftOriginalDetailsError>)?
|
||||
|
|
@ -5658,6 +5675,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
allSubjects: [GiftViewScreen.Subject]? = nil,
|
||||
index: Int? = nil,
|
||||
forceDark: Bool = false,
|
||||
profileGiftsContext: ProfileGiftsContext? = nil,
|
||||
updateSavedToProfile: ((StarGiftReference, Bool) -> Void)? = nil,
|
||||
convertToStars: ((StarGiftReference) -> Void)? = nil,
|
||||
dropOriginalDetails: ((StarGiftReference) -> Signal<Never, DropStarGiftOriginalDetailsError>)? = nil,
|
||||
|
|
@ -5672,6 +5690,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||
self.context = context
|
||||
self.subject = subject
|
||||
|
||||
self.profileGiftsContext = profileGiftsContext
|
||||
self.updateSavedToProfile = updateSavedToProfile
|
||||
self.convertToStars = convertToStars
|
||||
self.dropOriginalDetails = dropOriginalDetails
|
||||
|
|
|
|||
|
|
@ -2368,6 +2368,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let controller = GiftViewScreen(
|
||||
context: self.context,
|
||||
subject: .profileGift(self.peerId, gift),
|
||||
profileGiftsContext: profileGifts,
|
||||
updateSavedToProfile: { [weak profileGifts] reference, added in
|
||||
guard let profileGifts else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -606,6 +606,7 @@ final class GiftsListView: UIView {
|
|||
subject: .profileGift(self.peerId, product),
|
||||
allSubjects: allSubjects,
|
||||
index: index,
|
||||
profileGiftsContext: self.profileGifts,
|
||||
updateSavedToProfile: { [weak self] reference, added in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ swift_library(
|
|||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -12,6 +12,7 @@ import BalancedTextComponent
|
|||
import Markdown
|
||||
import TelegramStringFormatting
|
||||
import BundleIconComponent
|
||||
import GlassBarButtonComponent
|
||||
|
||||
public final class ButtonSubtitleComponent: CombinedComponent {
|
||||
public let title: String
|
||||
|
|
@ -124,23 +125,29 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
}
|
||||
let cancelButtonSize = cancelButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(Text(text: environment.strings.Common_Cancel, font: Font.regular(17.0), color: environment.theme.list.itemAccentColor)),
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: nil,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
component.dismiss()
|
||||
}
|
||||
).minSize(CGSize(width: 8.0, height: 44.0))),
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 200.0, height: 100.0)
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
)
|
||||
if let cancelButtonView = cancelButton.view {
|
||||
if cancelButtonView.superview == nil {
|
||||
self.addSubview(cancelButtonView)
|
||||
}
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: 16.0, y: 6.0), size: cancelButtonSize))
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: cancelButtonSize))
|
||||
}
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
|
@ -152,7 +159,8 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
content: LottieComponent.AppBundleContent(name: "StoryUpgradeSheet"),
|
||||
color: nil,
|
||||
startingPosition: .begin,
|
||||
size: CGSize(width: 100.0, height: 100.0)
|
||||
size: CGSize(width: 100.0, height: 100.0),
|
||||
loop: true
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
|
|
@ -217,16 +225,18 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
color: environment.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7)
|
||||
))))
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let buttonSize = self.button.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
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(
|
||||
VStack(buttonContents, spacing: 3.0)
|
||||
VStack(buttonContents, spacing: 1.0)
|
||||
)),
|
||||
isEnabled: true,
|
||||
allowActionWhenDisabled: true,
|
||||
|
|
@ -240,9 +250,9 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: buttonSize)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
|
|
@ -250,12 +260,7 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
contentHeight += buttonSize.height
|
||||
|
||||
if environment.safeInsets.bottom.isZero {
|
||||
contentHeight += 16.0
|
||||
} else {
|
||||
contentHeight += environment.safeInsets.bottom + 14.0
|
||||
}
|
||||
contentHeight += buttonInsets.bottom
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
}
|
||||
|
|
@ -355,7 +360,8 @@ private final class StoryQualityUpgradeSheetScreenComponent: Component {
|
|||
})
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.overallDarkAppearance ? environment.theme.list.itemBlocksBackgroundColor : environment.theme.list.blocksBackgroundColor),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.list.modalPlainBackgroundColor),
|
||||
animateOut: self.sheetAnimateOut
|
||||
)),
|
||||
environment: {
|
||||
|
|
|
|||
|
|
@ -3899,8 +3899,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return GiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, attributes: attributes, completion: completion)
|
||||
}
|
||||
|
||||
public func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController {
|
||||
return GiftCraftScreen(context: context, gift: gift)
|
||||
public func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift, profileGiftsContext: ProfileGiftsContext?) -> ViewController {
|
||||
return GiftCraftScreen(context: context, gift: gift, profileGiftsContext: profileGiftsContext)
|
||||
}
|
||||
|
||||
public func makeGiftDemoScreen(context: AccountContext) -> ViewController {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue