Update API [skip ci]

This commit is contained in:
Ilya Laktyushin 2024-12-27 09:16:41 +04:00
parent d64208df43
commit e3ee1dde7e
31 changed files with 1041 additions and 459 deletions

View File

@ -13469,15 +13469,21 @@ Sorry for the inconvenience.";
"Gift.View.Status.NonUnique" = "Non-Unique";
"Gift.View.Status.Upgrade" = "upgrade";
"Gift.View.DisplayedInfoHide" = "The gift is visible on your Page. [Hide >]()";
"Gift.View.HiddenInfoShow" = "This gift is hidden. Only you can see it. [Show >]()";
"Gift.Upgrade.Title" = "Upgrade Gift";
"Gift.Upgrade.IncludeTitle" = "Make Unique";
"Gift.Upgrade.Description" = "Turn your gift into a unique collectible that you can transfer or auction.";
"Gift.Upgrade.IncludeDescription" = "Let %@ turn your gift into a unique collectible.";
"Gift.Upgrade.Unique.Title" = "Unique";
"Gift.Upgrade.Unique.Description" = "Get a unique number, model backdrop and and symbol for your gift.";
"Gift.Upgrade.Unique.Description" = "Get a unique number, model, backdrop, and symbol for your gift.";
"Gift.Upgrade.Unique.IncludeDescription" = "The recipient will get a unique number, model, backdrop, and symbol for the gift.";
"Gift.Upgrade.Transferable.Title" = "Transferable";
"Gift.Upgrade.Transferable.Description" = "Send your upgraded gift to any of your friends on Telegram.";
"Gift.Upgrade.Transferable.IncludeDescription" = "The recipient will be able to send the gift to anyone Telegram.";
"Gift.Upgrade.Tradable.Title" = "Tradable";
"Gift.Upgrade.Tradable.Description" = "Sell or auction your gift on third-party NFT marketplaces.";
"Gift.Upgrade.Tradable.IncludeDescription" = "The recipient will be able to auction the gift on third-party NFT marketplaces.";
"Gift.Upgrade.Soon" = "SOON";
"Gift.Upgrade.AddName" = "Add sender's name";
"Gift.Upgrade.AddNameAndComment" = "Add sender's name and comment";
@ -13525,6 +13531,7 @@ Sorry for the inconvenience.";
"Gift.Transfer.Confirmation.Transfer" = "Transfer for";
"Gift.Transfer.Confirmation.TransferFree" = "Transfer";
"Gift.View.UpgradeForFree" = "Upgrade for Free";
"Gift.View.KeepUpgradeOrConvertDescription" = "You can keep this gift, upgrade it, or sell it for %@. [More About Stars >]()";
"PeerInfo.VerificationInfo.Bot" = "This bot is verified as official by the representatives of Telegram.";
@ -13537,8 +13544,21 @@ Sorry for the inconvenience.";
"Gift.Send.Upgrade" = "Make Unique for %@";
"Gift.Send.Upgrade.Info" = "Enable this to let %1$@ turn your gift into a unique collectible. [Learn More >]()";
"Notification.StarGift.Unpack" = "Unpack";
"Notification.StarGift.Model" = "Model";
"Notification.StarGift.Backdrop" = "Backdrop";
"Notification.StarGift.Symbol" = "Symbol";
"Notification.StarGift.Gift" = "gift";
"Notification.StarsGift.Upgrade" = "%@ turned the gift from you to a unique collectible";
"Notification.StarsGift.UpgradeYou" = "You turned the gift from %@ to a unique collectible";
"Notification.StarsGift.Transfer" = "%@ transferred you a unique collectible";
"Notification.StarsGift.TransferYou" = "You transferred a unique collectible";
"Notification.StarGift.Subtitle.Refunded" = "This gift cannot be converted to Stars because the payment related to it was refunded.";
"Notification.StarGift.Subtitle.Downgraded" = "This gift was downgraded because a request to refund the payment related to this gift was made, and the money was returned.";
"Gift.View.KeepOrUpgradeDescription" = "You can keep this gift or upgrade it.";

View File

@ -3107,7 +3107,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
var titleLeftOffset: CGFloat = 0.0
if let currentVerifiedIconContent {
if titleLeftOffset.isZero, currentVerifiedIconContent != .none {
if titleLeftOffset.isZero, case .animation = currentVerifiedIconContent {
titleLeftOffset += 20.0
}
@ -3127,10 +3127,6 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
if let currentCredibilityIconContent {
if titleLeftOffset.isZero, case .verified = currentCredibilityIconContent {
titleLeftOffset += 20.0
}
if titleIconsWidth.isZero {
titleIconsWidth += 4.0
} else {
@ -4500,11 +4496,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
)
strongSelf.credibilityIconComponent = credibilityIconComponent
var iconOrigin: CGFloat = nextTitleIconOrigin
let iconOrigin: CGFloat = nextTitleIconOrigin
let containerSize = CGSize(width: 20.0, height: 20.0)
if case .verified = currentCredibilityIconContent {
iconOrigin = contentRect.origin.x
}
let iconSize = credibilityIconView.update(
transition: .immediate,
component: AnyComponent(credibilityIconComponent),
@ -4512,10 +4505,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
containerSize: containerSize
)
transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.maxY - lastLineRect.height * 0.5 - iconSize.height / 2.0) - UIScreenPixel), size: iconSize))
if case .verified = currentCredibilityIconContent {
} else {
nextTitleIconOrigin += credibilityIconView.bounds.width + 4.0
}
nextTitleIconOrigin += credibilityIconView.bounds.width + 4.0
} else if let credibilityIconView = strongSelf.credibilityIconView {
strongSelf.credibilityIconView = nil
credibilityIconView.removeFromSuperview()
@ -4541,7 +4531,12 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
)
strongSelf.verifiedIconComponent = verifiedIconComponent
let iconOrigin = contentRect.origin.x
let iconOrigin: CGFloat
if case .animation = currentVerifiedIconContent {
iconOrigin = contentRect.origin.x
} else {
iconOrigin = nextTitleIconOrigin
}
let containerSize = CGSize(width: 16.0, height: 16.0)
let iconSize = verifiedIconView.update(

View File

@ -582,77 +582,6 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
view.cornerRadius = layer.cornerRadius
view.backgroundColor = layer.backgroundColor
view.layerTintColor = layer.layerTintColor
/*
open var path: CGPath?
/* The color to fill the path, or nil for no fill. Defaults to opaque
* black. Animatable. */
open var fillColor: CGColor?
/* The fill rule used when filling the path. Options are `non-zero' and
* `even-odd'. Defaults to `non-zero'. */
open var fillRule: CAShapeLayerFillRule
/* The color to fill the path's stroked outline, or nil for no stroking.
* Defaults to nil. Animatable. */
open var strokeColor: CGColor?
/* These values define the subregion of the path used to draw the
* stroked outline. The values must be in the range [0,1] with zero
* representing the start of the path and one the end. Values in
* between zero and one are interpolated linearly along the path
* length. strokeStart defaults to zero and strokeEnd to one. Both are
* animatable. */
open var strokeStart: CGFloat
open var strokeEnd: CGFloat
/* The line width used when stroking the path. Defaults to one.
* Animatable. */
open var lineWidth: CGFloat
/* The miter limit used when stroking the path. Defaults to ten.
* Animatable. */
open var miterLimit: CGFloat
/* The cap style used when stroking the path. Options are `butt', `round'
* and `square'. Defaults to `butt'. */
open var lineCap: CAShapeLayerLineCap
/* The join style used when stroking the path. Options are `miter', `round'
* and `bevel'. Defaults to `miter'. */
open var lineJoin: CAShapeLayerLineJoin
/* The phase of the dashing pattern applied when creating the stroke.
* Defaults to zero. Animatable. */
open var lineDashPhase: CGFloat
/* The dash pattern (an array of NSNumbers) applied when creating the
* stroked version of the path. Defaults to nil. */
open var lineDashPattern: [NSNumber]?
*/
view.path = layer.path
view.fillColor = layer.fillColor
view.fillRule = layer.fillRule
@ -666,6 +595,40 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
view.lineDashPhase = layer.lineDashPhase
view.lineDashPattern = layer.lineDashPattern
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.transform = sublayer.transform
subtree.position = sublayer.position
subtree.bounds = sublayer.bounds
subtree.anchorPoint = sublayer.anchorPoint
view.addSublayer(subtree)
} else {
return nil
}
}
}
return view
} else if let layer = layer as? CAGradientLayer {
let view = CAGradientLayer()
view.isHidden = layer.isHidden
view.opacity = layer.opacity
view.contents = layer.contents
view.contentsRect = layer.contentsRect
view.contentsScale = layer.contentsScale
view.contentsCenter = layer.contentsCenter
view.contentsGravity = layer.contentsGravity
view.masksToBounds = layer.masksToBounds
view.cornerRadius = layer.cornerRadius
view.backgroundColor = layer.backgroundColor
view.layerTintColor = layer.layerTintColor
view.colors = layer.colors
view.locations = layer.locations
view.startPoint = layer.startPoint
view.endPoint = layer.endPoint
view.type = layer.type
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)

View File

@ -47,6 +47,7 @@ final class DrawingMetalView: MTKView {
super.init(frame: CGRect(origin: .zero, size: size), device: device)
self.drawableSize = self.size
self.colorPixelFormat = .bgra8Unorm
self.autoResizeDrawable = false
self.isOpaque = false
self.contentScaleFactor = 1.0
@ -123,7 +124,7 @@ final class DrawingMetalView: MTKView {
let pipelineDescription = MTLRenderPipelineDescriptor()
pipelineDescription.vertexFunction = vertexFunction
pipelineDescription.fragmentFunction = fragmentFunction
pipelineDescription.colorAttachments[0].pixelFormat = colorPixelFormat
pipelineDescription.colorAttachments[0].pixelFormat = self.colorPixelFormat
do {
self.pipelineState = try self.device?.makeRenderPipelineState(descriptor: pipelineDescription)
@ -250,6 +251,7 @@ private class Drawable {
attachment?.texture = self.texture?.texture
attachment?.loadAction = .load
attachment?.storeAction = .store
attachment?.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
self.updateBuffer(with: size)
}
@ -288,7 +290,6 @@ private class Drawable {
return commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
}
internal func commit(wait: Bool = false) {
self.commandBuffer?.commit()
if wait {
@ -673,9 +674,9 @@ final class Texture {
origin: MTLOrigin(x: 0, y: 0, z: 0),
size: MTLSize(width: self.width, height: self.height, depth: 1)
)
let data = Data(capacity: Int(self.bytesPerRow * self.height))
if let bytes = data.withUnsafeBytes({ $0.baseAddress }) {
self.texture.replace(region: region, mipmapLevel: 0, withBytes: bytes, bytesPerRow: self.bytesPerRow)
let zeroData = [UInt8](repeating: 0, count: self.bytesPerRow * self.height)
zeroData.withUnsafeBytes { bytes in
self.texture.replace(region: region, mipmapLevel: 0, withBytes: bytes.baseAddress!, bytesPerRow: self.bytesPerRow)
}
}

View File

@ -583,7 +583,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1434950843] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) }
dict[1348510708] = { return Api.MessageAction.parse_messageActionSetChatWallPaper($0) }
dict[1007897979] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
dict[1785072017] = { return Api.MessageAction.parse_messageActionStarGift($0) }
dict[-655036249] = { return Api.MessageAction.parse_messageActionStarGift($0) }
dict[638024601] = { return Api.MessageAction.parse_messageActionStarGiftUnique($0) }
dict[1474192222] = { return Api.MessageAction.parse_messageActionSuggestProfilePhoto($0) }
dict[228168278] = { return Api.MessageAction.parse_messageActionTopicCreate($0) }

View File

@ -373,7 +373,7 @@ public extension Api {
case messageActionSetChatTheme(emoticon: String)
case messageActionSetChatWallPaper(flags: Int32, wallpaper: Api.WallPaper)
case messageActionSetMessagesTTL(flags: Int32, period: Int32, autoSettingFrom: Int64?)
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeStars: Int64?)
case messageActionStarGift(flags: Int32, gift: Api.StarGift, message: Api.TextWithEntities?, convertStars: Int64?, upgradeMsgId: Int32?, upgradeStars: Int64?)
case messageActionStarGiftUnique(flags: Int32, gift: Api.StarGift, canExportAt: Int32?, transferStars: Int64?)
case messageActionSuggestProfilePhoto(photo: Api.Photo)
case messageActionTopicCreate(flags: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?)
@ -720,14 +720,15 @@ public extension Api {
serializeInt32(period, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(autoSettingFrom!, buffer: buffer, boxed: false)}
break
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeStars):
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars):
if boxed {
buffer.appendInt32(1785072017)
buffer.appendInt32(-655036249)
}
serializeInt32(flags, buffer: buffer, boxed: false)
gift.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {message!.serialize(buffer, true)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt64(convertStars!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 5) != 0 {serializeInt32(upgradeMsgId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(upgradeStars!, buffer: buffer, boxed: false)}
break
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars):
@ -864,8 +865,8 @@ public extension Api {
return ("messageActionSetChatWallPaper", [("flags", flags as Any), ("wallpaper", wallpaper as Any)])
case .messageActionSetMessagesTTL(let flags, let period, let autoSettingFrom):
return ("messageActionSetMessagesTTL", [("flags", flags as Any), ("period", period as Any), ("autoSettingFrom", autoSettingFrom as Any)])
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeStars):
return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeStars", upgradeStars as Any)])
case .messageActionStarGift(let flags, let gift, let message, let convertStars, let upgradeMsgId, let upgradeStars):
return ("messageActionStarGift", [("flags", flags as Any), ("gift", gift as Any), ("message", message as Any), ("convertStars", convertStars as Any), ("upgradeMsgId", upgradeMsgId as Any), ("upgradeStars", upgradeStars as Any)])
case .messageActionStarGiftUnique(let flags, let gift, let canExportAt, let transferStars):
return ("messageActionStarGiftUnique", [("flags", flags as Any), ("gift", gift as Any), ("canExportAt", canExportAt as Any), ("transferStars", transferStars as Any)])
case .messageActionSuggestProfilePhoto(let photo):
@ -1528,15 +1529,18 @@ public extension Api {
} }
var _4: Int64?
if Int(_1!) & Int(1 << 4) != 0 {_4 = reader.readInt64() }
var _5: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_5 = reader.readInt64() }
var _5: Int32?
if Int(_1!) & Int(1 << 5) != 0 {_5 = reader.readInt32() }
var _6: Int64?
if Int(_1!) & Int(1 << 8) != 0 {_6 = reader.readInt64() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 8) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeStars: _5)
let _c5 = (Int(_1!) & Int(1 << 5) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 8) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.MessageAction.messageActionStarGift(flags: _1!, gift: _2!, message: _3, convertStars: _4, upgradeMsgId: _5, upgradeStars: _6)
}
else {
return nil

View File

@ -9463,6 +9463,25 @@ public extension Api.functions.payments {
})
}
}
public extension Api.functions.payments {
static func getUserStarGift(msgId: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.UserStarGifts>) {
let buffer = Buffer()
buffer.appendInt32(-1258101595)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(msgId.count))
for item in msgId {
serializeInt32(item, buffer: buffer, boxed: false)
}
return (FunctionDescription(name: "payments.getUserStarGift", parameters: [("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.UserStarGifts? in
let reader = BufferReader(buffer)
var result: Api.payments.UserStarGifts?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.payments.UserStarGifts
}
return result
})
}
}
public extension Api.functions.payments {
static func getUserStarGifts(userId: Api.InputUser, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.UserStarGifts>) {
let buffer = Buffer()

View File

@ -132,7 +132,6 @@ enum AccountStateMutationOperation {
case UpdateStarsBalance(peerId: PeerId, balance: Api.StarsAmount)
case UpdateStarsRevenueStatus(peerId: PeerId, status: StarsRevenueStats.Balances)
case UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool)
case UpdateUpgradedStarGift(from: Api.UserStarGift, to: Api.UserStarGift)
}
struct HoleFromPreviousState {
@ -702,14 +701,10 @@ struct AccountMutableState {
mutating func updateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool) {
self.addOperation(.UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: isAnonymous))
}
mutating func updateUpgradedStarGift(from: Api.UserStarGift, to: Api.UserStarGift) {
self.addOperation(.UpdateUpgradedStarGift(from: from, to: to))
}
mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault, .UpdateUpgradedStarGift:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault:
break
case let .AddMessages(messages, location):
for message in messages {
@ -856,7 +851,6 @@ struct AccountReplayedFinalState {
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
let updatedStarsBalance: [PeerId: StarsAmount]
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
let updatedUpgradedStarGifts:[(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)]
let sentScheduledMessageIds: Set<MessageId>
}
@ -888,13 +882,12 @@ struct AccountFinalStateEvents {
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
let updatedStarsBalance: [PeerId: StarsAmount]
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
let updatedUpgradedStarGifts: [(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)]
var isEmpty: Bool {
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedRevenueBalances.isEmpty && self.updatedStarsBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty && self.updatedUpgradedStarGifts.isEmpty
return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.sentScheduledMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.storyUpdates.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.dismissBotWebViews.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty && !self.updateConfig && !self.isPremiumUpdated && self.updatedRevenueBalances.isEmpty && self.updatedStarsBalance.isEmpty && self.updatedStarsRevenueStatus.isEmpty
}
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set(), updatedUpgradedStarGifts: [(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)] = []) {
init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, reaction: MessageReaction.Reaction, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], storyUpdates: [InternalStoryUpdate] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], dismissBotWebViews: [Int64] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set<PeerId> = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:], updateConfig: Bool = false, isPremiumUpdated: Bool = false, updatedRevenueBalances: [PeerId: RevenueStats.Balances] = [:], updatedStarsBalance: [PeerId: StarsAmount] = [:], updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:], sentScheduledMessageIds: Set<MessageId> = Set()) {
self.addedIncomingMessageIds = addedIncomingMessageIds
self.addedReactionEvents = addedReactionEvents
self.wasScheduledMessageIds = wasScheduledMessageIds
@ -921,7 +914,6 @@ struct AccountFinalStateEvents {
self.updatedRevenueBalances = updatedRevenueBalances
self.updatedStarsBalance = updatedStarsBalance
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
self.updatedUpgradedStarGifts = updatedUpgradedStarGifts
self.sentScheduledMessageIds = sentScheduledMessageIds
}
@ -952,7 +944,6 @@ struct AccountFinalStateEvents {
self.updatedRevenueBalances = state.updatedRevenueBalances
self.updatedStarsBalance = state.updatedStarsBalance
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
self.updatedUpgradedStarGifts = state.updatedUpgradedStarGifts
self.sentScheduledMessageIds = state.sentScheduledMessageIds
}
@ -986,6 +977,6 @@ struct AccountFinalStateEvents {
var sentScheduledMessageIds = self.sentScheduledMessageIds
sentScheduledMessageIds.formUnion(other.sentScheduledMessageIds)
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, storyUpdates: self.storyUpdates + other.storyUpdates, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated, updatedRevenueBalances: self.updatedRevenueBalances.merging(other.updatedRevenueBalances, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsBalance: self.updatedStarsBalance.merging(other.updatedStarsBalance, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsRevenueStatus: self.updatedStarsRevenueStatus.merging(other.updatedStarsRevenueStatus, uniquingKeysWith: { lhs, _ in lhs }), sentScheduledMessageIds: sentScheduledMessageIds, updatedUpgradedStarGifts: self.updatedUpgradedStarGifts + other.updatedUpgradedStarGifts)
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, storyUpdates: self.storyUpdates + other.storyUpdates, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated, updatedRevenueBalances: self.updatedRevenueBalances.merging(other.updatedRevenueBalances, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsBalance: self.updatedStarsBalance.merging(other.updatedStarsBalance, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsRevenueStatus: self.updatedStarsRevenueStatus.merging(other.updatedStarsRevenueStatus, uniquingKeysWith: { lhs, _ in lhs }), sentScheduledMessageIds: sentScheduledMessageIds)
}
}

View File

@ -171,7 +171,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
return TelegramMediaAction(action: .paymentRefunded(peerId: peer.peerId, currency: currency, totalAmount: totalAmount, payload: payload?.makeData(), transactionId: transactionId))
case let .messageActionPrizeStars(flags, stars, transactionId, boostPeer, giveawayMsgId):
return TelegramMediaAction(action: .prizeStars(amount: stars, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer.peerId, transactionId: transactionId, giveawayMessageId: MessageId(peerId: boostPeer.peerId, namespace: Namespaces.Message.Cloud, id: giveawayMsgId)))
case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeStars):
case let .messageActionStarGift(flags, apiGift, message, convertStars, _, upgradeStars):
let text: String?
let entities: [MessageTextEntity]?
switch message {
@ -185,12 +185,12 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
guard let gift = StarGift(apiStarGift: apiGift) else {
return nil
}
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, upgradeStars: upgradeStars))
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0))
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars):
guard let gift = StarGift(apiStarGift: apiGift) else {
return nil
}
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars))
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0))
}
}

View File

@ -3282,7 +3282,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
for operation in operations {
switch operation {
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault, .UpdateUpgradedStarGift:
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault:
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
}
@ -3421,7 +3421,6 @@ func replayFinalState(
var updatedStarsBalance: [PeerId: StarsAmount] = [:]
var updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:]
var updatedStarsReactionsAreAnonymousByDefault: Bool?
var updatedUpgradedStarGifts: [(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)] = []
var holesFromPreviousStateMessageIds: [MessageId] = []
var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
@ -4856,10 +4855,6 @@ func replayFinalState(
updatedStarsRevenueStatus[peerId] = status
case let .UpdateStarsReactionsAreAnonymousByDefault(value):
updatedStarsReactionsAreAnonymousByDefault = value
case let .UpdateUpgradedStarGift(from, to):
if let fromGift = ProfileGiftsContext.State.StarGift(apiUserStarGift: from, transaction: transaction), let toGift = ProfileGiftsContext.State.StarGift(apiUserStarGift: to, transaction: transaction) {
updatedUpgradedStarGifts.append((fromGift, toGift))
}
}
}
@ -5381,7 +5376,6 @@ func replayFinalState(
updatedRevenueBalances: updatedRevenueBalances,
updatedStarsBalance: updatedStarsBalance,
updatedStarsRevenueStatus: updatedStarsRevenueStatus,
updatedUpgradedStarGifts: updatedUpgradedStarGifts,
sentScheduledMessageIds: finalState.state.sentScheduledMessageIds
)
}

View File

@ -56,10 +56,6 @@ private final class UpdatedStarsRevenueStatusSubscriberContext {
let subscribers = Bag<([PeerId: StarsRevenueStats.Balances]) -> Void>()
}
private final class UpgradedStarGiftsSubscriberContext {
let subscribers = Bag<([(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)]) -> Void>()
}
public enum DeletedMessageId: Hashable {
case global(Int32)
case messageId(MessageId)
@ -350,7 +346,6 @@ public final class AccountStateManager {
private var updatedRevenueBalancesContext = UpdatedRevenueBalancesSubscriberContext()
private var updatedStarsBalanceContext = UpdatedStarsBalanceSubscriberContext()
private var updatedStarsRevenueStatusContext = UpdatedStarsRevenueStatusSubscriberContext()
private var upgradedStarGiftsContext = UpgradedStarGiftsSubscriberContext()
private let delayNotificatonsUntil = Atomic<Int32?>(value: nil)
private let appliedMaxMessageIdPromise = Promise<Int32?>(nil)
@ -1113,9 +1108,6 @@ public final class AccountStateManager {
if !events.updatedStarsRevenueStatus.isEmpty {
strongSelf.notifyUpdatedStarsRevenueStatus(events.updatedStarsRevenueStatus)
}
if !events.updatedUpgradedStarGifts.isEmpty {
strongSelf.notifyUpgradedStarGifts(events.updatedUpgradedStarGifts)
}
if !events.updatedCalls.isEmpty {
for call in events.updatedCalls {
strongSelf.callSessionManager?.updateSession(call, completion: { _ in })
@ -1774,34 +1766,7 @@ public final class AccountStateManager {
subscriber(updatedStarsRevenueStatus)
}
}
public func upgradedStarGifts() -> Signal<[(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)], NoError> {
let queue = self.queue
return Signal { [weak self] subscriber in
let disposable = MetaDisposable()
queue.async {
if let strongSelf = self {
let index = strongSelf.upgradedStarGiftsContext.subscribers.add({ upgradedGifts in
subscriber.putNext(upgradedGifts)
})
disposable.set(ActionDisposable {
if let strongSelf = self {
strongSelf.upgradedStarGiftsContext.subscribers.remove(index)
}
})
}
}
return disposable
}
}
private func notifyUpgradedStarGifts(_ upgradedStarGifts: [(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)]) {
for subscriber in self.upgradedStarGiftsContext.subscribers.copyItems() {
subscriber(upgradedStarGifts)
}
}
func notifyDeletedMessages(messageIds: [MessageId]) {
self.deletedMessagesPipe.putNext(messageIds.map { .messageId($0) })
}
@ -2143,12 +2108,6 @@ public final class AccountStateManager {
}
}
public func upgradedStarGifts() -> Signal<[(ProfileGiftsContext.State.StarGift, ProfileGiftsContext.State.StarGift)], NoError> {
return self.impl.signalWith { impl, subscriber in
return impl.upgradedStarGifts().start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)
}
}
func addCustomOperation<T, E>(_ f: Signal<T, E>) -> Signal<T, E> {
return self.impl.signalWith { impl, subscriber in
return impl.addCustomOperation(f).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion)

View File

@ -137,6 +137,7 @@ public struct Namespaces {
public static let recommendedApps: Int8 = 40
public static let starsReactionDefaultToPrivate: Int8 = 41
public static let cachedPremiumGiftCodeOptions: Int8 = 42
public static let cachedProfileGifts: Int8 = 43
}
public struct UnorderedItemList {

View File

@ -130,8 +130,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String)
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?)
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, upgradeStars: Int64?)
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?)
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool)
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool)
public init(decoder: PostboxDecoder) {
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
@ -253,9 +253,9 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
}
self = .prizeStars(amount: decoder.decodeInt64ForKey("amount", orElse: 0), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: boostPeerId, transactionId: decoder.decodeOptionalStringForKey("transactionId"), giveawayMessageId: giveawayMessageId)
case 44:
self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"))
self = .starGift(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, convertStars: decoder.decodeOptionalInt64ForKey("convertStars"), text: decoder.decodeOptionalStringForKey("text"), entities: decoder.decodeOptionalObjectArrayWithDecoderForKey("entities"), nameHidden: decoder.decodeBoolForKey("nameHidden", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), converted: decoder.decodeBoolForKey("converted", orElse: false), upgraded: decoder.decodeBoolForKey("upgraded", orElse: false), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false))
case 45:
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"))
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false))
default:
self = .unknown
}
@ -548,7 +548,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
} else {
encoder.encodeNil(forKey: "giveawayMsgId")
}
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, upgradeStars):
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded):
encoder.encodeInt32(44, forKey: "_rawValue")
encoder.encodeObject(gift, forKey: "gift")
if let convertStars {
@ -567,12 +567,14 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
encoder.encodeBool(savedToProfile, forKey: "savedToProfile")
encoder.encodeBool(converted, forKey: "converted")
encoder.encodeBool(upgraded, forKey: "upgraded")
encoder.encodeBool(canUpgrade, forKey: "canUpgrade")
if let upgradeStars {
encoder.encodeInt64(upgradeStars, forKey: "upgradeStars")
} else {
encoder.encodeNil(forKey: "upgradeStars")
}
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars):
encoder.encodeBool(isRefunded, forKey: "isRefunded")
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded):
encoder.encodeInt32(45, forKey: "_rawValue")
encoder.encodeObject(gift, forKey: "gift")
encoder.encodeBool(isUpgrade, forKey: "isUpgrade")
@ -588,6 +590,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
} else {
encoder.encodeNil(forKey: "transferStars")
}
encoder.encodeBool(isRefunded, forKey: "isRefunded")
}
}

View File

@ -707,8 +707,17 @@ public enum UpgradeStarGiftError {
case generic
}
func _internal_upgradeStarGift(account: Account, prepaid: Bool, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
if prepaid {
func _internal_upgradeStarGift(account: Account, formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
if let formId {
let source: BotPaymentInvoiceSource = .starGiftUpgrade(keepOriginalInfo: keepOriginalInfo, messageId: messageId)
return _internal_sendStarsPaymentForm(account: account, formId: formId, source: source)
|> mapError { _ -> UpgradeStarGiftError in
return .generic
}
|> mapToSignal { _ in
return .complete()
}
} else {
var flags: Int32 = 0
if keepOriginalInfo {
flags |= (1 << 0)
@ -719,57 +728,35 @@ func _internal_upgradeStarGift(account: Account, prepaid: Bool, messageId: Engin
}
|> mapToSignal { updates in
account.stateManager.addUpdates(updates)
return account.stateManager.upgradedStarGifts()
|> castError(UpgradeStarGiftError.self)
|> take(until: { updates in
for update in updates {
if update.0.messageId == messageId {
return .init(passthrough: true, complete: true)
}
}
return .init(passthrough: false, complete: false)
})
|> mapToSignal { updates in
for update in updates {
if update.0.messageId == messageId {
return .single(update.1)
}
}
return .complete()
}
}
} else {
let source: BotPaymentInvoiceSource = .starGiftUpgrade(keepOriginalInfo: keepOriginalInfo, messageId: messageId)
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|> mapError { _ -> UpgradeStarGiftError in
return .generic
}
|> mapToSignal { paymentForm in
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|> mapError { _ -> UpgradeStarGiftError in
return .generic
}
|> mapToSignal { _ in
return account.stateManager.upgradedStarGifts()
|> castError(UpgradeStarGiftError.self)
|> take(until: { updates in
for update in updates {
if update.0.messageId == messageId {
return .init(passthrough: true, complete: true)
for update in updates.allUpdates {
switch update {
case let .updateNewMessage(message, _, _):
if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: false) {
for media in message.media {
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _) = action.action, case let .Id(messageId) = message.id {
return .single(ProfileGiftsContext.State.StarGift(
gift: gift,
fromPeer: nil,
date: message.timestamp,
text: nil,
entities: nil,
messageId: messageId,
nameHidden: false,
savedToProfile: savedToProfile,
convertStars: nil,
canUpgrade: false,
canExportDate: canExportDate,
upgradeStars: nil,
transferStars: transferStars
))
}
}
}
return .init(passthrough: false, complete: false)
})
|> mapToSignal { updates in
for update in updates {
if update.0.messageId == messageId {
return .single(update.1)
}
}
return .complete()
default:
break
}
}
return .fail(.generic)
}
}
}
@ -791,7 +778,49 @@ func _internal_starGiftUpgradePreview(account: Account, giftId: Int64) -> Signal
}
}
private var cachedAccountGifts: [EnginePeer.Id: [ProfileGiftsContext.State.StarGift]] = [:]
private final class CachedProfileGifts: Codable {
enum CodingKeys: String, CodingKey {
case gifts
case count
}
var gifts: [ProfileGiftsContext.State.StarGift]
let count: Int32
init(gifts: [ProfileGiftsContext.State.StarGift], count: Int32) {
self.gifts = gifts
self.count = count
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.gifts = try container.decode([ProfileGiftsContext.State.StarGift].self, forKey: .gifts)
self.count = try container.decode(Int32.self, forKey: .count)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.gifts, forKey: .gifts)
try container.encode(self.count, forKey: .count)
}
func render(transaction: Transaction) {
for i in 0 ..< self.gifts.count {
let gift = self.gifts[i]
if gift.fromPeer == nil, let fromPeerId = gift._fromPeerId, let peer = transaction.getPeer(fromPeerId) {
self.gifts[i] = gift.withFromPeer(EnginePeer(peer))
}
}
}
}
private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId {
let cacheKey = ValueBoxKey(length: 8)
cacheKey.setInt64(0, value: peerId.toInt64())
return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedProfileGifts, key: cacheKey)
}
private final class ProfileGiftsContextImpl {
private let queue: Queue
@ -799,6 +828,7 @@ private final class ProfileGiftsContextImpl {
private let peerId: PeerId
private let disposable = MetaDisposable()
private let cacheDisposable = MetaDisposable()
private let actionDisposable = MetaDisposable()
private var gifts: [ProfileGiftsContext.State.StarGift] = []
@ -821,22 +851,37 @@ private final class ProfileGiftsContextImpl {
deinit {
self.disposable.dispose()
self.cacheDisposable.dispose()
self.actionDisposable.dispose()
}
func loadMore() {
let peerId = self.peerId
let accountPeerId = self.account.peerId
let network = self.account.network
let postbox = self.account.postbox
if case let .ready(true, initialNextOffset) = self.dataState {
if self.gifts.isEmpty, self.peerId == self.account.peerId, let cachedGifts = cachedAccountGifts[self.peerId] {
self.gifts = cachedGifts
if self.gifts.isEmpty, initialNextOffset == nil {
self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in
let cachedGifts = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileGifts.self)
cachedGifts?.render(transaction: transaction)
return cachedGifts
} |> deliverOn(self.queue)).start(next: { [weak self] cachedGifts in
guard let self, let cachedGifts else {
return
}
if case .loading = self.dataState {
self.gifts = cachedGifts.gifts
self.count = cachedGifts.count
self.pushState()
}
}))
}
self.dataState = .loading
self.pushState()
let peerId = self.peerId
let accountPeerId = self.account.peerId
let network = self.account.network
let postbox = self.account.postbox
let signal: Signal<([ProfileGiftsContext.State.StarGift], Int32, String?), NoError> = self.account.postbox.transaction { transaction -> Api.InputUser? in
return transaction.getPeer(peerId).flatMap(apiInputUser)
}
@ -871,10 +916,15 @@ private final class ProfileGiftsContextImpl {
guard let strongSelf = self else {
return
}
if initialNextOffset == nil, strongSelf.peerId == strongSelf.account.peerId {
cachedAccountGifts[strongSelf.peerId] = gifts
if initialNextOffset == nil {
strongSelf.gifts = gifts
} else {
strongSelf.cacheDisposable.set(strongSelf.account.postbox.transaction { transaction in
if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count)) {
transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry)
}
}.start())
} else {
for gift in gifts {
strongSelf.gifts.append(gift)
}
@ -920,9 +970,14 @@ private final class ProfileGiftsContextImpl {
self.pushState()
}
func upgradeStarGift(prepaid: Bool, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
self.actionDisposable.set(
_internal_upgradeStarGift(account: self.account, prepaid: prepaid, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict()
_internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict(next: { [weak self] result in
guard let self else {
return
}
let _ = self
})
)
self.pushState()
}
@ -935,7 +990,23 @@ private final class ProfileGiftsContextImpl {
public final class ProfileGiftsContext {
public struct State: Equatable {
public struct StarGift: Equatable {
public struct StarGift: Equatable, Codable {
enum CodingKeys: String, CodingKey {
case gift
case fromPeerId
case date
case text
case entities
case messageId
case nameHidden
case savedToProfile
case convertStars
case canUpgrade
case canExportDate
case upgradeStars
case transferStars
}
public let gift: TelegramCore.StarGift
public let fromPeer: EnginePeer?
public let date: Int32
@ -950,6 +1021,76 @@ public final class ProfileGiftsContext {
public let upgradeStars: Int64?
public let transferStars: Int64?
fileprivate let _fromPeerId: EnginePeer.Id?
public init (
gift: TelegramCore.StarGift,
fromPeer: EnginePeer?,
date: Int32,
text: String?,
entities: [MessageTextEntity]?,
messageId: EngineMessage.Id?,
nameHidden: Bool,
savedToProfile: Bool,
convertStars: Int64?,
canUpgrade: Bool,
canExportDate: Int32?,
upgradeStars: Int64?,
transferStars: Int64?
) {
self.gift = gift
self.fromPeer = fromPeer
self._fromPeerId = fromPeer?.id
self.date = date
self.text = text
self.entities = entities
self.messageId = messageId
self.nameHidden = nameHidden
self.savedToProfile = savedToProfile
self.convertStars = convertStars
self.canUpgrade = canUpgrade
self.canExportDate = canExportDate
self.upgradeStars = upgradeStars
self.transferStars = transferStars
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.gift = try container.decode(TelegramCore.StarGift.self, forKey: .gift)
self.fromPeer = nil
self._fromPeerId = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .fromPeerId)
self.date = try container.decode(Int32.self, forKey: .date)
self.text = try container.decodeIfPresent(String.self, forKey: .text)
self.entities = try container.decodeIfPresent([MessageTextEntity].self, forKey: .entities)
self.messageId = try container.decodeIfPresent(EngineMessage.Id.self, forKey: .messageId)
self.nameHidden = try container.decode(Bool.self, forKey: .nameHidden)
self.savedToProfile = try container.decode(Bool.self, forKey: .savedToProfile)
self.convertStars = try container.decodeIfPresent(Int64.self, forKey: .convertStars)
self.canUpgrade = try container.decode(Bool.self, forKey: .canUpgrade)
self.canExportDate = try container.decodeIfPresent(Int32.self, forKey: .canExportDate)
self.upgradeStars = try container.decodeIfPresent(Int64.self, forKey: .upgradeStars)
self.transferStars = try container.decodeIfPresent(Int64.self, forKey: .transferStars)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.gift, forKey: .gift)
try container.encodeIfPresent(self.fromPeer?.id, forKey: .fromPeerId)
try container.encode(self.date, forKey: .date)
try container.encodeIfPresent(self.text, forKey: .text)
try container.encodeIfPresent(self.entities, forKey: .entities)
try container.encodeIfPresent(self.messageId, forKey: .messageId)
try container.encode(self.nameHidden, forKey: .nameHidden)
try container.encode(self.savedToProfile, forKey: .savedToProfile)
try container.encodeIfPresent(self.convertStars, forKey: .convertStars)
try container.encode(self.canUpgrade, forKey: .canUpgrade)
try container.encodeIfPresent(self.canExportDate, forKey: .canExportDate)
try container.encodeIfPresent(self.upgradeStars, forKey: .upgradeStars)
try container.encodeIfPresent(self.transferStars, forKey: .transferStars)
}
public func withSavedToProfile(_ savedToProfile: Bool) -> StarGift {
return StarGift(
gift: self.gift,
@ -967,6 +1108,24 @@ public final class ProfileGiftsContext {
transferStars: self.transferStars
)
}
fileprivate func withFromPeer(_ fromPeer: EnginePeer?) -> StarGift {
return StarGift(
gift: self.gift,
fromPeer: fromPeer,
date: self.date,
text: self.text,
entities: self.entities,
messageId: self.messageId,
nameHidden: self.nameHidden,
savedToProfile: savedToProfile,
convertStars: self.convertStars,
canUpgrade: self.canUpgrade,
canExportDate: self.canExportDate,
upgradeStars: self.upgradeStars,
transferStars: self.transferStars
)
}
}
public enum DataState: Equatable {
@ -1027,9 +1186,9 @@ public final class ProfileGiftsContext {
}
}
public func upgradeStarGift(prepaid: Bool, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
self.impl.with { impl in
impl.upgradeStarGift(prepaid: prepaid, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
impl.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
}
}
@ -1055,6 +1214,7 @@ extension ProfileGiftsContext.State.StarGift {
} else {
self.fromPeer = nil
}
self._fromPeerId = self.fromPeer?.id
self.date = date
if let message {
@ -1081,7 +1241,7 @@ extension ProfileGiftsContext.State.StarGift {
self.nameHidden = (flags & (1 << 0)) != 0
self.savedToProfile = (flags & (1 << 5)) == 0
self.convertStars = convertStars
self.canUpgrade = (flags & (1 << 6)) != 0
self.canUpgrade = (flags & (1 << 10)) != 0
self.canExportDate = canExportDate
self.upgradeStars = upgradeStars
self.transferStars = transferStars

View File

@ -125,8 +125,8 @@ public extension TelegramEngine {
return _internal_transferStarGift(account: self.account, prepaid: prepaid, messageId: messageId, peerId: peerId)
}
public func upgradeStarGift(prepaid: Bool, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
return _internal_upgradeStarGift(account: self.account, prepaid: prepaid, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
return _internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
}
public func starGiftUpgradePreview(giftId: Int64) -> Signal<[StarGift.UniqueGift.Attribute], NoError> {

View File

@ -1066,7 +1066,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = mutableString
case .prizeStars:
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
case let .starGift(gift, _, text, entities, _, _, _, _, _):
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _):
if !forAdditionalServiceMessage {
if let text {
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
@ -1075,7 +1075,11 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = NSAttributedString(string: strings.Notification_Gift, font: titleFont, textColor: primaryTextColor)
}
} else if case let .generic(gift) = gift {
let starsPrice = strings.Notification_StarsGift_Stars(Int32(gift.price))
var finalPrice = gift.price
if let upgradeStars {
finalPrice += upgradeStars
}
let starsPrice = strings.Notification_StarsGift_Stars(Int32(finalPrice))
var authorName = compactAuthorName
var peerIds: [(Int, EnginePeer.Id?)] = [(0, message.author?.id)]
if message.id.peerId.namespace == Namespaces.Peer.CloudUser && message.id.peerId.id._internalGetInt64Value() == 777000 {
@ -1090,7 +1094,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
}
}
case let .starGiftUnique(gift, isUpgrade, isTransferred, _, _, _):
case let .starGiftUnique(gift, isUpgrade, isTransferred, _, _, _, _):
if case let .unique(gift) = gift {
if !forAdditionalServiceMessage {
attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor)

View File

@ -32,6 +32,7 @@ swift_library(
"//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon",
"//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/InvisibleInkDustNode",
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
],
visibility = [
"//visibility:public",

View File

@ -3,6 +3,7 @@ import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import ComponentFlow
import TelegramCore
import AccountContext
import TelegramPresentationData
@ -22,6 +23,7 @@ import ChatMessageBubbleContentNode
import ChatMessageItemCommon
import TextNodeWithEntities
import InvisibleInkDustNode
import PeerInfoCoverComponent
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? {
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true)
@ -33,6 +35,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private let backgroundMaskNode: ASImageNode
private var linkHighlightingNode: LinkHighlightingNode?
private let patternView = ComponentView<Empty>()
private let mediaBackgroundMaskNode: ASImageNode
private var mediaBackgroundContent: WallpaperBubbleBackgroundNode?
private let titleNode: TextNode
@ -43,6 +46,13 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private let placeholderNode: StickerShimmerEffectNode
private let animationNode: AnimatedStickerNode
private let modelTitleTextNode: TextNode
private let modelValueTextNode: TextNode
private let backdropTitleTextNode: TextNode
private let backdropValueTextNode: TextNode
private let symbolTitleTextNode: TextNode
private let symbolValueTextNode: TextNode
private let ribbonBackgroundNode: ASImageNode
private let ribbonTextNode: TextNode
@ -50,6 +60,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
private let buttonNode: HighlightTrackingButtonNode
private let buttonStarsNode: PremiumStarsNode
private let buttonTitleNode: TextNode
private let buttonIconNode: ASImageNode
private let moreTextNode: TextNode
@ -120,6 +131,25 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.textClippingNode = ASDisplayNode()
self.textClippingNode.clipsToBounds = true
self.modelTitleTextNode = TextNode()
self.modelTitleTextNode.isUserInteractionEnabled = false
self.modelTitleTextNode.displaysAsynchronously = false
self.modelValueTextNode = TextNode()
self.modelValueTextNode.isUserInteractionEnabled = false
self.modelValueTextNode.displaysAsynchronously = false
self.backdropTitleTextNode = TextNode()
self.backdropTitleTextNode.isUserInteractionEnabled = false
self.backdropTitleTextNode.displaysAsynchronously = false
self.backdropValueTextNode = TextNode()
self.backdropValueTextNode.isUserInteractionEnabled = false
self.backdropValueTextNode.displaysAsynchronously = false
self.symbolTitleTextNode = TextNode()
self.symbolTitleTextNode.isUserInteractionEnabled = false
self.symbolTitleTextNode.displaysAsynchronously = false
self.symbolValueTextNode = TextNode()
self.symbolValueTextNode.isUserInteractionEnabled = false
self.symbolValueTextNode.displaysAsynchronously = false
self.buttonNode = HighlightTrackingButtonNode()
self.buttonNode.clipsToBounds = true
self.buttonNode.cornerRadius = 17.0
@ -136,6 +166,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.buttonTitleNode.isUserInteractionEnabled = false
self.buttonTitleNode.displaysAsynchronously = false
self.buttonIconNode = ASImageNode()
self.buttonIconNode.displaysAsynchronously = false
self.buttonIconNode.isUserInteractionEnabled = false
self.ribbonBackgroundNode = ASImageNode()
self.ribbonBackgroundNode.displaysAsynchronously = false
@ -161,6 +195,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
self.addSubnode(self.buttonNode)
self.buttonNode.addSubnode(self.buttonStarsNode)
self.buttonNode.addSubnode(self.buttonTitleNode)
self.buttonNode.addSubnode(self.buttonIconNode)
self.addSubnode(self.ribbonBackgroundNode)
self.addSubnode(self.ribbonTextNode)
@ -292,6 +327,13 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let makeRibbonTextLayout = TextNode.asyncLayout(self.ribbonTextNode)
let makeMeasureTextLayout = TextNode.asyncLayout(nil)
let makeMoreTextLayout = TextNode.asyncLayout(self.moreTextNode)
let makeModelTitleLayout = TextNode.asyncLayout(self.modelTitleTextNode)
let makeModelValueLayout = TextNode.asyncLayout(self.modelValueTextNode)
let makeBackdropTitleLayout = TextNode.asyncLayout(self.backdropTitleTextNode)
let makeBackdropValueLayout = TextNode.asyncLayout(self.backdropValueTextNode)
let makeSymbolTitleLayout = TextNode.asyncLayout(self.symbolTitleTextNode)
let makeSymbolValueLayout = TextNode.asyncLayout(self.symbolValueTextNode)
let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage
@ -303,7 +345,12 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in
var giftSize = CGSize(width: 220.0, height: 240.0)
let incoming = item.message.effectivelyIncoming(item.context.account.peerId)
let incoming: Bool
if item.message.id.peerId == item.context.account.peerId && item.message.forwardInfo == nil {
incoming = true
} else {
incoming = item.message.effectivelyIncoming(item.context.account.peerId)
}
let attributedString = attributedServiceMessageString(theme: item.presentationData.theme, strings: item.presentationData.strings, nameDisplayOrder: item.presentationData.nameDisplayOrder, dateTimeFormat: item.presentationData.dateTimeFormat, message: EngineMessage(item.message), accountPeerId: item.context.account.peerId)
@ -314,12 +361,26 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
var animationFile: TelegramMediaFile?
var title = item.presentationData.strings.Notification_PremiumGift_Title
var text = ""
var subtitleColor = primaryTextColor
var entities: [MessageTextEntity] = []
var buttonTitle = item.presentationData.strings.Notification_PremiumGift_View
var buttonIcon: String?
var ribbonTitle = ""
var hasServiceMessage = true
var textSpacing: CGFloat = 0.0
var isStarGift = false
var modelTitle: String?
var modelValue: String?
var backdropTitle: String?
var backdropValue: String?
var symbolTitle: String?
var symbolValue: String?
var uniqueBackgroundColor: UIColor?
var uniqueSecondBackgroundColor: UIColor?
var uniquePatternColor: UIColor?
var uniquePatternFile: TelegramMediaFile?
for media in item.message.media {
if let action = media as? TelegramMediaAction {
switch action.action {
@ -406,7 +467,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
hasServiceMessage = false
}
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, _, _):
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, _, upgradeStars, isRefunded):
if case let .generic(gift) = gift {
isStarGift = true
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
@ -415,7 +476,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
text = giftText
entities = giftEntities ?? []
} else {
if incoming {
if isRefunded {
text = item.presentationData.strings.Notification_StarGift_Subtitle_Refunded
} else if incoming {
if converted {
text = item.presentationData.strings.Notification_StarGift_Subtitle_Converted(item.presentationData.strings.Notification_StarGift_Subtitle_Converted_Stars(Int32(convertStars ?? 0))).string
} else if savedToProfile {
@ -461,26 +524,49 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
ribbonTitle = item.presentationData.strings.Notification_StarGift_OneOf(availabilityString).string
}
if incoming {
buttonTitle = item.presentationData.strings.Notification_StarGift_View
if incoming, let upgradeStars, upgradeStars > 0, !upgraded {
buttonTitle = item.presentationData.strings.Notification_StarGift_Unpack
buttonIcon = "Premium/GiftUnpack"
} else {
buttonTitle = ""
buttonTitle = item.presentationData.strings.Notification_StarGift_View
}
}
case let .starGiftUnique(gift, _, _, _, _, _):
case let .starGiftUnique(gift, _, _, _, _, _, isRefunded):
if case let .unique(uniqueGift) = gift {
isStarGift = true
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
text = "**\(uniqueGift.title) #\(uniqueGift.number)**"
ribbonTitle = item.presentationData.strings.Notification_StarGift_Gift
buttonTitle = item.presentationData.strings.Notification_StarGift_View
modelTitle = item.presentationData.strings.Notification_StarGift_Model
backdropTitle = item.presentationData.strings.Notification_StarGift_Backdrop
symbolTitle = item.presentationData.strings.Notification_StarGift_Symbol
for attribute in uniqueGift.attributes {
if case let .model(_, file, _) = attribute {
switch attribute {
case let .model(name, file, _):
modelValue = name
animationFile = file
case let .backdrop(name, innerColor, outerColor, patternColor, _, _):
uniqueBackgroundColor = UIColor(rgb: UInt32(bitPattern: outerColor))
uniqueSecondBackgroundColor = UIColor(rgb: UInt32(bitPattern: innerColor))
uniquePatternColor = UIColor(rgb: UInt32(bitPattern: patternColor))
backdropValue = name
subtitleColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
case let .pattern(name, file, _):
symbolValue = name
uniquePatternFile = file
default:
break
}
}
//TODO:localize
ribbonTitle = "gift"
buttonTitle = item.presentationData.strings.Notification_StarGift_View
} else if isRefunded, case let .generic(gift) = gift {
isStarGift = true
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
text = item.presentationData.strings.Notification_StarGift_Subtitle_Refunded
animationFile = gift.file
}
default:
break
@ -510,9 +596,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
attributedText = stringWithAppliedEntities(text, entities: entities, baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.monospace(13.0), blockQuoteFont: Font.regular(13.0), message: nil)
} else {
attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: primaryTextColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: primaryTextColor),
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: subtitleColor),
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: subtitleColor),
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: subtitleColor),
linkAttribute: { url in
return ("URL", url)
}
@ -533,12 +619,57 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
clippedTextHeight = measuredTextLayout.size.height
}
}
let infoConstrainedSize = CGSize(width: (giftSize.width - 32.0) * 0.7, height: CGFloat.greatestFiniteMagnitude)
let modelTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let modelTitle {
modelTitleLayoutAndApply = makeModelTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: modelTitle, font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
modelTitleLayoutAndApply = nil
}
let modelValueLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let modelValue {
modelValueLayoutAndApply = makeModelValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: modelValue, font: Font.semibold(13.0), textColor: primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
modelValueLayoutAndApply = nil
}
let backdropTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let backdropTitle {
backdropTitleLayoutAndApply = makeBackdropTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: backdropTitle, font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
backdropTitleLayoutAndApply = nil
}
let backdropValueLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let backdropValue {
backdropValueLayoutAndApply = makeBackdropValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: backdropValue, font: Font.semibold(13.0), textColor: primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
backdropValueLayoutAndApply = nil
}
let symbolTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let symbolTitle {
symbolTitleLayoutAndApply = makeSymbolTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: symbolTitle, font: Font.regular(13.0), textColor: subtitleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
symbolTitleLayoutAndApply = nil
}
let symbolValueLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let symbolValue {
symbolValueLayoutAndApply = makeSymbolValueLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: symbolValue, font: Font.semibold(13.0), textColor: primaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: infoConstrainedSize, alignment: .center, cutout: nil, insets: UIEdgeInsets()))
} else {
symbolValueLayoutAndApply = nil
}
let (buttonTitleLayout, buttonTitleApply) = makeButtonTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: buttonTitle, font: Font.semibold(15.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
let (ribbonTextLayout, ribbonTextApply) = makeRibbonTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: ribbonTitle, font: Font.semibold(11.0), textColor: primaryTextColor, paragraphAlignment: .center), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: giftSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets()))
giftSize.height = titleLayout.size.height + textSpacing + clippedTextHeight + 164.0
if let _ = modelTitle {
giftSize.height += 70.0
}
if !buttonTitle.isEmpty {
giftSize.height += 48.0
}
@ -614,6 +745,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.buttonNode.isHidden = buttonTitle.isEmpty
strongSelf.buttonTitleNode.isHidden = buttonTitle.isEmpty
strongSelf.buttonIconNode.isHidden = buttonIcon == nil
if strongSelf.item == nil {
strongSelf.animationNode.started = { [weak self] in
@ -677,7 +809,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
strongSelf.titleNode.frame = titleFrame
let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0) , y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight))
let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight))
let subtitleFrame = CGRect(origin: .zero, size: subtitleLayout.size)
strongSelf.subtitleNode.textNode.frame = subtitleFrame
@ -727,19 +859,97 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.dustNode = nil
}
let buttonSize = CGSize(width: buttonTitleLayout.size.width + 38.0, height: 34.0)
strongSelf.buttonTitleNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((buttonSize.width - buttonTitleLayout.size.width) / 2.0), y: 8.0), size: buttonTitleLayout.size)
var middleX = mediaBackgroundFrame.width / 2.0
if let (modelValueLayout, _) = modelValueLayoutAndApply, let (backdropValueLayout, _) = backdropValueLayoutAndApply, let (symbolValueLayout, _) = symbolValueLayoutAndApply {
let maxWidth = max(modelValueLayout.size.width, max(backdropValueLayout.size.width, symbolValueLayout.size.width))
middleX = min(mediaBackgroundFrame.width - maxWidth - 16.0, middleX)
}
let titleMaxX: CGFloat = mediaBackgroundFrame.minX + middleX - 2.0
let valueMinX: CGFloat = mediaBackgroundFrame.minX + middleX + 3.0
if let (modelTitleLayout, modelTitleApply) = modelTitleLayoutAndApply {
if strongSelf.modelTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.modelTitleTextNode)
}
let _ = modelTitleApply()
strongSelf.modelTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - modelTitleLayout.size.width, y: clippingTextFrame.maxY + 10.0), size: modelTitleLayout.size)
}
animation.animator.updateFrame(layer: strongSelf.buttonNode.layer, frame: CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonSize.width) / 2.0), y: clippingTextFrame.maxY + 10.0), size: buttonSize), completion: nil)
if let (modelValueLayout, modelValueApply) = modelValueLayoutAndApply {
if strongSelf.modelValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.modelValueTextNode)
}
let _ = modelValueApply()
strongSelf.modelValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 10.0), size: modelValueLayout.size)
}
if let (backdropTitleLayout, backdropTitleApply) = backdropTitleLayoutAndApply {
if strongSelf.backdropTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.backdropTitleTextNode)
}
let _ = backdropTitleApply()
strongSelf.backdropTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - backdropTitleLayout.size.width, y: clippingTextFrame.maxY + 32.0), size: backdropTitleLayout.size)
}
if let (backdropValueLayout, backdropValueApply) = backdropValueLayoutAndApply {
if strongSelf.backdropValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.backdropValueTextNode)
}
let _ = backdropValueApply()
strongSelf.backdropValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 32.0), size: backdropValueLayout.size)
}
if let (symbolTitleLayout, symbolTitleApply) = symbolTitleLayoutAndApply {
if strongSelf.symbolTitleTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.symbolTitleTextNode)
}
let _ = symbolTitleApply()
strongSelf.symbolTitleTextNode.frame = CGRect(origin: CGPoint(x: titleMaxX - symbolTitleLayout.size.width, y: clippingTextFrame.maxY + 54.0), size: symbolTitleLayout.size)
}
if let (symbolValueLayout, symbolValueApply) = symbolValueLayoutAndApply {
if strongSelf.symbolValueTextNode.supernode == nil {
strongSelf.addSubnode(strongSelf.symbolValueTextNode)
}
let _ = symbolValueApply()
strongSelf.symbolValueTextNode.frame = CGRect(origin: CGPoint(x: valueMinX, y: clippingTextFrame.maxY + 54.0), size: symbolValueLayout.size)
}
var buttonSize = CGSize(width: buttonTitleLayout.size.width + 38.0, height: 34.0)
var buttonOriginY = clippingTextFrame.maxY + 10.0
if modelTitleLayoutAndApply != nil {
buttonOriginY = clippingTextFrame.maxY + 80.0
}
if let buttonIcon {
buttonSize.width += 15.0
if strongSelf.buttonIconNode.image == nil {
strongSelf.buttonIconNode.image = generateTintedImage(image: UIImage(bundleImageName: buttonIcon), color: .white)
}
}
strongSelf.buttonTitleNode.frame = CGRect(origin: CGPoint(x: 19.0, y: 8.0), size: buttonTitleLayout.size)
strongSelf.buttonIconNode.frame = CGRect(origin: CGPoint(x: buttonSize.width - 30.0, y: 9.0), size: CGSize(width: 14.0, height: 14.0))
animation.animator.updateFrame(layer: strongSelf.buttonNode.layer, frame: CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - buttonSize.width) / 2.0), y: buttonOriginY), size: buttonSize), completion: nil)
strongSelf.buttonStarsNode.frame = CGRect(origin: .zero, size: buttonSize)
if ribbonTextLayout.size.width > 0.0 {
if strongSelf.ribbonBackgroundNode.image == nil {
let ribbonImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/GiftRibbon"), color: overlayColor)
strongSelf.ribbonBackgroundNode.image = ribbonImage
if let uniqueBackgroundColor {
let colors = [
uniqueBackgroundColor.withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89),
uniqueBackgroundColor.withMultiplied(hue: 1.01, saturation: 1.22, brightness: 1.04)
]
strongSelf.ribbonBackgroundNode.image = generateGradientTintedImage(image: UIImage(bundleImageName: "Premium/GiftRibbon"), colors: colors, direction: .mirroredDiagonal)
} else {
strongSelf.ribbonBackgroundNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/GiftRibbon"), color: overlayColor)
}
}
if let ribbonImage = strongSelf.ribbonBackgroundNode.image {
let ribbonFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.maxX - ribbonImage.size.width + 2.0, y: mediaBackgroundFrame.minY - 2.0), size: ribbonImage.size)
var ribbonFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.maxX - ribbonImage.size.width + 2.0, y: mediaBackgroundFrame.minY - 2.0), size: ribbonImage.size)
if let _ = uniqueBackgroundColor {
ribbonFrame = ribbonFrame.offsetBy(dx: -4.0, dy: 4.0)
}
strongSelf.ribbonBackgroundNode.frame = ribbonFrame
strongSelf.ribbonTextNode.transform = CATransform3DMakeRotation(.pi / 4.0, 0.0, 0.0, 1.0)
@ -757,7 +967,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
if let backgroundContent = strongSelf.mediaBackgroundContent {
if ribbonTextLayout.size.width > 0.0 {
if ribbonTextLayout.size.width > 0.0, uniqueBackgroundColor == nil {
let backgroundMaskFrame = mediaBackgroundFrame.insetBy(dx: -2.0, dy: -2.0)
backgroundContent.frame = backgroundMaskFrame
animation.animator.updateFrame(layer: backgroundContent.layer, frame: backgroundMaskFrame, completion: nil)
@ -787,6 +997,36 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
}
}
if let uniqueBackgroundColor, let uniqueSecondBackgroundColor, let uniquePatternColor, let uniquePatternFile {
let patternInset: CGFloat = 4.0
let patternSize = CGSize(width: mediaBackgroundFrame.width - patternInset * 2.0, height: mediaBackgroundFrame.height - patternInset * 2.0)
let files: [Int64: TelegramMediaFile] = [uniquePatternFile.fileId.id: uniquePatternFile]
let _ = strongSelf.patternView.update(
transition: .immediate,
component: AnyComponent(PeerInfoCoverComponent(
context: item.context,
subject: .custom(uniqueBackgroundColor, uniqueSecondBackgroundColor, uniquePatternColor, uniquePatternFile.fileId.id),
files: files,
isDark: false,
avatarCenter: CGPoint(x: patternSize.width / 2.0, y: 106.0),
avatarScale: 1.0,
defaultHeight: patternSize.height,
avatarTransitionFraction: 0.0,
patternTransitionFraction: 0.0
)),
environment: {},
containerSize: patternSize
)
if let backgroundView = strongSelf.patternView.view {
if backgroundView.superview == nil {
backgroundView.layer.cornerRadius = 20.0
backgroundView.clipsToBounds = true
strongSelf.view.insertSubview(backgroundView, belowSubview: strongSelf.titleNode.view)
}
backgroundView.frame = CGRect(origin: .zero, size: patternSize).offsetBy(dx: mediaBackgroundFrame.minX + patternInset, dy: mediaBackgroundFrame.minY + patternInset)
}
}
let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
if let (offset, image) = backgroundMaskImage {
if strongSelf.backgroundNode == nil {

View File

@ -909,7 +909,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var titleFrame: CGRect
if size.height > 40.0 {
var titleInsets: UIEdgeInsets = .zero
if verifiedIconWidth > 0.0 {
if case .emojiStatus = self.titleVerifiedIcon, verifiedIconWidth > 0.0 {
titleInsets.left = verifiedIconWidth + 2.0
}
@ -951,7 +951,18 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
var nextIconX: CGFloat = titleFrame.width
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)
var verifiedIconX: CGFloat
if case .emojiStatus = self.titleVerifiedIcon {
verifiedIconX = 0.0
} else {
verifiedIconX = nextIconX - titleVerifiedSize.width
}
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: verifiedIconX, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize)
if case .emojiStatus = self.titleVerifiedIcon {
} else {
nextIconX -= titleVerifiedSize.width
}
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/Components/ViewControllerComponent",
"//submodules/Components/BundleIconComponent",
"//submodules/Components/MultilineTextComponent",
"//submodules/Components/MultilineTextWithEntitiesComponent",
"//submodules/Components/BalancedTextComponent",
"//submodules/TelegramUI/Components/ListSectionComponent",
"//submodules/TelegramUI/Components/ListActionItemComponent",
@ -45,6 +46,7 @@ swift_library(
"//submodules/InAppPurchaseManager",
"//submodules/Components/BlurredBackgroundComponent",
"//submodules/ProgressNavigationButtonNode",
"//submodules/TelegramUI/Components/Gifts/GiftViewScreen",
],
visibility = [
"//visibility:public",

View File

@ -143,6 +143,9 @@ final class ChatGiftPreviewItem: ListViewItem, ItemListItem, ListItemComponentAd
if lhs.entities != rhs.entities {
return false
}
if lhs.includeUpgrade != rhs.includeUpgrade {
return false
}
return true
}
}
@ -224,7 +227,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
case let .starGift(gift):
media = [
TelegramMediaAction(
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, upgradeStars: item.includeUpgrade ? 0 : gift.upgradeStars)
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, canUpgrade: true, upgradeStars: item.includeUpgrade ? gift.upgradeStars : 0, isRefunded: false)
)
]
}

View File

@ -13,6 +13,7 @@ import AccountContext
import ComponentFlow
import ViewControllerComponent
import MultilineTextComponent
import MultilineTextWithEntitiesComponent
import BalancedTextComponent
import ListSectionComponent
import ListActionItemComponent
@ -32,6 +33,7 @@ import InAppPurchaseManager
import BlurredBackgroundComponent
import ProgressNavigationButtonNode
import Markdown
import GiftViewScreen
final class GiftSetupScreenComponent: Component {
typealias EnvironmentType = ViewControllerComponentContainer.Environment
@ -131,6 +133,7 @@ final class GiftSetupScreenComponent: Component {
}
}
private let optionsPromise = ValuePromise<[StarsTopUpOption]?>(nil)
private let previewPromise = Promise<[StarGift.UniqueGift.Attribute]?>(nil)
private var cachedChevronImage: (UIImage, PresentationTheme)?
@ -578,7 +581,7 @@ final class GiftSetupScreenComponent: Component {
}
)
if case .starGift = component.subject {
if case let .starGift(gift) = component.subject {
self.optionsDisposable = (component.context.engine.payments.starsTopUpOptions()
|> deliverOnMainQueue).start(next: { [weak self] options in
guard let self else {
@ -586,6 +589,13 @@ final class GiftSetupScreenComponent: Component {
}
self.options = options
})
if let _ = gift.upgradeStars {
self.previewPromise.set(
component.context.engine.payments.starGiftUpgradePreview(giftId: gift.id)
|> map(Optional.init)
)
}
}
}
@ -845,6 +855,13 @@ final class GiftSetupScreenComponent: Component {
upgradeFooterText.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: upgradeFooterText.string))
}
let upgradeAttributedText = NSMutableAttributedString(string: environment.strings.Gift_Send_Upgrade("#\(upgradeStars)").string, font: Font.regular(presentationData.listsFontSize.baseDisplaySize), textColor: environment.theme.list.itemPrimaryTextColor)
let range = (upgradeAttributedText.string as NSString).range(of: "#")
if range.location != NSNotFound {
upgradeAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: range)
upgradeAttributedText.addAttribute(.baselineOffset, value: 1.0, range: range)
}
let upgradeSectionSize = self.upgradeSection.update(
transition: transition,
component: AnyComponent(ListSectionComponent(
@ -852,20 +869,47 @@ final class GiftSetupScreenComponent: Component {
header: nil,
footer: AnyComponent(MultilineTextComponent(
text: .plain(upgradeFooterText),
maximumNumberOfLines: 0
maximumNumberOfLines: 0,
highlightColor: environment.theme.list.itemAccentColor.withAlphaComponent(0.1),
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
highlightAction: { attributes in
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
} else {
return nil
}
},
tapAction: { [weak self] _, _ in
guard let self else {
return
}
let _ = (self.previewPromise.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] attributes in
guard let self, let component = self.component, let controller = self.environment?.controller(), let attributes else {
return
}
let previewController = GiftViewScreen(
context: component.context,
subject: .upgradePreview(attributes, peerName)
)
controller.push(previewController)
})
}
)),
items: [
AnyComponentWithIdentity(id: 0, component: AnyComponent(ListActionItemComponent(
theme: environment.theme,
title: AnyComponent(VStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.Gift_Send_Upgrade("\(upgradeStars)").string,
font: Font.regular(presentationData.listsFontSize.baseDisplaySize),
textColor: environment.theme.list.itemPrimaryTextColor
)),
maximumNumberOfLines: 1
))),
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(
MultilineTextWithEntitiesComponent(
context: component.context,
animationCache: component.context.animationCache,
animationRenderer: component.context.animationRenderer,
placeholderColor: environment.theme.list.mediaPlaceholderColor,
text: .plain(upgradeAttributedText)
)
)),
], alignment: .left, spacing: 2.0)),
accessory: .toggle(ListActionItemComponent.Toggle(style: .regular, isOn: self.includeUpgrade, action: { [weak self] _ in
guard let self else {
@ -982,7 +1026,11 @@ final class GiftSetupScreenComponent: Component {
let amountString = product.price
buttonString = "\(environment.strings.Gift_Send_Send) \(amountString)"
case let .starGift(starGift):
let amountString = presentationStringsFormattedNumber(Int32(starGift.price), presentationData.dateTimeFormat.groupingSeparator)
var finalPrice: Int64 = starGift.price
if self.includeUpgrade, let upgradePrice = starGift.upgradeStars {
finalPrice += upgradePrice
}
let amountString = presentationStringsFormattedNumber(Int32(finalPrice), presentationData.dateTimeFormat.groupingSeparator)
buttonString = "\(environment.strings.Gift_Send_Send) # \(amountString)"
if let availability = starGift.availability, availability.remains == 0 {
buttonIsEnabled = false

View File

@ -14,6 +14,13 @@ import AnimatedStickerNode
import TelegramAnimatedStickerNode
final class GiftCompositionComponent: Component {
public class ExternalState {
public fileprivate(set) var previewPatternColor: UIColor?
public init() {
self.previewPatternColor = nil
}
}
enum Subject: Equatable {
case generic(TelegramMediaFile)
case unique(StarGift.UniqueGift)
@ -23,15 +30,21 @@ final class GiftCompositionComponent: Component {
let context: AccountContext
let theme: PresentationTheme
let subject: Subject
let externalState: ExternalState
let requestUpdate: () -> Void
init(
context: AccountContext,
theme: PresentationTheme,
subject: Subject
subject: Subject,
externalState: ExternalState,
requestUpdate: @escaping () -> Void
) {
self.context = context
self.theme = theme
self.subject = subject
self.externalState = externalState
self.requestUpdate = requestUpdate
}
static func ==(lhs: GiftCompositionComponent, rhs: GiftCompositionComponent) -> Bool {
@ -193,6 +206,8 @@ final class GiftCompositionComponent: Component {
self.previewTimer?.start()
}
}
component.externalState.previewPatternColor = secondBackgroundColor
var animateTransition = false
if self.animatePreviewTransition {
@ -200,6 +215,10 @@ final class GiftCompositionComponent: Component {
self.animatePreviewTransition = false
} else if let previousComponent, case .preview = previousComponent.subject, case .unique = component.subject {
animateTransition = true
} else if let previousComponent, case .generic = previousComponent.subject, case .preview = component.subject {
animateTransition = true
} else if let previousComponent, case .preview = previousComponent.subject, case .generic = component.subject {
animateTransition = true
}
if let backgroundColor {
@ -235,7 +254,7 @@ final class GiftCompositionComponent: Component {
backgroundTransition.setFrame(view: backgroundView, frame: CGRect(origin: .zero, size: availableSize))
}
} else if let backgroundView = self.background.view, backgroundView.superview != nil {
backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, completion: { _ in
backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
backgroundView.removeFromSuperview()
})
}
@ -264,7 +283,6 @@ final class GiftCompositionComponent: Component {
if let startFromIndex {
animationNode.play(firstFrame: false, fromIndex: startFromIndex)
//animationNode.seekTo(.frameIndex(startFromIndex))
} else {
animationNode.playLoop()
}
@ -276,31 +294,6 @@ final class GiftCompositionComponent: Component {
}
}
}
// if self.animationLayer == nil, let animationFile {
// let emoji = ChatTextInputTextCustomEmojiAttribute(
// interactivelySelectedFromPackId: nil,
// fileId: animationFile.fileId.id,
// file: animationFile
// )
//
// let animationLayer = InlineStickerItemLayer(
// context: .account(component.context),
// userLocation: .other,
// attemptSynchronousLoad: false,
// emoji: emoji,
// file: animationFile,
// cache: component.context.animationCache,
// renderer: component.context.animationRenderer,
// unique: true,
// placeholderColor: component.theme.list.mediaPlaceholderColor,
// pointSize: CGSize(width: iconSize.width * 1.2, height: iconSize.height * 1.2),
// loopCount: 1
// )
// animationLayer.isVisibleForAnimations = true
// self.animationLayer = animationLayer
// self.layer.addSublayer(animationLayer)
// }
if let animationNode = self.animationNode {
transition.setFrame(layer: animationNode.layer, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: 25.0), size: iconSize))
}

View File

@ -51,7 +51,7 @@ private final class GiftViewSheetContent: CombinedComponent {
init(
context: AccountContext,
subject: GiftViewScreen.Subject,
cancel: @escaping (Bool) -> Void,
cancel: @escaping (Bool) -> Void,
openPeer: @escaping (EnginePeer) -> Void,
updateSavedToProfile: @escaping (Bool) -> Void,
convertToStars: @escaping () -> Void,
@ -108,11 +108,13 @@ private final class GiftViewSheetContent: CombinedComponent {
var inProgress = false
var inUpgradePreview = false
var upgradeForm: BotPaymentForm?
var upgradeDisposable: Disposable?
var sampleGiftAttributes: [StarGift.UniqueGift.Attribute]?
var sampleDisposable: Disposable?
let sampleDisposable = DisposableSet()
var keepOriginalInfo = false
var upgradeDisposable: Disposable?
private var optionsDisposable: Disposable?
private(set) var options: [StarsTopUpOption] = [] {
@ -151,15 +153,40 @@ private final class GiftViewSheetContent: CombinedComponent {
break
}
}
} else if case let .generic(gift) = arguments.gift, let _ = arguments.upgradeStars {
self.sampleDisposable = (context.engine.payments.starGiftUpgradePreview(giftId: gift.id)
|> deliverOnMainQueue).start(next: { [weak self] attributes in
guard let self else {
return
} else if case let .generic(gift) = arguments.gift {
if arguments.canUpgrade || arguments.upgradeStars != nil {
self.sampleDisposable.add((context.engine.payments.starGiftUpgradePreview(giftId: gift.id)
|> deliverOnMainQueue).start(next: { [weak self] attributes in
guard let self else {
return
}
self.sampleGiftAttributes = attributes
for attribute in attributes {
switch attribute {
case let .model(_, file, _):
self.sampleDisposable.add(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
case let .pattern(_, file, _):
self.sampleDisposable.add(freeMediaFileResourceInteractiveFetched(account: self.context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start())
default:
break
}
}
self.updated()
}))
if arguments.upgradeStars == nil, let messageId = arguments.messageId {
self.upgradeDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftUpgrade(keepOriginalInfo: false, messageId: messageId), themeParams: nil)
|> deliverOnMainQueue).start(next: { [weak self] paymentForm in
guard let self else {
return
}
self.upgradeForm = paymentForm
self.updated()
})
}
self.sampleGiftAttributes = attributes
self.updated()
})
}
}
self.disposable = combineLatest(queue: Queue.mainQueue(),
context.engine.data.get(EngineDataMap(
@ -209,7 +236,7 @@ private final class GiftViewSheetContent: CombinedComponent {
deinit {
self.disposable?.dispose()
self.sampleDisposable?.dispose()
self.sampleDisposable.dispose()
self.upgradeDisposable?.dispose()
}
@ -217,20 +244,22 @@ private final class GiftViewSheetContent: CombinedComponent {
guard let _ = self.subject.arguments?.upgradeStars else {
return
}
self.context.starsContext?.load(force: false)
self.inUpgradePreview = true
self.updated(transition: .spring(duration: 0.4))
}
func commitUpgrade() {
guard let arguments = self.subject.arguments, let messageId = arguments.messageId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState, let upgradeStars = arguments.upgradeStars else {
guard let arguments = self.subject.arguments, let messageId = arguments.messageId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
return
}
let peerId = arguments.peerId
let proceed: (Bool) -> Void = { prepaid in
let proceed: (Int64?) -> Void = { formId in
self.inProgress = true
self.updated()
let _ = (self.context.engine.payments.upgradeStarGift(prepaid: prepaid, messageId: messageId, keepOriginalInfo: self.keepOriginalInfo)
let _ = (self.context.engine.payments.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: self.keepOriginalInfo)
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let self, let controller = self.getController() as? GiftViewScreen else {
return
@ -245,8 +274,10 @@ private final class GiftViewSheetContent: CombinedComponent {
})
}
if upgradeStars > 0 {
if starsState.balance < StarsAmount(value: upgradeStars, nanos: 0) {
if let upgradeStars = arguments.upgradeStars, upgradeStars > 0 {
proceed(nil)
} else if let upgradeForm = self.upgradeForm, let price = upgradeForm.invoice.prices.first?.amount {
if starsState.balance < StarsAmount(value: price, nanos: 0) {
let _ = (self.optionsPromise.get()
|> filter { $0 != nil }
|> take(1)
@ -258,21 +289,19 @@ private final class GiftViewSheetContent: CombinedComponent {
context: self.context,
starsContext: starsContext,
options: options ?? [],
purpose: .upgradeStarGift(requiredStars: upgradeStars),
purpose: .upgradeStarGift(requiredStars: price),
completion: { [weak starsContext] stars in
starsContext?.add(balance: StarsAmount(value: stars, nanos: 0))
Queue.mainQueue().after(2.0) {
proceed(false)
proceed(upgradeForm.id)
}
}
)
controller.push(purchaseController)
})
} else {
proceed(false)
proceed(upgradeForm.id)
}
} else {
proceed(true)
}
}
}
@ -298,6 +327,8 @@ private final class GiftViewSheetContent: CombinedComponent {
let spaceRegex = try? NSRegularExpression(pattern: "\\[(.*?)\\]", options: [])
let giftCompositionExternalState = GiftCompositionComponent.ExternalState()
return { context in
let environment = context.environment[ViewControllerComponentContainer.Environment.self].value
@ -326,6 +357,8 @@ private final class GiftViewSheetContent: CombinedComponent {
var date: Int32?
var soldOut = false
var nameHidden = false
var upgraded = false
var canUpgrade = false
var upgradeStars: Int64?
var uniqueGift: StarGift.UniqueGift?
if case let .soldOutGift(gift) = subject {
@ -351,6 +384,8 @@ private final class GiftViewSheetContent: CombinedComponent {
converted = arguments.converted
giftId = gift.id
date = arguments.date
upgraded = arguments.upgraded
canUpgrade = arguments.canUpgrade
upgradeStars = arguments.upgradeStars
case let .unique(gift):
stars = 0
@ -387,11 +422,27 @@ private final class GiftViewSheetContent: CombinedComponent {
state.cachedOverlayCloseImage = closeOverlayImage
}
var showUpgradePreview = false
if state.inUpgradePreview, let _ = state.sampleGiftAttributes {
showUpgradePreview = true
} else if case .upgradePreview = component.subject {
showUpgradePreview = true
}
let cancel = component.cancel
let closeButton = closeButton.update(
component: Button(
content: AnyComponent(Image(image: state.inUpgradePreview || uniqueGift != nil ? closeOverlayImage : closeImage)),
action: { [weak component] in
component?.cancel(true)
content: AnyComponent(Image(image: showUpgradePreview || uniqueGift != nil ? closeOverlayImage : closeImage)),
action: { [weak state] in
guard let state else {
return
}
if state.inUpgradePreview {
state.inUpgradePreview = false
state.updated(transition: .spring(duration: 0.4))
} else {
cancel(true)
}
}
),
availableSize: CGSize(width: 30.0, height: 30.0),
@ -405,9 +456,12 @@ private final class GiftViewSheetContent: CombinedComponent {
if let uniqueGift {
animationHeight = 240.0
animationSubject = .unique(uniqueGift)
} else if state.inUpgradePreview, let sampleGiftAttributes = state.sampleGiftAttributes {
} else if state.inUpgradePreview, let attributes = state.sampleGiftAttributes {
animationHeight = 258.0
animationSubject = .preview(sampleGiftAttributes)
animationSubject = .preview(attributes)
} else if case let .upgradePreview(attributes, _) = component.subject {
animationHeight = 258.0
animationSubject = .preview(attributes)
} else if let animationFile {
animationHeight = 210.0
animationSubject = .generic(animationFile)
@ -420,7 +474,11 @@ private final class GiftViewSheetContent: CombinedComponent {
component: GiftCompositionComponent(
context: component.context,
theme: environment.theme,
subject: animationSubject
subject: animationSubject,
externalState: giftCompositionExternalState,
requestUpdate: { [weak state] in
state?.updated()
}
),
availableSize: CGSize(width: context.availableSize.width, height: animationHeight),
transition: .immediate
@ -430,13 +488,31 @@ private final class GiftViewSheetContent: CombinedComponent {
)
}
originY += animationHeight
if state.inUpgradePreview, let _ = state.sampleGiftAttributes {
if showUpgradePreview {
let title: String
let description: String
let uniqueText: String
let transferableText: String
let tradableText: String
if case let .upgradePreview(_, name) = component.subject {
title = environment.strings.Gift_Upgrade_IncludeTitle
description = environment.strings.Gift_Upgrade_IncludeDescription(name).string
uniqueText = strings.Gift_Upgrade_Unique_IncludeDescription
transferableText = strings.Gift_Upgrade_Transferable_IncludeDescription
tradableText = strings.Gift_Upgrade_Tradable_IncludeDescription
} else {
title = environment.strings.Gift_Upgrade_Title
description = environment.strings.Gift_Upgrade_Description
uniqueText = strings.Gift_Upgrade_Unique_Description
transferableText = strings.Gift_Upgrade_Transferable_Description
tradableText = strings.Gift_Upgrade_Tradable_Description
}
let upgradeTitle = upgradeTitle.update(
component: MultilineTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.Gift_Upgrade_Title,
string: title,
font: Font.bold(20.0),
textColor: .white,
paragraphAlignment: .center
@ -447,18 +523,18 @@ private final class GiftViewSheetContent: CombinedComponent {
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
context.add(upgradeTitle
.position(CGPoint(x: context.availableSize.width / 2.0, y: 191.0))
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
let descriptionColor: UIColor
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
descriptionColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
} else {
descriptionColor = UIColor.white.withAlphaComponent(0.6)
}
let upgradeDescription = upgradeDescription.update(
component: BalancedTextComponent(
text: .plain(NSAttributedString(
string: environment.strings.Gift_Upgrade_Description,
string: description,
font: Font.regular(13.0),
textColor: UIColor.white.withAlphaComponent(0.6),
textColor: descriptionColor,
paragraphAlignment: .center
)),
horizontalAlignment: .center,
@ -468,8 +544,18 @@ private final class GiftViewSheetContent: CombinedComponent {
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
transition: .immediate
)
let spacing: CGFloat = 6.0
let totalHeight: CGFloat = upgradeTitle.size.height + spacing + upgradeDescription.size.height
context.add(upgradeTitle
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 - totalHeight / 2.0 + upgradeTitle.size.height / 2.0)))
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
context.add(upgradeDescription
.position(CGPoint(x: context.availableSize.width / 2.0, y: 208.0 + upgradeDescription.size.height / 2.0))
.position(CGPoint(x: context.availableSize.width / 2.0, y: floor(212.0 + totalHeight / 2.0 - upgradeDescription.size.height / 2.0)))
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
@ -486,7 +572,7 @@ private final class GiftViewSheetContent: CombinedComponent {
component: AnyComponent(ParagraphComponent(
title: strings.Gift_Upgrade_Unique_Title,
titleColor: textColor,
text: strings.Gift_Upgrade_Unique_Description,
text: uniqueText,
textColor: secondaryTextColor,
accentColor: linkColor,
iconName: "Premium/Collectible/Unique",
@ -500,7 +586,7 @@ private final class GiftViewSheetContent: CombinedComponent {
component: AnyComponent(ParagraphComponent(
title: strings.Gift_Upgrade_Transferable_Title,
titleColor: textColor,
text: strings.Gift_Upgrade_Transferable_Description,
text: transferableText,
textColor: secondaryTextColor,
accentColor: linkColor,
iconName: "Premium/Collectible/Transferable",
@ -514,7 +600,7 @@ private final class GiftViewSheetContent: CombinedComponent {
component: AnyComponent(ParagraphComponent(
title: strings.Gift_Upgrade_Tradable_Title,
titleColor: textColor,
text: strings.Gift_Upgrade_Tradable_Description,
text: tradableText,
textColor: secondaryTextColor,
accentColor: linkColor,
iconName: "Premium/Collectible/Tradable",
@ -538,55 +624,59 @@ private final class GiftViewSheetContent: CombinedComponent {
originY += upgradePerks.size.height
originY += 16.0
let checkTheme = CheckComponent.Theme(
backgroundColor: theme.list.itemCheckColors.fillColor,
strokeColor: theme.list.itemCheckColors.foregroundColor,
borderColor: theme.list.itemCheckColors.strokeColor,
overlayBorder: false,
hasInset: false,
hasShadow: false
)
let keepInfoText: String
if let nameHidden = subject.arguments?.nameHidden, nameHidden {
keepInfoText = strings.Gift_Upgrade_AddMyName
if case .upgradePreview = component.subject {
} else {
keepInfoText = text != nil ? strings.Gift_Upgrade_AddNameAndComment : strings.Gift_Upgrade_AddName
let checkTheme = CheckComponent.Theme(
backgroundColor: theme.list.itemCheckColors.fillColor,
strokeColor: theme.list.itemCheckColors.foregroundColor,
borderColor: theme.list.itemCheckColors.strokeColor,
overlayBorder: false,
hasInset: false,
hasShadow: false
)
let keepInfoText: String
if let nameHidden = subject.arguments?.nameHidden, nameHidden {
keepInfoText = strings.Gift_Upgrade_AddMyName
} else {
keepInfoText = text != nil ? strings.Gift_Upgrade_AddNameAndComment : strings.Gift_Upgrade_AddName
}
let upgradeKeepName = upgradeKeepName.update(
component: PlainButtonComponent(
content: AnyComponent(HStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent(
theme: checkTheme,
size: CGSize(width: 18.0, height: 18.0),
selected: state.keepOriginalInfo
))),
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: keepInfoText, font: Font.regular(13.0), textColor: theme.list.itemSecondaryTextColor))
)))
],
spacing: 10.0
)),
effectAlignment: .center,
action: { [weak state] in
guard let state else {
return
}
state.keepOriginalInfo = !state.keepOriginalInfo
state.updated(transition: .easeInOut(duration: 0.2))
},
animateAlpha: false,
animateScale: false
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 1000.0),
transition: context.transition
)
context.add(upgradeKeepName
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + upgradeKeepName.size.height / 2.0))
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
originY += upgradeKeepName.size.height
originY += 18.0
}
let upgradeKeepName = upgradeKeepName.update(
component: PlainButtonComponent(
content: AnyComponent(HStack([
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(CheckComponent(
theme: checkTheme,
size: CGSize(width: 18.0, height: 18.0),
selected: state.keepOriginalInfo
))),
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
text: .plain(NSAttributedString(string: keepInfoText, font: Font.regular(13.0), textColor: theme.list.itemSecondaryTextColor))
)))
],
spacing: 10.0
)),
effectAlignment: .center,
action: { [weak state] in
guard let state else {
return
}
state.keepOriginalInfo = !state.keepOriginalInfo
state.updated(transition: .easeInOut(duration: 0.2))
},
animateAlpha: false,
animateScale: false
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 1000.0),
transition: context.transition
)
context.add(upgradeKeepName
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + upgradeKeepName.size.height / 2.0))
.appear(.default(alpha: true))
.disappear(.default(alpha: true))
)
originY += upgradeKeepName.size.height
originY += 18.0
} else {
var descriptionText: String
if let uniqueGift {
@ -595,12 +685,18 @@ private final class GiftViewSheetContent: CombinedComponent {
} else if soldOut {
descriptionText = strings.Gift_View_UnavailableDescription
} else if incoming {
if let convertStars {
if let convertStars, !upgraded {
if !converted {
descriptionText = strings.Gift_View_KeepUpgradeOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
if canUpgrade || upgradeStars != nil {
descriptionText = strings.Gift_View_KeepUpgradeOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
} else {
descriptionText = strings.Gift_View_KeepOrConvertDescription(strings.Gift_View_KeepOrConvertDescription_Stars(Int32(convertStars))).string
}
} else {
descriptionText = strings.Gift_View_ConvertedDescription(strings.Gift_View_ConvertedDescription_Stars(Int32(convertStars))).string
}
} else if (canUpgrade || upgradeStars != nil) && !upgraded {
descriptionText = strings.Gift_View_KeepOrUpgradeDescription
} else {
descriptionText = strings.Gift_View_BotDescription
}
@ -660,17 +756,13 @@ private final class GiftViewSheetContent: CombinedComponent {
let textFont: UIFont
let textColor: UIColor
if let uniqueGift {
if let _ = uniqueGift {
textFont = Font.regular(13.0)
var giftTextColor: UIColor?
for attribute in uniqueGift.attributes {
if case let .backdrop(_, _, _, _, textColor, _) = attribute {
giftTextColor = UIColor(rgb: UInt32(bitPattern: textColor))
break
}
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
textColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
} else {
textColor = UIColor.white.withAlphaComponent(0.6)
}
textColor = giftTextColor ?? UIColor.white.withAlphaComponent(0.6)
} else {
textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0)
textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor
@ -1043,7 +1135,11 @@ private final class GiftViewSheetContent: CombinedComponent {
))
}
let valueString = "⭐️\(presentationStringsFormattedNumber(abs(Int32(stars)), dateTimeFormat.groupingSeparator))"
var finalStars = stars
if let upgradeStars, upgradeStars > 0 {
finalStars += upgradeStars
}
let valueString = "⭐️\(presentationStringsFormattedNumber(abs(Int32(finalStars)), dateTimeFormat.groupingSeparator))"
let valueAttributedString = NSMutableAttributedString(string: valueString, font: tableFont, textColor: tableTextColor)
let range = (valueAttributedString.string as NSString).range(of: "⭐️")
if range.location != NSNotFound {
@ -1115,7 +1211,7 @@ private final class GiftViewSheetContent: CombinedComponent {
))
}
if !soldOut && upgradeStars != nil {
if !soldOut && canUpgrade {
var items: [AnyComponentWithIdentity<Empty>] = []
items.append(
AnyComponentWithIdentity(
@ -1190,7 +1286,14 @@ private final class GiftViewSheetContent: CombinedComponent {
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
}
let descriptionText = savedToProfile ? strings.Gift_View_DisplayedInfoHide : strings.Gift_View_HiddenInfo
let descriptionText: String
if savedToProfile {
descriptionText = strings.Gift_View_DisplayedInfoHide
} else if let upgradeStars, upgradeStars > 0 && !upgraded {
descriptionText = strings.Gift_View_HiddenInfoShow
} else {
descriptionText = strings.Gift_View_HiddenInfo
}
let textFont = Font.regular(13.0)
let textColor = theme.list.itemSecondaryTextColor
@ -1219,7 +1322,7 @@ private final class GiftViewSheetContent: CombinedComponent {
}
},
tapAction: { _, _ in
component.updateSavedToProfile(false)
component.updateSavedToProfile(!savedToProfile)
Queue.mainQueue().after(1.0, {
component.cancel(false)
})
@ -1239,11 +1342,15 @@ private final class GiftViewSheetContent: CombinedComponent {
}
let buttonChild: _UpdatedChildComponent
if state.inUpgradePreview, let upgradeStars = subject.arguments?.upgradeStars {
if state.inUpgradePreview {
if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme {
state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: theme.list.itemCheckColors.foregroundColor)!, theme)
}
let buttonTitle = upgradeStars > 0 ? "\(strings.Gift_Upgrade_Upgrade) # \(upgradeStars)" : strings.Gift_Upgrade_Confirm
var upgradeString = strings.Gift_Upgrade_Upgrade
if let upgradeForm = state.upgradeForm, let price = upgradeForm.invoice.prices.first?.amount {
upgradeString += " # \(price)"
}
let buttonTitle = subject.arguments?.upgradeStars != nil ? strings.Gift_Upgrade_Confirm : upgradeString
let buttonAttributedString = NSMutableAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center)
if let range = buttonAttributedString.string.range(of: "#"), let starImage = state.cachedStarImage?.0 {
buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string))
@ -1259,7 +1366,7 @@ private final class GiftViewSheetContent: CombinedComponent {
cornerRadius: 10.0
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
id: AnyHashable("upgrade"),
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
),
isEnabled: true,
@ -1270,7 +1377,8 @@ private final class GiftViewSheetContent: CombinedComponent {
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
transition: context.transition
)
} else if incoming && !converted && !savedToProfile {
} else if incoming && !converted && !upgraded, let upgradeStars, upgradeStars > 0 {
let buttonTitle = strings.Gift_View_UpgradeForFree
buttonChild = button.update(
component: ButtonComponent(
background: ButtonComponent.Background(
@ -1280,8 +1388,36 @@ private final class GiftViewSheetContent: CombinedComponent {
cornerRadius: 10.0
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: savedToProfile ? strings.Gift_View_Hide : strings.Gift_View_Display, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
id: AnyHashable("freeUpgrade"),
component: AnyComponent(HStack([
AnyComponentWithIdentity(id: 0, component: AnyComponent(
MultilineTextComponent(text: .plain(NSAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))
))),
AnyComponentWithIdentity(id: 1, component: AnyComponent(BundleIconComponent(name: "Premium/GiftUpgrade", tintColor: theme.list.itemCheckColors.foregroundColor)))
], spacing: 6.0))
),
isEnabled: true,
displaysProgress: state.inProgress,
action: { [weak state] in
state?.requestUpgradePreview()
}
),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
transition: context.transition
)
} else if incoming && !converted && !savedToProfile {
let buttonTitle = savedToProfile ? strings.Gift_View_Hide : strings.Gift_View_Display
buttonChild = button.update(
component: ButtonComponent(
background: ButtonComponent.Background(
color: theme.list.itemCheckColors.fillColor,
foreground: theme.list.itemCheckColors.foregroundColor,
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
cornerRadius: 10.0
),
content: AnyComponentWithIdentity(
id: AnyHashable("button"),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
),
isEnabled: true,
displaysProgress: state.inProgress,
@ -1301,7 +1437,7 @@ private final class GiftViewSheetContent: CombinedComponent {
cornerRadius: 10.0
),
content: AnyComponentWithIdentity(
id: AnyHashable(0),
id: AnyHashable("ok"),
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Common_OK, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
),
isEnabled: true,
@ -1486,24 +1622,31 @@ public class GiftViewScreen: ViewControllerComponentContainer {
case message(EngineMessage)
case profileGift(EnginePeer.Id, ProfileGiftsContext.State.StarGift)
case soldOutGift(StarGift.Gift)
case upgradePreview([StarGift.UniqueGift.Attribute], String)
var arguments: (peerId: EnginePeer.Id, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, canExportDate: Int32?)? {
var arguments: (peerId: EnginePeer.Id, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, canExportDate: Int32?)? {
switch self {
case let .message(message):
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
switch action.action {
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, _, upgradeStars):
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, converted, false, upgradeStars, nil, nil)
case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars):
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, false, nil, transferStars, canExportDate)
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _):
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, nil, nil)
case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _):
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, true, false, nil, transferStars, canExportDate)
default:
return nil
}
}
case let .profileGift(peerId, gift):
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, gift.messageId, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, gift.canExportDate)
var upgraded = false
if case .unique = gift.gift {
upgraded = true
}
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, gift.messageId, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false, upgraded, gift.canUpgrade, gift.upgradeStars, gift.transferStars, gift.canExportDate)
case .soldOutGift:
return nil
case .upgradePreview:
return nil
}
return nil
}
@ -1772,6 +1915,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
guard let self else {
return
}
self.dismissAllTooltips()
guard let sourceView = self.node.hostView.findTaggedView(tag: tag), let absoluteLocation = sourceView.superview?.convert(sourceView.center, to: self.view) else {
return
}
@ -1818,13 +1963,16 @@ public class GiftViewScreen: ViewControllerComponentContainer {
fileprivate func dismissAllTooltips() {
self.window?.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss()
controller.dismiss(inPlace: false)
}
if let controller = controller as? UndoOverlayController {
controller.dismiss()
}
})
self.forEachController({ controller in
if let controller = controller as? TooltipScreen {
controller.dismiss(inPlace: false)
}
if let controller = controller as? UndoOverlayController {
controller.dismiss()
}

View File

@ -222,29 +222,24 @@ public final class PeerInfoCoverComponent: Component {
}
public func animateTransition() {
if let snapshotLayer = self.backgroundView.layer.snapshotContentTree() {
self.layer.insertSublayer(snapshotLayer, above: self.backgroundGradientLayer)
if let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() {
self.layer.insertSublayer(gradientSnapshotLayer, above: snapshotLayer)
snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
snapshotLayer.removeFromSuperlayer()
})
gradientSnapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
gradientSnapshotLayer.removeFromSuperlayer()
})
}
if let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() {
gradientSnapshotLayer.frame = self.backgroundGradientLayer.frame
self.layer.insertSublayer(gradientSnapshotLayer, above: self.backgroundGradientLayer)
gradientSnapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
gradientSnapshotLayer.removeFromSuperlayer()
})
}
for layer in self.avatarPatternContentLayers {
if let snapshot = layer.snapshotContentTree() {
if let _ = layer.contents, let snapshot = layer.snapshotContentTree() {
layer.superlayer?.addSublayer(snapshot)
snapshot.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
snapshot.removeFromSuperlayer()
})
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
let values: [NSNumber] = [1.0, 1.08, 1.0]
self.avatarBackgroundPatternContentsLayer.animateKeyframes(values: values, duration: 0.25, keyPath: "transform.scale")
self.avatarBackgroundPatternContentsLayer.animateKeyframes(values: values, duration: 0.25, keyPath: "sublayerTransform.scale")
}
private func loadPatternFromFile() {
@ -298,7 +293,10 @@ public final class PeerInfoCoverComponent: Component {
}
func update(component: PeerInfoCoverComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
if self.component?.subject?.fileId != component.subject?.fileId {
let previousComponent = self.component
self.component = component
if previousComponent?.subject?.fileId != component.subject?.fileId {
if let fileId = component.subject?.fileId, fileId != 0 {
if self.patternContentsTarget == nil {
self.patternContentsTarget = PatternContentsTarget(imageUpdated: { [weak self] hadContents in
@ -337,8 +335,7 @@ public final class PeerInfoCoverComponent: Component {
self.updatePatternLayerImages(animated: false)
}
}
self.component = component
self.state = state
let backgroundColor: UIColor

View File

@ -1210,9 +1210,15 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
case .gifts:
title = presentationData.strings.PeerInfo_PaneGifts
icons = data?.profileGiftsContext?.currentState?.gifts.prefix(3).compactMap { gift in
if case let .generic(gift) = gift.gift {
switch gift.gift {
case let .generic(gift):
return gift.file
} else {
case let .unique(gift):
for attribute in gift.attributes {
if case let .model(_, file, _) = attribute {
return file
}
}
return nil
}
} ?? []

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "unpack_14.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "upgrade_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -2289,18 +2289,13 @@ public final class SharedAccountContextImpl: SharedAccountContext {
var currentBirthdays: [EnginePeer.Id: TelegramBirthday]?
if case let .starGiftTransfer(birthdays, _, _, _, _) = source {
if let birthdays, !birthdays.isEmpty {
mode = .starsGifting(birthdays: birthdays, hasActions: false, showSelf: true)
currentBirthdays = birthdays
} else {
mode = .starsGifting(birthdays: nil, hasActions: false, showSelf: true)
}
}
if case let .chatList(birthdays) = source, let birthdays, !birthdays.isEmpty {
mode = .starsGifting(birthdays: birthdays, hasActions: true, showSelf: false)
mode = .starsGifting(birthdays: birthdays, hasActions: false, showSelf: false)
currentBirthdays = birthdays
} else if case let .settings(birthdays) = source, let birthdays, !birthdays.isEmpty {
mode = .starsGifting(birthdays: birthdays, hasActions: true, showSelf: false)
} else if case let .chatList(birthdays) = source {
mode = .starsGifting(birthdays: birthdays, hasActions: true, showSelf: true)
currentBirthdays = birthdays
} else if case let .settings(birthdays) = source {
mode = .starsGifting(birthdays: birthdays, hasActions: true, showSelf: true)
currentBirthdays = birthdays
} else {
mode = .starsGifting(birthdays: nil, hasActions: true, showSelf: false)