Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Mikhail Filimonov 2026-01-30 08:22:01 +01:00
commit 609c22582a
15 changed files with 406 additions and 180 deletions

View file

@ -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";

View file

@ -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)?)

View file

@ -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 {

View file

@ -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)

View file

@ -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

View file

@ -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
}

View file

@ -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(

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -22,6 +22,7 @@ swift_library(
"//submodules/Components/SheetComponent",
"//submodules/TelegramUI/Components/ButtonComponent",
"//submodules/TelegramUI/Components/LottieComponent",
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
],
visibility = [
"//visibility:public",

View file

@ -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: {

View file

@ -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 {