mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Various improvements
This commit is contained in:
parent
4660f67271
commit
2cb0c7f147
12 changed files with 464 additions and 192 deletions
|
|
@ -15517,6 +15517,9 @@ Error: %8$@";
|
|||
"Attachment.FilesSearchPlaceholder" = "Search shared audio";
|
||||
"Chat.GiftPurchaseOffer.Reject" = "Reject";
|
||||
"Chat.GiftPurchaseOffer.Accept" = "Accept";
|
||||
"Chat.GiftPurchaseOffer.RejectConfirmation.Title" = "Reject Offer";
|
||||
"Chat.GiftPurchaseOffer.RejectConfirmation.Text" = "Are you sure you want to reject the offer from **%@**?";
|
||||
"Chat.GiftPurchaseOffer.RejectConfirmation.Reject" = "Reject";
|
||||
|
||||
"Passkeys.DeleteAlert.Title" = "Delete Passkey?";
|
||||
"Passkeys.DeleteAlert.Text" = "Once deleted, this passkey can't be used to log in.\n\nDon't forget to remove it from your password manager too.";
|
||||
|
|
@ -15559,13 +15562,13 @@ Error: %8$@";
|
|||
"Gift.Variants.Backdrops" = "Backdrops";
|
||||
"Gift.Variants.Symbols" = "Symbols";
|
||||
|
||||
"Gift.Variants.CollectionInfo" = "This collection features";
|
||||
"Gift.Variants.CollectionInfo.Model_1" = "%@ unique model";
|
||||
"Gift.Variants.CollectionInfo.Model_any" = "%@ unique models";
|
||||
"Gift.Variants.CollectionInfo.Backdrop_1" = "%@ unique backdrop";
|
||||
"Gift.Variants.CollectionInfo.Backdrop_any" = "%@ unique backdrops";
|
||||
"Gift.Variants.CollectionInfo.Symbol_1" = "%@ unique symbol";
|
||||
"Gift.Variants.CollectionInfo.Symbol_any" = "%@ unique symbols";
|
||||
"Gift.Variants.CollectionInfo" = "This collection features %@";
|
||||
"Gift.Variants.CollectionInfo.Model_1" = "**%@** unique model";
|
||||
"Gift.Variants.CollectionInfo.Model_any" = "**%@** unique models";
|
||||
"Gift.Variants.CollectionInfo.Backdrop_1" = "**%@** unique backdrop";
|
||||
"Gift.Variants.CollectionInfo.Backdrop_any" = "**%@** unique backdrops";
|
||||
"Gift.Variants.CollectionInfo.Symbol_1" = "**%@** unique symbol";
|
||||
"Gift.Variants.CollectionInfo.Symbol_any" = "**%@** unique symbols";
|
||||
|
||||
"Gift.Auction.GiftAuction" = "Upcoming Auction";
|
||||
"Gift.Auction.UpcomingAuction" = "Upcoming Auction";
|
||||
|
|
@ -15589,6 +15592,34 @@ Error: %8$@";
|
|||
"Gift.Offer.Duration.Hours_1" = "%@ Hour";
|
||||
"Gift.Offer.Duration.Hours_any" = "%@ Hours";
|
||||
"Gift.Offer.Offer" = "Offer";
|
||||
"Gift.Offer.GiftMinAmountToast.Text" = "You cannot offer less than %@ for this gift";
|
||||
|
||||
"Gift.WearPreview.Limited" = "limited";
|
||||
"Gift.WearPreview.Upgraded" = "upgraded";
|
||||
"Gift.WearPreview.FreeUpgrade" = "Free\nUpgrade";
|
||||
"Gift.WearPreview.LearnMore" = "Learn more about wearing Telegram Gifts >";
|
||||
|
||||
"Notification.StarGift.Sold" = "sold";
|
||||
"Notification.StarGiftOffer.Offer" = "**%1$@** offered you **%2$@** for your gift **%3$@**.";
|
||||
"Notification.StarGiftOffer.OfferYou" = "You offered **%1$@** **%2$@** for their gift **%3$@**.";
|
||||
"Notification.StarGiftOffer.Offer.Stars_1" = "%@ Star";
|
||||
"Notification.StarGiftOffer.Offer.Stars_any" = "%@ Stars";
|
||||
|
||||
"Notification.StarGiftOffer.Status.Accepted" = "This offer was accepted.";
|
||||
"Notification.StarGiftOffer.Status.Expired" = "This offer has expired.";
|
||||
"Notification.StarGiftOffer.Status.Expires" = "This offer expires in %@";
|
||||
"Notification.StarGiftOffer.Status.Rejected" = "This offer was rejected.";
|
||||
|
||||
"Notification.StarGiftOffer.Expiration.Hours_1" = "%@ h";
|
||||
"Notification.StarGiftOffer.Expiration.Hours_any" = "%@ h";
|
||||
"Notification.StarGiftOffer.Expiration.Minutes_1" = "%@ m";
|
||||
"Notification.StarGiftOffer.Expiration.Minutes_any" = "%@ m";
|
||||
"Notification.StarGiftOffer.Expiration.Delimiter" = "";
|
||||
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Title" = "Confirm Sale";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Text" = "Do you want to sell **%1$@** to %2$@ for **%3$@**? You'll receive **%4$@** after fees.";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Text.Stars_1" = "%@ Star";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Text.Stars_any" = "%@ Stars";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.BadValue" = "The value of this gift is **%@** higher than the offer.";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Confirm" = "Confirm Sale";
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public class ChatMessageGiftOfferBubbleContentNode: ChatMessageBubbleContentNode
|
|||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = "\(amount.amount) Stars"
|
||||
priceString = item.presentationData.strings.Notification_StarGiftOffer_Offer_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
|
@ -143,41 +143,35 @@ public class ChatMessageGiftOfferBubbleContentNode: ChatMessageBubbleContentNode
|
|||
giftTitle = ""
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
if incoming {
|
||||
text = "**\(peerName)** offered you **\(priceString)** for your gift **\(giftTitle)**."
|
||||
text = item.presentationData.strings.Notification_StarGiftOffer_Offer(peerName, priceString, giftTitle).string
|
||||
} else {
|
||||
text = "You offered **\(peerName)** **\(priceString)** for their gift **\(giftTitle)**."
|
||||
text = item.presentationData.strings.Notification_StarGiftOffer_OfferYou(peerName, priceString, giftTitle).string
|
||||
}
|
||||
|
||||
if isAccepted {
|
||||
additionalText = "This offer was accepted."
|
||||
additionalText = item.presentationData.strings.Notification_StarGiftOffer_Status_Accepted
|
||||
} else if isDeclined {
|
||||
additionalText = "This offer was rejected."
|
||||
additionalText = item.presentationData.strings.Notification_StarGiftOffer_Status_Rejected
|
||||
} else if expireDate > currentTimestamp {
|
||||
func textForTimeout(_ value: Int32) -> String {
|
||||
if value < 3600 {
|
||||
let minutes = value / 60
|
||||
//let seconds = value % 60
|
||||
//let secondsPadding = seconds < 10 ? "0" : ""
|
||||
return "\(minutes)m" //\(secondsPadding)\(seconds)"
|
||||
return item.presentationData.strings.Notification_StarGiftOffer_Expiration_Minutes(minutes)
|
||||
} else {
|
||||
let hours = value / 3600
|
||||
let minutes = (value % 3600) / 60
|
||||
let minutesPadding = minutes < 10 ? "0" : ""
|
||||
//let seconds = value % 60
|
||||
//let secondsPadding = seconds < 10 ? "0" : ""
|
||||
return "\(hours)h \(minutesPadding)\(minutes)m" //:\(secondsPadding)\(seconds)"
|
||||
return item.presentationData.strings.Notification_StarGiftOffer_Expiration_Hours(hours) + item.presentationData.strings.Notification_StarGiftOffer_Expiration_Delimiter + item.presentationData.strings.Notification_StarGiftOffer_Expiration_Minutes(minutes)
|
||||
}
|
||||
}
|
||||
let delta = expireDate - currentTimestamp
|
||||
additionalText = "This offer expires in \(textForTimeout(delta))."
|
||||
|
||||
additionalText = item.presentationData.strings.Notification_StarGiftOffer_Status_Expires(textForTimeout(delta)).string
|
||||
|
||||
if incoming {
|
||||
hasActionButtons = true
|
||||
}
|
||||
} else {
|
||||
additionalText = "This offer has expired."
|
||||
additionalText = item.presentationData.strings.Notification_StarGiftOffer_Status_Expired
|
||||
}
|
||||
} else {
|
||||
text = ""
|
||||
|
|
@ -277,8 +271,6 @@ public class ChatMessageGiftOfferBubbleContentNode: ChatMessageBubbleContentNode
|
|||
)
|
||||
if let giftIconView = strongSelf.giftIcon.view {
|
||||
if giftIconView.superview == nil {
|
||||
// backgroundView.layer.cornerRadius = 20.0
|
||||
//backgroundView.clipsToBounds = true
|
||||
strongSelf.view.addSubview(giftIconView)
|
||||
}
|
||||
giftIconView.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY + 17.0), size: iconSize)
|
||||
|
|
|
|||
|
|
@ -601,7 +601,9 @@ public final class GiftItemComponent: Component {
|
|||
}
|
||||
case let .preview(attributes, _):
|
||||
animationOffset = 16.0
|
||||
explicitAnimationOffset = -4.0
|
||||
if component.mode != .tableIcon {
|
||||
explicitAnimationOffset = -4.0
|
||||
}
|
||||
for attribute in attributes {
|
||||
switch attribute {
|
||||
case let .model(_, file, _):
|
||||
|
|
|
|||
|
|
@ -1727,7 +1727,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||
inactiveValue: "",
|
||||
inactiveTitleColor: theme.list.itemSecondaryTextColor,
|
||||
activeTitle: "",
|
||||
activeValue: environment.strings.Gift_Send_Sold(sold),
|
||||
activeValue: sold > 0 ? environment.strings.Gift_Send_Sold(sold) : "",
|
||||
activeTitleColor: .white,
|
||||
badgeText: "",
|
||||
badgePosition: position,
|
||||
|
|
@ -1830,22 +1830,30 @@ private final class GiftSetupScreenComponent: Component {
|
|||
|
||||
var buttonTitleItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
if let _ = self.giftAuction {
|
||||
let buttonAttributedString = NSMutableAttributedString(string: environment.strings.Gift_Setup_PlaceBid, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
buttonTitleItems.append(AnyComponentWithIdentity(id: "bid", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(buttonAttributedString))
|
||||
)))
|
||||
var isUpcoming = false
|
||||
if let giftAuctionState = self.giftAuctionState {
|
||||
switch giftAuctionState.auctionState {
|
||||
case let .ongoing(_, _, endTime, _, _, _, _, _, _, _, _, _):
|
||||
case let .ongoing(_, startTime, endTime, _, _, _, _, _, _, _, _, _):
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
let endTimeout = max(0, endTime - currentTime)
|
||||
let endTimeout: Int32
|
||||
if currentTime < startTime {
|
||||
endTimeout = max(0, startTime - currentTime)
|
||||
isUpcoming = true
|
||||
} else {
|
||||
endTimeout = max(0, endTime - currentTime)
|
||||
}
|
||||
|
||||
let hours = Int(endTimeout / 3600)
|
||||
let minutes = Int((endTimeout % 3600) / 60)
|
||||
let seconds = Int(endTimeout % 60)
|
||||
|
||||
let rawString = hours > 0 ? environment.strings.Gift_Auction_TimeLeftHours : environment.strings.Gift_Auction_TimeLeftMinutes
|
||||
let rawString: String
|
||||
if isUpcoming {
|
||||
rawString = hours > 0 ? environment.strings.Gift_Auction_StartsInHours : environment.strings.Gift_Auction_StartsInMinutes
|
||||
} else {
|
||||
rawString = hours > 0 ? environment.strings.Gift_Auction_TimeLeftHours : environment.strings.Gift_Auction_TimeLeftMinutes
|
||||
}
|
||||
|
||||
var buttonAnimatedTitleItems: [AnimatedTextComponent.Item] = []
|
||||
var startIndex = rawString.startIndex
|
||||
while true {
|
||||
|
|
@ -1885,6 +1893,10 @@ private final class GiftSetupScreenComponent: Component {
|
|||
buttonIsEnabled = false
|
||||
}
|
||||
}
|
||||
let buttonAttributedString = NSMutableAttributedString(string: isUpcoming ? environment.strings.Gift_Auction_EarlyBid : environment.strings.Gift_Setup_PlaceBid, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
buttonTitleItems.insert(AnyComponentWithIdentity(id: "bid", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(buttonAttributedString))
|
||||
)), at: 0)
|
||||
} else {
|
||||
let buttonAttributedString = NSMutableAttributedString(string: buttonString, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
|
||||
if let range = buttonAttributedString.string.range(of: "#"), let starImage = self.cachedStarImage?.0 {
|
||||
|
|
|
|||
|
|
@ -2493,7 +2493,19 @@ private final class GiftAuctionBidScreenComponent: Component {
|
|||
untilNextRoundAnimatedItems.append(AnimatedTextComponent.Item(id: "s", content: .number(seconds, minDigits: 2)))
|
||||
}
|
||||
|
||||
dropsLeftAnimatedItems = [AnimatedTextComponent.Item(id: "drops", content: .number(Int(dropsLeft), minDigits: 1))]
|
||||
if dropsLeft >= 10000 {
|
||||
var compactString = compactNumericCountString(Int(dropsLeft), decimalSeparator: ".", showDecimalPart: false)
|
||||
let suffix = String(compactString.suffix(1))
|
||||
compactString.removeLast()
|
||||
if let value = Int(compactString) {
|
||||
dropsLeftAnimatedItems = [
|
||||
AnimatedTextComponent.Item(id: "drops", content: .number(value, minDigits: 1)),
|
||||
AnimatedTextComponent.Item(id: "suffix", content: .text(suffix))
|
||||
]
|
||||
}
|
||||
} else {
|
||||
dropsLeftAnimatedItems = [AnimatedTextComponent.Item(id: "drops", content: .number(Int(dropsLeft), minDigits: 1))]
|
||||
}
|
||||
}
|
||||
|
||||
auctionStats.append((
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
private(set) var previewBackdropIndex: Int = -1
|
||||
private(set) var previewSymbolIndex: Int = -1
|
||||
|
||||
private var disposables = DisposableSet()
|
||||
private var fetchedFiles = Set<Int64>()
|
||||
private(set) var previewModels: [StarGift.UniqueGift.Attribute] = []
|
||||
private(set) var previewBackdrops: [StarGift.UniqueGift.Attribute] = []
|
||||
private(set) var previewSymbols: [StarGift.UniqueGift.Attribute] = []
|
||||
|
|
@ -154,6 +156,16 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
}).shuffled().prefix(5))
|
||||
self.previewSymbols = randomSymbols
|
||||
|
||||
for case let .model(_, file, _) in self.previewModels where !self.fetchedFiles.contains(file.fileId.id) {
|
||||
self.disposables.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
||||
self.fetchedFiles.insert(file.fileId.id)
|
||||
}
|
||||
|
||||
for case let .pattern(_, file, _) in self.previewSymbols where !self.fetchedFiles.contains(file.fileId.id) {
|
||||
self.disposables.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
|
||||
self.fetchedFiles.insert(file.fileId.id)
|
||||
}
|
||||
|
||||
self.updated()
|
||||
})
|
||||
|
||||
|
|
@ -169,6 +181,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
self.disposables.dispose()
|
||||
self.giftAuctionAcquiredGiftsDisposable.dispose()
|
||||
self.giftAuctionTimer?.invalidate()
|
||||
self.previewTimer?.invalidate()
|
||||
|
|
@ -509,78 +522,6 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
|
||||
var buttonColor: UIColor = .white.withAlphaComponent(0.1)
|
||||
var secondaryTextColor: UIColor = .white.withAlphaComponent(0.4)
|
||||
if let genericGift {
|
||||
var attributes: [StarGift.UniqueGift.Attribute] = []
|
||||
if state.previewModelIndex == -1 {
|
||||
attributes.append(.model(name: "", file: genericGift.file, rarity: 0))
|
||||
if let background = genericGift.background {
|
||||
attributes.append(.backdrop(name: "", id: 0, innerColor: background.centerColor, outerColor: background.edgeColor, patternColor: 0, textColor: 0, rarity: 0))
|
||||
}
|
||||
} else if !state.previewModels.isEmpty {
|
||||
attributes.append(state.previewModels[state.previewModelIndex])
|
||||
if !state.previewBackdrops.isEmpty {
|
||||
attributes.append(state.previewBackdrops[state.previewBackdropIndex])
|
||||
}
|
||||
if !state.previewSymbols.isEmpty {
|
||||
attributes.append(state.previewSymbols[state.previewSymbolIndex])
|
||||
}
|
||||
}
|
||||
|
||||
if let backdropAttribute = attributes.first(where: { attribute in
|
||||
if case .backdrop = attribute {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute {
|
||||
let topColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.01, saturation: 1.22, brightness: 1.04)
|
||||
let bottomColor = UIColor(rgb: UInt32(bitPattern: outerColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89)
|
||||
buttonColor = topColor.mixedWith(bottomColor, alpha: 0.8).withMultipliedBrightnessBy(1.25)
|
||||
|
||||
secondaryTextColor = topColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.5)
|
||||
}
|
||||
|
||||
let header = header.update(
|
||||
component: GiftCompositionComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
subject: .preview(attributes),
|
||||
animationOffset: CGPoint(x: 0.0, y: -4.0),
|
||||
animationScale: nil,
|
||||
displayAnimationStars: false,
|
||||
revealedAttributes: Set(),
|
||||
externalState: giftCompositionExternalState,
|
||||
requestUpdate: { [weak state] transition in
|
||||
state?.updated(transition: transition)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: 264.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(header
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: header.size.height * 0.5))
|
||||
)
|
||||
originY += header.size.height
|
||||
originY += 16.0
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: titleString,
|
||||
font: Font.bold(20.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 209.0))
|
||||
)
|
||||
|
||||
var descriptionText: String = ""
|
||||
|
||||
|
|
@ -771,14 +712,89 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let genericGift {
|
||||
var attributes: [StarGift.UniqueGift.Attribute] = []
|
||||
if state.previewModelIndex == -1 {
|
||||
attributes.append(.model(name: "", file: genericGift.file, rarity: 0))
|
||||
if let background = genericGift.background {
|
||||
attributes.append(.backdrop(name: "", id: 0, innerColor: background.centerColor, outerColor: background.edgeColor, patternColor: 0, textColor: 0, rarity: 0))
|
||||
}
|
||||
} else if !state.previewModels.isEmpty {
|
||||
attributes.append(state.previewModels[state.previewModelIndex])
|
||||
if !state.previewBackdrops.isEmpty {
|
||||
attributes.append(state.previewBackdrops[state.previewBackdropIndex])
|
||||
}
|
||||
if !state.previewSymbols.isEmpty {
|
||||
attributes.append(state.previewSymbols[state.previewSymbolIndex])
|
||||
}
|
||||
}
|
||||
|
||||
if let backdropAttribute = attributes.first(where: { attribute in
|
||||
if case .backdrop = attribute {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute {
|
||||
let topColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.01, saturation: 1.22, brightness: 1.04)
|
||||
let bottomColor = UIColor(rgb: UInt32(bitPattern: outerColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89)
|
||||
buttonColor = topColor.mixedWith(bottomColor, alpha: 0.8).withMultipliedBrightnessBy(1.25)
|
||||
|
||||
secondaryTextColor = topColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.5)
|
||||
}
|
||||
|
||||
let headerHeight: CGFloat = 264.0
|
||||
|
||||
let header = header.update(
|
||||
component: GiftCompositionComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
subject: .preview(attributes),
|
||||
animationOffset: CGPoint(x: 0.0, y: -4.0),
|
||||
animationScale: nil,
|
||||
displayAnimationStars: false,
|
||||
revealedAttributes: Set(),
|
||||
externalState: giftCompositionExternalState,
|
||||
requestUpdate: { [weak state] transition in
|
||||
state?.updated(transition: transition)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: headerHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(header
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: header.size.height * 0.5))
|
||||
)
|
||||
originY += header.size.height
|
||||
originY += 16.0
|
||||
}
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: titleString,
|
||||
font: Font.bold(20.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 209.0))
|
||||
)
|
||||
|
||||
let description = description.update(
|
||||
component: StarsButtonContentComponent(
|
||||
context: component.context,
|
||||
text: descriptionText,
|
||||
color: buttonColor,
|
||||
tinted: true,
|
||||
starsColor: UIColor(white: 1.0, alpha: 0.5),
|
||||
starsColor: isEnded ? .clear : UIColor(white: 1.0, alpha: 0.5),
|
||||
font: Font.medium(13.0),
|
||||
height: 24.0
|
||||
),
|
||||
|
|
@ -1198,7 +1214,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
component: GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: buttonColor,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
isDark: false,
|
||||
state: .tintedGlass,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
|
|
@ -1224,7 +1240,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
component: GlassBarButtonComponent(
|
||||
size: CGSize(width: 40.0, height: 40.0),
|
||||
backgroundColor: buttonColor,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
isDark: false,
|
||||
state: .tintedGlass,
|
||||
component: AnyComponentWithIdentity(id: "more", component: AnyComponent(
|
||||
LottieComponent(
|
||||
|
|
|
|||
|
|
@ -335,8 +335,7 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent {
|
|||
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
let learnMoreAttributedString = parseMarkdownIntoAttributedString("Learn more about wearing Telegram Gifts >", attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||
let learnMoreAttributedString = parseMarkdownIntoAttributedString(strings.Gift_WearPreview_LearnMore, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = learnMoreAttributedString.string.range(of: ">"), let chevronImage = state.cachedSmallChevronImage?.0 {
|
||||
learnMoreAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: learnMoreAttributedString.string))
|
||||
}
|
||||
|
|
@ -367,7 +366,7 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent {
|
|||
theme: theme,
|
||||
strings: strings,
|
||||
subject: .starGift(gift: gift, price: ""),
|
||||
ribbon: GiftItemComponent.Ribbon(text: "limited", color: .blue),
|
||||
ribbon: GiftItemComponent.Ribbon(text: strings.Gift_WearPreview_Limited, color: .blue),
|
||||
mode: .thumbnail
|
||||
),
|
||||
availableSize: CGSize(width: 120.0, height: 120.0),
|
||||
|
|
@ -391,7 +390,7 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent {
|
|||
theme: theme,
|
||||
strings: strings,
|
||||
subject: .preview(attributes: attributes, rarity: 0),
|
||||
ribbon: GiftItemComponent.Ribbon(text: "upgraded", color: ribbonColor),
|
||||
ribbon: GiftItemComponent.Ribbon(text: strings.Gift_WearPreview_Upgraded, color: ribbonColor),
|
||||
animateChanges: true,
|
||||
mode: .thumbnail
|
||||
),
|
||||
|
|
@ -414,7 +413,7 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent {
|
|||
|
||||
let upgradeLabel = upgradeLabel.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "Free\nUpgrade", font: Font.medium(13.0), textColor: theme.list.itemSecondaryTextColor)),
|
||||
text: .plain(NSAttributedString(string: strings.Gift_WearPreview_FreeUpgrade, font: Font.medium(13.0), textColor: theme.list.itemSecondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 2,
|
||||
lineSpacing: 0.1
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import ChatMessagePaymentAlertController
|
|||
import ActivityIndicator
|
||||
import TooltipUI
|
||||
import MultilineTextComponent
|
||||
import BalancedTextComponent
|
||||
import TelegramStringFormatting
|
||||
|
||||
private final class GiftOfferAlertContentNode: AlertContentNode {
|
||||
|
|
@ -24,6 +25,7 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
private var presentationTheme: PresentationTheme
|
||||
private let title: String
|
||||
private let text: String
|
||||
private let amount: CurrencyAmount
|
||||
private let gift: StarGift.UniqueGift
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
|
|
@ -32,6 +34,7 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
private let arrowNode: ASImageNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let tableView = ComponentView<Empty>()
|
||||
private let valueDelta = ComponentView<Empty>()
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
|
|
@ -68,6 +71,7 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
peer: EnginePeer,
|
||||
title: String,
|
||||
text: String,
|
||||
amount: CurrencyAmount,
|
||||
actions: [TextAlertAction]
|
||||
) {
|
||||
self.context = context
|
||||
|
|
@ -75,6 +79,7 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
self.presentationTheme = ptheme
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.amount = amount
|
||||
self.gift = gift
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
|
|
@ -339,17 +344,6 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
if let valueAmount = self.gift.valueAmount, let valueCurrency = self.gift.valueCurrency {
|
||||
tableItems.append(.init(
|
||||
id: "fiatValue",
|
||||
title: strings.Gift_Unique_Value,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "~\(formatCurrencyAmount(valueAmount, currency: valueCurrency))", font: tableFont, textColor: tableTextColor)))
|
||||
),
|
||||
insets: UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 12.0)
|
||||
))
|
||||
}
|
||||
|
||||
let tableSize = self.tableView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
|
|
@ -370,7 +364,57 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
view.frame = tableFrame
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + 40.0 + insets.top + insets.bottom)
|
||||
var valueDeltaHeight: CGFloat = 0.0
|
||||
if let valueAmount = self.gift.valueUsdAmount {
|
||||
let resaleConfiguration = StarsSubscriptionConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let usdRate: Double
|
||||
switch self.amount.currency {
|
||||
case .stars:
|
||||
usdRate = Double(resaleConfiguration.usdWithdrawRate) / 1000.0 / 100.0
|
||||
case .ton:
|
||||
usdRate = Double(resaleConfiguration.tonUsdRate) / 1000.0 / 1000000.0
|
||||
}
|
||||
let offerUsdValue = Double(self.amount.amount.value) * usdRate
|
||||
let giftUsdValue = Double(valueAmount) / 100.0
|
||||
|
||||
let fraction = giftUsdValue / offerUsdValue
|
||||
let percentage = Int(fraction * 100) - 100
|
||||
|
||||
if percentage > 20 {
|
||||
let textColor = self.presentationTheme.list.itemDestructiveColor
|
||||
let markdownAttributes = MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: textColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: textColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
}
|
||||
)
|
||||
let valueDeltaSize = self.valueDelta.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BalancedTextComponent(
|
||||
text: .markdown(text: strings.Chat_GiftPurchaseOffer_AcceptConfirmation_BadValue("\(percentage)%").string, attributes: markdownAttributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: contentWidth - 32.0, height: size.height)
|
||||
)
|
||||
let valueDeltaFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - valueDeltaSize.width) / 2.0), y: avatarSize.height + titleSize.height + textSize.height + 73.0 + tableSize.height), size: valueDeltaSize)
|
||||
if let view = self.valueDelta.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
view.frame = valueDeltaFrame
|
||||
}
|
||||
valueDeltaHeight += valueDeltaSize.height + 10.0
|
||||
}
|
||||
}
|
||||
|
||||
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + tableSize.height + actionsHeight + valueDeltaHeight + 40.0 + insets.top + insets.bottom)
|
||||
transition.updateFrame(node: self.actionNodesSeparator, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
|
||||
var actionOffset: CGFloat = 0.0
|
||||
|
|
@ -380,14 +424,14 @@ private final class GiftOfferAlertContentNode: AlertContentNode {
|
|||
if separatorIndex >= 0 {
|
||||
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||
do {
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight + actionOffset - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
}
|
||||
separatorIndex += 1
|
||||
|
||||
let currentActionWidth: CGFloat
|
||||
do {
|
||||
currentActionWidth = resultSize.width
|
||||
currentActionWidth = resultSize.width
|
||||
}
|
||||
|
||||
let actionNodeFrame: CGRect
|
||||
|
|
@ -433,13 +477,13 @@ public func giftOfferAlertController(
|
|||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let strings = presentationData.strings
|
||||
|
||||
let title = "Confirm Sale"
|
||||
let buttonText: String = "Confirm Sale"
|
||||
let title = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Title
|
||||
let buttonText: String = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Confirm
|
||||
|
||||
let priceString: String
|
||||
switch amount.currency {
|
||||
case .stars:
|
||||
priceString = "\(amount.amount) Stars"
|
||||
priceString = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text_Stars(Int32(clamping: amount.amount.value))
|
||||
case .ton:
|
||||
priceString = "\(amount.amount) TON"
|
||||
}
|
||||
|
|
@ -449,15 +493,14 @@ public func giftOfferAlertController(
|
|||
switch amount.currency {
|
||||
case .stars:
|
||||
let starsValue = Int32(floor(Float(amount.amount.value) * Float(resaleConfiguration.starGiftCommissionStarsPermille) / 1000.0))
|
||||
finalPriceString = "\(starsValue) Stars"
|
||||
finalPriceString = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text_Stars(starsValue)
|
||||
case .ton:
|
||||
let tonValue = Int64(Float(amount.amount.value) * Float(resaleConfiguration.starGiftCommissionTonPermille) / 1000.0)
|
||||
finalPriceString = formatTonAmountText(tonValue, dateTimeFormat: presentationData.dateTimeFormat, maxDecimalPositions: 3) + " TON"
|
||||
}
|
||||
|
||||
let giftTitle = "\(gift.title) #\(formatCollectibleNumber(gift.number, dateTimeFormat: presentationData.dateTimeFormat))"
|
||||
let text = "Do you want to sell **\(giftTitle)** to \(peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)) for **\(priceString)**? You'll receive **\(finalPriceString)** after fees."
|
||||
|
||||
let text = strings.Chat_GiftPurchaseOffer_AcceptConfirmation_Text(giftTitle, peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), priceString, finalPriceString).string
|
||||
|
||||
var contentNode: GiftOfferAlertContentNode?
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
|
|
@ -468,7 +511,7 @@ public func giftOfferAlertController(
|
|||
dismissImpl?(true)
|
||||
})]
|
||||
|
||||
contentNode = GiftOfferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, actions: actions)
|
||||
contentNode = GiftOfferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: peer, title: title, text: text, amount: amount, actions: actions)
|
||||
|
||||
let controller = ChatMessagePaymentAlertController(context: context, presentationData: presentationData, contentNode: contentNode!, navigationController: nil, chatPeerId: context.account.peerId, showBalance: false)
|
||||
contentNode?.getController = { [weak controller] in
|
||||
|
|
|
|||
|
|
@ -94,6 +94,8 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
|
||||
private var attributeInfos: [ComponentView<Empty>] = []
|
||||
|
||||
private let topEdgeSolidView = UIView()
|
||||
private let topEdgeEffectView: EdgeEffectView
|
||||
private let segmentControl = ComponentView<Empty>()
|
||||
private let descriptionText = ComponentView<Empty>()
|
||||
|
||||
|
|
@ -146,6 +148,9 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
|
||||
self.navigationBarContainer = SparseContainerView()
|
||||
|
||||
self.topEdgeEffectView = EdgeEffectView()
|
||||
self.topEdgeEffectView.alpha = 0.0
|
||||
|
||||
self.glassContainerView = GlassBackgroundContainerView()
|
||||
|
||||
self.scrollView = ScrollView()
|
||||
|
|
@ -235,6 +240,8 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
var topOffsetFraction = self.scrollView.bounds.minY / 100.0
|
||||
topOffsetFraction = max(0.0, min(1.0, topOffsetFraction))
|
||||
|
||||
self.topEdgeEffectView.alpha = max(0.0, min(1.0, self.scrollView.bounds.minY / 8.0))
|
||||
|
||||
let minScale: CGFloat = (itemLayout.containerSize.width - 6.0 * 2.0) / itemLayout.containerSize.width
|
||||
let minScaledTranslation: CGFloat = (itemLayout.containerSize.height - itemLayout.containerSize.height * minScale) * 0.5 - 6.0
|
||||
let minScaledCornerRadius: CGFloat = itemLayout.containerCornerRadius
|
||||
|
|
@ -785,6 +792,14 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
let edgeEffectHeight: CGFloat = 44.0
|
||||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight + 44.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
|
||||
let edgeSolidFrame = CGRect(origin: CGPoint(x: 0.0, y: contentHeight), size: CGSize(width: availableSize.width, height: 44.0))
|
||||
transition.setFrame(view: self.topEdgeSolidView, frame: edgeSolidFrame)
|
||||
transition.setFrame(view: self.topEdgeEffectView, frame: edgeEffectFrame)
|
||||
self.topEdgeSolidView.backgroundColor = theme.list.blocksBackgroundColor
|
||||
self.topEdgeEffectView.update(content: theme.list.blocksBackgroundColor, blur: true, alpha: 1.0, rect: edgeEffectFrame, edge: .top, edgeSize: edgeEffectFrame.height, transition: transition)
|
||||
|
||||
contentHeight += 16.0
|
||||
|
||||
let segmentedSize = self.segmentControl.update(
|
||||
|
|
@ -792,9 +807,9 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
component: AnyComponent(SegmentControlComponent(
|
||||
theme: environment.theme,
|
||||
items: [
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.models), title: "Models"),
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.backdrops), title: "Backgrounds"),
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.symbols), title: "Symbols")
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.models), title: environment.strings.Gift_Variants_Models),
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.backdrops), title: environment.strings.Gift_Variants_Backdrops),
|
||||
SegmentControlComponent.Item(id: AnyHashable(SelectedSection.symbols), title: environment.strings.Gift_Variants_Symbols)
|
||||
],
|
||||
selectedId: "models",
|
||||
action: { [weak self] id in
|
||||
|
|
@ -805,7 +820,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
self.isPlaying = false
|
||||
|
||||
self.updateEffectiveGifts(attributes: component.attributes)
|
||||
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring))) //.withUserData(AnimationHint(value: .modeChanged)))
|
||||
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
})),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: fillingSize - 8.0 * 2.0, height: 100.0)
|
||||
|
|
@ -813,22 +828,30 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
let segmentedControlFrame = CGRect(origin: CGPoint(x: floor((fillingSize - segmentedSize.width) * 0.5), y: contentHeight), size: segmentedSize)
|
||||
if let segmentedControlComponentView = self.segmentControl.view {
|
||||
if segmentedControlComponentView.superview == nil {
|
||||
self.scrollContentView.addSubview(segmentedControlComponentView)
|
||||
self.navigationBarContainer.addSubview(self.topEdgeSolidView)
|
||||
self.navigationBarContainer.addSubview(self.topEdgeEffectView)
|
||||
self.navigationBarContainer.addSubview(segmentedControlComponentView)
|
||||
}
|
||||
transition.setFrame(view: segmentedControlComponentView, frame: segmentedControlFrame)
|
||||
}
|
||||
contentHeight += segmentedSize.height
|
||||
contentHeight += 18.0
|
||||
|
||||
let itemHeight: CGFloat = 126.0
|
||||
let itemSpacing: CGFloat = 10.0
|
||||
|
||||
let descriptionText: String
|
||||
let itemCount: Int32
|
||||
switch self.selectedSection {
|
||||
case .models:
|
||||
descriptionText = "This collection features **\(self.modelCount)** unique models."
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Model(self.modelCount)).string
|
||||
itemCount = self.modelCount
|
||||
case .backdrops:
|
||||
descriptionText = "This collection features **\(self.backdropCount)** unique backdrops."
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Backdrop(self.backdropCount)).string
|
||||
itemCount = self.backdropCount
|
||||
case .symbols:
|
||||
descriptionText = "This collection features **\(self.symbolCount)** unique symbols."
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Symbol(self.symbolCount)).string
|
||||
itemCount = self.symbolCount
|
||||
}
|
||||
|
||||
let descriptionFont = Font.regular(13.0)
|
||||
|
|
@ -839,7 +862,7 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
})
|
||||
|
||||
let descriptionSize = self.descriptionText.update(
|
||||
transition: transition,
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .markdown(text: descriptionText, attributes: descriptionMarkdownAttributes)
|
||||
)),
|
||||
|
|
@ -851,11 +874,13 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
if descriptionView.superview == nil {
|
||||
self.scrollContentView.addSubview(descriptionView)
|
||||
}
|
||||
transition.setFrame(view: descriptionView, frame: descriptionFrame)
|
||||
descriptionView.frame = descriptionFrame
|
||||
}
|
||||
contentHeight += descriptionSize.height
|
||||
contentHeight += 26.0
|
||||
|
||||
contentHeight += (itemHeight + itemSpacing) * ceil(CGFloat(itemCount) / 3.0)
|
||||
|
||||
if self.backgroundHandleView.image == nil {
|
||||
self.backgroundHandleView.image = generateStretchableFilledCircleImage(diameter: 5.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
|
|
@ -896,44 +921,20 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
if let closeButtonView = self.closeButton.view {
|
||||
if closeButtonView.superview == nil {
|
||||
self.navigationBarContainer.addSubview(self.glassContainerView)
|
||||
self.glassContainerView.contentView.addSubview(closeButtonView)
|
||||
self.navigationBarContainer.addSubview(closeButtonView)
|
||||
}
|
||||
transition.setFrame(view: closeButtonView, frame: closeButtonFrame)
|
||||
}
|
||||
|
||||
var playbackItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
var playbackSize = CGSize(width: 40.0, height: 40.0)
|
||||
if self.isPlaying {
|
||||
playbackItems.append(AnyComponentWithIdentity(id: "pause", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Media Gallery/PictureInPicturePause",
|
||||
tintColor: .white
|
||||
)
|
||||
)))
|
||||
} else {
|
||||
if self.showRandomizeTip {
|
||||
playbackItems.append(AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Randomize Traits", font: Font.semibold(17.0), textColor: .white)))
|
||||
)))
|
||||
playbackSize.width = 186.0
|
||||
}
|
||||
playbackItems.append(AnyComponentWithIdentity(id: "play", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Media Gallery/PlayButton",
|
||||
tintColor: .white
|
||||
)
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
let playbackButtonSize = self.playbackButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: playbackSize,
|
||||
size: nil,
|
||||
backgroundColor: buttonColor,
|
||||
isDark: false,
|
||||
state: .tintedGlass,
|
||||
component: AnyComponentWithIdentity(id: "content", component: AnyComponent(
|
||||
HStack(playbackItems, spacing: 1.0)
|
||||
PlayButtonComponent(isPlay: !self.isPlaying, title: !self.isPlaying && self.showRandomizeTip ? environment.strings.Gift_Variants_Randomize : nil)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
guard let self else {
|
||||
|
|
@ -962,20 +963,16 @@ private final class GiftUpgradePreviewScreenComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: playbackSize
|
||||
containerSize: CGSize(width: 160.0, height: 40.0)
|
||||
)
|
||||
let playbackButtonFrame = CGRect(origin: CGPoint(x: fillingSize - 16.0 - playbackButtonSize.width, y: 16.0), size: playbackButtonSize)
|
||||
if let playbackButtonView = self.playbackButton.view {
|
||||
if playbackButtonView.superview == nil {
|
||||
playbackButtonView.clipsToBounds = true
|
||||
self.glassContainerView.contentView.addSubview(playbackButtonView)
|
||||
}
|
||||
transition.setFrame(view: playbackButtonView, frame: playbackButtonFrame)
|
||||
}
|
||||
|
||||
//TODO:release
|
||||
contentHeight += 126.0 * 17.0
|
||||
|
||||
let containerInset: CGFloat = environment.statusBarHeight + 10.0
|
||||
contentHeight += environment.safeInsets.bottom
|
||||
|
||||
|
|
@ -1181,15 +1178,15 @@ private final class AttributeInfoComponent: Component {
|
|||
switch component.attribute {
|
||||
case let .model(name, _, rarityValue):
|
||||
title = name
|
||||
subtitle = "model"
|
||||
subtitle = component.strings.Gift_Variants_Model
|
||||
rarity = rarityValue
|
||||
case let .backdrop(name, _, _, _, _, _, rarityValue):
|
||||
title = name
|
||||
subtitle = "backdrop"
|
||||
subtitle = component.strings.Gift_Variants_Backdrop
|
||||
rarity = rarityValue
|
||||
case let .pattern(name, _, rarityValue):
|
||||
title = name
|
||||
subtitle = "symbol"
|
||||
subtitle = component.strings.Gift_Variants_Symbol
|
||||
rarity = rarityValue
|
||||
default:
|
||||
title = ""
|
||||
|
|
@ -1284,3 +1281,154 @@ private final class AttributeInfoComponent: Component {
|
|||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final class PlayButtonComponent: Component {
|
||||
let isPlay: Bool
|
||||
let title: String?
|
||||
|
||||
public init(
|
||||
isPlay: Bool,
|
||||
title: String?
|
||||
) {
|
||||
self.isPlay = isPlay
|
||||
self.title = title
|
||||
}
|
||||
|
||||
static func ==(lhs: PlayButtonComponent, rhs: PlayButtonComponent) -> Bool {
|
||||
if lhs.isPlay != rhs.isPlay {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private var component: PlayButtonComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
private let containerView = UIView()
|
||||
private let titleContainerView = UIView()
|
||||
private let title = ComponentView<Empty>()
|
||||
private let play = ComponentView<Empty>()
|
||||
private let pause = ComponentView<Empty>()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.containerView.clipsToBounds = true
|
||||
self.containerView.layer.cornerRadius = 20.0
|
||||
self.addSubview(self.containerView)
|
||||
|
||||
self.titleContainerView.clipsToBounds = true
|
||||
self.containerView.addSubview(self.titleContainerView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PlayButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.componentState = state
|
||||
|
||||
var contentSize = CGSize(width: 15.0, height: 21.0)
|
||||
|
||||
var titleSize = CGSize()
|
||||
if let titleString = component.title {
|
||||
titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: titleString, font: Font.semibold(17.0), textColor: .white)))),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: 9.0, y: 10.0), size: titleSize)
|
||||
if let titleView = self.title.view {
|
||||
titleView.alpha = 1.0
|
||||
if titleView.superview == nil {
|
||||
self.titleContainerView.addSubview(titleView)
|
||||
transition.animateAlpha(view: titleView, from: 0.0, to: 1.0)
|
||||
}
|
||||
titleView.frame = titleFrame
|
||||
}
|
||||
contentSize.width += titleSize.width + 4.0
|
||||
} else if let titleView = self.title.view {
|
||||
transition.setAlpha(view: titleView, alpha: 0.0, completion: { finished in
|
||||
if finished {
|
||||
titleView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
}
|
||||
transition.setFrame(view: self.titleContainerView, frame: CGRect(origin: .zero, size: CGSize(width: titleSize.width + 14.0, height: 40.0)))
|
||||
|
||||
if component.isPlay {
|
||||
let iconSize = self.play.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BundleIconComponent(name: "Media Gallery/PlayButton", tintColor: .white)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: contentSize.width - iconSize.width + 21.0, y: 5.0), size: iconSize)
|
||||
if let iconView = self.play.view {
|
||||
iconView.alpha = 1.0
|
||||
if iconView.superview == nil {
|
||||
self.containerView.addSubview(iconView)
|
||||
transition.animateAlpha(view: iconView, from: 0.0, to: 1.0)
|
||||
transition.animateScale(view: iconView, from: 0.01, to: 1.0)
|
||||
}
|
||||
transition.setFrame(view: iconView, frame: iconFrame)
|
||||
}
|
||||
} else if let iconView = self.play.view {
|
||||
transition.setAlpha(view: iconView, alpha: 0.0, completion: { finished in
|
||||
if finished {
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
transition.animateScale(view: iconView, from: 1.0, to: 0.01)
|
||||
}
|
||||
|
||||
if !component.isPlay {
|
||||
let iconSize = self.pause.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BundleIconComponent(name: "Media Gallery/PictureInPicturePause", tintColor: .white)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: contentSize.width - iconSize.width + 12.0 - UIScreenPixel, y: 13.0 - UIScreenPixel), size: iconSize)
|
||||
if let iconView = self.pause.view {
|
||||
iconView.alpha = 1.0
|
||||
if iconView.superview == nil {
|
||||
self.containerView.addSubview(iconView)
|
||||
transition.animateAlpha(view: iconView, from: 0.0, to: 1.0)
|
||||
transition.animateScale(view: iconView, from: 0.01, to: 1.0)
|
||||
}
|
||||
transition.setFrame(view: iconView, frame: iconFrame)
|
||||
}
|
||||
} else if let iconView = self.pause.view {
|
||||
transition.setAlpha(view: iconView, alpha: 0.0, completion: { finished in
|
||||
if finished {
|
||||
iconView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
transition.animateScale(view: iconView, from: 1.0, to: 0.01)
|
||||
}
|
||||
|
||||
let containerWidth: CGFloat = contentSize.width + 26.0
|
||||
let containerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((contentSize.width - containerWidth) / 2.0), y: floorToScreenPixels((contentSize.height - 40.0) / 2.0)), size: CGSize(width: containerWidth, height: 40.0))
|
||||
transition.setFrame(view: self.containerView, frame: containerFrame)
|
||||
|
||||
return contentSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ public final class GlassBarButtonComponent: Component {
|
|||
super.init(frame: frame)
|
||||
|
||||
self.containerView.isUserInteractionEnabled = false
|
||||
self.containerView.layer.rasterizationScale = UIScreenScale
|
||||
|
||||
self.addSubview(self.containerView)
|
||||
self.containerView.addSubview(self.genericContainerView)
|
||||
|
|
|
|||
|
|
@ -1449,6 +1449,14 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
|||
case .ton:
|
||||
break
|
||||
}
|
||||
} else if case .starGiftOffer = self.mode {
|
||||
switch currency {
|
||||
case .stars:
|
||||
text = presentationData.strings.Gift_Offer_GiftMinAmountToast_Text("\(presentationData.strings.Stars_Withdraw_Withdraw_ErrorMinimum_Stars(Int32(clamping: minAmount)))").string
|
||||
case .ton:
|
||||
let amountString = formatTonAmountText(minAmount, dateTimeFormat: presentationData.dateTimeFormat) + " TON"
|
||||
text = presentationData.strings.Gift_Offer_GiftMinAmountToast_Text(amountString).string
|
||||
}
|
||||
}
|
||||
|
||||
let resultController = UndoOverlayController(
|
||||
|
|
@ -2682,14 +2690,8 @@ private final class DurationMenuComponent: Component {
|
|||
self.itemViews[value] = itemView
|
||||
}
|
||||
|
||||
let repeatString: String
|
||||
let hours = value / 3600
|
||||
//TODO:localize
|
||||
if hours == 1 {
|
||||
repeatString = "1 hour"
|
||||
} else {
|
||||
repeatString = "\(hours) hours"
|
||||
}
|
||||
let repeatString: String = component.strings.Gift_Offer_Duration_Hours(Int32(hours))
|
||||
|
||||
let itemSize = itemView.update(
|
||||
transition: transition,
|
||||
|
|
|
|||
|
|
@ -2425,7 +2425,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
|
||||
switch buttonType {
|
||||
case 0:
|
||||
let _ = strongSelf.context.engine.payments.resolveStarGiftOffer(messageId: message.id, accept: false).startStandalone()
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
self.present(textAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, title: self.presentationData.strings.Chat_GiftPurchaseOffer_RejectConfirmation_Title, text: self.presentationData.strings.Chat_GiftPurchaseOffer_RejectConfirmation_Text(peer.compactDisplayTitle).string, actions: [
|
||||
TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Chat_GiftPurchaseOffer_RejectConfirmation_Reject, action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self.context.engine.payments.resolveStarGiftOffer(messageId: message.id, accept: false).startStandalone()
|
||||
})
|
||||
], parseMarkdown: true), in: .window(.root))
|
||||
})
|
||||
case 1:
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue