mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-21 21:59:01 +00:00
Various improvements
This commit is contained in:
parent
e3ee1dde7e
commit
5d4213c4fc
@ -980,7 +980,7 @@ private final class NotificationServiceHandler {
|
|||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
case logout
|
case logout
|
||||||
case poll(peerId: PeerId, content: NotificationContent, messageId: MessageId?)
|
case poll(peerId: PeerId, content: NotificationContent, messageId: MessageId?, reportDelivery: Bool)
|
||||||
case pollStories(peerId: PeerId, content: NotificationContent, storyId: Int32, isReaction: Bool)
|
case pollStories(peerId: PeerId, content: NotificationContent, storyId: Int32, isReaction: Bool)
|
||||||
case deleteMessage([MessageId])
|
case deleteMessage([MessageId])
|
||||||
case readReactions([MessageId])
|
case readReactions([MessageId])
|
||||||
@ -999,7 +999,7 @@ private final class NotificationServiceHandler {
|
|||||||
action = .logout
|
action = .logout
|
||||||
case "MESSAGE_MUTED":
|
case "MESSAGE_MUTED":
|
||||||
if let peerId = peerId {
|
if let peerId = peerId {
|
||||||
action = .poll(peerId: peerId, content: NotificationContent(isLockedMessage: nil), messageId: nil)
|
action = .poll(peerId: peerId, content: NotificationContent(isLockedMessage: nil), messageId: nil, reportDelivery: false)
|
||||||
}
|
}
|
||||||
case "MESSAGE_DELETED":
|
case "MESSAGE_DELETED":
|
||||||
if let peerId = peerId {
|
if let peerId = peerId {
|
||||||
@ -1183,9 +1183,16 @@ private final class NotificationServiceHandler {
|
|||||||
|
|
||||||
action = .pollStories(peerId: peerId, content: content, storyId: storyId, isReaction: isReaction)
|
action = .pollStories(peerId: peerId, content: content, storyId: storyId, isReaction: isReaction)
|
||||||
} else {
|
} else {
|
||||||
action = .poll(peerId: peerId, content: content, messageId: messageIdValue)
|
var reportDelivery = false
|
||||||
|
if let reportDeliveryUntilDate = aps["report_delivery_until_date"] as? Int32, let messageId = messageIdValue {
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
if reportDeliveryUntilDate > currentTime {
|
||||||
|
reportDelivery = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action = .poll(peerId: peerId, content: content, messageId: messageIdValue, reportDelivery: reportDelivery)
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1245,7 +1252,7 @@ private final class NotificationServiceHandler {
|
|||||||
let content = NotificationContent(isLockedMessage: nil)
|
let content = NotificationContent(isLockedMessage: nil)
|
||||||
updateCurrentContent(content)
|
updateCurrentContent(content)
|
||||||
completed()
|
completed()
|
||||||
case let .poll(peerId, initialContent, messageId):
|
case let .poll(peerId, initialContent, messageId, reportDelivery):
|
||||||
Logger.shared.log("NotificationService \(episode)", "Will poll")
|
Logger.shared.log("NotificationService \(episode)", "Will poll")
|
||||||
if let stateManager = strongSelf.stateManager {
|
if let stateManager = strongSelf.stateManager {
|
||||||
let shouldKeepConnection = stateManager.network.shouldKeepConnection
|
let shouldKeepConnection = stateManager.network.shouldKeepConnection
|
||||||
@ -1683,12 +1690,23 @@ private final class NotificationServiceHandler {
|
|||||||
pollWithUpdatedContent = pollSignal
|
pollWithUpdatedContent = pollSignal
|
||||||
|> map { _ -> (NotificationContent, Media?) in }
|
|> map { _ -> (NotificationContent, Media?) in }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reportDeliverySignal: Signal<Bool, NoError>
|
||||||
|
if reportDelivery, let messageId {
|
||||||
|
reportDeliverySignal = _internal_reportMessageDelivery(postbox: stateManager.postbox, network: stateManager.network, messageIds: [messageId], fromPushNotification: true)
|
||||||
|
|> mapToSignal { _ -> Signal<Bool, NoError> in
|
||||||
|
return .single(true)
|
||||||
|
}
|
||||||
|
|> then(.single(true))
|
||||||
|
} else {
|
||||||
|
reportDeliverySignal = .single(true)
|
||||||
|
}
|
||||||
|
|
||||||
var updatedContent = initialContent
|
var updatedContent = initialContent
|
||||||
var updatedMedia: Media?
|
var updatedMedia: Media?
|
||||||
strongSelf.pollDisposable.set(pollWithUpdatedContent.start(next: { content, media in
|
strongSelf.pollDisposable.set(combineLatest(pollWithUpdatedContent, reportDeliverySignal).start(next: { contentAndMedia, _ in
|
||||||
updatedContent = content
|
updatedContent = contentAndMedia.0
|
||||||
updatedMedia = media
|
updatedMedia = contentAndMedia.1
|
||||||
}, completed: {
|
}, completed: {
|
||||||
pollCompletion(updatedContent, updatedMedia)
|
pollCompletion(updatedContent, updatedMedia)
|
||||||
}))
|
}))
|
||||||
@ -1951,6 +1969,8 @@ private final class NotificationServiceHandler {
|
|||||||
} else {
|
} else {
|
||||||
pollWithUpdatedContent = .complete()
|
pollWithUpdatedContent = .complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var updatedContent = initialContent
|
var updatedContent = initialContent
|
||||||
strongSelf.pollDisposable.set(pollWithUpdatedContent.start(next: { content in
|
strongSelf.pollDisposable.set(pollWithUpdatedContent.start(next: { content in
|
||||||
|
|||||||
BIN
Telegram/Telegram-iOS/Resources/GiftUpgraded.tgs
Normal file
BIN
Telegram/Telegram-iOS/Resources/GiftUpgraded.tgs
Normal file
Binary file not shown.
@ -12299,6 +12299,7 @@ Sorry for the inconvenience.";
|
|||||||
"Stars.Intro.Transaction.ConvertedGift" = "Converted Gift";
|
"Stars.Intro.Transaction.ConvertedGift" = "Converted Gift";
|
||||||
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
|
"Stars.Intro.Transaction.Unsupported.Title" = "Unsupported";
|
||||||
"Stars.Intro.Transaction.Refund" = "Refund";
|
"Stars.Intro.Transaction.Refund" = "Refund";
|
||||||
|
"Stars.Intro.Transaction.GiftUpgrade" = "Gift Upgrade";
|
||||||
|
|
||||||
"Stars.Intro.PurchasedTitle" = "Stars Acquired";
|
"Stars.Intro.PurchasedTitle" = "Stars Acquired";
|
||||||
"Stars.Intro.PurchasedText" = "**%@** added to your balance.";
|
"Stars.Intro.PurchasedText" = "**%@** added to your balance.";
|
||||||
@ -13503,9 +13504,9 @@ Sorry for the inconvenience.";
|
|||||||
"Gift.Unique.Availability" = "Availability";
|
"Gift.Unique.Availability" = "Availability";
|
||||||
"Gift.Unique.Issued" = "%@ issued";
|
"Gift.Unique.Issued" = "%@ issued";
|
||||||
"Gift.Unique.OriginalInfo" = "Gifted to %1$@ on %2$@.";
|
"Gift.Unique.OriginalInfo" = "Gifted to %1$@ on %2$@.";
|
||||||
"Gift.Unique.OriginalInfoWithText" = "Gifted to %1$@ on %2$@ with comment \"%3$@\"";
|
"Gift.Unique.OriginalInfoWithText" = "Gifted to %1$@ on %2$@ with comment \"%3$@\".";
|
||||||
"Gift.Unique.OriginalInfoSender" = "Gifted by %1$@ to %2$@ on %3$@.";
|
"Gift.Unique.OriginalInfoSender" = "Gifted by %1$@ to %2$@ on %3$@.";
|
||||||
"Gift.Unique.OriginalInfoSenderWithText" = "Gifted by %1$@ to %2$@ on %3$@ with comment \"%4$@\"";
|
"Gift.Unique.OriginalInfoSenderWithText" = "Gifted by %1$@ to %2$@ on %3$@ with comment \"%4$@\".";
|
||||||
|
|
||||||
"Gift.Unique.AttributeDescription" = "Only %@ of such collectibles have this attribute.";
|
"Gift.Unique.AttributeDescription" = "Only %@ of such collectibles have this attribute.";
|
||||||
|
|
||||||
@ -13533,6 +13534,7 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Gift.View.UpgradeForFree" = "Upgrade for Free";
|
"Gift.View.UpgradeForFree" = "Upgrade for Free";
|
||||||
"Gift.View.KeepUpgradeOrConvertDescription" = "You can keep this gift, upgrade it, or sell it for %@. [More About Stars >]()";
|
"Gift.View.KeepUpgradeOrConvertDescription" = "You can keep this gift, upgrade it, or sell it for %@. [More About Stars >]()";
|
||||||
|
"Gift.View.KeepOrUpgradeDescription" = "You can keep this gift or upgrade it.";
|
||||||
|
|
||||||
"PeerInfo.VerificationInfo.Bot" = "This bot is verified as official by the representatives of Telegram.";
|
"PeerInfo.VerificationInfo.Bot" = "This bot is verified as official by the representatives of Telegram.";
|
||||||
"PeerInfo.VerificationInfo.Channel" = "This channel is verified as official by the representatives of Telegram.";
|
"PeerInfo.VerificationInfo.Channel" = "This channel is verified as official by the representatives of Telegram.";
|
||||||
@ -13561,4 +13563,7 @@ Sorry for the inconvenience.";
|
|||||||
"Notification.StarGift.Subtitle.Refunded" = "This gift cannot be converted to Stars because the payment related to it was refunded.";
|
"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.";
|
"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.";
|
"Notification.StarGift.Subtitle.Other" = "%1$@ can keep this gift or upgrade it.";
|
||||||
|
|
||||||
|
"Stars.Transaction.GiftFrom" = "Gift From";
|
||||||
|
"Stars.Transaction.GiftUpgrade" = "Gift Upgrade";
|
||||||
|
|||||||
@ -218,7 +218,7 @@ public final class DefaultAnimatedStickerNodeImpl: ASDisplayNode, AnimatedSticke
|
|||||||
public var isPlaying: Bool = false
|
public var isPlaying: Bool = false
|
||||||
private var currentLoopCount: Int = 0
|
private var currentLoopCount: Int = 0
|
||||||
private var canDisplayFirstFrame: Bool = false
|
private var canDisplayFirstFrame: Bool = false
|
||||||
private var playbackMode: AnimatedStickerPlaybackMode = .loop
|
public var playbackMode: AnimatedStickerPlaybackMode = .loop
|
||||||
|
|
||||||
public var stopAtNearestLoop: Bool = false
|
public var stopAtNearestLoop: Bool = false
|
||||||
|
|
||||||
|
|||||||
@ -1527,7 +1527,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case let .done(receiptMessageId, _):
|
case let .done(receiptMessageId, _, _):
|
||||||
proceedWithCompletion(true, receiptMessageId)
|
proceedWithCompletion(true, receiptMessageId)
|
||||||
case let .externalVerificationRequired(url):
|
case let .externalVerificationRequired(url):
|
||||||
strongSelf.updateActionButton()
|
strongSelf.updateActionButton()
|
||||||
|
|||||||
@ -1352,6 +1352,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
let _ = titleApply()
|
let _ = titleApply()
|
||||||
|
|
||||||
var titleLeftOffset: CGFloat = 0.0
|
var titleLeftOffset: CGFloat = 0.0
|
||||||
|
var nextIconX: CGFloat = titleFrame.maxX
|
||||||
if let verifiedIcon {
|
if let verifiedIcon {
|
||||||
let animationCache = item.context.animationCache
|
let animationCache = item.context.animationCache
|
||||||
let animationRenderer = item.context.animationRenderer
|
let animationRenderer = item.context.animationRenderer
|
||||||
@ -1375,17 +1376,30 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
emojiFileUpdated: nil
|
emojiFileUpdated: nil
|
||||||
)
|
)
|
||||||
strongSelf.verifiedIconComponent = verifiedIconComponent
|
strongSelf.verifiedIconComponent = verifiedIconComponent
|
||||||
|
|
||||||
|
let iconOrigin: CGFloat
|
||||||
|
if case .animation = verifiedIcon {
|
||||||
|
iconOrigin = titleFrame.minX
|
||||||
|
} else {
|
||||||
|
nextIconX += 4.0
|
||||||
|
iconOrigin = nextIconX
|
||||||
|
}
|
||||||
|
let containerSize = CGSize(width: 16.0, height: 16.0)
|
||||||
|
|
||||||
let iconSize = verifiedIconView.update(
|
let iconSize = verifiedIconView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(verifiedIconComponent),
|
component: AnyComponent(verifiedIconComponent),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 16.0, height: 16.0)
|
containerSize: containerSize
|
||||||
)
|
)
|
||||||
|
|
||||||
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: titleFrame.minX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||||
|
|
||||||
titleLeftOffset += iconSize.width + 4.0
|
if case .animation = verifiedIcon {
|
||||||
|
titleLeftOffset += iconSize.width + 4.0
|
||||||
|
} else {
|
||||||
|
nextIconX += iconSize.width
|
||||||
|
}
|
||||||
} else if let verifiedIconView = strongSelf.verifiedIconView {
|
} else if let verifiedIconView = strongSelf.verifiedIconView {
|
||||||
strongSelf.verifiedIconView = nil
|
strongSelf.verifiedIconView = nil
|
||||||
verifiedIconView.removeFromSuperview()
|
verifiedIconView.removeFromSuperview()
|
||||||
@ -1424,7 +1438,6 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextIconX: CGFloat = titleFrame.maxX
|
|
||||||
if let credibilityIcon {
|
if let credibilityIcon {
|
||||||
let animationCache = item.context.animationCache
|
let animationCache = item.context.animationCache
|
||||||
let animationRenderer = item.context.animationRenderer
|
let animationRenderer = item.context.animationRenderer
|
||||||
|
|||||||
@ -126,7 +126,7 @@ public final class TextAlertContentActionNode: HighlightableButtonNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let attributedString = NSMutableAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center)
|
let attributedString = NSMutableAttributedString(string: self.action.title, font: font, textColor: color, paragraphAlignment: .center)
|
||||||
if let range = attributedString.string.range(of: "⭐️") {
|
if let range = attributedString.string.range(of: "$") {
|
||||||
attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.attachment, value: UIImage(bundleImageName: "Item List/PremiumIcon")!, range: NSRange(range, in: attributedString.string))
|
||||||
attributedString.addAttribute(.foregroundColor, value: color, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.foregroundColor, value: color, range: NSRange(range, in: attributedString.string))
|
||||||
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
||||||
|
|||||||
@ -1436,6 +1436,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
var titleFrame = CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset + verticalOffset), size: titleLayout.size)
|
var titleFrame = CGRect(origin: CGPoint(x: leftInset + revealOffset + editingOffset, y: verticalInset + verticalOffset), size: titleLayout.size)
|
||||||
|
|
||||||
var titleLeftOffset: CGFloat = 0.0
|
var titleLeftOffset: CGFloat = 0.0
|
||||||
|
var nextIconX: CGFloat = titleFrame.maxX
|
||||||
if let verifiedIcon = verifiedIcon {
|
if let verifiedIcon = verifiedIcon {
|
||||||
let animationCache = item.context.animationCache
|
let animationCache = item.context.animationCache
|
||||||
let animationRenderer = item.context.animationRenderer
|
let animationRenderer = item.context.animationRenderer
|
||||||
@ -1461,6 +1462,15 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
emojiFileUpdated: nil
|
emojiFileUpdated: nil
|
||||||
)
|
)
|
||||||
strongSelf.verifiedIconComponent = verifiedIconComponent
|
strongSelf.verifiedIconComponent = verifiedIconComponent
|
||||||
|
|
||||||
|
let iconOrigin: CGFloat
|
||||||
|
if case .animation = verifiedIcon {
|
||||||
|
iconOrigin = titleFrame.minX
|
||||||
|
} else {
|
||||||
|
nextIconX += 4.0
|
||||||
|
iconOrigin = nextIconX
|
||||||
|
}
|
||||||
|
|
||||||
let iconSize = verifiedIconView.update(
|
let iconSize = verifiedIconView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(verifiedIconComponent),
|
component: AnyComponent(verifiedIconComponent),
|
||||||
@ -1468,9 +1478,13 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
containerSize: CGSize(width: 20.0, height: 20.0)
|
containerSize: CGSize(width: 20.0, height: 20.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||||
|
|
||||||
titleLeftOffset += iconSize.width + 4.0
|
if case .animation = verifiedIcon {
|
||||||
|
titleLeftOffset += iconSize.width + 4.0
|
||||||
|
} else {
|
||||||
|
nextIconX += iconSize.width
|
||||||
|
}
|
||||||
} else if let verifiedIconView = strongSelf.verifiedIconView {
|
} else if let verifiedIconView = strongSelf.verifiedIconView {
|
||||||
strongSelf.verifiedIconView = nil
|
strongSelf.verifiedIconView = nil
|
||||||
verifiedIconView.removeFromSuperview()
|
verifiedIconView.removeFromSuperview()
|
||||||
@ -1512,7 +1526,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||||||
containerSize: CGSize(width: 20.0, height: 20.0)
|
containerSize: CGSize(width: 20.0, height: 20.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: titleFrame.maxX + 4.0, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
nextIconX += 4.0
|
||||||
|
transition.updateFrame(view: credibilityIconView, frame: CGRect(origin: CGPoint(x: nextIconX, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||||
} else if let credibilityIconView = strongSelf.credibilityIconView {
|
} else if let credibilityIconView = strongSelf.credibilityIconView {
|
||||||
strongSelf.credibilityIconView = nil
|
strongSelf.credibilityIconView = nil
|
||||||
credibilityIconView.removeFromSuperview()
|
credibilityIconView.removeFromSuperview()
|
||||||
|
|||||||
@ -336,7 +336,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
theme: item.presentationData.theme,
|
theme: item.presentationData.theme,
|
||||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
||||||
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0),
|
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0),
|
||||||
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: item.context, theme: item.presentationData.theme, peer: item.transaction.peer, photo: nil, media: [], backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor))), false),
|
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: item.context, theme: item.presentationData.theme, peer: item.transaction.peer, photo: nil, media: [], uniqueGift: nil, backgroundColor: item.presentationData.theme.list.itemBlocksBackgroundColor))), false),
|
||||||
icon: nil,
|
icon: nil,
|
||||||
accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
||||||
action: { [weak self] _ in
|
action: { [weak self] _ in
|
||||||
|
|||||||
@ -132,6 +132,7 @@ enum AccountStateMutationOperation {
|
|||||||
case UpdateStarsBalance(peerId: PeerId, balance: Api.StarsAmount)
|
case UpdateStarsBalance(peerId: PeerId, balance: Api.StarsAmount)
|
||||||
case UpdateStarsRevenueStatus(peerId: PeerId, status: StarsRevenueStats.Balances)
|
case UpdateStarsRevenueStatus(peerId: PeerId, status: StarsRevenueStats.Balances)
|
||||||
case UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool)
|
case UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool)
|
||||||
|
case ReportMessageDelivery([MessageId])
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HoleFromPreviousState {
|
struct HoleFromPreviousState {
|
||||||
@ -702,9 +703,13 @@ struct AccountMutableState {
|
|||||||
self.addOperation(.UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: isAnonymous))
|
self.addOperation(.UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: isAnonymous))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutating func addReportMessageDelivery(messageIds: [MessageId]) {
|
||||||
|
self.addOperation(.ReportMessageDelivery(messageIds))
|
||||||
|
}
|
||||||
|
|
||||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||||
switch operation {
|
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:
|
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, .ReportMessageDelivery:
|
||||||
break
|
break
|
||||||
case let .AddMessages(messages, location):
|
case let .AddMessages(messages, location):
|
||||||
for message in messages {
|
for message in messages {
|
||||||
@ -852,6 +857,7 @@ struct AccountReplayedFinalState {
|
|||||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||||
let sentScheduledMessageIds: Set<MessageId>
|
let sentScheduledMessageIds: Set<MessageId>
|
||||||
|
let reportMessageDelivery: Set<MessageId>
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AccountFinalStateEvents {
|
struct AccountFinalStateEvents {
|
||||||
@ -882,12 +888,13 @@ struct AccountFinalStateEvents {
|
|||||||
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
|
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
|
||||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||||
|
let reportMessageDelivery: Set<MessageId>
|
||||||
|
|
||||||
var isEmpty: Bool {
|
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
|
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.reportMessageDelivery.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()) {
|
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(), reportMessageDelivery: Set<MessageId> = Set()) {
|
||||||
self.addedIncomingMessageIds = addedIncomingMessageIds
|
self.addedIncomingMessageIds = addedIncomingMessageIds
|
||||||
self.addedReactionEvents = addedReactionEvents
|
self.addedReactionEvents = addedReactionEvents
|
||||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||||
@ -915,6 +922,7 @@ struct AccountFinalStateEvents {
|
|||||||
self.updatedStarsBalance = updatedStarsBalance
|
self.updatedStarsBalance = updatedStarsBalance
|
||||||
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
|
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
|
||||||
self.sentScheduledMessageIds = sentScheduledMessageIds
|
self.sentScheduledMessageIds = sentScheduledMessageIds
|
||||||
|
self.reportMessageDelivery = reportMessageDelivery
|
||||||
}
|
}
|
||||||
|
|
||||||
init(state: AccountReplayedFinalState) {
|
init(state: AccountReplayedFinalState) {
|
||||||
@ -945,6 +953,7 @@ struct AccountFinalStateEvents {
|
|||||||
self.updatedStarsBalance = state.updatedStarsBalance
|
self.updatedStarsBalance = state.updatedStarsBalance
|
||||||
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
|
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
|
||||||
self.sentScheduledMessageIds = state.sentScheduledMessageIds
|
self.sentScheduledMessageIds = state.sentScheduledMessageIds
|
||||||
|
self.reportMessageDelivery = state.reportMessageDelivery
|
||||||
}
|
}
|
||||||
|
|
||||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||||
@ -977,6 +986,9 @@ struct AccountFinalStateEvents {
|
|||||||
var sentScheduledMessageIds = self.sentScheduledMessageIds
|
var sentScheduledMessageIds = self.sentScheduledMessageIds
|
||||||
sentScheduledMessageIds.formUnion(other.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)
|
var reportMessageDelivery = self.reportMessageDelivery
|
||||||
|
reportMessageDelivery.formUnion(other.reportMessageDelivery)
|
||||||
|
|
||||||
|
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, reportMessageDelivery: reportMessageDelivery)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
return TelegramMediaAction(action: .paymentRefunded(peerId: peer.peerId, currency: currency, totalAmount: totalAmount, payload: payload?.makeData(), transactionId: transactionId))
|
||||||
case let .messageActionPrizeStars(flags, stars, transactionId, boostPeer, giveawayMsgId):
|
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)))
|
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, upgradeMessageId, upgradeStars):
|
||||||
let text: String?
|
let text: String?
|
||||||
let entities: [MessageTextEntity]?
|
let entities: [MessageTextEntity]?
|
||||||
switch message {
|
switch message {
|
||||||
@ -185,7 +185,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||||
return nil
|
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, canUpgrade: (flags & (1 << 10)) != 0, upgradeStars: upgradeStars, isRefunded: (flags & (1 << 9)) != 0))
|
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, upgradeMessageId: upgradeMessageId))
|
||||||
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars):
|
case let .messageActionStarGiftUnique(flags, apiGift, canExportAt, transferStars):
|
||||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -721,6 +721,7 @@ func finalStateWithDifference(accountPeerId: PeerId, postbox: Postbox, network:
|
|||||||
updatedState.mergeChats(chats)
|
updatedState.mergeChats(chats)
|
||||||
updatedState.mergeUsers(users)
|
updatedState.mergeUsers(users)
|
||||||
|
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
for message in messages {
|
for message in messages {
|
||||||
if let preCachedResources = message.preCachedResources {
|
if let preCachedResources = message.preCachedResources {
|
||||||
for (resource, data) in preCachedResources {
|
for (resource, data) in preCachedResources {
|
||||||
@ -738,6 +739,10 @@ func finalStateWithDifference(accountPeerId: PeerId, postbox: Postbox, network:
|
|||||||
}
|
}
|
||||||
if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) {
|
if let message = StoreMessage(apiMessage: message, accountPeerId: accountPeerId, peerIsForum: peerIsForum) {
|
||||||
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
||||||
|
|
||||||
|
if let reportDeliveryAttribute = message.attributes.first(where: { $0 is ReportDeliveryMessageAttribute }) as? ReportDeliveryMessageAttribute, case let .Id(id) = message.id, reportDeliveryAttribute.untilDate > currentTime {
|
||||||
|
updatedState.addReportMessageDelivery(messageIds: [id])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,6 +906,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
|
||||||
var missingUpdatesFromChannels = Set<PeerId>()
|
var missingUpdatesFromChannels = Set<PeerId>()
|
||||||
|
|
||||||
for update in sortedUpdates(updates) {
|
for update in sortedUpdates(updates) {
|
||||||
@ -1110,6 +1117,10 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
updatedState.addMessages([message], location: .UpperHistoryBlock)
|
||||||
|
|
||||||
|
if let reportDeliveryAttribute = message.attributes.first(where: { $0 is ReportDeliveryMessageAttribute }) as? ReportDeliveryMessageAttribute, case let .Id(id) = message.id, reportDeliveryAttribute.untilDate > currentTime {
|
||||||
|
updatedState.addReportMessageDelivery(messageIds: [id])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case let .updateServiceNotification(flags, date, type, text, media, entities):
|
case let .updateServiceNotification(flags, date, type, text, media, entities):
|
||||||
let popup = (flags & (1 << 0)) != 0
|
let popup = (flags & (1 << 0)) != 0
|
||||||
@ -3282,7 +3293,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
|||||||
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
||||||
for operation in operations {
|
for operation in operations {
|
||||||
switch operation {
|
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:
|
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, .ReportMessageDelivery:
|
||||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||||
}
|
}
|
||||||
@ -3421,6 +3432,7 @@ func replayFinalState(
|
|||||||
var updatedStarsBalance: [PeerId: StarsAmount] = [:]
|
var updatedStarsBalance: [PeerId: StarsAmount] = [:]
|
||||||
var updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:]
|
var updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:]
|
||||||
var updatedStarsReactionsAreAnonymousByDefault: Bool?
|
var updatedStarsReactionsAreAnonymousByDefault: Bool?
|
||||||
|
var reportMessageDelivery = Set<MessageId>()
|
||||||
|
|
||||||
var holesFromPreviousStateMessageIds: [MessageId] = []
|
var holesFromPreviousStateMessageIds: [MessageId] = []
|
||||||
var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
|
var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
|
||||||
@ -4855,6 +4867,8 @@ func replayFinalState(
|
|||||||
updatedStarsRevenueStatus[peerId] = status
|
updatedStarsRevenueStatus[peerId] = status
|
||||||
case let .UpdateStarsReactionsAreAnonymousByDefault(value):
|
case let .UpdateStarsReactionsAreAnonymousByDefault(value):
|
||||||
updatedStarsReactionsAreAnonymousByDefault = value
|
updatedStarsReactionsAreAnonymousByDefault = value
|
||||||
|
case let .ReportMessageDelivery(messageIds):
|
||||||
|
reportMessageDelivery = Set(messageIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5376,6 +5390,7 @@ func replayFinalState(
|
|||||||
updatedRevenueBalances: updatedRevenueBalances,
|
updatedRevenueBalances: updatedRevenueBalances,
|
||||||
updatedStarsBalance: updatedStarsBalance,
|
updatedStarsBalance: updatedStarsBalance,
|
||||||
updatedStarsRevenueStatus: updatedStarsRevenueStatus,
|
updatedStarsRevenueStatus: updatedStarsRevenueStatus,
|
||||||
sentScheduledMessageIds: finalState.state.sentScheduledMessageIds
|
sentScheduledMessageIds: finalState.state.sentScheduledMessageIds,
|
||||||
|
reportMessageDelivery: reportMessageDelivery
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -352,6 +352,7 @@ public final class AccountStateManager {
|
|||||||
private let appliedMaxMessageIdDisposable = MetaDisposable()
|
private let appliedMaxMessageIdDisposable = MetaDisposable()
|
||||||
private let appliedQtsPromise = Promise<Int32?>(nil)
|
private let appliedQtsPromise = Promise<Int32?>(nil)
|
||||||
private let appliedQtsDisposable = MetaDisposable()
|
private let appliedQtsDisposable = MetaDisposable()
|
||||||
|
private let reportMessageDeliveryDisposable = DisposableSet()
|
||||||
|
|
||||||
let updateConfigRequested: (() -> Void)?
|
let updateConfigRequested: (() -> Void)?
|
||||||
let isPremiumUpdated: (() -> Void)?
|
let isPremiumUpdated: (() -> Void)?
|
||||||
@ -391,6 +392,7 @@ public final class AccountStateManager {
|
|||||||
self.operationDisposable.dispose()
|
self.operationDisposable.dispose()
|
||||||
self.appliedMaxMessageIdDisposable.dispose()
|
self.appliedMaxMessageIdDisposable.dispose()
|
||||||
self.appliedQtsDisposable.dispose()
|
self.appliedQtsDisposable.dispose()
|
||||||
|
self.reportMessageDeliveryDisposable.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reset() {
|
public func reset() {
|
||||||
@ -1130,6 +1132,9 @@ public final class AccountStateManager {
|
|||||||
if !events.sentScheduledMessageIds.isEmpty {
|
if !events.sentScheduledMessageIds.isEmpty {
|
||||||
strongSelf.sentScheduledMessageIdsPipe.putNext(events.sentScheduledMessageIds)
|
strongSelf.sentScheduledMessageIdsPipe.putNext(events.sentScheduledMessageIds)
|
||||||
}
|
}
|
||||||
|
if !events.reportMessageDelivery.isEmpty {
|
||||||
|
strongSelf.reportMessageDeliveryDisposable.add(_internal_reportMessageDelivery(postbox: strongSelf.postbox, network: strongSelf.network, messageIds: Array(events.reportMessageDelivery), fromPushNotification: false).start())
|
||||||
|
}
|
||||||
if !events.isContactUpdates.isEmpty {
|
if !events.isContactUpdates.isEmpty {
|
||||||
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,24 +4,20 @@ import TelegramApi
|
|||||||
|
|
||||||
public final class ReportDeliveryMessageAttribute: Equatable, MessageAttribute {
|
public final class ReportDeliveryMessageAttribute: Equatable, MessageAttribute {
|
||||||
public let untilDate: Int32
|
public let untilDate: Int32
|
||||||
public let isReported: Bool
|
|
||||||
|
|
||||||
public init(untilDate: Int32, isReported: Bool) {
|
public init(untilDate: Int32, isReported: Bool) {
|
||||||
self.untilDate = untilDate
|
self.untilDate = untilDate
|
||||||
self.isReported = isReported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(decoder: PostboxDecoder) {
|
required public init(decoder: PostboxDecoder) {
|
||||||
self.untilDate = decoder.decodeInt32ForKey("d", orElse: 0)
|
self.untilDate = decoder.decodeInt32ForKey("d", orElse: 0)
|
||||||
self.isReported = decoder.decodeBoolForKey("r", orElse: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
encoder.encodeInt32(self.untilDate, forKey: "d")
|
encoder.encodeInt32(self.untilDate, forKey: "d")
|
||||||
encoder.encodeBool(self.isReported, forKey: "r")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: ReportDeliveryMessageAttribute, rhs: ReportDeliveryMessageAttribute) -> Bool {
|
public static func ==(lhs: ReportDeliveryMessageAttribute, rhs: ReportDeliveryMessageAttribute) -> Bool {
|
||||||
return lhs.untilDate == rhs.untilDate && lhs.isReported == rhs.isReported
|
return lhs.untilDate == rhs.untilDate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -130,7 +130,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
case paymentRefunded(peerId: PeerId, currency: String, totalAmount: Int64, payload: Data?, transactionId: String)
|
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 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 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, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool)
|
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, upgradeMessageId: Int32?)
|
||||||
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool)
|
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool)
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -253,7 +253,7 @@ 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)
|
self = .prizeStars(amount: decoder.decodeInt64ForKey("amount", orElse: 0), isUnclaimed: decoder.decodeBoolForKey("unclaimed", orElse: false), boostPeerId: boostPeerId, transactionId: decoder.decodeOptionalStringForKey("transactionId"), giveawayMessageId: giveawayMessageId)
|
||||||
case 44:
|
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), canUpgrade: decoder.decodeBoolForKey("canUpgrade", orElse: false), upgradeStars: decoder.decodeOptionalInt64ForKey("upgradeStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false))
|
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), upgradeMessageId: decoder.decodeOptionalInt32ForKey("upgradeMessageId"))
|
||||||
case 45:
|
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"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false))
|
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:
|
default:
|
||||||
@ -548,7 +548,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
encoder.encodeNil(forKey: "giveawayMsgId")
|
encoder.encodeNil(forKey: "giveawayMsgId")
|
||||||
}
|
}
|
||||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded):
|
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, upgradeMessageId):
|
||||||
encoder.encodeInt32(44, forKey: "_rawValue")
|
encoder.encodeInt32(44, forKey: "_rawValue")
|
||||||
encoder.encodeObject(gift, forKey: "gift")
|
encoder.encodeObject(gift, forKey: "gift")
|
||||||
if let convertStars {
|
if let convertStars {
|
||||||
@ -574,6 +574,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||||||
encoder.encodeNil(forKey: "upgradeStars")
|
encoder.encodeNil(forKey: "upgradeStars")
|
||||||
}
|
}
|
||||||
encoder.encodeBool(isRefunded, forKey: "isRefunded")
|
encoder.encodeBool(isRefunded, forKey: "isRefunded")
|
||||||
|
if let upgradeMessageId {
|
||||||
|
encoder.encodeInt32(upgradeMessageId, forKey: "upgradeMessageId")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "upgradeMessageId")
|
||||||
|
}
|
||||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded):
|
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded):
|
||||||
encoder.encodeInt32(45, forKey: "_rawValue")
|
encoder.encodeInt32(45, forKey: "_rawValue")
|
||||||
encoder.encodeObject(gift, forKey: "gift")
|
encoder.encodeObject(gift, forKey: "gift")
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
import Foundation
|
||||||
|
import Postbox
|
||||||
|
import SwiftSignalKit
|
||||||
|
import TelegramApi
|
||||||
|
|
||||||
|
public func _internal_reportMessageDelivery(postbox: Postbox, network: Network, messageIds: [EngineMessage.Id], fromPushNotification: Bool) -> Signal<Never, NoError> {
|
||||||
|
var signals: [Signal<Void, NoError>] = []
|
||||||
|
for (peerId, messageIds) in messagesIdsGroupedByPeerId(messageIds) {
|
||||||
|
signals.append(_internal_reportMessageDeliveryByPeerId(postbox: postbox, network: network, peerId: peerId, messageIds: messageIds, fromPushNotification: fromPushNotification))
|
||||||
|
}
|
||||||
|
return combineLatest(signals)
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _internal_reportMessageDeliveryByPeerId(postbox: Postbox, network: Network, peerId: EnginePeer.Id, messageIds: [EngineMessage.Id], fromPushNotification: Bool) -> Signal<Void, NoError> {
|
||||||
|
return postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
|
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
var flags: Int32 = 0
|
||||||
|
if fromPushNotification {
|
||||||
|
flags |= (1 << 0)
|
||||||
|
}
|
||||||
|
return network.request(Api.functions.messages.reportMessagesDelivery(flags: flags, peer: inputPeer, id: messageIds.map { $0.id }))
|
||||||
|
|> `catch` { error -> Signal<Api.Bool, NoError> in
|
||||||
|
return .single(.boolFalse)
|
||||||
|
}
|
||||||
|
|> mapToSignal { _ in
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> switchToLatest
|
||||||
|
}
|
||||||
@ -175,6 +175,7 @@ extension BotPaymentMethod {
|
|||||||
public enum BotPaymentFormRequestError {
|
public enum BotPaymentFormRequestError {
|
||||||
case generic
|
case generic
|
||||||
case alreadyActive
|
case alreadyActive
|
||||||
|
case noPaymentNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BotPaymentInvoice {
|
extension BotPaymentInvoice {
|
||||||
@ -457,7 +458,10 @@ func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, netw
|
|||||||
}
|
}
|
||||||
|
|
||||||
return network.request(Api.functions.payments.getPaymentForm(flags: flags, invoice: invoice, themeParams: serializedThemeParams))
|
return network.request(Api.functions.payments.getPaymentForm(flags: flags, invoice: invoice, themeParams: serializedThemeParams))
|
||||||
|> `catch` { _ -> Signal<Api.payments.PaymentForm, BotPaymentFormRequestError> in
|
|> `catch` { error -> Signal<Api.payments.PaymentForm, BotPaymentFormRequestError> in
|
||||||
|
if error.errorDescription == "NO_PAYMENT_NEEDED" {
|
||||||
|
return .fail(.noPaymentNeeded)
|
||||||
|
}
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
|
|> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
|
||||||
@ -622,7 +626,7 @@ public enum SendBotPaymentFormError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SendBotPaymentResult {
|
public enum SendBotPaymentResult {
|
||||||
case done(receiptMessageId: MessageId?, subscriptionPeerId: PeerId?)
|
case done(receiptMessageId: MessageId?, subscriptionPeerId: PeerId?, uniqueStarGift: ProfileGiftsContext.State.StarGift?)
|
||||||
case externalVerificationRequired(url: String)
|
case externalVerificationRequired(url: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -671,12 +675,12 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
|
|||||||
case .starsChatSubscription:
|
case .starsChatSubscription:
|
||||||
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
||||||
if let first = chats.first {
|
if let first = chats.first {
|
||||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id)
|
return .done(receiptMessageId: nil, subscriptionPeerId: first.id, uniqueStarGift: nil)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
for apiMessage in updates.messages {
|
for apiMessage in updates.messages {
|
||||||
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
@ -721,7 +725,7 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .done(receiptMessageId: receiptMessageId, subscriptionPeerId: nil)
|
return .done(receiptMessageId: receiptMessageId, subscriptionPeerId: nil, uniqueStarGift: nil)
|
||||||
case let .paymentVerificationNeeded(url):
|
case let .paymentVerificationNeeded(url):
|
||||||
return .externalVerificationRequired(url: url)
|
return .externalVerificationRequired(url: url)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -689,15 +689,23 @@ func _internal_transferStarGift(account: Account, prepaid: Bool, messageId: Engi
|
|||||||
} else {
|
} else {
|
||||||
let source: BotPaymentInvoiceSource = .starGiftTransfer(messageId: messageId, toPeerId: peerId)
|
let source: BotPaymentInvoiceSource = .starGiftTransfer(messageId: messageId, toPeerId: peerId)
|
||||||
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|
||||||
|> mapError { _ -> TransferStarGiftError in
|
|> map(Optional.init)
|
||||||
return .generic
|
|> `catch` { error -> Signal<BotPaymentForm?, TransferStarGiftError> in
|
||||||
|
if case .noPaymentNeeded = error {
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
|> mapToSignal { paymentForm in
|
|> mapToSignal { paymentForm in
|
||||||
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|
if let paymentForm {
|
||||||
|> mapError { _ -> TransferStarGiftError in
|
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|
||||||
return .generic
|
|> mapError { _ -> TransferStarGiftError in
|
||||||
|
return .generic
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
} else {
|
||||||
|
return _internal_transferStarGift(account: account, prepaid: true, messageId: messageId, peerId: peerId)
|
||||||
}
|
}
|
||||||
|> ignoreValues
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -714,8 +722,12 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, messageId: Engi
|
|||||||
|> mapError { _ -> UpgradeStarGiftError in
|
|> mapError { _ -> UpgradeStarGiftError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|> mapToSignal { _ in
|
|> mapToSignal { result in
|
||||||
return .complete()
|
if case let .done(_, _, gift) = result, let gift {
|
||||||
|
return .single(gift)
|
||||||
|
} else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var flags: Int32 = 0
|
var flags: Int32 = 0
|
||||||
@ -970,16 +982,30 @@ private final class ProfileGiftsContextImpl {
|
|||||||
self.pushState()
|
self.pushState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
|
func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
|
||||||
self.actionDisposable.set(
|
return Signal { [weak self] subscriber in
|
||||||
_internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict(next: { [weak self] result in
|
guard let self else {
|
||||||
guard let self else {
|
return EmptyDisposable
|
||||||
return
|
}
|
||||||
}
|
let disposable = MetaDisposable()
|
||||||
let _ = self
|
disposable.set(
|
||||||
})
|
_internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict(next: { [weak self] result in
|
||||||
)
|
guard let self else {
|
||||||
self.pushState()
|
return
|
||||||
|
}
|
||||||
|
if let index = self.gifts.firstIndex(where: { $0.messageId == messageId }) {
|
||||||
|
self.gifts[index] = result
|
||||||
|
self.pushState()
|
||||||
|
}
|
||||||
|
subscriber.putNext(result)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return disposable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pushState() {
|
private func pushState() {
|
||||||
@ -1186,9 +1212,19 @@ public final class ProfileGiftsContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
|
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
|
||||||
self.impl.with { impl in
|
return Signal { subscriber in
|
||||||
impl.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
|
let disposable = MetaDisposable()
|
||||||
|
self.impl.with { impl in
|
||||||
|
disposable.set(impl.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).start(next: { value in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
}, error: { error in
|
||||||
|
subscriber.putError(error)
|
||||||
|
}, completed: {
|
||||||
|
subscriber.putCompletion()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return disposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1418,12 +1418,13 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
|
|||||||
case .starsChatSubscription:
|
case .starsChatSubscription:
|
||||||
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
||||||
if let first = chats.first {
|
if let first = chats.first {
|
||||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id)
|
return .done(receiptMessageId: nil, subscriptionPeerId: first.id, uniqueStarGift: nil)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var receiptMessageId: MessageId?
|
var receiptMessageId: MessageId?
|
||||||
|
var resultGift: ProfileGiftsContext.State.StarGift?
|
||||||
for apiMessage in updates.messages {
|
for apiMessage in updates.messages {
|
||||||
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
||||||
for media in message.media {
|
for media in message.media {
|
||||||
@ -1463,12 +1464,28 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
|
|||||||
case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer:
|
case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer:
|
||||||
receiptMessageId = nil
|
receiptMessageId = nil
|
||||||
}
|
}
|
||||||
|
} else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _) = action.action, case let .Id(messageId) = message.id {
|
||||||
|
resultGift = 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 .done(receiptMessageId: receiptMessageId, subscriptionPeerId: nil)
|
return .done(receiptMessageId: receiptMessageId, subscriptionPeerId: nil, uniqueStarGift: resultGift)
|
||||||
case let .paymentVerificationNeeded(url):
|
case let .paymentVerificationNeeded(url):
|
||||||
return .externalVerificationRequired(url: url)
|
return .externalVerificationRequired(url: url)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1066,7 +1066,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
attributedString = mutableString
|
attributedString = mutableString
|
||||||
case .prizeStars:
|
case .prizeStars:
|
||||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _):
|
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, _):
|
||||||
if !forAdditionalServiceMessage {
|
if !forAdditionalServiceMessage {
|
||||||
if let text {
|
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()))
|
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()))
|
||||||
|
|||||||
@ -467,7 +467,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
||||||
hasServiceMessage = false
|
hasServiceMessage = false
|
||||||
}
|
}
|
||||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, _, upgradeStars, isRefunded):
|
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, _, upgradeStars, isRefunded, _):
|
||||||
if case let .generic(gift) = gift {
|
if case let .generic(gift) = gift {
|
||||||
isStarGift = true
|
isStarGift = true
|
||||||
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||||
@ -488,7 +488,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle_Displaying
|
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle_Displaying
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let convertStars {
|
if let convertStars, convertStars > 0 {
|
||||||
text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string
|
text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string
|
||||||
} else {
|
} else {
|
||||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
||||||
@ -500,16 +500,20 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
peerName = EnginePeer(peer).compactDisplayTitle
|
peerName = EnginePeer(peer).compactDisplayTitle
|
||||||
}
|
}
|
||||||
if peerName.isEmpty {
|
if peerName.isEmpty {
|
||||||
if let convertStars {
|
if let convertStars, convertStars > 0 {
|
||||||
text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string
|
text = item.presentationData.strings.Notification_StarGift_Subtitle(item.presentationData.strings.Notification_StarGift_Subtitle_Stars(Int32(convertStars))).string
|
||||||
} else {
|
} else {
|
||||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars ?? 0)))
|
if let convertStars, convertStars > 0 {
|
||||||
text = formattedString.string
|
let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars)))
|
||||||
if let starsRange = formattedString.ranges.last {
|
text = formattedString.string
|
||||||
entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold))
|
if let starsRange = formattedString.ranges.last {
|
||||||
|
entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -524,17 +528,22 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
ribbonTitle = item.presentationData.strings.Notification_StarGift_OneOf(availabilityString).string
|
ribbonTitle = item.presentationData.strings.Notification_StarGift_OneOf(availabilityString).string
|
||||||
}
|
}
|
||||||
if incoming, let upgradeStars, upgradeStars > 0, !upgraded {
|
if incoming || item.presentationData.isPreview, let upgradeStars, upgradeStars > 0, !upgraded {
|
||||||
buttonTitle = item.presentationData.strings.Notification_StarGift_Unpack
|
buttonTitle = item.presentationData.strings.Notification_StarGift_Unpack
|
||||||
buttonIcon = "Premium/GiftUnpack"
|
buttonIcon = "Premium/GiftUnpack"
|
||||||
} else {
|
} else {
|
||||||
buttonTitle = item.presentationData.strings.Notification_StarGift_View
|
buttonTitle = item.presentationData.strings.Notification_StarGift_View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .starGiftUnique(gift, _, _, _, _, _, isRefunded):
|
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded):
|
||||||
if case let .unique(uniqueGift) = gift {
|
if case let .unique(uniqueGift) = gift {
|
||||||
isStarGift = true
|
isStarGift = true
|
||||||
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
let authorName: String
|
||||||
|
if isUpgrade && item.message.author?.id == item.context.account.peerId {
|
||||||
|
authorName = item.message.peers[item.message.id.peerId].flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||||
|
} else {
|
||||||
|
authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||||
|
}
|
||||||
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
|
title = item.presentationData.strings.Notification_StarGift_Title(authorName).string
|
||||||
text = "**\(uniqueGift.title) #\(uniqueGift.number)**"
|
text = "**\(uniqueGift.title) #\(uniqueGift.number)**"
|
||||||
ribbonTitle = item.presentationData.strings.Notification_StarGift_Gift
|
ribbonTitle = item.presentationData.strings.Notification_StarGift_Gift
|
||||||
|
|||||||
@ -24,6 +24,9 @@ swift_library(
|
|||||||
"//submodules/PresentationDataUtils",
|
"//submodules/PresentationDataUtils",
|
||||||
"//submodules/TextFormat",
|
"//submodules/TextFormat",
|
||||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||||
|
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||||
|
"//submodules/AnimatedStickerNode",
|
||||||
|
"//submodules/TelegramAnimatedStickerNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -13,15 +13,21 @@ public final class GiftAnimationComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let file: TelegramMediaFile?
|
let file: TelegramMediaFile?
|
||||||
|
let still: Bool
|
||||||
|
let size: CGSize?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
file: TelegramMediaFile?
|
file: TelegramMediaFile?,
|
||||||
|
still: Bool = false,
|
||||||
|
size: CGSize? = nil
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.file = file
|
self.file = file
|
||||||
|
self.still = still
|
||||||
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: GiftAnimationComponent, rhs: GiftAnimationComponent) -> Bool {
|
public static func ==(lhs: GiftAnimationComponent, rhs: GiftAnimationComponent) -> Bool {
|
||||||
@ -34,6 +40,12 @@ public final class GiftAnimationComponent: Component {
|
|||||||
if lhs.file != rhs.file {
|
if lhs.file != rhs.file {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.still != rhs.still {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.size != rhs.size {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +73,7 @@ public final class GiftAnimationComponent: Component {
|
|||||||
file: component.file
|
file: component.file
|
||||||
)
|
)
|
||||||
|
|
||||||
let iconSize = availableSize
|
let iconSize = component.size ?? availableSize
|
||||||
if self.animationLayer == nil {
|
if self.animationLayer == nil {
|
||||||
let animationLayer = InlineStickerItemLayer(
|
let animationLayer = InlineStickerItemLayer(
|
||||||
context: .account(component.context),
|
context: .account(component.context),
|
||||||
@ -71,12 +83,12 @@ public final class GiftAnimationComponent: Component {
|
|||||||
file: component.file,
|
file: component.file,
|
||||||
cache: component.context.animationCache,
|
cache: component.context.animationCache,
|
||||||
renderer: component.context.animationRenderer,
|
renderer: component.context.animationRenderer,
|
||||||
unique: true,
|
unique: !component.still,
|
||||||
placeholderColor: component.theme.list.mediaPlaceholderColor,
|
placeholderColor: component.theme.list.mediaPlaceholderColor,
|
||||||
pointSize: CGSize(width: iconSize.width * 1.2, height: iconSize.height * 1.2),
|
pointSize: CGSize(width: iconSize.width * 1.2, height: iconSize.height * 1.2),
|
||||||
loopCount: 1
|
loopCount: component.still ? 0 : 1
|
||||||
)
|
)
|
||||||
animationLayer.isVisibleForAnimations = true
|
animationLayer.isVisibleForAnimations = !component.still
|
||||||
self.animationLayer = animationLayer
|
self.animationLayer = animationLayer
|
||||||
self.layer.addSublayer(animationLayer)
|
self.layer.addSublayer(animationLayer)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import PeerInfoCoverComponent
|
|||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
|
|
||||||
final class GiftCompositionComponent: Component {
|
public final class GiftCompositionComponent: Component {
|
||||||
public class ExternalState {
|
public class ExternalState {
|
||||||
public fileprivate(set) var previewPatternColor: UIColor?
|
public fileprivate(set) var previewPatternColor: UIColor?
|
||||||
public init() {
|
public init() {
|
||||||
@ -21,7 +21,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Subject: Equatable {
|
public enum Subject: Equatable {
|
||||||
case generic(TelegramMediaFile)
|
case generic(TelegramMediaFile)
|
||||||
case unique(StarGift.UniqueGift)
|
case unique(StarGift.UniqueGift)
|
||||||
case preview([StarGift.UniqueGift.Attribute])
|
case preview([StarGift.UniqueGift.Attribute])
|
||||||
@ -30,15 +30,15 @@ final class GiftCompositionComponent: Component {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let subject: Subject
|
let subject: Subject
|
||||||
let externalState: ExternalState
|
let externalState: ExternalState?
|
||||||
let requestUpdate: () -> Void
|
let requestUpdate: () -> Void
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
subject: Subject,
|
subject: Subject,
|
||||||
externalState: ExternalState,
|
externalState: ExternalState? = nil,
|
||||||
requestUpdate: @escaping () -> Void
|
requestUpdate: @escaping () -> Void = {}
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -47,7 +47,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
self.requestUpdate = requestUpdate
|
self.requestUpdate = requestUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: GiftCompositionComponent, rhs: GiftCompositionComponent) -> Bool {
|
public static func ==(lhs: GiftCompositionComponent, rhs: GiftCompositionComponent) -> Bool {
|
||||||
if lhs.context !== rhs.context {
|
if lhs.context !== rhs.context {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -60,7 +60,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
final class View: UIView {
|
public final class View: UIView {
|
||||||
private var component: GiftCompositionComponent?
|
private var component: GiftCompositionComponent?
|
||||||
private weak var componentState: EmptyComponentState?
|
private weak var componentState: EmptyComponentState?
|
||||||
|
|
||||||
@ -84,6 +84,8 @@ final class GiftCompositionComponent: Component {
|
|||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap)))
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -94,7 +96,16 @@ final class GiftCompositionComponent: Component {
|
|||||||
self.disposables.dispose()
|
self.disposables.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(component: GiftCompositionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
@objc private func handleTap() {
|
||||||
|
guard let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if case .once = animationNode.playbackMode, !animationNode.isPlaying {
|
||||||
|
animationNode.playOnce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(component: GiftCompositionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
let previousComponent = self.component
|
let previousComponent = self.component
|
||||||
|
|
||||||
self.component = component
|
self.component = component
|
||||||
@ -107,6 +118,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
var patternFile: TelegramMediaFile?
|
var patternFile: TelegramMediaFile?
|
||||||
var files: [Int64: TelegramMediaFile] = [:]
|
var files: [Int64: TelegramMediaFile] = [:]
|
||||||
|
|
||||||
|
var loop = false
|
||||||
switch component.subject {
|
switch component.subject {
|
||||||
case let .generic(file):
|
case let .generic(file):
|
||||||
animationFile = file
|
animationFile = file
|
||||||
@ -142,6 +154,8 @@ final class GiftCompositionComponent: Component {
|
|||||||
self.previewTimer = nil
|
self.previewTimer = nil
|
||||||
}
|
}
|
||||||
case let .preview(sampleAttributes):
|
case let .preview(sampleAttributes):
|
||||||
|
loop = true
|
||||||
|
|
||||||
if self.previewModels.isEmpty {
|
if self.previewModels.isEmpty {
|
||||||
var models: [StarGift.UniqueGift.Attribute] = []
|
var models: [StarGift.UniqueGift.Attribute] = []
|
||||||
var patterns: [StarGift.UniqueGift.Attribute] = []
|
var patterns: [StarGift.UniqueGift.Attribute] = []
|
||||||
@ -198,8 +212,21 @@ final class GiftCompositionComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.previewModelIndex = (self.previewModelIndex + 1) % Int32(self.previewModels.count)
|
self.previewModelIndex = (self.previewModelIndex + 1) % Int32(self.previewModels.count)
|
||||||
self.previewPatternIndex = (self.previewPatternIndex + 1) % Int32(self.previewPatterns.count)
|
|
||||||
self.previewBackdropIndex = (self.previewBackdropIndex + 1) % Int32(self.previewBackdrops.count)
|
let previousPatternIndex = self.previewPatternIndex
|
||||||
|
var randomPatternIndex = previousPatternIndex
|
||||||
|
while randomPatternIndex == previousPatternIndex {
|
||||||
|
randomPatternIndex = Int32.random(in: 0 ..< Int32(self.previewPatterns.count))
|
||||||
|
}
|
||||||
|
self.previewPatternIndex = randomPatternIndex
|
||||||
|
|
||||||
|
let previousBackdropIndex = self.previewBackdropIndex
|
||||||
|
var randomBackdropIndex = previousBackdropIndex
|
||||||
|
while randomBackdropIndex == previousBackdropIndex {
|
||||||
|
randomBackdropIndex = Int32.random(in: 0 ..< Int32(self.previewBackdrops.count))
|
||||||
|
}
|
||||||
|
self.previewBackdropIndex = randomBackdropIndex
|
||||||
|
|
||||||
self.animatePreviewTransition = true
|
self.animatePreviewTransition = true
|
||||||
self.componentState?.updated(transition: .easeInOut(duration: 0.25))
|
self.componentState?.updated(transition: .easeInOut(duration: 0.25))
|
||||||
}, queue: Queue.mainQueue())
|
}, queue: Queue.mainQueue())
|
||||||
@ -207,7 +234,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component.externalState.previewPatternColor = secondBackgroundColor
|
component.externalState?.previewPatternColor = secondBackgroundColor
|
||||||
|
|
||||||
var animateTransition = false
|
var animateTransition = false
|
||||||
if self.animatePreviewTransition {
|
if self.animatePreviewTransition {
|
||||||
@ -247,6 +274,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
if backgroundView.superview == nil {
|
if backgroundView.superview == nil {
|
||||||
backgroundTransition = .immediate
|
backgroundTransition = .immediate
|
||||||
backgroundView.clipsToBounds = true
|
backgroundView.clipsToBounds = true
|
||||||
|
backgroundView.isUserInteractionEnabled = false
|
||||||
self.insertSubview(backgroundView, at: 0)
|
self.insertSubview(backgroundView, at: 0)
|
||||||
|
|
||||||
backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
@ -259,7 +287,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconSize = CGSize(width: 128.0, height: 128.0)
|
let iconSize = CGSize(width: 136.0, height: 136.0)
|
||||||
|
|
||||||
var startFromIndex: Int?
|
var startFromIndex: Int?
|
||||||
if animateTransition, let disappearingAnimationNode = self.animationNode {
|
if animateTransition, let disappearingAnimationNode = self.animationNode {
|
||||||
@ -274,17 +302,25 @@ final class GiftCompositionComponent: Component {
|
|||||||
let animationNode: AnimatedStickerNode
|
let animationNode: AnimatedStickerNode
|
||||||
if self.animationNode == nil {
|
if self.animationNode == nil {
|
||||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||||
|
animationNode.isUserInteractionEnabled = false
|
||||||
self.animationNode = animationNode
|
self.animationNode = animationNode
|
||||||
|
|
||||||
self.addSubview(animationNode.view)
|
self.addSubview(animationNode.view)
|
||||||
|
|
||||||
let pathPrefix = component.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
let pathPrefix = component.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(file.resource.id)
|
||||||
animationNode.setup(source: AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(iconSize.width * 1.6), height: Int(iconSize.height * 1.6), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
animationNode.setup(source: AnimatedStickerResourceSource(account: component.context.account, resource: file.resource, isVideo: file.isVideoSticker), width: Int(iconSize.width * 1.6), height: Int(iconSize.height * 1.6), playbackMode: .loop, mode: .direct(cachePathPrefix: pathPrefix))
|
||||||
|
|
||||||
if let startFromIndex {
|
if let startFromIndex {
|
||||||
|
if let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl {
|
||||||
|
animationNode.playbackMode = loop ? .loop : .once
|
||||||
|
}
|
||||||
animationNode.play(firstFrame: false, fromIndex: startFromIndex)
|
animationNode.play(firstFrame: false, fromIndex: startFromIndex)
|
||||||
} else {
|
} else {
|
||||||
animationNode.playLoop()
|
if loop {
|
||||||
|
animationNode.playLoop()
|
||||||
|
} else {
|
||||||
|
animationNode.playOnce()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
animationNode.visibility = true
|
animationNode.visibility = true
|
||||||
animationNode.updateLayout(size: iconSize)
|
animationNode.updateLayout(size: iconSize)
|
||||||
@ -295,7 +331,7 @@ final class GiftCompositionComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let animationNode = self.animationNode {
|
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))
|
transition.setFrame(layer: animationNode.layer, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: 20.0), size: iconSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
@ -227,7 +227,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
|
|||||||
case let .starGift(gift):
|
case let .starGift(gift):
|
||||||
media = [
|
media = [
|
||||||
TelegramMediaAction(
|
TelegramMediaAction(
|
||||||
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)
|
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 ? 1 : nil, isRefunded: false, upgradeMessageId: nil)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,7 +139,7 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.scrollView = ScrollView()
|
self.scrollView = ScrollView()
|
||||||
self.scrollView.showsVerticalScrollIndicator = true
|
self.scrollView.showsVerticalScrollIndicator = false
|
||||||
self.scrollView.showsHorizontalScrollIndicator = false
|
self.scrollView.showsHorizontalScrollIndicator = false
|
||||||
self.scrollView.scrollsToTop = false
|
self.scrollView.scrollsToTop = false
|
||||||
self.scrollView.delaysContentTouches = false
|
self.scrollView.delaysContentTouches = false
|
||||||
@ -253,6 +253,10 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
if let completion {
|
if let completion {
|
||||||
completion()
|
completion()
|
||||||
|
|
||||||
|
if let self, let controller = self.environment?.controller() {
|
||||||
|
controller.dismiss()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
guard let self, case .purchased = status, let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController else {
|
guard let self, case .purchased = status, let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
@ -631,7 +635,6 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
transition.setFrame(view: navigationTitleView, frame: navigationTitleFrame)
|
transition.setFrame(view: navigationTitleView, frame: navigationTitleFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
let bottomContentInset: CGFloat = 24.0
|
|
||||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||||
let sectionSpacing: CGFloat = 24.0
|
let sectionSpacing: CGFloat = 24.0
|
||||||
|
|
||||||
@ -842,8 +845,8 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor),
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.freeTextColor),
|
||||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor),
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor),
|
||||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
|
||||||
linkAttribute: { url in
|
linkAttribute: { contents in
|
||||||
return ("URL", url)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
let upgradeFooterText = NSMutableAttributedString(attributedString: parsedString)
|
let upgradeFooterText = NSMutableAttributedString(attributedString: parsedString)
|
||||||
@ -986,19 +989,19 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
contentHeight += hideSectionSize.height
|
contentHeight += hideSectionSize.height
|
||||||
}
|
}
|
||||||
|
|
||||||
contentHeight += bottomContentInset
|
contentHeight += 24.0
|
||||||
|
|
||||||
let combinedBottomInset = max(inputHeight, environment.safeInsets.bottom)
|
|
||||||
contentHeight += combinedBottomInset
|
|
||||||
|
|
||||||
if self.starImage == nil || self.starImage?.1 !== environment.theme {
|
|
||||||
self.starImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: environment.theme.list.itemCheckColors.foregroundColor)!, environment.theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
let buttonHeight: CGFloat = 50.0
|
let buttonHeight: CGFloat = 50.0
|
||||||
let bottomPanelPadding: CGFloat = 12.0
|
let bottomPanelPadding: CGFloat = 12.0
|
||||||
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
||||||
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
|
let bottomPanelHeight = bottomPanelPadding + buttonHeight + bottomInset
|
||||||
|
|
||||||
|
let combinedBottomInset = max(inputHeight, environment.safeInsets.bottom)
|
||||||
|
contentHeight += max(bottomPanelHeight, combinedBottomInset)
|
||||||
|
|
||||||
|
if self.starImage == nil || self.starImage?.1 !== environment.theme {
|
||||||
|
self.starImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: environment.theme.list.itemCheckColors.foregroundColor)!, environment.theme)
|
||||||
|
}
|
||||||
|
|
||||||
let bottomPanelSize = self.buttonBackground.update(
|
let bottomPanelSize = self.buttonBackground.update(
|
||||||
transition: transition,
|
transition: transition,
|
||||||
|
|||||||
@ -37,12 +37,9 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
||||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
|
||||||
"//submodules/TelegramUI/Components/CheckComponent",
|
"//submodules/TelegramUI/Components/CheckComponent",
|
||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
"//submodules/ConfettiEffect",
|
"//submodules/ConfettiEffect",
|
||||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
|
||||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
|
||||||
"//submodules/TooltipUI",
|
"//submodules/TooltipUI",
|
||||||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -259,7 +259,7 @@ public func giftTransferAlertController(context: AccountContext, gift: StarGift.
|
|||||||
let buttonText: String
|
let buttonText: String
|
||||||
if transferStars > 0 {
|
if transferStars > 0 {
|
||||||
text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(transferStars))).string
|
text = strings.Gift_Transfer_Confirmation_Text("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder), strings.Gift_Transfer_Confirmation_Text_Stars(Int32(transferStars))).string
|
||||||
buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) ⭐️\(transferStars)"
|
buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) $ \(transferStars)"
|
||||||
} else {
|
} else {
|
||||||
text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||||
buttonText = strings.Gift_Transfer_Confirmation_TransferFree
|
buttonText = strings.Gift_Transfer_Confirmation_TransferFree
|
||||||
|
|||||||
@ -27,6 +27,7 @@ import ConfettiEffect
|
|||||||
import PlainButtonComponent
|
import PlainButtonComponent
|
||||||
import CheckComponent
|
import CheckComponent
|
||||||
import TooltipUI
|
import TooltipUI
|
||||||
|
import GiftAnimationComponent
|
||||||
|
|
||||||
private let modelButtonTag = GenericComponentViewTag()
|
private let modelButtonTag = GenericComponentViewTag()
|
||||||
private let backdropButtonTag = GenericComponentViewTag()
|
private let backdropButtonTag = GenericComponentViewTag()
|
||||||
@ -45,6 +46,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let sendGift: (EnginePeer.Id) -> Void
|
let sendGift: (EnginePeer.Id) -> Void
|
||||||
let openMyGifts: () -> Void
|
let openMyGifts: () -> Void
|
||||||
let transferGift: () -> Void
|
let transferGift: () -> Void
|
||||||
|
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||||
let showAttributeInfo: (Any, Float) -> Void
|
let showAttributeInfo: (Any, Float) -> Void
|
||||||
let getController: () -> ViewController?
|
let getController: () -> ViewController?
|
||||||
|
|
||||||
@ -59,6 +61,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
sendGift: @escaping (EnginePeer.Id) -> Void,
|
sendGift: @escaping (EnginePeer.Id) -> Void,
|
||||||
openMyGifts: @escaping () -> Void,
|
openMyGifts: @escaping () -> Void,
|
||||||
transferGift: @escaping () -> Void,
|
transferGift: @escaping () -> Void,
|
||||||
|
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||||
showAttributeInfo: @escaping (Any, Float) -> Void,
|
showAttributeInfo: @escaping (Any, Float) -> Void,
|
||||||
getController: @escaping () -> ViewController?
|
getController: @escaping () -> ViewController?
|
||||||
) {
|
) {
|
||||||
@ -72,6 +75,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
self.sendGift = sendGift
|
self.sendGift = sendGift
|
||||||
self.openMyGifts = openMyGifts
|
self.openMyGifts = openMyGifts
|
||||||
self.transferGift = transferGift
|
self.transferGift = transferGift
|
||||||
|
self.upgradeGift = upgradeGift
|
||||||
self.showAttributeInfo = showAttributeInfo
|
self.showAttributeInfo = showAttributeInfo
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
}
|
}
|
||||||
@ -88,7 +92,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
final class State: ComponentState {
|
final class State: ComponentState {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
var subject: GiftViewScreen.Subject
|
private(set) var subject: GiftViewScreen.Subject
|
||||||
|
private let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||||
private let getController: () -> ViewController?
|
private let getController: () -> ViewController?
|
||||||
|
|
||||||
private var disposable: Disposable?
|
private var disposable: Disposable?
|
||||||
@ -109,6 +114,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
var inUpgradePreview = false
|
var inUpgradePreview = false
|
||||||
var upgradeForm: BotPaymentForm?
|
var upgradeForm: BotPaymentForm?
|
||||||
|
var upgradeFormDisposable: Disposable?
|
||||||
var upgradeDisposable: Disposable?
|
var upgradeDisposable: Disposable?
|
||||||
|
|
||||||
var sampleGiftAttributes: [StarGift.UniqueGift.Attribute]?
|
var sampleGiftAttributes: [StarGift.UniqueGift.Attribute]?
|
||||||
@ -130,14 +136,24 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
var upgradedMockBackgroundColor: UIColor = .white
|
var upgradedMockBackgroundColor: UIColor = .white
|
||||||
var upgradedMockIcon: TelegramMediaFile?
|
var upgradedMockIcon: TelegramMediaFile?
|
||||||
|
|
||||||
init(context: AccountContext, subject: GiftViewScreen.Subject, getController: @escaping () -> ViewController?) {
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
subject: GiftViewScreen.Subject,
|
||||||
|
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||||
|
getController: @escaping () -> ViewController?
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
|
self.upgradeGift = upgradeGift
|
||||||
self.getController = getController
|
self.getController = getController
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
if let arguments = subject.arguments {
|
if let arguments = subject.arguments {
|
||||||
|
if let upgradeStars = arguments.upgradeStars, upgradeStars > 0 {
|
||||||
|
self.keepOriginalInfo = true
|
||||||
|
}
|
||||||
|
|
||||||
var peerIds: [EnginePeer.Id] = [arguments.peerId, context.account.peerId]
|
var peerIds: [EnginePeer.Id] = [arguments.peerId, context.account.peerId]
|
||||||
if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) {
|
if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) {
|
||||||
peerIds.append(fromPeerId)
|
peerIds.append(fromPeerId)
|
||||||
@ -177,7 +193,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if arguments.upgradeStars == nil, let messageId = arguments.messageId {
|
if arguments.upgradeStars == nil, let messageId = arguments.messageId {
|
||||||
self.upgradeDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftUpgrade(keepOriginalInfo: false, messageId: messageId), themeParams: nil)
|
self.upgradeFormDisposable = (context.engine.payments.fetchBotPaymentForm(source: .starGiftUpgrade(keepOriginalInfo: false, messageId: messageId), themeParams: nil)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] paymentForm in
|
|> deliverOnMainQueue).start(next: { [weak self] paymentForm in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -237,11 +253,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
deinit {
|
deinit {
|
||||||
self.disposable?.dispose()
|
self.disposable?.dispose()
|
||||||
self.sampleDisposable.dispose()
|
self.sampleDisposable.dispose()
|
||||||
|
self.upgradeFormDisposable?.dispose()
|
||||||
self.upgradeDisposable?.dispose()
|
self.upgradeDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestUpgradePreview() {
|
func requestUpgradePreview() {
|
||||||
guard let _ = self.subject.arguments?.upgradeStars else {
|
guard let arguments = self.subject.arguments, arguments.canUpgrade || arguments.upgradeStars != nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.context.starsContext?.load(force: false)
|
self.context.starsContext?.load(force: false)
|
||||||
@ -251,7 +268,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func commitUpgrade() {
|
func commitUpgrade() {
|
||||||
guard let arguments = self.subject.arguments, let messageId = arguments.messageId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
|
guard let arguments = self.subject.arguments, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let peerId = arguments.peerId
|
let peerId = arguments.peerId
|
||||||
@ -259,7 +276,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
self.inProgress = true
|
self.inProgress = true
|
||||||
self.updated()
|
self.updated()
|
||||||
|
|
||||||
let _ = (self.context.engine.payments.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: self.keepOriginalInfo)
|
self.upgradeDisposable = (self.upgradeGift(formId, self.keepOriginalInfo)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
guard let self, let controller = self.getController() as? GiftViewScreen else {
|
guard let self, let controller = self.getController() as? GiftViewScreen else {
|
||||||
return
|
return
|
||||||
@ -307,7 +324,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeState() -> State {
|
func makeState() -> State {
|
||||||
return State(context: self.context, subject: self.subject, getController: self.getController)
|
return State(context: self.context, subject: self.subject, upgradeGift: self.upgradeGift, getController: self.getController)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var body: Body {
|
static var body: Body {
|
||||||
@ -379,8 +396,6 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
entities = arguments.entities
|
entities = arguments.entities
|
||||||
limitTotal = gift.availability?.total
|
limitTotal = gift.availability?.total
|
||||||
convertStars = arguments.convertStars
|
convertStars = arguments.convertStars
|
||||||
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
|
||||||
savedToProfile = arguments.savedToProfile
|
|
||||||
converted = arguments.converted
|
converted = arguments.converted
|
||||||
giftId = gift.id
|
giftId = gift.id
|
||||||
date = arguments.date
|
date = arguments.date
|
||||||
@ -395,6 +410,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
convertStars = nil
|
convertStars = nil
|
||||||
uniqueGift = gift
|
uniqueGift = gift
|
||||||
}
|
}
|
||||||
|
savedToProfile = arguments.savedToProfile
|
||||||
|
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
||||||
nameHidden = arguments.nameHidden
|
nameHidden = arguments.nameHidden
|
||||||
titleString = incoming ? strings.Gift_View_ReceivedTitle : strings.Gift_View_Title
|
titleString = incoming ? strings.Gift_View_ReceivedTitle : strings.Gift_View_Title
|
||||||
} else {
|
} else {
|
||||||
@ -748,8 +765,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
// originY -= 12.0
|
// originY -= 12.0
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let linkColor = theme.actionSheet.controlAccentColor
|
|
||||||
if !descriptionText.isEmpty {
|
if !descriptionText.isEmpty {
|
||||||
|
let linkColor = theme.actionSheet.controlAccentColor
|
||||||
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme {
|
||||||
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme)
|
||||||
}
|
}
|
||||||
@ -767,7 +784,6 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0)
|
textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0)
|
||||||
textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor
|
textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
@ -850,45 +866,65 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
if !soldOut {
|
if !soldOut {
|
||||||
if let uniqueGift {
|
if let uniqueGift {
|
||||||
if let peer = state.peerMap[uniqueGift.ownerPeerId] {
|
if let peer = state.peerMap[uniqueGift.ownerPeerId] {
|
||||||
let ownerComponent = AnyComponent(
|
let ownerComponent: AnyComponent<Empty>
|
||||||
HStack([
|
if let _ = subject.arguments?.transferStars {
|
||||||
AnyComponentWithIdentity(
|
ownerComponent = AnyComponent(
|
||||||
id: AnyHashable(0),
|
HStack([
|
||||||
component: AnyComponent(Button(
|
AnyComponentWithIdentity(
|
||||||
content: AnyComponent(
|
id: AnyHashable(0),
|
||||||
PeerCellComponent(
|
component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(
|
||||||
|
PeerCellComponent(
|
||||||
|
context: component.context,
|
||||||
|
theme: theme,
|
||||||
|
strings: strings,
|
||||||
|
peer: peer
|
||||||
|
)
|
||||||
|
),
|
||||||
|
action: {
|
||||||
|
component.openPeer(peer)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
),
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: AnyHashable(1),
|
||||||
|
component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(ButtonContentComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: theme,
|
text: strings.Gift_Unique_Transfer,
|
||||||
strings: strings,
|
color: theme.list.itemAccentColor
|
||||||
peer: peer
|
)),
|
||||||
)
|
action: {
|
||||||
),
|
component.transferGift()
|
||||||
action: {
|
Queue.mainQueue().after(1.0, {
|
||||||
component.openPeer(peer)
|
component.cancel(false)
|
||||||
Queue.mainQueue().after(1.0, {
|
})
|
||||||
component.cancel(false)
|
}
|
||||||
})
|
))
|
||||||
}
|
)
|
||||||
))
|
], spacing: 4.0)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ownerComponent = AnyComponent(Button(
|
||||||
|
content: AnyComponent(
|
||||||
|
PeerCellComponent(
|
||||||
|
context: component.context,
|
||||||
|
theme: theme,
|
||||||
|
strings: strings,
|
||||||
|
peer: peer
|
||||||
|
)
|
||||||
),
|
),
|
||||||
AnyComponentWithIdentity(
|
action: {
|
||||||
id: AnyHashable(1),
|
component.openPeer(peer)
|
||||||
component: AnyComponent(Button(
|
Queue.mainQueue().after(1.0, {
|
||||||
content: AnyComponent(ButtonContentComponent(
|
component.cancel(false)
|
||||||
context: component.context,
|
})
|
||||||
text: strings.Gift_Unique_Transfer,
|
}
|
||||||
color: theme.list.itemAccentColor
|
))
|
||||||
)),
|
}
|
||||||
action: {
|
|
||||||
component.transferGift()
|
|
||||||
Queue.mainQueue().after(1.0, {
|
|
||||||
component.cancel(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
))
|
|
||||||
)
|
|
||||||
], spacing: 4.0)
|
|
||||||
)
|
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "owner",
|
id: "owner",
|
||||||
title: strings.Gift_Unique_Owner,
|
title: strings.Gift_Unique_Owner,
|
||||||
@ -1042,13 +1078,22 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSenderWithText(senderName!, recipientName, dateString, "") : strings.Gift_Unique_OriginalInfoWithText(recipientName, dateString, "")
|
let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSenderWithText(senderName!, recipientName, dateString, "") : strings.Gift_Unique_OriginalInfoWithText(recipientName, dateString, "")
|
||||||
let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor)
|
let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor)
|
||||||
string.replaceCharacters(in: format.ranges[format.ranges.count - 1].range, with: attributedText)
|
string.replaceCharacters(in: format.ranges[format.ranges.count - 1].range, with: attributedText)
|
||||||
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
if let _ = senderName {
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[1].range)
|
||||||
|
} else {
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
||||||
|
}
|
||||||
value = string
|
value = string
|
||||||
} else {
|
} else {
|
||||||
let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSender(senderName!, recipientName, dateString) : strings.Gift_Unique_OriginalInfo(recipientName, dateString)
|
let format = senderName != nil ? strings.Gift_Unique_OriginalInfoSender(senderName!, recipientName, dateString) : strings.Gift_Unique_OriginalInfo(recipientName, dateString)
|
||||||
let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor)
|
let string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor)
|
||||||
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
if let _ = senderName {
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[1].range)
|
||||||
|
} else {
|
||||||
|
string.addAttribute(NSAttributedString.Key.foregroundColor, value: tableLinkColor, range: format.ranges[0].range)
|
||||||
|
}
|
||||||
|
|
||||||
value = string
|
value = string
|
||||||
}
|
}
|
||||||
@ -1068,6 +1113,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
animationRenderer: component.context.animationRenderer,
|
animationRenderer: component.context.animationRenderer,
|
||||||
placeholderColor: theme.list.mediaPlaceholderColor,
|
placeholderColor: theme.list.mediaPlaceholderColor,
|
||||||
text: .plain(value),
|
text: .plain(value),
|
||||||
|
horizontalAlignment: .center,
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 0,
|
||||||
handleSpoilers: true
|
handleSpoilers: true
|
||||||
)
|
)
|
||||||
@ -1281,64 +1327,65 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
.disappear(.default(alpha: true))
|
.disappear(.default(alpha: true))
|
||||||
)
|
)
|
||||||
originY += table.size.height + 23.0
|
originY += table.size.height + 23.0
|
||||||
|
}
|
||||||
if incoming && !converted {
|
|
||||||
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
if incoming && !converted && !upgraded && !showUpgradePreview {
|
||||||
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
let linkColor = theme.actionSheet.controlAccentColor
|
||||||
}
|
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
||||||
let descriptionText: String
|
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
||||||
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
|
|
||||||
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
|
||||||
return (TelegramTextAttributes.URL, contents)
|
|
||||||
})
|
|
||||||
let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
|
||||||
if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedSmallChevronImage?.0 {
|
|
||||||
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
|
||||||
}
|
|
||||||
|
|
||||||
originY -= 5.0
|
|
||||||
let additionalText = additionalText.update(
|
|
||||||
component: MultilineTextComponent(
|
|
||||||
text: .plain(attributedString),
|
|
||||||
horizontalAlignment: .center,
|
|
||||||
maximumNumberOfLines: 5,
|
|
||||||
lineSpacing: 0.2,
|
|
||||||
highlightColor: linkColor.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: { _, _ in
|
|
||||||
component.updateSavedToProfile(!savedToProfile)
|
|
||||||
Queue.mainQueue().after(1.0, {
|
|
||||||
component.cancel(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
),
|
|
||||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
|
||||||
transition: .immediate
|
|
||||||
)
|
|
||||||
context.add(additionalText
|
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + additionalText.size.height / 2.0))
|
|
||||||
.appear(.default(alpha: true))
|
|
||||||
.disappear(.default(alpha: true))
|
|
||||||
)
|
|
||||||
originY += additionalText.size.height
|
|
||||||
originY += 16.0
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in
|
||||||
|
return (TelegramTextAttributes.URL, contents)
|
||||||
|
})
|
||||||
|
let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||||
|
if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedSmallChevronImage?.0 {
|
||||||
|
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
||||||
|
}
|
||||||
|
|
||||||
|
originY -= 5.0
|
||||||
|
let additionalText = additionalText.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(attributedString),
|
||||||
|
horizontalAlignment: .center,
|
||||||
|
maximumNumberOfLines: 5,
|
||||||
|
lineSpacing: 0.2,
|
||||||
|
highlightColor: linkColor.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: { _, _ in
|
||||||
|
component.updateSavedToProfile(!savedToProfile)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
context.add(additionalText
|
||||||
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + additionalText.size.height / 2.0))
|
||||||
|
.appear(.default(alpha: true))
|
||||||
|
.disappear(.default(alpha: true))
|
||||||
|
)
|
||||||
|
originY += additionalText.size.height
|
||||||
|
originY += 16.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let buttonChild: _UpdatedChildComponent
|
let buttonChild: _UpdatedChildComponent
|
||||||
@ -1480,6 +1527,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
|||||||
let sendGift: (EnginePeer.Id) -> Void
|
let sendGift: (EnginePeer.Id) -> Void
|
||||||
let openMyGifts: () -> Void
|
let openMyGifts: () -> Void
|
||||||
let transferGift: () -> Void
|
let transferGift: () -> Void
|
||||||
|
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||||
let showAttributeInfo: (Any, Float) -> Void
|
let showAttributeInfo: (Any, Float) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
@ -1492,6 +1540,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
|||||||
sendGift: @escaping (EnginePeer.Id) -> Void,
|
sendGift: @escaping (EnginePeer.Id) -> Void,
|
||||||
openMyGifts: @escaping () -> Void,
|
openMyGifts: @escaping () -> Void,
|
||||||
transferGift: @escaping () -> Void,
|
transferGift: @escaping () -> Void,
|
||||||
|
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||||
showAttributeInfo: @escaping (Any, Float) -> Void
|
showAttributeInfo: @escaping (Any, Float) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -1503,6 +1552,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
|||||||
self.sendGift = sendGift
|
self.sendGift = sendGift
|
||||||
self.openMyGifts = openMyGifts
|
self.openMyGifts = openMyGifts
|
||||||
self.transferGift = transferGift
|
self.transferGift = transferGift
|
||||||
|
self.upgradeGift = upgradeGift
|
||||||
self.showAttributeInfo = showAttributeInfo
|
self.showAttributeInfo = showAttributeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1550,6 +1600,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
|||||||
sendGift: context.component.sendGift,
|
sendGift: context.component.sendGift,
|
||||||
openMyGifts: context.component.openMyGifts,
|
openMyGifts: context.component.openMyGifts,
|
||||||
transferGift: context.component.transferGift,
|
transferGift: context.component.transferGift,
|
||||||
|
upgradeGift: context.component.upgradeGift,
|
||||||
showAttributeInfo: context.component.showAttributeInfo,
|
showAttributeInfo: context.component.showAttributeInfo,
|
||||||
getController: controller
|
getController: controller
|
||||||
)),
|
)),
|
||||||
@ -1629,20 +1680,20 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
case let .message(message):
|
case let .message(message):
|
||||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, _):
|
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)
|
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, _):
|
case let .starGiftUnique(gift, isUpgrade, _, 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)
|
var incoming = message.flags.contains(.Incoming)
|
||||||
|
if isUpgrade && message.author?.id != message.id.peerId {
|
||||||
|
incoming = true
|
||||||
|
}
|
||||||
|
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, false, false, nil, transferStars, canExportDate)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .profileGift(peerId, gift):
|
case let .profileGift(peerId, gift):
|
||||||
var upgraded = false
|
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, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, gift.canExportDate)
|
||||||
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:
|
case .soldOutGift:
|
||||||
return nil
|
return nil
|
||||||
case .upgradePreview:
|
case .upgradePreview:
|
||||||
@ -1663,7 +1714,9 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
subject: GiftViewScreen.Subject,
|
subject: GiftViewScreen.Subject,
|
||||||
forceDark: Bool = false,
|
forceDark: Bool = false,
|
||||||
updateSavedToProfile: ((Bool) -> Void)? = nil,
|
updateSavedToProfile: ((Bool) -> Void)? = nil,
|
||||||
convertToStars: (() -> Void)? = nil
|
convertToStars: (() -> Void)? = nil,
|
||||||
|
transferGift: ((Bool, EnginePeer.Id) -> Void)? = nil,
|
||||||
|
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -1676,6 +1729,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
var openMyGiftsImpl: (() -> Void)?
|
var openMyGiftsImpl: (() -> Void)?
|
||||||
var transferGiftImpl: (() -> Void)?
|
var transferGiftImpl: (() -> Void)?
|
||||||
var showAttributeInfoImpl: ((Any, Float) -> Void)?
|
var showAttributeInfoImpl: ((Any, Float) -> Void)?
|
||||||
|
var upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
|
||||||
|
|
||||||
super.init(
|
super.init(
|
||||||
context: context,
|
context: context,
|
||||||
@ -1703,6 +1757,9 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
transferGift: {
|
transferGift: {
|
||||||
transferGiftImpl?()
|
transferGiftImpl?()
|
||||||
},
|
},
|
||||||
|
upgradeGift: { formId, keepOriginalInfo in
|
||||||
|
return upgradeGiftImpl?(formId, keepOriginalInfo) ?? .complete()
|
||||||
|
},
|
||||||
showAttributeInfo: { tag, rarity in
|
showAttributeInfo: { tag, rarity in
|
||||||
showAttributeInfoImpl?(tag, rarity)
|
showAttributeInfoImpl?(tag, rarity)
|
||||||
}
|
}
|
||||||
@ -1737,6 +1794,20 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
guard let self, let arguments = self.subject.arguments, let messageId = arguments.messageId else {
|
guard let self, let arguments = self.subject.arguments, let messageId = arguments.messageId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var animationFile: TelegramMediaFile?
|
||||||
|
switch arguments.gift {
|
||||||
|
case let .generic(gift):
|
||||||
|
animationFile = gift.file
|
||||||
|
case let .unique(gift):
|
||||||
|
for attribute in gift.attributes {
|
||||||
|
if case let .model(_, file, _) = attribute {
|
||||||
|
animationFile = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let updateSavedToProfile {
|
if let updateSavedToProfile {
|
||||||
updateSavedToProfile(added)
|
updateSavedToProfile(added)
|
||||||
} else {
|
} else {
|
||||||
@ -1749,10 +1820,10 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
let text = added ? presentationData.strings.Gift_Displayed_NewText : presentationData.strings.Gift_Hidden_NewText
|
let text = added ? presentationData.strings.Gift_Displayed_NewText : presentationData.strings.Gift_Hidden_NewText
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
Queue.mainQueue().after(0.5) {
|
Queue.mainQueue().after(0.5) {
|
||||||
if let lastController = navigationController.viewControllers.last as? ViewController, case let .generic(gift) = arguments.gift {
|
if let lastController = navigationController.viewControllers.last as? ViewController, let animationFile {
|
||||||
let resultController = UndoOverlayController(
|
let resultController = UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .sticker(context: context, file: gift.file, loop: false, title: nil, text: text, undoText: updateSavedToProfile == nil ? presentationData.strings.Gift_Displayed_View : nil, customAction: nil),
|
content: .sticker(context: context, file: animationFile, loop: false, title: nil, text: text, undoText: updateSavedToProfile == nil ? presentationData.strings.Gift_Displayed_View : nil, customAction: nil),
|
||||||
elevatedLayout: lastController is ChatController,
|
elevatedLayout: lastController is ChatController,
|
||||||
action: { [weak navigationController] action in
|
action: { [weak navigationController] action in
|
||||||
if case .undo = action, let navigationController {
|
if case .undo = action, let navigationController {
|
||||||
@ -1792,7 +1863,6 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
let starsConvertMaxDate = arguments.date + configuration.convertToStarsPeriod
|
let starsConvertMaxDate = arguments.date + configuration.convertToStarsPeriod
|
||||||
|
|
||||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
|
||||||
if currentTime > starsConvertMaxDate {
|
if currentTime > starsConvertMaxDate {
|
||||||
let days: Int32 = Int32(ceil(Float(configuration.convertToStarsPeriod) / 86400.0))
|
let days: Int32 = Int32(ceil(Float(configuration.convertToStarsPeriod) / 86400.0))
|
||||||
let controller = textAlertController(
|
let controller = textAlertController(
|
||||||
@ -1857,6 +1927,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
self.present(controller, in: .window(.root))
|
self.present(controller, in: .window(.root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openStarsIntroImpl = { [weak self] in
|
openStarsIntroImpl = { [weak self] in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1864,6 +1935,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
let introController = context.sharedContext.makeStarsIntroScreen(context: context)
|
let introController = context.sharedContext.makeStarsIntroScreen(context: context)
|
||||||
self.push(introController)
|
self.push(introController)
|
||||||
}
|
}
|
||||||
|
|
||||||
sendGiftImpl = { [weak self] peerId in
|
sendGiftImpl = { [weak self] peerId in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1876,6 +1948,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
self.push(controller)
|
self.push(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
openMyGiftsImpl = { [weak self] in
|
openMyGiftsImpl = { [weak self] in
|
||||||
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
||||||
return
|
return
|
||||||
@ -1906,11 +1979,42 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
let _ = (context.account.stateManager.contactBirthdays
|
let _ = (context.account.stateManager.contactBirthdays
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { birthdays in
|
|> deliverOnMainQueue).start(next: { birthdays in
|
||||||
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .starGiftTransfer(birthdays, messageId, gift, transferStars, arguments.canExportDate), completion: nil)
|
let controller = context.sharedContext.makePremiumGiftController(context: context, source: .starGiftTransfer(birthdays, messageId, gift, transferStars, arguments.canExportDate), completion: { peerIds in
|
||||||
|
guard let peerId = peerIds.first else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let transferGift {
|
||||||
|
transferGift(transferStars == 0, peerId)
|
||||||
|
} else {
|
||||||
|
let _ = (context.engine.payments.transferStarGift(prepaid: transferStars == 0, messageId: messageId, peerId: peerId)
|
||||||
|
|> deliverOnMainQueue).start()
|
||||||
|
}
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
if transferStars > 0 {
|
||||||
|
context.starsContext?.load(force: true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
navigationController.pushViewController(controller)
|
navigationController.pushViewController(controller)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upgradeGiftImpl = { [weak self] formId, keepOriginalInfo in
|
||||||
|
guard let self, let arguments = self.subject.arguments, let messageId = arguments.messageId else {
|
||||||
|
return .complete()
|
||||||
|
}
|
||||||
|
if let upgradeGift {
|
||||||
|
return upgradeGift(formId, keepOriginalInfo)
|
||||||
|
} else {
|
||||||
|
return self.context.engine.payments.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
|
||||||
|
|> afterCompleted {
|
||||||
|
if formId != nil {
|
||||||
|
context.starsContext?.load(force: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showAttributeInfoImpl = { [weak self] tag, rarity in
|
showAttributeInfoImpl = { [weak self] tag, rarity in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1950,6 +2054,17 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
fileprivate func animateSuccess() {
|
fileprivate func animateSuccess() {
|
||||||
self.navigationController?.view.addSubview(ConfettiView(frame: self.view.bounds))
|
self.navigationController?.view.addSubview(ConfettiView(frame: self.view.bounds))
|
||||||
|
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
self.present(UndoOverlayController(presentationData: presentationData, content: .universal(
|
||||||
|
animation: "GiftUpgraded",
|
||||||
|
scale: 0.066,
|
||||||
|
colors: [:],
|
||||||
|
title: presentationData.strings.Gift_Upgrade_Succeed_Title,
|
||||||
|
text: presentationData.strings.Gift_Upgrade_Succeed_Text,
|
||||||
|
customUndoText: nil,
|
||||||
|
timeout: 4.0
|
||||||
|
), elevatedLayout: false, position: .bottom, action: { _ in return true }), in: .current)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dismissAnimated() {
|
public func dismissAnimated() {
|
||||||
|
|||||||
@ -351,10 +351,21 @@ public final class PeerInfoCoverComponent: Component {
|
|||||||
|
|
||||||
self.backgroundView.backgroundColor = secondaryBackgroundColor
|
self.backgroundView.backgroundColor = secondaryBackgroundColor
|
||||||
|
|
||||||
self.backgroundGradientLayer.type = .axial
|
if case .custom = component.subject {
|
||||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
|
if availableSize.width < availableSize.height {
|
||||||
self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
|
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.25)
|
||||||
self.backgroundGradientLayer.colors = [backgroundColor.cgColor, secondaryBackgroundColor.cgColor]
|
} else {
|
||||||
|
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
|
||||||
|
}
|
||||||
|
self.backgroundGradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
|
||||||
|
self.backgroundGradientLayer.type = .radial
|
||||||
|
self.backgroundGradientLayer.colors = [secondaryBackgroundColor.cgColor, backgroundColor.cgColor]
|
||||||
|
} else {
|
||||||
|
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
|
||||||
|
self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
|
||||||
|
self.backgroundGradientLayer.type = .axial
|
||||||
|
self.backgroundGradientLayer.colors = [backgroundColor.cgColor, secondaryBackgroundColor.cgColor]
|
||||||
|
}
|
||||||
self.backgroundGradientLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
|
self.backgroundGradientLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
|
||||||
|
|
||||||
let gradientHeight: CGFloat = component.defaultHeight
|
let gradientHeight: CGFloat = component.defaultHeight
|
||||||
@ -431,7 +442,7 @@ public final class PeerInfoCoverComponent: Component {
|
|||||||
} else if availableSize.width < 150.0 {
|
} else if availableSize.width < 150.0 {
|
||||||
baseDistance *= 0.6
|
baseDistance *= 0.6
|
||||||
baseRowDistance *= 0.6
|
baseRowDistance *= 0.6
|
||||||
baseItemSize *= 0.75
|
baseItemSize *= 0.83
|
||||||
}
|
}
|
||||||
|
|
||||||
var avatarBackgroundPatternLayerCount = 0
|
var avatarBackgroundPatternLayerCount = 0
|
||||||
|
|||||||
@ -1306,7 +1306,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
var nextIconX: CGFloat = titleSize.width
|
var nextIconX: CGFloat = titleSize.width
|
||||||
var nextExpandedIconX: CGFloat = titleExpandedSize.width
|
var nextExpandedIconX: CGFloat = titleExpandedSize.width
|
||||||
|
|
||||||
if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize {
|
if let credibilityIconSize = self.credibilityIconSize, let titleExpandedCredibilityIconSize = self.titleExpandedCredibilityIconSize, credibilityIconSize.width > 0.0 {
|
||||||
let offset = (credibilityIconSize.width + 4.0) / 2.0
|
let offset = (credibilityIconSize.width + 4.0) / 2.0
|
||||||
|
|
||||||
let leftOffset: CGFloat = nextIconX + 4.0
|
let leftOffset: CGFloat = nextIconX + 4.0
|
||||||
@ -1325,13 +1325,21 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
nextExpandedIconX += 4.0 + titleExpandedCredibilityIconSize.width
|
nextExpandedIconX += 4.0 + titleExpandedCredibilityIconSize.width
|
||||||
}
|
}
|
||||||
|
|
||||||
if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize {
|
if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize, verifiedIconSize.width > 0.0 {
|
||||||
let offset = (verifiedIconSize.width + 4.0) / 2.0
|
let leftOffset: CGFloat
|
||||||
titleHorizontalOffset += offset
|
let leftExpandedOffset: CGFloat
|
||||||
titleExpandedHorizontalOffset += offset
|
if case .verified = verifiedIcon {
|
||||||
|
titleHorizontalOffset -= (verifiedIconSize.width + 4.0) / 2.0
|
||||||
let leftOffset: CGFloat = -verifiedIconSize.width - 4.0
|
|
||||||
let leftExpandedOffset: CGFloat = -titleExpandedVerifiedIconSize.width - 4.0
|
leftOffset = nextIconX + 4.0
|
||||||
|
leftExpandedOffset = nextExpandedIconX + 4.0
|
||||||
|
} else {
|
||||||
|
titleHorizontalOffset += (verifiedIconSize.width + 4.0) / 2.0
|
||||||
|
titleExpandedHorizontalOffset += titleExpandedVerifiedIconSize.width
|
||||||
|
|
||||||
|
leftOffset = -verifiedIconSize.width - 4.0
|
||||||
|
leftExpandedOffset = -titleExpandedVerifiedIconSize.width - 8.0
|
||||||
|
}
|
||||||
|
|
||||||
var collapsedTransitionOffset: CGFloat = 0.0
|
var collapsedTransitionOffset: CGFloat = 0.0
|
||||||
if let navigationTransition = self.navigationTransition {
|
if let navigationTransition = self.navigationTransition {
|
||||||
@ -1340,6 +1348,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
transition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftOffset + collapsedTransitionOffset, y: floor((titleSize.height - verifiedIconSize.height) / 2.0)), size: verifiedIconSize))
|
transition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftOffset + collapsedTransitionOffset, y: floor((titleSize.height - verifiedIconSize.height) / 2.0)), size: verifiedIconSize))
|
||||||
transition.updateFrame(view: self.titleExpandedVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftExpandedOffset, y: floor((titleExpandedSize.height - titleExpandedVerifiedIconSize.height) / 2.0) + 1.0), size: titleExpandedVerifiedIconSize))
|
transition.updateFrame(view: self.titleExpandedVerifiedIconView, frame: CGRect(origin: CGPoint(x: leftExpandedOffset, y: floor((titleExpandedSize.height - titleExpandedVerifiedIconSize.height) / 2.0) + 1.0), size: titleExpandedVerifiedIconSize))
|
||||||
|
|
||||||
|
if case .verified = verifiedIcon {
|
||||||
|
nextIconX += 4.0 + verifiedIconSize.width
|
||||||
|
nextExpandedIconX += 4.0 + titleExpandedVerifiedIconSize.width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleFrame: CGRect
|
var titleFrame: CGRect
|
||||||
@ -1759,7 +1772,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
|
|
||||||
var titleFrame = titleFrame
|
var titleFrame = titleFrame
|
||||||
if !self.isAvatarExpanded {
|
if !self.isAvatarExpanded {
|
||||||
titleFrame = titleFrame.offsetBy(dx: self.isAvatarExpanded ? titleExpandedHorizontalOffset : titleHorizontalOffset * titleScale, dy: 0.0)
|
titleFrame = titleFrame.offsetBy(dx: titleHorizontalOffset * titleScale, dy: 0.0)
|
||||||
|
} else {
|
||||||
|
titleFrame = titleFrame.offsetBy(dx: titleExpandedHorizontalOffset, dy: 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
let titleCenter = CGPoint(x: transitionFraction * transitionSourceTitleFrame.midX + (1.0 - transitionFraction) * titleFrame.midX, y: transitionFraction * transitionSourceTitleFrame.midY + (1.0 - transitionFraction) * titleFrame.midY)
|
||||||
|
|||||||
@ -3986,8 +3986,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
self?.controller?.updateProfilePhoto(image, mode: .generic)
|
self?.controller?.updateProfilePhoto(image, mode: .generic)
|
||||||
}
|
}
|
||||||
galleryController.avatarVideoEditCompletion = { [weak self] image, asset, adjustments in
|
galleryController.avatarVideoEditCompletion = { [weak self] image, asset, adjustments in
|
||||||
let _ = self
|
self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic)
|
||||||
//self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: .generic)
|
|
||||||
}
|
}
|
||||||
galleryController.removedEntry = { [weak self] entry in
|
galleryController.removedEntry = { [weak self] entry in
|
||||||
if let item = PeerInfoAvatarListItem(entry: entry) {
|
if let item = PeerInfoAvatarListItem(entry: entry) {
|
||||||
@ -9605,7 +9604,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func oldOpenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||||
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
|
guard let peer = self.data?.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: self.data?.threadData) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -9694,21 +9693,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings || strongSelf.isMyProfile, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: [.custom, .suggest].contains(mode))!
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasDeleteButton, hasViewButton: false, personalPhoto: strongSelf.isSettings || strongSelf.isMyProfile, isVideo: currentIsVideo, saveEditedPhotos: false, saveCapturedMedia: false, signup: false, forum: isForum, title: title, isSuggesting: [.custom, .suggest].contains(mode))!
|
||||||
mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context)
|
mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context)
|
||||||
let _ = strongSelf.currentAvatarMixin.swap(mixin)
|
let _ = strongSelf.currentAvatarMixin.swap(mixin)
|
||||||
// mixin.requestSearchController = { [weak self, weak parentController] assetsController in
|
|
||||||
// guard let strongSelf = self else {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: (strongSelf.isSettings || strongSelf.isMyProfile) ? nil : peer.compactDisplayTitle, completion: { [weak self] result in
|
|
||||||
// assetsController?.dismiss()
|
|
||||||
// self?.updateProfilePhoto(result, mode: mode)
|
|
||||||
// }))
|
|
||||||
// controller.navigationPresentation = .modal
|
|
||||||
// parentController?.push(controller)
|
|
||||||
//
|
|
||||||
// if fromGallery {
|
|
||||||
// completion(nil)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
var isFromEditor = false
|
var isFromEditor = false
|
||||||
mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in
|
mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in
|
||||||
guard let strongSelf = self, let imageCompletion, let videoCompletion else {
|
guard let strongSelf = self, let imageCompletion, let videoCompletion else {
|
||||||
@ -9755,18 +9739,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// mixin.didFinishWithImage = { [weak self] image in
|
mixin.didFinishWithImage = { [weak self] image in
|
||||||
// if let image = image {
|
if let image = image {
|
||||||
// completion(image)
|
completion(image)
|
||||||
// self?.updateProfilePhoto(image, mode: mode)
|
self?.controller?.updateProfilePhoto(image, mode: mode)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
||||||
// if let image = image, let asset = asset {
|
if let image = image, let asset = asset {
|
||||||
// completion(image)
|
completion(image)
|
||||||
// self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
|
self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
mixin.didFinishWithDelete = {
|
mixin.didFinishWithDelete = {
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -12766,6 +12750,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
proceed()
|
proceed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||||
|
self.controllerNode.openAvatarForEditing(mode: mode, fromGallery: fromGallery, completion: completion)
|
||||||
|
}
|
||||||
|
|
||||||
static func openPeer(context: AccountContext, peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) {
|
static func openPeer(context: AccountContext, peerId: PeerId, navigation: ChatControllerInteractionNavigateToPeer, navigationController: NavigationController) {
|
||||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|
|||||||
@ -15,166 +15,167 @@ import OverlayStatusController
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import PeerAvatarGalleryUI
|
import PeerAvatarGalleryUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
|
import LegacyComponents
|
||||||
|
|
||||||
extension PeerInfoScreenImpl {
|
extension PeerInfoScreenImpl {
|
||||||
func openAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
// func newopenAvatarForEditing(mode: PeerInfoAvatarEditingMode = .generic, fromGallery: Bool = false, completion: @escaping (UIImage?) -> Void = { _ in }) {
|
||||||
guard let data = self.controllerNode.data, let peer = data.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: data.threadData) else {
|
// guard let data = self.controllerNode.data, let peer = data.peer, mode != .generic || canEditPeerInfo(context: self.context, peer: peer, chatLocation: self.chatLocation, threadData: data.threadData) else {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
self.view.endEditing(true)
|
// self.view.endEditing(true)
|
||||||
|
//
|
||||||
let peerId = self.peerId
|
// let peerId = self.peerId
|
||||||
var isForum = false
|
// var isForum = false
|
||||||
if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) {
|
// if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) {
|
||||||
isForum = true
|
// isForum = true
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var currentIsVideo = false
|
// var currentIsVideo = false
|
||||||
var emojiMarkup: TelegramMediaImage.EmojiMarkup?
|
// var emojiMarkup: TelegramMediaImage.EmojiMarkup?
|
||||||
let item = self.controllerNode.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
|
// let item = self.controllerNode.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
|
||||||
if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item {
|
// if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item {
|
||||||
currentIsVideo = !videoRepresentations.isEmpty
|
// currentIsVideo = !videoRepresentations.isEmpty
|
||||||
emojiMarkup = emojiMarkupValue
|
// emojiMarkup = emojiMarkupValue
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let _ = isForum
|
// let _ = isForum
|
||||||
let _ = currentIsVideo
|
// let _ = currentIsVideo
|
||||||
|
//
|
||||||
let _ = (self.context.engine.data.get(
|
// let _ = (self.context.engine.data.get(
|
||||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
// TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||||
)
|
// )
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
// |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||||
guard let self, let peer else {
|
// guard let self, let peer else {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let keyboardInputData = Promise<AvatarKeyboardInputData>()
|
// let keyboardInputData = Promise<AvatarKeyboardInputData>()
|
||||||
keyboardInputData.set(AvatarEditorScreen.inputData(context: self.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser))
|
// keyboardInputData.set(AvatarEditorScreen.inputData(context: self.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser))
|
||||||
|
//
|
||||||
var hasPhotos = false
|
// var hasPhotos = false
|
||||||
if !peer.profileImageRepresentations.isEmpty {
|
// if !peer.profileImageRepresentations.isEmpty {
|
||||||
hasPhotos = true
|
// hasPhotos = true
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var hasDeleteButton = false
|
// var hasDeleteButton = false
|
||||||
if case .generic = mode {
|
// if case .generic = mode {
|
||||||
hasDeleteButton = hasPhotos && !fromGallery
|
// hasDeleteButton = hasPhotos && !fromGallery
|
||||||
} else if case .custom = mode {
|
// } else if case .custom = mode {
|
||||||
hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true
|
// hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true
|
||||||
} else if case .fallback = mode {
|
// } else if case .fallback = mode {
|
||||||
if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto {
|
// if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto {
|
||||||
hasDeleteButton = photo != nil
|
// hasDeleteButton = photo != nil
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let _ = hasDeleteButton
|
// let _ = hasDeleteButton
|
||||||
|
//
|
||||||
let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController
|
// let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController
|
||||||
|
//
|
||||||
var dismissImpl: (() -> Void)?
|
// var dismissImpl: (() -> Void)?
|
||||||
let mainController = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in
|
// let mainController = self.context.sharedContext.makeAvatarMediaPickerScreen(context: self.context, getSourceRect: { return nil }, canDelete: hasDeleteButton, performDelete: { [weak self] in
|
||||||
self?.openAvatarRemoval(mode: mode, peer: peer, item: item)
|
// self?.openAvatarRemoval(mode: mode, peer: peer, item: item)
|
||||||
}, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
// }, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
||||||
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
// let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||||
if let asset = result as? PHAsset {
|
// if let asset = result as? PHAsset {
|
||||||
subject = .single(.asset(asset))
|
// subject = .single(.asset(asset))
|
||||||
} else if let image = result as? UIImage {
|
// } else if let image = result as? UIImage {
|
||||||
subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight))
|
// subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight))
|
||||||
} else if let result = result as? Signal<CameraScreenImpl.Result, NoError> {
|
// } else if let result = result as? Signal<CameraScreenImpl.Result, NoError> {
|
||||||
subject = result
|
// subject = result
|
||||||
|> map { value -> MediaEditorScreenImpl.Subject? in
|
// |> map { value -> MediaEditorScreenImpl.Subject? in
|
||||||
switch value {
|
// switch value {
|
||||||
case .pendingImage:
|
// case .pendingImage:
|
||||||
return nil
|
// return nil
|
||||||
case let .image(image):
|
// case let .image(image):
|
||||||
return .image(image: image.image, dimensions: PixelDimensions(image.image.size), additionalImage: nil, additionalImagePosition: .topLeft)
|
// return .image(image: image.image, dimensions: PixelDimensions(image.image.size), additionalImage: nil, additionalImagePosition: .topLeft)
|
||||||
case let .video(video):
|
// case let .video(video):
|
||||||
return .video(videoPath: video.videoPath, thumbnail: video.coverImage, mirror: video.mirror, additionalVideoPath: nil, additionalThumbnail: nil, dimensions: video.dimensions, duration: video.duration, videoPositionChanges: [], additionalVideoPosition: .topLeft)
|
// return .video(videoPath: video.videoPath, thumbnail: video.coverImage, mirror: video.mirror, additionalVideoPath: nil, additionalThumbnail: nil, dimensions: video.dimensions, duration: video.duration, videoPositionChanges: [], additionalVideoPosition: .topLeft)
|
||||||
default:
|
// default:
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
let peerType: AvatarEditorScreen.PeerType
|
// let peerType: AvatarEditorScreen.PeerType
|
||||||
if mode == .suggest {
|
// if mode == .suggest {
|
||||||
peerType = .suggest
|
// peerType = .suggest
|
||||||
} else if case .legacyGroup = peer {
|
// } else if case .legacyGroup = peer {
|
||||||
peerType = .group
|
// peerType = .group
|
||||||
} else if case let .channel(channel) = peer {
|
// } else if case let .channel(channel) = peer {
|
||||||
if case .group = channel.info {
|
// if case .group = channel.info {
|
||||||
peerType = channel.flags.contains(.isForum) ? .forum : .group
|
// peerType = channel.flags.contains(.isForum) ? .forum : .group
|
||||||
} else {
|
// } else {
|
||||||
peerType = .channel
|
// peerType = .channel
|
||||||
}
|
// }
|
||||||
} else {
|
// } else {
|
||||||
peerType = .user
|
// peerType = .user
|
||||||
}
|
// }
|
||||||
let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
|
// let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
|
||||||
//controller.imageCompletion = imageCompletion
|
// //controller.imageCompletion = imageCompletion
|
||||||
//controller.videoCompletion = videoCompletion
|
// //controller.videoCompletion = videoCompletion
|
||||||
parentController?.push(controller)
|
// parentController?.push(controller)
|
||||||
//isFromEditor = true
|
// //isFromEditor = true
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
let editorController = MediaEditorScreenImpl(
|
// let editorController = MediaEditorScreenImpl(
|
||||||
context: self.context,
|
// context: self.context,
|
||||||
mode: .avatarEditor,
|
// mode: .avatarEditor,
|
||||||
subject: subject,
|
// subject: subject,
|
||||||
transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
// transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
||||||
MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn(
|
// MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn(
|
||||||
sourceView: $0,
|
// sourceView: $0,
|
||||||
sourceRect: transitionRect,
|
// sourceRect: transitionRect,
|
||||||
sourceImage: transitionImage
|
// sourceImage: transitionImage
|
||||||
)
|
// )
|
||||||
) }),
|
// ) }),
|
||||||
transitionOut: { finished, isNew in
|
// transitionOut: { finished, isNew in
|
||||||
if !finished, let transitionView {
|
// if !finished, let transitionView {
|
||||||
return MediaEditorScreenImpl.TransitionOut(
|
// return MediaEditorScreenImpl.TransitionOut(
|
||||||
destinationView: transitionView,
|
// destinationView: transitionView,
|
||||||
destinationRect: transitionView.bounds,
|
// destinationRect: transitionView.bounds,
|
||||||
destinationCornerRadius: 0.0
|
// destinationCornerRadius: 0.0
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}, completion: { [weak self] result, commit in
|
// }, completion: { [weak self] result, commit in
|
||||||
dismissImpl?()
|
// dismissImpl?()
|
||||||
|
//
|
||||||
switch result.media {
|
// switch result.media {
|
||||||
case let .image(image, _):
|
// case let .image(image, _):
|
||||||
self?.updateProfilePhoto(image, mode: mode)
|
// self?.updateProfilePhoto(image, mode: mode)
|
||||||
commit({})
|
// commit({})
|
||||||
case let .video(video, coverImage, values, _, _):
|
// case let .video(video, coverImage, values, _, _):
|
||||||
if let coverImage {
|
// if let coverImage {
|
||||||
self?.updateProfileVideo(coverImage, asset: video, adjustments: values, mode: mode)
|
// self?.updateProfileVideo(coverImage, asset: video, adjustments: values, mode: mode)
|
||||||
}
|
// }
|
||||||
commit({})
|
// commit({})
|
||||||
default:
|
// default:
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
// } as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||||
)
|
// )
|
||||||
editorController.cancelled = { _ in
|
// editorController.cancelled = { _ in
|
||||||
cancelled()
|
// cancelled()
|
||||||
}
|
// }
|
||||||
self.push(editorController)
|
// self.push(editorController)
|
||||||
}, dismissed: {
|
// }, dismissed: {
|
||||||
|
//
|
||||||
})
|
// })
|
||||||
dismissImpl = { [weak mainController] in
|
// dismissImpl = { [weak mainController] in
|
||||||
if let mainController, let navigationController = mainController.navigationController {
|
// if let mainController, let navigationController = mainController.navigationController {
|
||||||
var viewControllers = navigationController.viewControllers
|
// var viewControllers = navigationController.viewControllers
|
||||||
viewControllers = viewControllers.filter { c in
|
// viewControllers = viewControllers.filter { c in
|
||||||
return !(c is CameraScreen) && c !== mainController
|
// return !(c is CameraScreen) && c !== mainController
|
||||||
}
|
// }
|
||||||
navigationController.setViewControllers(viewControllers, animated: false)
|
// navigationController.setViewControllers(viewControllers, animated: false)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
mainController.navigationPresentation = .flatModal
|
// mainController.navigationPresentation = .flatModal
|
||||||
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
// mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
self.push(mainController)
|
// self.push(mainController)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) {
|
func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) {
|
||||||
let proceed = { [weak self] in
|
let proceed = { [weak self] in
|
||||||
@ -368,7 +369,7 @@ extension PeerInfoScreenImpl {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: MediaEditorValues?, mode: PeerInfoAvatarEditingMode) {
|
public func updateProfileVideo(_ image: UIImage, asset: Any?, adjustments: TGVideoEditAdjustments?, mode: PeerInfoAvatarEditingMode) {
|
||||||
guard let data = image.jpegData(compressionQuality: 0.6) else {
|
guard let data = image.jpegData(compressionQuality: 0.6) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -234,6 +234,18 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.profileGifts.convertStarGift(messageId: messageId)
|
self.profileGifts.convertStarGift(messageId: messageId)
|
||||||
|
},
|
||||||
|
transferGift: { [weak self] prepaid, peerId in
|
||||||
|
guard let self, let messageId = product.messageId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.profileGifts.transferStarGift(prepaid: prepaid, messageId: messageId, peerId: peerId)
|
||||||
|
},
|
||||||
|
upgradeGift: { [weak self] formId, keepOriginalInfo in
|
||||||
|
guard let self, let messageId = product.messageId else {
|
||||||
|
return .never()
|
||||||
|
}
|
||||||
|
return self.profileGifts.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.parentController?.push(controller)
|
self.parentController?.push(controller)
|
||||||
|
|||||||
@ -18,15 +18,26 @@ public final class StarsAvatarComponent: Component {
|
|||||||
let peer: StarsContext.State.Transaction.Peer?
|
let peer: StarsContext.State.Transaction.Peer?
|
||||||
let photo: TelegramMediaWebFile?
|
let photo: TelegramMediaWebFile?
|
||||||
let media: [Media]
|
let media: [Media]
|
||||||
|
let uniqueGift: StarGift.UniqueGift?
|
||||||
let backgroundColor: UIColor
|
let backgroundColor: UIColor
|
||||||
let size: CGSize?
|
let size: CGSize?
|
||||||
|
|
||||||
public init(context: AccountContext, theme: PresentationTheme, peer: StarsContext.State.Transaction.Peer?, photo: TelegramMediaWebFile?, media: [Media], backgroundColor: UIColor, size: CGSize? = nil) {
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
theme: PresentationTheme,
|
||||||
|
peer: StarsContext.State.Transaction.Peer?,
|
||||||
|
photo: TelegramMediaWebFile?,
|
||||||
|
media: [Media],
|
||||||
|
uniqueGift: StarGift.UniqueGift?,
|
||||||
|
backgroundColor: UIColor,
|
||||||
|
size: CGSize? = nil
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.photo = photo
|
self.photo = photo
|
||||||
self.media = media
|
self.media = media
|
||||||
|
self.uniqueGift = uniqueGift
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
@ -47,6 +58,9 @@ public final class StarsAvatarComponent: Component {
|
|||||||
if !areMediaArraysEqual(lhs.media, rhs.media) {
|
if !areMediaArraysEqual(lhs.media, rhs.media) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.uniqueGift != rhs.uniqueGift {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.backgroundColor != rhs.backgroundColor {
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,6 +39,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let openAppExamples: () -> Void
|
let openAppExamples: () -> Void
|
||||||
let copyTransactionId: (String) -> Void
|
let copyTransactionId: (String) -> Void
|
||||||
let updateSubscription: () -> Void
|
let updateSubscription: () -> Void
|
||||||
|
let sendGift: (EnginePeer.Id) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -49,7 +50,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||||
openAppExamples: @escaping () -> Void,
|
openAppExamples: @escaping () -> Void,
|
||||||
copyTransactionId: @escaping (String) -> Void,
|
copyTransactionId: @escaping (String) -> Void,
|
||||||
updateSubscription: @escaping () -> Void
|
updateSubscription: @escaping () -> Void,
|
||||||
|
sendGift: @escaping (EnginePeer.Id) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -60,6 +62,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
self.openAppExamples = openAppExamples
|
self.openAppExamples = openAppExamples
|
||||||
self.copyTransactionId = copyTransactionId
|
self.copyTransactionId = copyTransactionId
|
||||||
self.updateSubscription = updateSubscription
|
self.updateSubscription = updateSubscription
|
||||||
|
self.sendGift = sendGift
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
||||||
@ -80,6 +83,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||||
|
|
||||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||||
|
var cachedOverlayCloseImage: UIImage?
|
||||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||||
|
|
||||||
var inProgress = false
|
var inProgress = false
|
||||||
@ -149,7 +153,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let title = Child(MultilineTextComponent.self)
|
let title = Child(MultilineTextComponent.self)
|
||||||
let star = Child(StarsImageComponent.self)
|
let star = Child(StarsImageComponent.self)
|
||||||
let activeStar = Child(PremiumStarComponent.self)
|
let activeStar = Child(PremiumStarComponent.self)
|
||||||
let gift = Child(GiftAnimationComponent.self)
|
let gift = Child(GiftCompositionComponent.self)
|
||||||
let amountBackground = Child(RoundedRectangle.self)
|
let amountBackground = Child(RoundedRectangle.self)
|
||||||
let amount = Child(BalancedTextComponent.self)
|
let amount = Child(BalancedTextComponent.self)
|
||||||
let amountStar = Child(BundleIconComponent.self)
|
let amountStar = Child(BundleIconComponent.self)
|
||||||
@ -188,17 +192,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
state.cachedCloseImage = (closeImage, theme)
|
state.cachedCloseImage = (closeImage, theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
let closeButton = closeButton.update(
|
let closeOverlayImage: UIImage
|
||||||
component: Button(
|
if let image = state.cachedOverlayCloseImage {
|
||||||
content: AnyComponent(Image(image: closeImage)),
|
closeOverlayImage = image
|
||||||
action: { [weak component] in
|
} else {
|
||||||
component?.cancel(true)
|
closeOverlayImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: .white)!
|
||||||
}
|
state.cachedOverlayCloseImage = closeOverlayImage
|
||||||
),
|
}
|
||||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
|
||||||
transition: .immediate
|
|
||||||
)
|
|
||||||
|
|
||||||
let titleText: String
|
let titleText: String
|
||||||
let amountText: String
|
let amountText: String
|
||||||
var descriptionText: String
|
var descriptionText: String
|
||||||
@ -219,7 +220,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
var via: String?
|
var via: String?
|
||||||
var messageId: EngineMessage.Id?
|
var messageId: EngineMessage.Id?
|
||||||
var toPeer: EnginePeer?
|
var toPeer: EnginePeer?
|
||||||
// var toString: String?
|
|
||||||
var transactionPeer: StarsContext.State.Transaction.Peer?
|
var transactionPeer: StarsContext.State.Transaction.Peer?
|
||||||
var media: [AnyMediaReference] = []
|
var media: [AnyMediaReference] = []
|
||||||
var photo: TelegramMediaWebFile?
|
var photo: TelegramMediaWebFile?
|
||||||
@ -234,7 +234,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
var isReaction = false
|
var isReaction = false
|
||||||
var giveawayMessageId: MessageId?
|
var giveawayMessageId: MessageId?
|
||||||
var isBoost = false
|
var isBoost = false
|
||||||
var giftAnimation: TelegramMediaFile?
|
var giftAnimationSubject: GiftCompositionComponent.Subject?
|
||||||
|
var isGiftUpgrade = false
|
||||||
|
var giftAvailability: StarGift.Gift.Availability?
|
||||||
var isRefProgram = false
|
var isRefProgram = false
|
||||||
|
|
||||||
var delayedCloseOnOpenPeer = true
|
var delayedCloseOnOpenPeer = true
|
||||||
@ -250,7 +252,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
count = StarsAmount(value: stars, nanos: 0)
|
count = StarsAmount(value: stars, nanos: 0)
|
||||||
date = boost.date
|
date = boost.date
|
||||||
toPeer = state.peerMap[peerId]
|
toPeer = state.peerMap[peerId]
|
||||||
// toString = strings.Stars_Transaction_Giveaway_Boost_Subscribers(boost.quantity)
|
|
||||||
giveawayMessageId = boost.giveawayMessageId
|
giveawayMessageId = boost.giveawayMessageId
|
||||||
isBoost = true
|
isBoost = true
|
||||||
case let .importer(peer, pricing, importer, usdRate):
|
case let .importer(peer, pricing, importer, usdRate):
|
||||||
@ -368,9 +369,15 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
toPeer = peer
|
toPeer = peer
|
||||||
}
|
}
|
||||||
transactionPeer = transaction.peer
|
transactionPeer = transaction.peer
|
||||||
if case let .generic(gift) = starGift {
|
|
||||||
giftAnimation = gift.file
|
switch starGift {
|
||||||
|
case let .generic(gift):
|
||||||
|
giftAnimationSubject = .generic(gift.file)
|
||||||
|
giftAvailability = gift.availability
|
||||||
|
case let .unique(gift):
|
||||||
|
giftAnimationSubject = .unique(gift)
|
||||||
}
|
}
|
||||||
|
isGiftUpgrade = transaction.flags.contains(.isStarGiftUpgrade)
|
||||||
} else if let giveawayMessageIdValue = transaction.giveawayMessageId {
|
} else if let giveawayMessageIdValue = transaction.giveawayMessageId {
|
||||||
titleText = strings.Stars_Transaction_Giveaway_Title
|
titleText = strings.Stars_Transaction_Giveaway_Title
|
||||||
descriptionText = ""
|
descriptionText = ""
|
||||||
@ -583,6 +590,28 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
descriptionText = modifiedString
|
descriptionText = modifiedString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var closeButtonImage = closeImage
|
||||||
|
if case .unique = giftAnimationSubject {
|
||||||
|
closeButtonImage = closeOverlayImage
|
||||||
|
}
|
||||||
|
let closeButton = closeButton.update(
|
||||||
|
component: Button(
|
||||||
|
content: AnyComponent(Image(image: closeButtonImage)),
|
||||||
|
action: { [weak component] in
|
||||||
|
component?.cancel(true)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||||
|
transition: .immediate
|
||||||
|
)
|
||||||
|
|
||||||
|
let headerTextColor: UIColor
|
||||||
|
if case .unique = giftAnimationSubject {
|
||||||
|
headerTextColor = .white
|
||||||
|
} else {
|
||||||
|
headerTextColor = theme.actionSheet.primaryTextColor
|
||||||
|
}
|
||||||
|
|
||||||
let absCount = StarsAmount(value: abs(count.value), nanos: abs(count.nanos))
|
let absCount = StarsAmount(value: abs(count.value), nanos: abs(count.nanos))
|
||||||
let formattedAmount = presentationStringsFormattedNumber(absCount, dateTimeFormat.groupingSeparator)
|
let formattedAmount = presentationStringsFormattedNumber(absCount, dateTimeFormat.groupingSeparator)
|
||||||
let countColor: UIColor
|
let countColor: UIColor
|
||||||
@ -601,18 +630,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
countColor = theme.list.itemPrimaryTextColor
|
countColor = theme.list.itemPrimaryTextColor
|
||||||
} else if count < StarsAmount.zero {
|
} else if count < StarsAmount.zero {
|
||||||
amountText = "- \(formattedAmount)"
|
amountText = "- \(formattedAmount)"
|
||||||
countColor = theme.list.itemDestructiveColor
|
if case .unique = giftAnimationSubject {
|
||||||
|
countColor = .white
|
||||||
|
} else {
|
||||||
|
countColor = theme.list.itemDestructiveColor
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
amountText = "+ \(formattedAmount)"
|
amountText = "+ \(formattedAmount)"
|
||||||
countColor = theme.list.itemDisclosureActions.constructive.fillColor
|
countColor = theme.list.itemDisclosureActions.constructive.fillColor
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = title.update(
|
let title = title.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: titleText,
|
string: titleText,
|
||||||
font: Font.bold(25.0),
|
font: Font.bold(25.0),
|
||||||
textColor: theme.actionSheet.primaryTextColor,
|
textColor: headerTextColor,
|
||||||
paragraphAlignment: .center
|
paragraphAlignment: .center
|
||||||
)),
|
)),
|
||||||
horizontalAlignment: .center,
|
horizontalAlignment: .center,
|
||||||
@ -647,17 +680,25 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
imageIcon = nil
|
imageIcon = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var starOriginY: CGFloat = 81.0
|
||||||
var starChild: _UpdatedChildComponent
|
var starChild: _UpdatedChildComponent
|
||||||
if let giftAnimation {
|
if let giftAnimationSubject {
|
||||||
|
let animationHeight: CGFloat
|
||||||
|
if case .unique = giftAnimationSubject {
|
||||||
|
animationHeight = 240.0
|
||||||
|
} else {
|
||||||
|
animationHeight = 210.0
|
||||||
|
}
|
||||||
starChild = gift.update(
|
starChild = gift.update(
|
||||||
component: GiftAnimationComponent(
|
component: GiftCompositionComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
file: giftAnimation
|
subject: giftAnimationSubject
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: 128.0, height: 128.0),
|
availableSize: CGSize(width: context.availableSize.width, height: animationHeight),
|
||||||
transition: .immediate
|
transition: .immediate
|
||||||
)
|
)
|
||||||
|
starOriginY = animationHeight / 2.0
|
||||||
} else if isBoost {
|
} else if isBoost {
|
||||||
starChild = activeStar.update(
|
starChild = activeStar.update(
|
||||||
component: PremiumStarComponent(
|
component: PremiumStarComponent(
|
||||||
@ -721,6 +762,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
let tableLinkColor = theme.list.itemAccentColor
|
let tableLinkColor = theme.list.itemAccentColor
|
||||||
var tableItems: [TableComponent.Item] = []
|
var tableItems: [TableComponent.Item] = []
|
||||||
|
|
||||||
|
if isGiftUpgrade {
|
||||||
|
tableItems.append(.init(
|
||||||
|
id: "reason",
|
||||||
|
title: strings.Stars_Transaction_Giveaway_Reason,
|
||||||
|
component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Stars_Transaction_GiftUpgrade, font: tableFont, textColor: tableTextColor)))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if isGift, toPeer == nil {
|
if isGift, toPeer == nil {
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "from",
|
id: "from",
|
||||||
@ -746,7 +797,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
))
|
))
|
||||||
} else if let toPeer, !isRefProgram {
|
} else if let toPeer, !isRefProgram {
|
||||||
let title: String
|
let title: String
|
||||||
if isSubscription {
|
if isGiftUpgrade {
|
||||||
|
title = strings.Stars_Transaction_GiftFrom
|
||||||
|
} else if isSubscription {
|
||||||
if isBotSubscription {
|
if isBotSubscription {
|
||||||
title = strings.Stars_Transaction_Subscription_Bot
|
title = strings.Stars_Transaction_Subscription_Bot
|
||||||
} else if isBusinessSubscription {
|
} else if isBusinessSubscription {
|
||||||
@ -759,10 +812,56 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
title = count < StarsAmount.zero || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
title = count < StarsAmount.zero || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
||||||
}
|
}
|
||||||
tableItems.append(.init(
|
|
||||||
id: "to",
|
let toComponent: AnyComponent<Empty>
|
||||||
title: title,
|
if let _ = giftAnimationSubject, !toPeer.isDeleted && !isGiftUpgrade {
|
||||||
component: AnyComponent(
|
toComponent = AnyComponent(
|
||||||
|
HStack([
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: AnyHashable(0),
|
||||||
|
component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(
|
||||||
|
PeerCellComponent(
|
||||||
|
context: component.context,
|
||||||
|
theme: theme,
|
||||||
|
peer: toPeer
|
||||||
|
)
|
||||||
|
),
|
||||||
|
action: {
|
||||||
|
if delayedCloseOnOpenPeer {
|
||||||
|
component.openPeer(toPeer, false)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if let controller = controller() as? StarsTransactionScreen, let navigationController = controller.navigationController, let chatController = navigationController.viewControllers.first(where: { $0 is ChatController }) as? ChatController {
|
||||||
|
chatController.playShakeAnimation()
|
||||||
|
}
|
||||||
|
component.cancel(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
),
|
||||||
|
AnyComponentWithIdentity(
|
||||||
|
id: AnyHashable(1),
|
||||||
|
component: AnyComponent(Button(
|
||||||
|
content: AnyComponent(ButtonContentComponent(
|
||||||
|
context: component.context,
|
||||||
|
text: strings.Gift_View_Send,
|
||||||
|
color: theme.list.itemAccentColor
|
||||||
|
)),
|
||||||
|
action: {
|
||||||
|
component.sendGift(toPeer.id)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
)
|
||||||
|
], spacing: 4.0)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toComponent = AnyComponent(
|
||||||
Button(
|
Button(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
PeerCellComponent(
|
PeerCellComponent(
|
||||||
@ -786,6 +885,11 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
tableItems.append(.init(
|
||||||
|
id: "to",
|
||||||
|
title: title,
|
||||||
|
component: toComponent
|
||||||
))
|
))
|
||||||
if case let .subscription(subscription) = component.subject, let title = subscription.title {
|
if case let .subscription(subscription) = component.subject, let title = subscription.title {
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
@ -1001,6 +1105,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
Button(
|
Button(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
TransactionCellComponent(
|
TransactionCellComponent(
|
||||||
|
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
|
||||||
textColor: tableTextColor,
|
textColor: tableTextColor,
|
||||||
accentColor: tableLinkColor,
|
accentColor: tableLinkColor,
|
||||||
transactionId: transactionId
|
transactionId: transactionId
|
||||||
@ -1048,6 +1153,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: date, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
if let giftAvailability {
|
||||||
|
let remainsString = presentationStringsFormattedNumber(giftAvailability.remains, environment.dateTimeFormat.groupingSeparator)
|
||||||
|
let totalString = presentationStringsFormattedNumber(giftAvailability.total, environment.dateTimeFormat.groupingSeparator)
|
||||||
|
tableItems.append(.init(
|
||||||
|
id: "availability",
|
||||||
|
title: strings.Gift_View_Availability,
|
||||||
|
component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_View_Availability_NewOf("\(remainsString)", "\(totalString)").string, font: tableFont, textColor: tableTextColor)))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if isSubscriber, let additionalDate {
|
if isSubscriber, let additionalDate {
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
@ -1103,15 +1219,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
|
|
||||||
context.add(starChild
|
context.add(starChild
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 200.0 / 2.0 - 19.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: starOriginY))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var originY: CGFloat = 156.0
|
||||||
|
if let _ = giftAnimationSubject {
|
||||||
|
originY += 18.0
|
||||||
|
}
|
||||||
context.add(title
|
context.add(title
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY))
|
||||||
)
|
)
|
||||||
|
originY += 21.0
|
||||||
var originY: CGFloat = 0.0
|
|
||||||
originY += 200.0 - 23.0
|
|
||||||
|
|
||||||
var descriptionSize: CGSize = .zero
|
var descriptionSize: CGSize = .zero
|
||||||
if !descriptionText.isEmpty {
|
if !descriptionText.isEmpty {
|
||||||
@ -1237,6 +1355,10 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
|||||||
context.add(amountStar
|
context.add(amountStar
|
||||||
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel + amountStarOffsetY))
|
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel + amountStarOffsetY))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if case .unique = giftAnimationSubject {
|
||||||
|
originY += 21.0
|
||||||
|
}
|
||||||
|
|
||||||
context.add(table
|
context.add(table
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
|
||||||
@ -1353,6 +1475,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
let openAppExamples: () -> Void
|
let openAppExamples: () -> Void
|
||||||
let copyTransactionId: (String) -> Void
|
let copyTransactionId: (String) -> Void
|
||||||
let updateSubscription: () -> Void
|
let updateSubscription: () -> Void
|
||||||
|
let sendGift: (EnginePeer.Id) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
@ -1362,7 +1485,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||||
openAppExamples: @escaping () -> Void,
|
openAppExamples: @escaping () -> Void,
|
||||||
copyTransactionId: @escaping (String) -> Void,
|
copyTransactionId: @escaping (String) -> Void,
|
||||||
updateSubscription: @escaping () -> Void
|
updateSubscription: @escaping () -> Void,
|
||||||
|
sendGift: @escaping (EnginePeer.Id) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -1372,6 +1496,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
self.openAppExamples = openAppExamples
|
self.openAppExamples = openAppExamples
|
||||||
self.copyTransactionId = copyTransactionId
|
self.copyTransactionId = copyTransactionId
|
||||||
self.updateSubscription = updateSubscription
|
self.updateSubscription = updateSubscription
|
||||||
|
self.sendGift = sendGift
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
||||||
@ -1416,7 +1541,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
|||||||
openMedia: context.component.openMedia,
|
openMedia: context.component.openMedia,
|
||||||
openAppExamples: context.component.openAppExamples,
|
openAppExamples: context.component.openAppExamples,
|
||||||
copyTransactionId: context.component.copyTransactionId,
|
copyTransactionId: context.component.copyTransactionId,
|
||||||
updateSubscription: context.component.updateSubscription
|
updateSubscription: context.component.updateSubscription,
|
||||||
|
sendGift: context.component.sendGift
|
||||||
)),
|
)),
|
||||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||||
followContentSizeChanges: true,
|
followContentSizeChanges: true,
|
||||||
@ -1516,6 +1642,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
var openAppExamplesImpl: (() -> Void)?
|
var openAppExamplesImpl: (() -> Void)?
|
||||||
var copyTransactionIdImpl: ((String) -> Void)?
|
var copyTransactionIdImpl: ((String) -> Void)?
|
||||||
var updateSubscriptionImpl: (() -> Void)?
|
var updateSubscriptionImpl: (() -> Void)?
|
||||||
|
var sendGiftImpl: ((EnginePeer.Id) -> Void)?
|
||||||
|
|
||||||
super.init(
|
super.init(
|
||||||
context: context,
|
context: context,
|
||||||
@ -1539,6 +1666,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
},
|
},
|
||||||
updateSubscription: {
|
updateSubscription: {
|
||||||
updateSubscriptionImpl?()
|
updateSubscriptionImpl?()
|
||||||
|
},
|
||||||
|
sendGift: { peerId in
|
||||||
|
sendGiftImpl?(peerId)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
navigationBarAppearance: .none,
|
navigationBarAppearance: .none,
|
||||||
@ -1690,6 +1820,19 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendGiftImpl = { [weak self] peerId in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let _ = (context.engine.payments.premiumGiftCodeOptions(peerId: nil, onlyCached: true)
|
||||||
|
|> filter { !$0.isEmpty }
|
||||||
|
|> deliverOnMainQueue).start(next: { giftOptions in
|
||||||
|
let premiumOptions = giftOptions.filter { $0.users == 1 }.map { CachedPremiumGiftOption(months: $0.months, currency: $0.currency, amount: $0.amount, botUrl: "", storeProductId: $0.storeProductId) }
|
||||||
|
let controller = context.sharedContext.makeGiftOptionsController(context: context, peerId: peerId, premiumOptions: premiumOptions, hasBirthday: false)
|
||||||
|
self.push(controller)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -2011,7 +2154,7 @@ private final class PeerCellComponent: Component {
|
|||||||
let avatarNaturalSize = self.avatar.update(
|
let avatarNaturalSize = self.avatar.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
StarsAvatarComponent(context: component.context, theme: component.theme, peer: peer, photo: nil, media: [], backgroundColor: .clear)
|
StarsAvatarComponent(context: component.context, theme: component.theme, peer: peer, photo: nil, media: [], uniqueGift: nil, backgroundColor: .clear)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||||
@ -2063,18 +2206,23 @@ private final class PeerCellComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class TransactionCellComponent: Component {
|
private final class TransactionCellComponent: Component {
|
||||||
|
let backgroundColor: UIColor
|
||||||
let textColor: UIColor
|
let textColor: UIColor
|
||||||
let accentColor: UIColor
|
let accentColor: UIColor
|
||||||
let transactionId: String
|
let transactionId: String
|
||||||
|
|
||||||
init(textColor: UIColor, accentColor: UIColor, transactionId: String) {
|
init(backgroundColor: UIColor, textColor: UIColor, accentColor: UIColor, transactionId: String) {
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
self.textColor = textColor
|
self.textColor = textColor
|
||||||
self.accentColor = accentColor
|
self.accentColor = accentColor
|
||||||
self.transactionId = transactionId
|
self.transactionId = transactionId
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: TransactionCellComponent, rhs: TransactionCellComponent) -> Bool {
|
static func ==(lhs: TransactionCellComponent, rhs: TransactionCellComponent) -> Bool {
|
||||||
if lhs.textColor !== rhs.textColor {
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.textColor != rhs.textColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.accentColor != rhs.accentColor {
|
if lhs.accentColor != rhs.accentColor {
|
||||||
@ -2089,12 +2237,17 @@ private final class TransactionCellComponent: Component {
|
|||||||
final class View: UIView {
|
final class View: UIView {
|
||||||
private let text = ComponentView<Empty>()
|
private let text = ComponentView<Empty>()
|
||||||
private let button = ComponentView<Empty>()
|
private let button = ComponentView<Empty>()
|
||||||
|
private let gradientView = UIImageView()
|
||||||
|
|
||||||
private var component: TransactionCellComponent?
|
private var component: TransactionCellComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.allowsGroupOpacity = true
|
||||||
|
|
||||||
|
self.gradientView.image = generateGradientImage(size: CGSize(width: 40.0, height: 1.0), colors: [UIColor.white.withAlphaComponent(0.0), UIColor.white, UIColor.white], locations: [0.0, 0.65, 1.0], direction: .horizontal)?.withRenderingMode(.alwaysTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -2104,55 +2257,49 @@ private final class TransactionCellComponent: Component {
|
|||||||
func update(component: TransactionCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
func update(component: TransactionCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
let spacing: CGFloat = 6.0
|
self.gradientView.tintColor = component.backgroundColor
|
||||||
|
|
||||||
let buttonSize = self.button.update(
|
let buttonSize = self.button.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: component.accentColor)
|
BundleIconComponent(
|
||||||
|
name: "Chat/Context Menu/Copy",
|
||||||
|
tintColor: component.accentColor
|
||||||
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
|
containerSize: CGSize(width: availableSize.width, height: availableSize.height)
|
||||||
)
|
)
|
||||||
|
|
||||||
func brokenLine(_ string: String) -> String {
|
|
||||||
if string.count > 30 {
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
let middleIndex = string.index(string.startIndex, offsetBy: string.count / 2)
|
|
||||||
var newString = string
|
|
||||||
newString.insert("\n", at: middleIndex)
|
|
||||||
return newString
|
|
||||||
}
|
|
||||||
|
|
||||||
let text: String
|
|
||||||
if availableSize.width > 230.0 {
|
|
||||||
text = component.transactionId
|
|
||||||
} else {
|
|
||||||
text = brokenLine(component.transactionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
let textSize = self.text.update(
|
let textSize = self.text.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(
|
MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: text,
|
string: component.transactionId,
|
||||||
font: Font.monospace(15.0),
|
font: Font.monospace(15.0),
|
||||||
textColor: component.textColor,
|
textColor: component.textColor,
|
||||||
paragraphAlignment: .left
|
paragraphAlignment: .left
|
||||||
)),
|
)),
|
||||||
maximumNumberOfLines: 0,
|
maximumNumberOfLines: 1
|
||||||
lineSpacing: 0.2
|
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: availableSize.width - buttonSize.width - spacing, height: availableSize.height)
|
containerSize: CGSize(width: availableSize.width - buttonSize.width + 10.0, height: availableSize.height)
|
||||||
)
|
)
|
||||||
|
|
||||||
let size = CGSize(width: availableSize.width, height: textSize.height)
|
let size = CGSize(width: availableSize.width, height: textSize.height)
|
||||||
|
|
||||||
|
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + 1.0), size: textSize)
|
||||||
|
if let textView = self.text.view {
|
||||||
|
if textView.superview == nil {
|
||||||
|
self.addSubview(textView)
|
||||||
|
self.addSubview(self.gradientView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: textView, frame: textFrame)
|
||||||
|
}
|
||||||
|
|
||||||
let buttonFrame = CGRect(origin: CGPoint(x: availableSize.width - buttonSize.width - 2.0, y: floorToScreenPixels((size.height - buttonSize.height) / 2.0)), size: buttonSize)
|
let buttonFrame = CGRect(origin: CGPoint(x: availableSize.width - buttonSize.width - 2.0, y: floorToScreenPixels((size.height - buttonSize.height) / 2.0)), size: buttonSize)
|
||||||
if let buttonView = self.button.view {
|
if let buttonView = self.button.view {
|
||||||
if buttonView.superview == nil {
|
if buttonView.superview == nil {
|
||||||
@ -2161,13 +2308,7 @@ private final class TransactionCellComponent: Component {
|
|||||||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||||
}
|
}
|
||||||
|
|
||||||
let textFrame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((size.height - textSize.height) / 2.0) + 1.0), size: textSize)
|
self.gradientView.frame = CGRect(x: size.width - buttonSize.width - 32.0, y: 0.0, width: 40.0, height: size.height)
|
||||||
if let textView = self.text.view {
|
|
||||||
if textView.superview == nil {
|
|
||||||
self.addSubview(textView)
|
|
||||||
}
|
|
||||||
transition.setFrame(view: textView, frame: textFrame)
|
|
||||||
}
|
|
||||||
|
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
@ -2202,3 +2343,92 @@ private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor:
|
|||||||
context.strokePath()
|
context.strokePath()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ButtonContentComponent: Component {
|
||||||
|
let context: AccountContext
|
||||||
|
let text: String
|
||||||
|
let color: UIColor
|
||||||
|
|
||||||
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
text: String,
|
||||||
|
color: UIColor
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
self.text = text
|
||||||
|
self.color = color
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ButtonContentComponent, rhs: ButtonContentComponent) -> Bool {
|
||||||
|
if lhs.context !== rhs.context {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.text != rhs.text {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.color != rhs.color {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIView {
|
||||||
|
private var component: ButtonContentComponent?
|
||||||
|
private weak var componentState: EmptyComponentState?
|
||||||
|
|
||||||
|
private let backgroundLayer = SimpleLayer()
|
||||||
|
private let title = ComponentView<Empty>()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.layer.addSublayer(self.backgroundLayer)
|
||||||
|
self.backgroundLayer.masksToBounds = true
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: ButtonContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
|
self.component = component
|
||||||
|
self.componentState = state
|
||||||
|
|
||||||
|
let attributedText = NSAttributedString(string: component.text, font: Font.regular(11.0), textColor: component.color)
|
||||||
|
let titleSize = self.title.update(
|
||||||
|
transition: transition,
|
||||||
|
component: AnyComponent(
|
||||||
|
MultilineTextComponent(text: .plain(attributedText))
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: availableSize
|
||||||
|
)
|
||||||
|
|
||||||
|
let padding: CGFloat = 6.0
|
||||||
|
let size = CGSize(width: titleSize.width + padding * 2.0, height: 18.0)
|
||||||
|
|
||||||
|
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((size.height - titleSize.height) / 2.0)), size: titleSize)
|
||||||
|
if let titleView = self.title.view {
|
||||||
|
if titleView.superview == nil {
|
||||||
|
self.addSubview(titleView)
|
||||||
|
}
|
||||||
|
transition.setFrame(view: titleView, frame: titleFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundColor = component.color.withAlphaComponent(0.1)
|
||||||
|
self.backgroundLayer.backgroundColor = backgroundColor.cgColor
|
||||||
|
transition.setFrame(layer: self.backgroundLayer, frame: CGRect(origin: .zero, size: size))
|
||||||
|
self.backgroundLayer.cornerRadius = size.height / 2.0
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -49,6 +49,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
||||||
"//submodules/TelegramUI/Components/LottieComponent",
|
"//submodules/TelegramUI/Components/LottieComponent",
|
||||||
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
||||||
|
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import AvatarNode
|
|||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
import StarsAvatarComponent
|
import StarsAvatarComponent
|
||||||
import LottieComponent
|
import GiftAnimationComponent
|
||||||
|
|
||||||
private extension StarsContext.State.Transaction {
|
private extension StarsContext.State.Transaction {
|
||||||
var extendedId: String {
|
var extendedId: String {
|
||||||
@ -300,13 +300,20 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
var itemDate: String
|
var itemDate: String
|
||||||
var itemPeer = item.peer
|
var itemPeer = item.peer
|
||||||
var itemFile: TelegramMediaFile?
|
var itemFile: TelegramMediaFile?
|
||||||
|
var uniqueGift: StarGift.UniqueGift?
|
||||||
switch item.peer {
|
switch item.peer {
|
||||||
case let .peer(peer):
|
case let .peer(peer):
|
||||||
if let starGift = item.starGift {
|
if let starGift = item.starGift {
|
||||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
if item.flags.contains(.isStarGiftUpgrade), case let .unique(gift) = starGift {
|
||||||
itemSubtitle = item.count > StarsAmount.zero ? environment.strings.Stars_Intro_Transaction_ConvertedGift : environment.strings.Stars_Intro_Transaction_Gift
|
itemTitle = "\(gift.title) #\(gift.number)"
|
||||||
if case let .generic(gift) = starGift {
|
itemSubtitle = environment.strings.Stars_Intro_Transaction_GiftUpgrade
|
||||||
itemFile = gift.file
|
uniqueGift = gift
|
||||||
|
} else {
|
||||||
|
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||||
|
itemSubtitle = item.count > StarsAmount.zero ? environment.strings.Stars_Intro_Transaction_ConvertedGift : environment.strings.Stars_Intro_Transaction_Gift
|
||||||
|
if case let .generic(gift) = starGift {
|
||||||
|
itemFile = gift.file
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let _ = item.giveawayMessageId {
|
} else if let _ = item.giveawayMessageId {
|
||||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||||
@ -394,7 +401,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
itemDate += " – \(environment.strings.Monetization_Transaction_Failed)"
|
itemDate += " – \(environment.strings.Monetization_Transaction_Failed)"
|
||||||
itemDateColor = environment.theme.list.itemDestructiveColor
|
itemDateColor = environment.theme.list.itemDestructiveColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
||||||
titleComponents.append(
|
titleComponents.append(
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||||
@ -411,24 +418,23 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
if let itemFile {
|
if let itemFile {
|
||||||
subtitleComponent = AnyComponent(
|
subtitleComponent = AnyComponent(
|
||||||
HStack([
|
HStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(LottieComponent(
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(
|
||||||
content: LottieComponent.ResourceContent(
|
GiftAnimationComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
|
theme: environment.theme,
|
||||||
file: itemFile,
|
file: itemFile,
|
||||||
attemptSynchronously: false,
|
still: true,
|
||||||
providesPlaceholder: true
|
size: CGSize(width: 20.0, height: 20.0)
|
||||||
),
|
)
|
||||||
color: nil,
|
)),
|
||||||
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(
|
||||||
size: CGSize(width: 20.0, height: 20.0),
|
MultilineTextComponent(
|
||||||
loop: false
|
text: .plain(NSAttributedString(
|
||||||
))),
|
string: itemSubtitle,
|
||||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
font: Font.regular(fontBaseDisplaySize * 16.0 / 17.0),
|
||||||
text: .plain(NSAttributedString(
|
textColor: environment.theme.list.itemPrimaryTextColor
|
||||||
string: itemSubtitle,
|
)
|
||||||
font: Font.regular(fontBaseDisplaySize * 16.0 / 17.0),
|
)
|
||||||
textColor: environment.theme.list.itemPrimaryTextColor
|
|
||||||
))
|
|
||||||
)))
|
)))
|
||||||
], spacing: 2.0)
|
], spacing: 2.0)
|
||||||
)
|
)
|
||||||
@ -463,7 +469,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
||||||
contentInsets: UIEdgeInsets(top: 9.0, left: environment.containerInsets.left, bottom: 8.0, right: environment.containerInsets.right),
|
contentInsets: UIEdgeInsets(top: 9.0, left: environment.containerInsets.left, bottom: 8.0, right: environment.containerInsets.right),
|
||||||
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: itemPeer, photo: item.photo, media: item.media, backgroundColor: environment.theme.list.plainBackgroundColor))), false),
|
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: itemPeer, photo: item.photo, media: item.media, uniqueGift: uniqueGift, backgroundColor: environment.theme.list.plainBackgroundColor))), false),
|
||||||
icon: nil,
|
icon: nil,
|
||||||
accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
accessory: .custom(ListActionItemComponent.CustomAccessory(component: AnyComponentWithIdentity(id: "label", component: AnyComponent(StarsLabelComponent(text: itemLabel))), insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
||||||
action: { [weak self] _ in
|
action: { [weak self] _ in
|
||||||
|
|||||||
@ -765,7 +765,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
if let photo = subscription.photo {
|
if let photo = subscription.photo {
|
||||||
nameGroupComponent = AnyComponent(
|
nameGroupComponent = AnyComponent(
|
||||||
HStack([
|
HStack([
|
||||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: nil, photo: photo, media: [], backgroundColor: .clear, size: CGSize(width: 19.0, height: 19.0)))),
|
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: nil, photo: photo, media: [], uniqueGift: nil, backgroundColor: .clear, size: CGSize(width: 19.0, height: 19.0)))),
|
||||||
AnyComponentWithIdentity(id: AnyHashable(1), component: nameComponent)
|
AnyComponentWithIdentity(id: AnyHashable(1), component: nameComponent)
|
||||||
], spacing: 6.0)
|
], spacing: 6.0)
|
||||||
)
|
)
|
||||||
@ -806,7 +806,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.0)),
|
||||||
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0),
|
contentInsets: UIEdgeInsets(top: 9.0, left: 0.0, bottom: 8.0, right: 0.0),
|
||||||
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: .peer(subscription.peer), photo: nil, media: [], backgroundColor: environment.theme.list.plainBackgroundColor))), false),
|
leftIcon: .custom(AnyComponentWithIdentity(id: "avatar", component: AnyComponent(StarsAvatarComponent(context: component.context, theme: environment.theme, peer: .peer(subscription.peer), photo: nil, media: [], uniqueGift: nil, backgroundColor: environment.theme.list.plainBackgroundColor))), false),
|
||||||
icon: nil,
|
icon: nil,
|
||||||
accessory: .custom(ListActionItemComponent.CustomAccessory(component: labelComponent, insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
accessory: .custom(ListActionItemComponent.CustomAccessory(component: labelComponent, insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
||||||
action: { [weak self] _ in
|
action: { [weak self] _ in
|
||||||
|
|||||||
@ -1228,8 +1228,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
controller.videoCompletion = { [weak self] image, url, adjustments, commit in
|
controller.videoCompletion = { [weak self] image, url, adjustments, commit in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let _ = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||||
//settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
|
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
||||||
commit()
|
commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1263,8 +1263,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}, videoCompletion: { [weak self] image, url, adjustments in
|
}, videoCompletion: { [weak self] image, url, adjustments in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let _ = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||||
//settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
|
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2472,12 +2472,31 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
presentTransferAlertImpl = { [weak controller] peer in
|
presentTransferAlertImpl = { [weak controller] peer in
|
||||||
guard let controller, case let .starGiftTransfer(_, messageId, gift, transferStars, _) = source else {
|
guard let controller, case let .starGiftTransfer(_, _, gift, transferStars, _) = source else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let alertController = giftTransferAlertController(context: context, gift: gift, peer: peer, transferStars: transferStars, commit: { [weak controller] in
|
let alertController = giftTransferAlertController(context: context, gift: gift, peer: peer, transferStars: transferStars, commit: { [weak controller] in
|
||||||
controller?.dismiss()
|
completion?([peer.id])
|
||||||
let _ = context.engine.payments.transferStarGift(prepaid: transferStars == 0, messageId: messageId, peerId: peer.id).start()
|
|
||||||
|
guard let controller, let navigationController = controller.navigationController as? NavigationController else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var controllers = navigationController.viewControllers
|
||||||
|
controllers = controllers.filter { !($0 is ContactSelectionController) }
|
||||||
|
var foundController = false
|
||||||
|
for controller in controllers.reversed() {
|
||||||
|
if let chatController = controller as? ChatController, case .peer(id: peer.id) = chatController.chatLocation {
|
||||||
|
chatController.hintPlayNextOutgoingGift()
|
||||||
|
foundController = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundController {
|
||||||
|
let chatController = context.sharedContext.makeChatController(context: context, chatLocation: .peer(id: peer.id), subject: nil, botStart: nil, mode: .standard(.default), params: nil)
|
||||||
|
chatController.hintPlayNextOutgoingGift()
|
||||||
|
controllers.append(chatController)
|
||||||
|
}
|
||||||
|
navigationController.setViewControllers(controllers, animated: true)
|
||||||
})
|
})
|
||||||
controller.present(alertController, in: .window(.root))
|
controller.present(alertController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user