mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
e3ee1dde7e
commit
5d4213c4fc
@ -980,7 +980,7 @@ private final class NotificationServiceHandler {
|
||||
|
||||
enum Action {
|
||||
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 deleteMessage([MessageId])
|
||||
case readReactions([MessageId])
|
||||
@ -999,7 +999,7 @@ private final class NotificationServiceHandler {
|
||||
action = .logout
|
||||
case "MESSAGE_MUTED":
|
||||
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":
|
||||
if let peerId = peerId {
|
||||
@ -1183,9 +1183,16 @@ private final class NotificationServiceHandler {
|
||||
|
||||
action = .pollStories(peerId: peerId, content: content, storyId: storyId, isReaction: isReaction)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
@ -1245,7 +1252,7 @@ private final class NotificationServiceHandler {
|
||||
let content = NotificationContent(isLockedMessage: nil)
|
||||
updateCurrentContent(content)
|
||||
completed()
|
||||
case let .poll(peerId, initialContent, messageId):
|
||||
case let .poll(peerId, initialContent, messageId, reportDelivery):
|
||||
Logger.shared.log("NotificationService \(episode)", "Will poll")
|
||||
if let stateManager = strongSelf.stateManager {
|
||||
let shouldKeepConnection = stateManager.network.shouldKeepConnection
|
||||
@ -1683,12 +1690,23 @@ private final class NotificationServiceHandler {
|
||||
pollWithUpdatedContent = pollSignal
|
||||
|> 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 updatedMedia: Media?
|
||||
strongSelf.pollDisposable.set(pollWithUpdatedContent.start(next: { content, media in
|
||||
updatedContent = content
|
||||
updatedMedia = media
|
||||
strongSelf.pollDisposable.set(combineLatest(pollWithUpdatedContent, reportDeliverySignal).start(next: { contentAndMedia, _ in
|
||||
updatedContent = contentAndMedia.0
|
||||
updatedMedia = contentAndMedia.1
|
||||
}, completed: {
|
||||
pollCompletion(updatedContent, updatedMedia)
|
||||
}))
|
||||
@ -1951,6 +1969,8 @@ private final class NotificationServiceHandler {
|
||||
} else {
|
||||
pollWithUpdatedContent = .complete()
|
||||
}
|
||||
|
||||
|
||||
|
||||
var updatedContent = initialContent
|
||||
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.Unsupported.Title" = "Unsupported";
|
||||
"Stars.Intro.Transaction.Refund" = "Refund";
|
||||
"Stars.Intro.Transaction.GiftUpgrade" = "Gift Upgrade";
|
||||
|
||||
"Stars.Intro.PurchasedTitle" = "Stars Acquired";
|
||||
"Stars.Intro.PurchasedText" = "**%@** added to your balance.";
|
||||
@ -13503,9 +13504,9 @@ Sorry for the inconvenience.";
|
||||
"Gift.Unique.Availability" = "Availability";
|
||||
"Gift.Unique.Issued" = "%@ issued";
|
||||
"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.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.";
|
||||
|
||||
@ -13533,6 +13534,7 @@ Sorry for the inconvenience.";
|
||||
|
||||
"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.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.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.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
|
||||
private var currentLoopCount: Int = 0
|
||||
private var canDisplayFirstFrame: Bool = false
|
||||
private var playbackMode: AnimatedStickerPlaybackMode = .loop
|
||||
public var playbackMode: AnimatedStickerPlaybackMode = .loop
|
||||
|
||||
public var stopAtNearestLoop: Bool = false
|
||||
|
||||
|
@ -1527,7 +1527,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
}
|
||||
|
||||
switch result {
|
||||
case let .done(receiptMessageId, _):
|
||||
case let .done(receiptMessageId, _, _):
|
||||
proceedWithCompletion(true, receiptMessageId)
|
||||
case let .externalVerificationRequired(url):
|
||||
strongSelf.updateActionButton()
|
||||
|
@ -1352,6 +1352,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
let _ = titleApply()
|
||||
|
||||
var titleLeftOffset: CGFloat = 0.0
|
||||
var nextIconX: CGFloat = titleFrame.maxX
|
||||
if let verifiedIcon {
|
||||
let animationCache = item.context.animationCache
|
||||
let animationRenderer = item.context.animationRenderer
|
||||
@ -1375,17 +1376,30 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
emojiFileUpdated: nil
|
||||
)
|
||||
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(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(verifiedIconComponent),
|
||||
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 {
|
||||
strongSelf.verifiedIconView = nil
|
||||
verifiedIconView.removeFromSuperview()
|
||||
@ -1424,7 +1438,6 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
var nextIconX: CGFloat = titleFrame.maxX
|
||||
if let credibilityIcon {
|
||||
let animationCache = item.context.animationCache
|
||||
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)
|
||||
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(.foregroundColor, value: color, 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 titleLeftOffset: CGFloat = 0.0
|
||||
var nextIconX: CGFloat = titleFrame.maxX
|
||||
if let verifiedIcon = verifiedIcon {
|
||||
let animationCache = item.context.animationCache
|
||||
let animationRenderer = item.context.animationRenderer
|
||||
@ -1461,6 +1462,15 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
emojiFileUpdated: nil
|
||||
)
|
||||
strongSelf.verifiedIconComponent = verifiedIconComponent
|
||||
|
||||
let iconOrigin: CGFloat
|
||||
if case .animation = verifiedIcon {
|
||||
iconOrigin = titleFrame.minX
|
||||
} else {
|
||||
nextIconX += 4.0
|
||||
iconOrigin = nextIconX
|
||||
}
|
||||
|
||||
let iconSize = verifiedIconView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(verifiedIconComponent),
|
||||
@ -1468,9 +1478,13 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
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))
|
||||
|
||||
titleLeftOffset += iconSize.width + 4.0
|
||||
transition.updateFrame(view: verifiedIconView, frame: CGRect(origin: CGPoint(x: iconOrigin, y: floorToScreenPixels(titleFrame.midY - iconSize.height / 2.0)), size: iconSize))
|
||||
|
||||
if case .animation = verifiedIcon {
|
||||
titleLeftOffset += iconSize.width + 4.0
|
||||
} else {
|
||||
nextIconX += iconSize.width
|
||||
}
|
||||
} else if let verifiedIconView = strongSelf.verifiedIconView {
|
||||
strongSelf.verifiedIconView = nil
|
||||
verifiedIconView.removeFromSuperview()
|
||||
@ -1512,7 +1526,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
||||
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 {
|
||||
strongSelf.credibilityIconView = nil
|
||||
credibilityIconView.removeFromSuperview()
|
||||
|
@ -336,7 +336,7 @@ final class StarsTransactionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
theme: item.presentationData.theme,
|
||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.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,
|
||||
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
|
||||
|
@ -132,6 +132,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateStarsBalance(peerId: PeerId, balance: Api.StarsAmount)
|
||||
case UpdateStarsRevenueStatus(peerId: PeerId, status: StarsRevenueStats.Balances)
|
||||
case UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: Bool)
|
||||
case ReportMessageDelivery([MessageId])
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
@ -702,9 +703,13 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateStarsReactionsAreAnonymousByDefault(isAnonymous: isAnonymous))
|
||||
}
|
||||
|
||||
mutating func addReportMessageDelivery(messageIds: [MessageId]) {
|
||||
self.addOperation(.ReportMessageDelivery(messageIds))
|
||||
}
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .UpdateWallpaper, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault:
|
||||
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
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
@ -852,6 +857,7 @@ struct AccountReplayedFinalState {
|
||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||
let sentScheduledMessageIds: Set<MessageId>
|
||||
let reportMessageDelivery: Set<MessageId>
|
||||
}
|
||||
|
||||
struct AccountFinalStateEvents {
|
||||
@ -882,12 +888,13 @@ struct AccountFinalStateEvents {
|
||||
let updatedRevenueBalances: [PeerId: RevenueStats.Balances]
|
||||
let updatedStarsBalance: [PeerId: StarsAmount]
|
||||
let updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances]
|
||||
let reportMessageDelivery: Set<MessageId>
|
||||
|
||||
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.addedReactionEvents = addedReactionEvents
|
||||
self.wasScheduledMessageIds = wasScheduledMessageIds
|
||||
@ -915,6 +922,7 @@ struct AccountFinalStateEvents {
|
||||
self.updatedStarsBalance = updatedStarsBalance
|
||||
self.updatedStarsRevenueStatus = updatedStarsRevenueStatus
|
||||
self.sentScheduledMessageIds = sentScheduledMessageIds
|
||||
self.reportMessageDelivery = reportMessageDelivery
|
||||
}
|
||||
|
||||
init(state: AccountReplayedFinalState) {
|
||||
@ -945,6 +953,7 @@ struct AccountFinalStateEvents {
|
||||
self.updatedStarsBalance = state.updatedStarsBalance
|
||||
self.updatedStarsRevenueStatus = state.updatedStarsRevenueStatus
|
||||
self.sentScheduledMessageIds = state.sentScheduledMessageIds
|
||||
self.reportMessageDelivery = state.reportMessageDelivery
|
||||
}
|
||||
|
||||
func union(with other: AccountFinalStateEvents) -> AccountFinalStateEvents {
|
||||
@ -977,6 +986,9 @@ struct AccountFinalStateEvents {
|
||||
var sentScheduledMessageIds = self.sentScheduledMessageIds
|
||||
sentScheduledMessageIds.formUnion(other.sentScheduledMessageIds)
|
||||
|
||||
return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, storyUpdates: self.storyUpdates + other.storyUpdates, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, dismissBotWebViews: self.dismissBotWebViews + other.dismissBotWebViews, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs }), updateConfig: updateConfig, isPremiumUpdated: isPremiumUpdated, updatedRevenueBalances: self.updatedRevenueBalances.merging(other.updatedRevenueBalances, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsBalance: self.updatedStarsBalance.merging(other.updatedStarsBalance, uniquingKeysWith: { lhs, _ in lhs }), updatedStarsRevenueStatus: self.updatedStarsRevenueStatus.merging(other.updatedStarsRevenueStatus, uniquingKeysWith: { lhs, _ in lhs }), sentScheduledMessageIds: sentScheduledMessageIds)
|
||||
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))
|
||||
case let .messageActionPrizeStars(flags, stars, transactionId, boostPeer, giveawayMsgId):
|
||||
return TelegramMediaAction(action: .prizeStars(amount: stars, isUnclaimed: (flags & (1 << 2)) != 0, boostPeerId: boostPeer.peerId, transactionId: transactionId, giveawayMessageId: MessageId(peerId: boostPeer.peerId, namespace: Namespaces.Message.Cloud, id: giveawayMsgId)))
|
||||
case let .messageActionStarGift(flags, apiGift, message, convertStars, _, upgradeStars):
|
||||
case let .messageActionStarGift(flags, apiGift, message, convertStars, upgradeMessageId, upgradeStars):
|
||||
let text: String?
|
||||
let entities: [MessageTextEntity]?
|
||||
switch message {
|
||||
@ -185,7 +185,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
}
|
||||
return TelegramMediaAction(action: .starGift(gift: gift, convertStars: convertStars, text: text, entities: entities, nameHidden: (flags & (1 << 0)) != 0, savedToProfile: (flags & (1 << 2)) != 0, converted: (flags & (1 << 3)) != 0, upgraded: (flags & (1 << 5)) != 0, 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):
|
||||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
|
@ -721,6 +721,7 @@ func finalStateWithDifference(accountPeerId: PeerId, postbox: Postbox, network:
|
||||
updatedState.mergeChats(chats)
|
||||
updatedState.mergeUsers(users)
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
for message in messages {
|
||||
if let preCachedResources = message.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) {
|
||||
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>()
|
||||
|
||||
for update in sortedUpdates(updates) {
|
||||
@ -1110,6 +1117,10 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox:
|
||||
}
|
||||
}
|
||||
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):
|
||||
let popup = (flags & (1 << 0)) != 0
|
||||
@ -3282,7 +3293,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddQuickReplyMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .UpdatePinnedSavedItemIds, .UpdatePinnedTopic, .UpdatePinnedTopicOrder, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia, .ResetForumTopic, .UpdateStory, .UpdateReadStories, .UpdateStoryStealthMode, .UpdateStorySentReaction, .UpdateNewAuthorization, .UpdateWallpaper, .UpdateRevenueBalances, .UpdateStarsBalance, .UpdateStarsRevenueStatus, .UpdateStarsReactionsAreAnonymousByDefault:
|
||||
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 {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@ -3421,6 +3432,7 @@ func replayFinalState(
|
||||
var updatedStarsBalance: [PeerId: StarsAmount] = [:]
|
||||
var updatedStarsRevenueStatus: [PeerId: StarsRevenueStats.Balances] = [:]
|
||||
var updatedStarsReactionsAreAnonymousByDefault: Bool?
|
||||
var reportMessageDelivery = Set<MessageId>()
|
||||
|
||||
var holesFromPreviousStateMessageIds: [MessageId] = []
|
||||
var clearHolesFromPreviousStateForChannelMessagesWithPts: [PeerIdAndMessageNamespace: Int32] = [:]
|
||||
@ -4855,6 +4867,8 @@ func replayFinalState(
|
||||
updatedStarsRevenueStatus[peerId] = status
|
||||
case let .UpdateStarsReactionsAreAnonymousByDefault(value):
|
||||
updatedStarsReactionsAreAnonymousByDefault = value
|
||||
case let .ReportMessageDelivery(messageIds):
|
||||
reportMessageDelivery = Set(messageIds)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5376,6 +5390,7 @@ func replayFinalState(
|
||||
updatedRevenueBalances: updatedRevenueBalances,
|
||||
updatedStarsBalance: updatedStarsBalance,
|
||||
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 appliedQtsPromise = Promise<Int32?>(nil)
|
||||
private let appliedQtsDisposable = MetaDisposable()
|
||||
private let reportMessageDeliveryDisposable = DisposableSet()
|
||||
|
||||
let updateConfigRequested: (() -> Void)?
|
||||
let isPremiumUpdated: (() -> Void)?
|
||||
@ -391,6 +392,7 @@ public final class AccountStateManager {
|
||||
self.operationDisposable.dispose()
|
||||
self.appliedMaxMessageIdDisposable.dispose()
|
||||
self.appliedQtsDisposable.dispose()
|
||||
self.reportMessageDeliveryDisposable.dispose()
|
||||
}
|
||||
|
||||
public func reset() {
|
||||
@ -1130,6 +1132,9 @@ public final class AccountStateManager {
|
||||
if !events.sentScheduledMessageIds.isEmpty {
|
||||
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 {
|
||||
strongSelf.addIsContactUpdates(events.isContactUpdates)
|
||||
}
|
||||
|
@ -4,24 +4,20 @@ import TelegramApi
|
||||
|
||||
public final class ReportDeliveryMessageAttribute: Equatable, MessageAttribute {
|
||||
public let untilDate: Int32
|
||||
public let isReported: Bool
|
||||
|
||||
public init(untilDate: Int32, isReported: Bool) {
|
||||
self.untilDate = untilDate
|
||||
self.isReported = isReported
|
||||
}
|
||||
|
||||
required public init(decoder: PostboxDecoder) {
|
||||
self.untilDate = decoder.decodeInt32ForKey("d", orElse: 0)
|
||||
self.isReported = decoder.decodeBoolForKey("r", orElse: false)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.untilDate, forKey: "d")
|
||||
encoder.encodeBool(self.isReported, forKey: "r")
|
||||
}
|
||||
|
||||
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 giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
||||
case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?)
|
||||
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, 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)
|
||||
|
||||
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)
|
||||
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:
|
||||
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:
|
||||
@ -548,7 +548,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "giveawayMsgId")
|
||||
}
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded):
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, upgradeMessageId):
|
||||
encoder.encodeInt32(44, forKey: "_rawValue")
|
||||
encoder.encodeObject(gift, forKey: "gift")
|
||||
if let convertStars {
|
||||
@ -574,6 +574,11 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
||||
encoder.encodeNil(forKey: "upgradeStars")
|
||||
}
|
||||
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):
|
||||
encoder.encodeInt32(45, forKey: "_rawValue")
|
||||
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 {
|
||||
case generic
|
||||
case alreadyActive
|
||||
case noPaymentNeeded
|
||||
}
|
||||
|
||||
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))
|
||||
|> `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)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
|
||||
@ -622,7 +626,7 @@ public enum SendBotPaymentFormError {
|
||||
}
|
||||
|
||||
public enum SendBotPaymentResult {
|
||||
case done(receiptMessageId: MessageId?, subscriptionPeerId: PeerId?)
|
||||
case done(receiptMessageId: MessageId?, subscriptionPeerId: PeerId?, uniqueStarGift: ProfileGiftsContext.State.StarGift?)
|
||||
case externalVerificationRequired(url: String)
|
||||
}
|
||||
|
||||
@ -671,12 +675,12 @@ func _internal_sendBotPaymentForm(account: Account, formId: Int64, source: BotPa
|
||||
case .starsChatSubscription:
|
||||
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
||||
if let first = chats.first {
|
||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id)
|
||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id, uniqueStarGift: nil)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
for apiMessage in updates.messages {
|
||||
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
||||
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):
|
||||
return .externalVerificationRequired(url: url)
|
||||
}
|
||||
|
@ -689,15 +689,23 @@ func _internal_transferStarGift(account: Account, prepaid: Bool, messageId: Engi
|
||||
} else {
|
||||
let source: BotPaymentInvoiceSource = .starGiftTransfer(messageId: messageId, toPeerId: peerId)
|
||||
return _internal_fetchBotPaymentForm(accountPeerId: account.peerId, postbox: account.postbox, network: account.network, source: source, themeParams: nil)
|
||||
|> mapError { _ -> TransferStarGiftError in
|
||||
return .generic
|
||||
|> map(Optional.init)
|
||||
|> `catch` { error -> Signal<BotPaymentForm?, TransferStarGiftError> in
|
||||
if case .noPaymentNeeded = error {
|
||||
return .single(nil)
|
||||
}
|
||||
return .fail(.generic)
|
||||
}
|
||||
|> mapToSignal { paymentForm in
|
||||
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|
||||
|> mapError { _ -> TransferStarGiftError in
|
||||
return .generic
|
||||
if let paymentForm {
|
||||
return _internal_sendStarsPaymentForm(account: account, formId: paymentForm.id, source: source)
|
||||
|> 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
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { _ in
|
||||
return .complete()
|
||||
|> mapToSignal { result in
|
||||
if case let .done(_, _, gift) = result, let gift {
|
||||
return .single(gift)
|
||||
} else {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var flags: Int32 = 0
|
||||
@ -970,16 +982,30 @@ private final class ProfileGiftsContextImpl {
|
||||
self.pushState()
|
||||
}
|
||||
|
||||
func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
|
||||
self.actionDisposable.set(
|
||||
_internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict(next: { [weak self] result in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let _ = self
|
||||
})
|
||||
)
|
||||
self.pushState()
|
||||
func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
|
||||
return Signal { [weak self] subscriber in
|
||||
guard let self else {
|
||||
return EmptyDisposable
|
||||
}
|
||||
let disposable = MetaDisposable()
|
||||
disposable.set(
|
||||
_internal_upgradeStarGift(account: self.account, formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo).startStrict(next: { [weak self] result in
|
||||
guard let self else {
|
||||
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() {
|
||||
@ -1186,9 +1212,19 @@ public final class ProfileGiftsContext {
|
||||
}
|
||||
}
|
||||
|
||||
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) {
|
||||
self.impl.with { impl in
|
||||
impl.upgradeStarGift(formId: formId, messageId: messageId, keepOriginalInfo: keepOriginalInfo)
|
||||
public func upgradeStarGift(formId: Int64?, messageId: EngineMessage.Id, keepOriginalInfo: Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
|
||||
return Signal { subscriber in
|
||||
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:
|
||||
let chats = updates.chats.compactMap { parseTelegramGroupOrChannel(chat: $0) }
|
||||
if let first = chats.first {
|
||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id)
|
||||
return .done(receiptMessageId: nil, subscriptionPeerId: first.id, uniqueStarGift: nil)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
var receiptMessageId: MessageId?
|
||||
var resultGift: ProfileGiftsContext.State.StarGift?
|
||||
for apiMessage in updates.messages {
|
||||
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
|
||||
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:
|
||||
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):
|
||||
return .externalVerificationRequired(url: url)
|
||||
}
|
||||
|
@ -1066,7 +1066,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
||||
attributedString = mutableString
|
||||
case .prizeStars:
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _):
|
||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, _):
|
||||
if !forAdditionalServiceMessage {
|
||||
if let text {
|
||||
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
|
||||
|
@ -467,7 +467,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
buttonTitle = item.presentationData.strings.Notification_PremiumPrize_View
|
||||
hasServiceMessage = false
|
||||
}
|
||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, _, upgradeStars, isRefunded):
|
||||
case let .starGift(gift, convertStars, giftText, giftEntities, _, savedToProfile, converted, upgraded, _, upgradeStars, isRefunded, _):
|
||||
if case let .generic(gift) = gift {
|
||||
isStarGift = true
|
||||
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||
@ -488,7 +488,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle_Displaying
|
||||
}
|
||||
} 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
|
||||
} else {
|
||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
||||
@ -500,16 +500,20 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
peerName = EnginePeer(peer).compactDisplayTitle
|
||||
}
|
||||
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
|
||||
} else {
|
||||
text = item.presentationData.strings.Notification_StarGift_Bot_Subtitle
|
||||
}
|
||||
} else {
|
||||
let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars ?? 0)))
|
||||
text = formattedString.string
|
||||
if let starsRange = formattedString.ranges.last {
|
||||
entities.append(MessageTextEntity(range: starsRange.range.lowerBound ..< starsRange.range.upperBound, type: .Bold))
|
||||
if let convertStars, convertStars > 0 {
|
||||
let formattedString = item.presentationData.strings.Notification_StarGift_Subtitle_Other(peerName, item.presentationData.strings.Notification_StarGift_Subtitle_Other_Stars(Int32(convertStars)))
|
||||
text = formattedString.string
|
||||
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
|
||||
}
|
||||
if incoming, let upgradeStars, upgradeStars > 0, !upgraded {
|
||||
if incoming || item.presentationData.isPreview, let upgradeStars, upgradeStars > 0, !upgraded {
|
||||
buttonTitle = item.presentationData.strings.Notification_StarGift_Unpack
|
||||
buttonIcon = "Premium/GiftUnpack"
|
||||
} else {
|
||||
buttonTitle = item.presentationData.strings.Notification_StarGift_View
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, isRefunded):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded):
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
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
|
||||
text = "**\(uniqueGift.title) #\(uniqueGift.number)**"
|
||||
ribbonTitle = item.presentationData.strings.Notification_StarGift_Gift
|
||||
|
@ -24,6 +24,9 @@ swift_library(
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -13,15 +13,21 @@ public final class GiftAnimationComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let file: TelegramMediaFile?
|
||||
let still: Bool
|
||||
let size: CGSize?
|
||||
|
||||
public init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
file: TelegramMediaFile?
|
||||
file: TelegramMediaFile?,
|
||||
still: Bool = false,
|
||||
size: CGSize? = nil
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.file = file
|
||||
self.still = still
|
||||
self.size = size
|
||||
}
|
||||
|
||||
public static func ==(lhs: GiftAnimationComponent, rhs: GiftAnimationComponent) -> Bool {
|
||||
@ -34,6 +40,12 @@ public final class GiftAnimationComponent: Component {
|
||||
if lhs.file != rhs.file {
|
||||
return false
|
||||
}
|
||||
if lhs.still != rhs.still {
|
||||
return false
|
||||
}
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -61,7 +73,7 @@ public final class GiftAnimationComponent: Component {
|
||||
file: component.file
|
||||
)
|
||||
|
||||
let iconSize = availableSize
|
||||
let iconSize = component.size ?? availableSize
|
||||
if self.animationLayer == nil {
|
||||
let animationLayer = InlineStickerItemLayer(
|
||||
context: .account(component.context),
|
||||
@ -71,12 +83,12 @@ public final class GiftAnimationComponent: Component {
|
||||
file: component.file,
|
||||
cache: component.context.animationCache,
|
||||
renderer: component.context.animationRenderer,
|
||||
unique: true,
|
||||
unique: !component.still,
|
||||
placeholderColor: component.theme.list.mediaPlaceholderColor,
|
||||
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.layer.addSublayer(animationLayer)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import PeerInfoCoverComponent
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
|
||||
final class GiftCompositionComponent: Component {
|
||||
public final class GiftCompositionComponent: Component {
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var previewPatternColor: UIColor?
|
||||
public init() {
|
||||
@ -21,7 +21,7 @@ final class GiftCompositionComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
enum Subject: Equatable {
|
||||
public enum Subject: Equatable {
|
||||
case generic(TelegramMediaFile)
|
||||
case unique(StarGift.UniqueGift)
|
||||
case preview([StarGift.UniqueGift.Attribute])
|
||||
@ -30,15 +30,15 @@ final class GiftCompositionComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let subject: Subject
|
||||
let externalState: ExternalState
|
||||
let externalState: ExternalState?
|
||||
let requestUpdate: () -> Void
|
||||
|
||||
init(
|
||||
public init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
subject: Subject,
|
||||
externalState: ExternalState,
|
||||
requestUpdate: @escaping () -> Void
|
||||
externalState: ExternalState? = nil,
|
||||
requestUpdate: @escaping () -> Void = {}
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
@ -47,7 +47,7 @@ final class GiftCompositionComponent: Component {
|
||||
self.requestUpdate = requestUpdate
|
||||
}
|
||||
|
||||
static func ==(lhs: GiftCompositionComponent, rhs: GiftCompositionComponent) -> Bool {
|
||||
public static func ==(lhs: GiftCompositionComponent, rhs: GiftCompositionComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
@ -60,7 +60,7 @@ final class GiftCompositionComponent: Component {
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
public final class View: UIView {
|
||||
private var component: GiftCompositionComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
@ -84,6 +84,8 @@ final class GiftCompositionComponent: Component {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.handleTap)))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@ -94,7 +96,16 @@ final class GiftCompositionComponent: Component {
|
||||
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
|
||||
|
||||
self.component = component
|
||||
@ -107,6 +118,7 @@ final class GiftCompositionComponent: Component {
|
||||
var patternFile: TelegramMediaFile?
|
||||
var files: [Int64: TelegramMediaFile] = [:]
|
||||
|
||||
var loop = false
|
||||
switch component.subject {
|
||||
case let .generic(file):
|
||||
animationFile = file
|
||||
@ -142,6 +154,8 @@ final class GiftCompositionComponent: Component {
|
||||
self.previewTimer = nil
|
||||
}
|
||||
case let .preview(sampleAttributes):
|
||||
loop = true
|
||||
|
||||
if self.previewModels.isEmpty {
|
||||
var models: [StarGift.UniqueGift.Attribute] = []
|
||||
var patterns: [StarGift.UniqueGift.Attribute] = []
|
||||
@ -198,8 +212,21 @@ final class GiftCompositionComponent: Component {
|
||||
return
|
||||
}
|
||||
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.componentState?.updated(transition: .easeInOut(duration: 0.25))
|
||||
}, queue: Queue.mainQueue())
|
||||
@ -207,7 +234,7 @@ final class GiftCompositionComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
component.externalState.previewPatternColor = secondBackgroundColor
|
||||
component.externalState?.previewPatternColor = secondBackgroundColor
|
||||
|
||||
var animateTransition = false
|
||||
if self.animatePreviewTransition {
|
||||
@ -247,6 +274,7 @@ final class GiftCompositionComponent: Component {
|
||||
if backgroundView.superview == nil {
|
||||
backgroundTransition = .immediate
|
||||
backgroundView.clipsToBounds = true
|
||||
backgroundView.isUserInteractionEnabled = false
|
||||
self.insertSubview(backgroundView, at: 0)
|
||||
|
||||
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?
|
||||
if animateTransition, let disappearingAnimationNode = self.animationNode {
|
||||
@ -274,17 +302,25 @@ final class GiftCompositionComponent: Component {
|
||||
let animationNode: AnimatedStickerNode
|
||||
if self.animationNode == nil {
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.isUserInteractionEnabled = false
|
||||
self.animationNode = animationNode
|
||||
|
||||
self.addSubview(animationNode.view)
|
||||
|
||||
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))
|
||||
|
||||
|
||||
if let startFromIndex {
|
||||
if let animationNode = animationNode as? DefaultAnimatedStickerNodeImpl {
|
||||
animationNode.playbackMode = loop ? .loop : .once
|
||||
}
|
||||
animationNode.play(firstFrame: false, fromIndex: startFromIndex)
|
||||
} else {
|
||||
animationNode.playLoop()
|
||||
if loop {
|
||||
animationNode.playLoop()
|
||||
} else {
|
||||
animationNode.playOnce()
|
||||
}
|
||||
}
|
||||
animationNode.visibility = true
|
||||
animationNode.updateLayout(size: iconSize)
|
||||
@ -295,7 +331,7 @@ final class GiftCompositionComponent: Component {
|
||||
}
|
||||
}
|
||||
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
|
@ -227,7 +227,7 @@ final class ChatGiftPreviewItemNode: ListViewItemNode {
|
||||
case let .starGift(gift):
|
||||
media = [
|
||||
TelegramMediaAction(
|
||||
action: .starGift(gift: .generic(gift), convertStars: gift.convertStars, text: item.text, entities: item.entities, nameHidden: false, savedToProfile: false, converted: false, upgraded: false, 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) {
|
||||
self.scrollView = ScrollView()
|
||||
self.scrollView.showsVerticalScrollIndicator = true
|
||||
self.scrollView.showsVerticalScrollIndicator = false
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.scrollsToTop = false
|
||||
self.scrollView.delaysContentTouches = false
|
||||
@ -253,6 +253,10 @@ final class GiftSetupScreenComponent: Component {
|
||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||
if let completion {
|
||||
completion()
|
||||
|
||||
if let self, let controller = self.environment?.controller() {
|
||||
controller.dismiss()
|
||||
}
|
||||
} else {
|
||||
guard let self, case .purchased = status, let controller = self.environment?.controller(), let navigationController = controller.navigationController as? NavigationController else {
|
||||
return
|
||||
@ -631,7 +635,6 @@ final class GiftSetupScreenComponent: Component {
|
||||
transition.setFrame(view: navigationTitleView, frame: navigationTitleFrame)
|
||||
}
|
||||
|
||||
let bottomContentInset: CGFloat = 24.0
|
||||
let sideInset: CGFloat = 16.0 + environment.safeInsets.left
|
||||
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),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: environment.theme.list.freeTextColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: environment.theme.list.itemAccentColor),
|
||||
linkAttribute: { url in
|
||||
return ("URL", url)
|
||||
linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
}))
|
||||
|
||||
let upgradeFooterText = NSMutableAttributedString(attributedString: parsedString)
|
||||
@ -986,19 +989,19 @@ final class GiftSetupScreenComponent: Component {
|
||||
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 bottomPanelPadding: CGFloat = 12.0
|
||||
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
||||
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(
|
||||
transition: transition,
|
||||
|
@ -37,12 +37,9 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Stars/StarsAvatarComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||
"//submodules/TelegramUI/Components/CheckComponent",
|
||||
"//submodules/UndoUI",
|
||||
"//submodules/ConfettiEffect",
|
||||
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||
"//submodules/TooltipUI",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||
],
|
||||
|
@ -259,7 +259,7 @@ public func giftTransferAlertController(context: AccountContext, gift: StarGift.
|
||||
let buttonText: String
|
||||
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
|
||||
buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) ⭐️\(transferStars)"
|
||||
buttonText = "\(strings.Gift_Transfer_Confirmation_Transfer) $ \(transferStars)"
|
||||
} else {
|
||||
text = strings.Gift_Transfer_Confirmation_TextFree("\(gift.title) #\(gift.number)", peer.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder)).string
|
||||
buttonText = strings.Gift_Transfer_Confirmation_TransferFree
|
||||
|
@ -27,6 +27,7 @@ import ConfettiEffect
|
||||
import PlainButtonComponent
|
||||
import CheckComponent
|
||||
import TooltipUI
|
||||
import GiftAnimationComponent
|
||||
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
@ -45,6 +46,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let sendGift: (EnginePeer.Id) -> Void
|
||||
let openMyGifts: () -> Void
|
||||
let transferGift: () -> Void
|
||||
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||
let showAttributeInfo: (Any, Float) -> Void
|
||||
let getController: () -> ViewController?
|
||||
|
||||
@ -59,6 +61,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
sendGift: @escaping (EnginePeer.Id) -> Void,
|
||||
openMyGifts: @escaping () -> Void,
|
||||
transferGift: @escaping () -> Void,
|
||||
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||
showAttributeInfo: @escaping (Any, Float) -> Void,
|
||||
getController: @escaping () -> ViewController?
|
||||
) {
|
||||
@ -72,6 +75,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.sendGift = sendGift
|
||||
self.openMyGifts = openMyGifts
|
||||
self.transferGift = transferGift
|
||||
self.upgradeGift = upgradeGift
|
||||
self.showAttributeInfo = showAttributeInfo
|
||||
self.getController = getController
|
||||
}
|
||||
@ -88,7 +92,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
|
||||
final class State: ComponentState {
|
||||
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 var disposable: Disposable?
|
||||
@ -109,6 +114,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
|
||||
var inUpgradePreview = false
|
||||
var upgradeForm: BotPaymentForm?
|
||||
var upgradeFormDisposable: Disposable?
|
||||
var upgradeDisposable: Disposable?
|
||||
|
||||
var sampleGiftAttributes: [StarGift.UniqueGift.Attribute]?
|
||||
@ -130,14 +136,24 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
var upgradedMockBackgroundColor: UIColor = .white
|
||||
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.subject = subject
|
||||
self.upgradeGift = upgradeGift
|
||||
self.getController = getController
|
||||
|
||||
super.init()
|
||||
|
||||
if let arguments = subject.arguments {
|
||||
if let upgradeStars = arguments.upgradeStars, upgradeStars > 0 {
|
||||
self.keepOriginalInfo = true
|
||||
}
|
||||
|
||||
var peerIds: [EnginePeer.Id] = [arguments.peerId, context.account.peerId]
|
||||
if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) {
|
||||
peerIds.append(fromPeerId)
|
||||
@ -177,7 +193,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}))
|
||||
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
@ -237,11 +253,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
deinit {
|
||||
self.disposable?.dispose()
|
||||
self.sampleDisposable.dispose()
|
||||
self.upgradeFormDisposable?.dispose()
|
||||
self.upgradeDisposable?.dispose()
|
||||
}
|
||||
|
||||
func requestUpgradePreview() {
|
||||
guard let _ = self.subject.arguments?.upgradeStars else {
|
||||
guard let arguments = self.subject.arguments, arguments.canUpgrade || arguments.upgradeStars != nil else {
|
||||
return
|
||||
}
|
||||
self.context.starsContext?.load(force: false)
|
||||
@ -251,7 +268,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
let peerId = arguments.peerId
|
||||
@ -259,7 +276,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.inProgress = true
|
||||
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
|
||||
guard let self, let controller = self.getController() as? GiftViewScreen else {
|
||||
return
|
||||
@ -307,7 +324,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -379,8 +396,6 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
entities = arguments.entities
|
||||
limitTotal = gift.availability?.total
|
||||
convertStars = arguments.convertStars
|
||||
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
||||
savedToProfile = arguments.savedToProfile
|
||||
converted = arguments.converted
|
||||
giftId = gift.id
|
||||
date = arguments.date
|
||||
@ -395,6 +410,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
convertStars = nil
|
||||
uniqueGift = gift
|
||||
}
|
||||
savedToProfile = arguments.savedToProfile
|
||||
incoming = arguments.incoming || arguments.peerId == component.context.account.peerId
|
||||
nameHidden = arguments.nameHidden
|
||||
titleString = incoming ? strings.Gift_View_ReceivedTitle : strings.Gift_View_Title
|
||||
} else {
|
||||
@ -748,8 +765,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
// originY -= 12.0
|
||||
// }
|
||||
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
if !descriptionText.isEmpty {
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.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)
|
||||
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
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
@ -850,45 +866,65 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
if !soldOut {
|
||||
if let uniqueGift {
|
||||
if let peer = state.peerMap[uniqueGift.ownerPeerId] {
|
||||
let ownerComponent = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(
|
||||
PeerCellComponent(
|
||||
let ownerComponent: AnyComponent<Empty>
|
||||
if let _ = subject.arguments?.transferStars {
|
||||
ownerComponent = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
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,
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
peer: peer
|
||||
)
|
||||
),
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
))
|
||||
text: strings.Gift_Unique_Transfer,
|
||||
color: theme.list.itemAccentColor
|
||||
)),
|
||||
action: {
|
||||
component.transferGift()
|
||||
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(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
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)
|
||||
)
|
||||
action: {
|
||||
component.openPeer(peer)
|
||||
Queue.mainQueue().after(1.0, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
))
|
||||
}
|
||||
tableItems.append(.init(
|
||||
id: "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 string = NSMutableAttributedString(string: format.string, font: tableFont, textColor: tableTextColor)
|
||||
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
|
||||
} else {
|
||||
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)
|
||||
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
|
||||
}
|
||||
@ -1068,6 +1113,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
placeholderColor: theme.list.mediaPlaceholderColor,
|
||||
text: .plain(value),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0,
|
||||
handleSpoilers: true
|
||||
)
|
||||
@ -1281,64 +1327,65 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
originY += table.size.height + 23.0
|
||||
|
||||
if incoming && !converted {
|
||||
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
||||
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if incoming && !converted && !upgraded && !showUpgradePreview {
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
||||
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
||||
}
|
||||
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
|
||||
@ -1480,6 +1527,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
let sendGift: (EnginePeer.Id) -> Void
|
||||
let openMyGifts: () -> Void
|
||||
let transferGift: () -> Void
|
||||
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||
let showAttributeInfo: (Any, Float) -> Void
|
||||
|
||||
init(
|
||||
@ -1492,6 +1540,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
sendGift: @escaping (EnginePeer.Id) -> Void,
|
||||
openMyGifts: @escaping () -> Void,
|
||||
transferGift: @escaping () -> Void,
|
||||
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||
showAttributeInfo: @escaping (Any, Float) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
@ -1503,6 +1552,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
self.sendGift = sendGift
|
||||
self.openMyGifts = openMyGifts
|
||||
self.transferGift = transferGift
|
||||
self.upgradeGift = upgradeGift
|
||||
self.showAttributeInfo = showAttributeInfo
|
||||
}
|
||||
|
||||
@ -1550,6 +1600,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
sendGift: context.component.sendGift,
|
||||
openMyGifts: context.component.openMyGifts,
|
||||
transferGift: context.component.transferGift,
|
||||
upgradeGift: context.component.upgradeGift,
|
||||
showAttributeInfo: context.component.showAttributeInfo,
|
||||
getController: controller
|
||||
)),
|
||||
@ -1629,20 +1680,20 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
case let .message(message):
|
||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, 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)
|
||||
case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _):
|
||||
return (message.id.peerId, message.author?.id, message.author?.compactDisplayTitle, message.id, message.flags.contains(.Incoming), gift, message.timestamp, nil, nil, nil, false, savedToProfile, false, true, false, nil, transferStars, canExportDate)
|
||||
case let .starGiftUnique(gift, isUpgrade, _, savedToProfile, canExportDate, transferStars, _):
|
||||
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:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
case let .profileGift(peerId, gift):
|
||||
var upgraded = false
|
||||
if case .unique = gift.gift {
|
||||
upgraded = true
|
||||
}
|
||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, gift.messageId, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, false, upgraded, gift.canUpgrade, gift.upgradeStars, gift.transferStars, gift.canExportDate)
|
||||
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)
|
||||
case .soldOutGift:
|
||||
return nil
|
||||
case .upgradePreview:
|
||||
@ -1663,7 +1714,9 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
subject: GiftViewScreen.Subject,
|
||||
forceDark: Bool = false,
|
||||
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.subject = subject
|
||||
@ -1676,6 +1729,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
var openMyGiftsImpl: (() -> Void)?
|
||||
var transferGiftImpl: (() -> Void)?
|
||||
var showAttributeInfoImpl: ((Any, Float) -> Void)?
|
||||
var upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
@ -1703,6 +1757,9 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
transferGift: {
|
||||
transferGiftImpl?()
|
||||
},
|
||||
upgradeGift: { formId, keepOriginalInfo in
|
||||
return upgradeGiftImpl?(formId, keepOriginalInfo) ?? .complete()
|
||||
},
|
||||
showAttributeInfo: { tag, rarity in
|
||||
showAttributeInfoImpl?(tag, rarity)
|
||||
}
|
||||
@ -1737,6 +1794,20 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
guard let self, let arguments = self.subject.arguments, let messageId = arguments.messageId else {
|
||||
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 {
|
||||
updateSavedToProfile(added)
|
||||
} else {
|
||||
@ -1749,10 +1820,10 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
let text = added ? presentationData.strings.Gift_Displayed_NewText : presentationData.strings.Gift_Hidden_NewText
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
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(
|
||||
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,
|
||||
action: { [weak navigationController] action in
|
||||
if case .undo = action, let navigationController {
|
||||
@ -1792,7 +1863,6 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
let starsConvertMaxDate = arguments.date + configuration.convertToStarsPeriod
|
||||
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
|
||||
if currentTime > starsConvertMaxDate {
|
||||
let days: Int32 = Int32(ceil(Float(configuration.convertToStarsPeriod) / 86400.0))
|
||||
let controller = textAlertController(
|
||||
@ -1857,6 +1927,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
self.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
openStarsIntroImpl = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
@ -1864,6 +1935,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
let introController = context.sharedContext.makeStarsIntroScreen(context: context)
|
||||
self.push(introController)
|
||||
}
|
||||
|
||||
sendGiftImpl = { [weak self] peerId in
|
||||
guard let self else {
|
||||
return
|
||||
@ -1876,6 +1948,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
self.push(controller)
|
||||
})
|
||||
}
|
||||
|
||||
openMyGiftsImpl = { [weak self] in
|
||||
guard let self, let navigationController = self.navigationController as? NavigationController else {
|
||||
return
|
||||
@ -1906,11 +1979,42 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
let _ = (context.account.stateManager.contactBirthdays
|
||||
|> take(1)
|
||||
|> 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)
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
guard let self else {
|
||||
return
|
||||
@ -1950,6 +2054,17 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
|
||||
fileprivate func animateSuccess() {
|
||||
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() {
|
||||
|
@ -351,10 +351,21 @@ public final class PeerInfoCoverComponent: Component {
|
||||
|
||||
self.backgroundView.backgroundColor = secondaryBackgroundColor
|
||||
|
||||
self.backgroundGradientLayer.type = .axial
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
|
||||
self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
|
||||
self.backgroundGradientLayer.colors = [backgroundColor.cgColor, secondaryBackgroundColor.cgColor]
|
||||
if case .custom = component.subject {
|
||||
if availableSize.width < availableSize.height {
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.25)
|
||||
} 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)
|
||||
|
||||
let gradientHeight: CGFloat = component.defaultHeight
|
||||
@ -431,7 +442,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
} else if availableSize.width < 150.0 {
|
||||
baseDistance *= 0.6
|
||||
baseRowDistance *= 0.6
|
||||
baseItemSize *= 0.75
|
||||
baseItemSize *= 0.83
|
||||
}
|
||||
|
||||
var avatarBackgroundPatternLayerCount = 0
|
||||
|
@ -1306,7 +1306,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
var nextIconX: CGFloat = titleSize.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 leftOffset: CGFloat = nextIconX + 4.0
|
||||
@ -1325,13 +1325,21 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
nextExpandedIconX += 4.0 + titleExpandedCredibilityIconSize.width
|
||||
}
|
||||
|
||||
if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize {
|
||||
let offset = (verifiedIconSize.width + 4.0) / 2.0
|
||||
titleHorizontalOffset += offset
|
||||
titleExpandedHorizontalOffset += offset
|
||||
|
||||
let leftOffset: CGFloat = -verifiedIconSize.width - 4.0
|
||||
let leftExpandedOffset: CGFloat = -titleExpandedVerifiedIconSize.width - 4.0
|
||||
if let verifiedIconSize = self.verifiedIconSize, let titleExpandedVerifiedIconSize = self.titleExpandedVerifiedIconSize, verifiedIconSize.width > 0.0 {
|
||||
let leftOffset: CGFloat
|
||||
let leftExpandedOffset: CGFloat
|
||||
if case .verified = verifiedIcon {
|
||||
titleHorizontalOffset -= (verifiedIconSize.width + 4.0) / 2.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
|
||||
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.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
|
||||
@ -1759,7 +1772,9 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
var titleFrame = titleFrame
|
||||
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)
|
||||
|
@ -3986,8 +3986,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
self?.controller?.updateProfilePhoto(image, mode: .generic)
|
||||
}
|
||||
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
|
||||
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 {
|
||||
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))!
|
||||
mixin.stickersContext = LegacyPaintStickersContext(context: strongSelf.context)
|
||||
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
|
||||
mixin.requestAvatarEditor = { [weak self, weak parentController] imageCompletion, videoCompletion in
|
||||
guard let strongSelf = self, let imageCompletion, let videoCompletion else {
|
||||
@ -9755,18 +9739,18 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}
|
||||
}
|
||||
}
|
||||
// mixin.didFinishWithImage = { [weak self] image in
|
||||
// if let image = image {
|
||||
// completion(image)
|
||||
// self?.updateProfilePhoto(image, mode: mode)
|
||||
// }
|
||||
// }
|
||||
// mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
||||
// if let image = image, let asset = asset {
|
||||
// completion(image)
|
||||
// self?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
|
||||
// }
|
||||
// }
|
||||
mixin.didFinishWithImage = { [weak self] image in
|
||||
if let image = image {
|
||||
completion(image)
|
||||
self?.controller?.updateProfilePhoto(image, mode: mode)
|
||||
}
|
||||
}
|
||||
mixin.didFinishWithVideo = { [weak self] image, asset, adjustments in
|
||||
if let image = image, let asset = asset {
|
||||
completion(image)
|
||||
self?.controller?.updateProfileVideo(image, asset: asset, adjustments: adjustments, mode: mode)
|
||||
}
|
||||
}
|
||||
mixin.didFinishWithDelete = {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -12766,6 +12750,10 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
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) {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|
@ -15,166 +15,167 @@ import OverlayStatusController
|
||||
import UndoUI
|
||||
import PeerAvatarGalleryUI
|
||||
import PresentationDataUtils
|
||||
import LegacyComponents
|
||||
|
||||
extension PeerInfoScreenImpl {
|
||||
func openAvatarForEditing(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 {
|
||||
return
|
||||
}
|
||||
self.view.endEditing(true)
|
||||
|
||||
let peerId = self.peerId
|
||||
var isForum = false
|
||||
if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) {
|
||||
isForum = true
|
||||
}
|
||||
|
||||
var currentIsVideo = false
|
||||
var emojiMarkup: TelegramMediaImage.EmojiMarkup?
|
||||
let item = self.controllerNode.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
|
||||
if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item {
|
||||
currentIsVideo = !videoRepresentations.isEmpty
|
||||
emojiMarkup = emojiMarkupValue
|
||||
}
|
||||
|
||||
let _ = isForum
|
||||
let _ = currentIsVideo
|
||||
|
||||
let _ = (self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
|
||||
let keyboardInputData = Promise<AvatarKeyboardInputData>()
|
||||
keyboardInputData.set(AvatarEditorScreen.inputData(context: self.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser))
|
||||
|
||||
var hasPhotos = false
|
||||
if !peer.profileImageRepresentations.isEmpty {
|
||||
hasPhotos = true
|
||||
}
|
||||
|
||||
var hasDeleteButton = false
|
||||
if case .generic = mode {
|
||||
hasDeleteButton = hasPhotos && !fromGallery
|
||||
} else if case .custom = mode {
|
||||
hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true
|
||||
} else if case .fallback = mode {
|
||||
if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto {
|
||||
hasDeleteButton = photo != nil
|
||||
}
|
||||
}
|
||||
|
||||
let _ = hasDeleteButton
|
||||
|
||||
let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
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)
|
||||
}, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
||||
let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||
if let asset = result as? PHAsset {
|
||||
subject = .single(.asset(asset))
|
||||
} else if let image = result as? UIImage {
|
||||
subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight))
|
||||
} else if let result = result as? Signal<CameraScreenImpl.Result, NoError> {
|
||||
subject = result
|
||||
|> map { value -> MediaEditorScreenImpl.Subject? in
|
||||
switch value {
|
||||
case .pendingImage:
|
||||
return nil
|
||||
case let .image(image):
|
||||
return .image(image: image.image, dimensions: PixelDimensions(image.image.size), additionalImage: nil, additionalImagePosition: .topLeft)
|
||||
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)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let peerType: AvatarEditorScreen.PeerType
|
||||
if mode == .suggest {
|
||||
peerType = .suggest
|
||||
} else if case .legacyGroup = peer {
|
||||
peerType = .group
|
||||
} else if case let .channel(channel) = peer {
|
||||
if case .group = channel.info {
|
||||
peerType = channel.flags.contains(.isForum) ? .forum : .group
|
||||
} else {
|
||||
peerType = .channel
|
||||
}
|
||||
} else {
|
||||
peerType = .user
|
||||
}
|
||||
let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
|
||||
//controller.imageCompletion = imageCompletion
|
||||
//controller.videoCompletion = videoCompletion
|
||||
parentController?.push(controller)
|
||||
//isFromEditor = true
|
||||
return
|
||||
}
|
||||
|
||||
let editorController = MediaEditorScreenImpl(
|
||||
context: self.context,
|
||||
mode: .avatarEditor,
|
||||
subject: subject,
|
||||
transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
||||
MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn(
|
||||
sourceView: $0,
|
||||
sourceRect: transitionRect,
|
||||
sourceImage: transitionImage
|
||||
)
|
||||
) }),
|
||||
transitionOut: { finished, isNew in
|
||||
if !finished, let transitionView {
|
||||
return MediaEditorScreenImpl.TransitionOut(
|
||||
destinationView: transitionView,
|
||||
destinationRect: transitionView.bounds,
|
||||
destinationCornerRadius: 0.0
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}, completion: { [weak self] result, commit in
|
||||
dismissImpl?()
|
||||
|
||||
switch result.media {
|
||||
case let .image(image, _):
|
||||
self?.updateProfilePhoto(image, mode: mode)
|
||||
commit({})
|
||||
case let .video(video, coverImage, values, _, _):
|
||||
if let coverImage {
|
||||
self?.updateProfileVideo(coverImage, asset: video, adjustments: values, mode: mode)
|
||||
}
|
||||
commit({})
|
||||
default:
|
||||
break
|
||||
}
|
||||
} as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||
)
|
||||
editorController.cancelled = { _ in
|
||||
cancelled()
|
||||
}
|
||||
self.push(editorController)
|
||||
}, dismissed: {
|
||||
|
||||
})
|
||||
dismissImpl = { [weak mainController] in
|
||||
if let mainController, let navigationController = mainController.navigationController {
|
||||
var viewControllers = navigationController.viewControllers
|
||||
viewControllers = viewControllers.filter { c in
|
||||
return !(c is CameraScreen) && c !== mainController
|
||||
}
|
||||
navigationController.setViewControllers(viewControllers, animated: false)
|
||||
}
|
||||
}
|
||||
mainController.navigationPresentation = .flatModal
|
||||
mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.push(mainController)
|
||||
})
|
||||
}
|
||||
// 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 {
|
||||
// return
|
||||
// }
|
||||
// self.view.endEditing(true)
|
||||
//
|
||||
// let peerId = self.peerId
|
||||
// var isForum = false
|
||||
// if let peer = peer as? TelegramChannel, peer.flags.contains(.isForum) {
|
||||
// isForum = true
|
||||
// }
|
||||
//
|
||||
// var currentIsVideo = false
|
||||
// var emojiMarkup: TelegramMediaImage.EmojiMarkup?
|
||||
// let item = self.controllerNode.headerNode.avatarListNode.listContainerNode.currentItemNode?.item
|
||||
// if let item = item, case let .image(_, _, videoRepresentations, _, _, emojiMarkupValue) = item {
|
||||
// currentIsVideo = !videoRepresentations.isEmpty
|
||||
// emojiMarkup = emojiMarkupValue
|
||||
// }
|
||||
//
|
||||
// let _ = isForum
|
||||
// let _ = currentIsVideo
|
||||
//
|
||||
// let _ = (self.context.engine.data.get(
|
||||
// TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
// )
|
||||
// |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
|
||||
// guard let self, let peer else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let keyboardInputData = Promise<AvatarKeyboardInputData>()
|
||||
// keyboardInputData.set(AvatarEditorScreen.inputData(context: self.context, isGroup: peer.id.namespace != Namespaces.Peer.CloudUser))
|
||||
//
|
||||
// var hasPhotos = false
|
||||
// if !peer.profileImageRepresentations.isEmpty {
|
||||
// hasPhotos = true
|
||||
// }
|
||||
//
|
||||
// var hasDeleteButton = false
|
||||
// if case .generic = mode {
|
||||
// hasDeleteButton = hasPhotos && !fromGallery
|
||||
// } else if case .custom = mode {
|
||||
// hasDeleteButton = peer.profileImageRepresentations.first?.isPersonal == true
|
||||
// } else if case .fallback = mode {
|
||||
// if let cachedData = data.cachedData as? CachedUserData, case let .known(photo) = cachedData.fallbackPhoto {
|
||||
// hasDeleteButton = photo != nil
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// let _ = hasDeleteButton
|
||||
//
|
||||
// let parentController = (self.context.sharedContext.mainWindow?.viewController as? NavigationController)?.topViewController as? ViewController
|
||||
//
|
||||
// var dismissImpl: (() -> Void)?
|
||||
// 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)
|
||||
// }, completion: { result, transitionView, transitionRect, transitionImage, fromCamera, transitionOut, cancelled in
|
||||
// let subject: Signal<MediaEditorScreenImpl.Subject?, NoError>
|
||||
// if let asset = result as? PHAsset {
|
||||
// subject = .single(.asset(asset))
|
||||
// } else if let image = result as? UIImage {
|
||||
// subject = .single(.image(image: image, dimensions: PixelDimensions(image.size), additionalImage: nil, additionalImagePosition: .bottomRight))
|
||||
// } else if let result = result as? Signal<CameraScreenImpl.Result, NoError> {
|
||||
// subject = result
|
||||
// |> map { value -> MediaEditorScreenImpl.Subject? in
|
||||
// switch value {
|
||||
// case .pendingImage:
|
||||
// return nil
|
||||
// case let .image(image):
|
||||
// return .image(image: image.image, dimensions: PixelDimensions(image.image.size), additionalImage: nil, additionalImagePosition: .topLeft)
|
||||
// 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)
|
||||
// default:
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// let peerType: AvatarEditorScreen.PeerType
|
||||
// if mode == .suggest {
|
||||
// peerType = .suggest
|
||||
// } else if case .legacyGroup = peer {
|
||||
// peerType = .group
|
||||
// } else if case let .channel(channel) = peer {
|
||||
// if case .group = channel.info {
|
||||
// peerType = channel.flags.contains(.isForum) ? .forum : .group
|
||||
// } else {
|
||||
// peerType = .channel
|
||||
// }
|
||||
// } else {
|
||||
// peerType = .user
|
||||
// }
|
||||
// let controller = AvatarEditorScreen(context: self.context, inputData: keyboardInputData.get(), peerType: peerType, markup: emojiMarkup)
|
||||
// //controller.imageCompletion = imageCompletion
|
||||
// //controller.videoCompletion = videoCompletion
|
||||
// parentController?.push(controller)
|
||||
// //isFromEditor = true
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let editorController = MediaEditorScreenImpl(
|
||||
// context: self.context,
|
||||
// mode: .avatarEditor,
|
||||
// subject: subject,
|
||||
// transitionIn: fromCamera ? .camera : transitionView.flatMap({ .gallery(
|
||||
// MediaEditorScreenImpl.TransitionIn.GalleryTransitionIn(
|
||||
// sourceView: $0,
|
||||
// sourceRect: transitionRect,
|
||||
// sourceImage: transitionImage
|
||||
// )
|
||||
// ) }),
|
||||
// transitionOut: { finished, isNew in
|
||||
// if !finished, let transitionView {
|
||||
// return MediaEditorScreenImpl.TransitionOut(
|
||||
// destinationView: transitionView,
|
||||
// destinationRect: transitionView.bounds,
|
||||
// destinationCornerRadius: 0.0
|
||||
// )
|
||||
// }
|
||||
// return nil
|
||||
// }, completion: { [weak self] result, commit in
|
||||
// dismissImpl?()
|
||||
//
|
||||
// switch result.media {
|
||||
// case let .image(image, _):
|
||||
// self?.updateProfilePhoto(image, mode: mode)
|
||||
// commit({})
|
||||
// case let .video(video, coverImage, values, _, _):
|
||||
// if let coverImage {
|
||||
// self?.updateProfileVideo(coverImage, asset: video, adjustments: values, mode: mode)
|
||||
// }
|
||||
// commit({})
|
||||
// default:
|
||||
// break
|
||||
// }
|
||||
// } as (MediaEditorScreenImpl.Result, @escaping (@escaping () -> Void) -> Void) -> Void
|
||||
// )
|
||||
// editorController.cancelled = { _ in
|
||||
// cancelled()
|
||||
// }
|
||||
// self.push(editorController)
|
||||
// }, dismissed: {
|
||||
//
|
||||
// })
|
||||
// dismissImpl = { [weak mainController] in
|
||||
// if let mainController, let navigationController = mainController.navigationController {
|
||||
// var viewControllers = navigationController.viewControllers
|
||||
// viewControllers = viewControllers.filter { c in
|
||||
// return !(c is CameraScreen) && c !== mainController
|
||||
// }
|
||||
// navigationController.setViewControllers(viewControllers, animated: false)
|
||||
// }
|
||||
// }
|
||||
// mainController.navigationPresentation = .flatModal
|
||||
// mainController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
// self.push(mainController)
|
||||
// })
|
||||
// }
|
||||
|
||||
func openAvatarRemoval(mode: PeerInfoAvatarEditingMode, peer: EnginePeer? = nil, item: PeerInfoAvatarListItem? = nil, completion: @escaping () -> Void = {}) {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
@ -234,6 +234,18 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
||||
return
|
||||
}
|
||||
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)
|
||||
|
@ -18,15 +18,26 @@ public final class StarsAvatarComponent: Component {
|
||||
let peer: StarsContext.State.Transaction.Peer?
|
||||
let photo: TelegramMediaWebFile?
|
||||
let media: [Media]
|
||||
let uniqueGift: StarGift.UniqueGift?
|
||||
let backgroundColor: UIColor
|
||||
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.theme = theme
|
||||
self.peer = peer
|
||||
self.photo = photo
|
||||
self.media = media
|
||||
self.uniqueGift = uniqueGift
|
||||
self.backgroundColor = backgroundColor
|
||||
self.size = size
|
||||
}
|
||||
@ -47,6 +58,9 @@ public final class StarsAvatarComponent: Component {
|
||||
if !areMediaArraysEqual(lhs.media, rhs.media) {
|
||||
return false
|
||||
}
|
||||
if lhs.uniqueGift != rhs.uniqueGift {
|
||||
return false
|
||||
}
|
||||
if lhs.backgroundColor != rhs.backgroundColor {
|
||||
return false
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let openAppExamples: () -> Void
|
||||
let copyTransactionId: (String) -> Void
|
||||
let updateSubscription: () -> Void
|
||||
let sendGift: (EnginePeer.Id) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
@ -49,7 +50,8 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||
openAppExamples: @escaping () -> Void,
|
||||
copyTransactionId: @escaping (String) -> Void,
|
||||
updateSubscription: @escaping () -> Void
|
||||
updateSubscription: @escaping () -> Void,
|
||||
sendGift: @escaping (EnginePeer.Id) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
@ -60,6 +62,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
self.openAppExamples = openAppExamples
|
||||
self.copyTransactionId = copyTransactionId
|
||||
self.updateSubscription = updateSubscription
|
||||
self.sendGift = sendGift
|
||||
}
|
||||
|
||||
static func ==(lhs: StarsTransactionSheetContent, rhs: StarsTransactionSheetContent) -> Bool {
|
||||
@ -80,6 +83,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
var peerMap: [EnginePeer.Id: EnginePeer] = [:]
|
||||
|
||||
var cachedCloseImage: (UIImage, PresentationTheme)?
|
||||
var cachedOverlayCloseImage: UIImage?
|
||||
var cachedChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
var inProgress = false
|
||||
@ -149,7 +153,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let star = Child(StarsImageComponent.self)
|
||||
let activeStar = Child(PremiumStarComponent.self)
|
||||
let gift = Child(GiftAnimationComponent.self)
|
||||
let gift = Child(GiftCompositionComponent.self)
|
||||
let amountBackground = Child(RoundedRectangle.self)
|
||||
let amount = Child(BalancedTextComponent.self)
|
||||
let amountStar = Child(BundleIconComponent.self)
|
||||
@ -188,17 +192,14 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
state.cachedCloseImage = (closeImage, theme)
|
||||
}
|
||||
|
||||
let closeButton = closeButton.update(
|
||||
component: Button(
|
||||
content: AnyComponent(Image(image: closeImage)),
|
||||
action: { [weak component] in
|
||||
component?.cancel(true)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let closeOverlayImage: UIImage
|
||||
if let image = state.cachedOverlayCloseImage {
|
||||
closeOverlayImage = image
|
||||
} else {
|
||||
closeOverlayImage = generateCloseButtonImage(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.1), foregroundColor: .white)!
|
||||
state.cachedOverlayCloseImage = closeOverlayImage
|
||||
}
|
||||
|
||||
let titleText: String
|
||||
let amountText: String
|
||||
var descriptionText: String
|
||||
@ -219,7 +220,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
var via: String?
|
||||
var messageId: EngineMessage.Id?
|
||||
var toPeer: EnginePeer?
|
||||
// var toString: String?
|
||||
var transactionPeer: StarsContext.State.Transaction.Peer?
|
||||
var media: [AnyMediaReference] = []
|
||||
var photo: TelegramMediaWebFile?
|
||||
@ -234,7 +234,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
var isReaction = false
|
||||
var giveawayMessageId: MessageId?
|
||||
var isBoost = false
|
||||
var giftAnimation: TelegramMediaFile?
|
||||
var giftAnimationSubject: GiftCompositionComponent.Subject?
|
||||
var isGiftUpgrade = false
|
||||
var giftAvailability: StarGift.Gift.Availability?
|
||||
var isRefProgram = false
|
||||
|
||||
var delayedCloseOnOpenPeer = true
|
||||
@ -250,7 +252,6 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
count = StarsAmount(value: stars, nanos: 0)
|
||||
date = boost.date
|
||||
toPeer = state.peerMap[peerId]
|
||||
// toString = strings.Stars_Transaction_Giveaway_Boost_Subscribers(boost.quantity)
|
||||
giveawayMessageId = boost.giveawayMessageId
|
||||
isBoost = true
|
||||
case let .importer(peer, pricing, importer, usdRate):
|
||||
@ -368,9 +369,15 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
toPeer = 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 {
|
||||
titleText = strings.Stars_Transaction_Giveaway_Title
|
||||
descriptionText = ""
|
||||
@ -583,6 +590,28 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
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 formattedAmount = presentationStringsFormattedNumber(absCount, dateTimeFormat.groupingSeparator)
|
||||
let countColor: UIColor
|
||||
@ -601,18 +630,22 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
countColor = theme.list.itemPrimaryTextColor
|
||||
} else if count < StarsAmount.zero {
|
||||
amountText = "- \(formattedAmount)"
|
||||
countColor = theme.list.itemDestructiveColor
|
||||
if case .unique = giftAnimationSubject {
|
||||
countColor = .white
|
||||
} else {
|
||||
countColor = theme.list.itemDestructiveColor
|
||||
}
|
||||
} else {
|
||||
amountText = "+ \(formattedAmount)"
|
||||
countColor = theme.list.itemDisclosureActions.constructive.fillColor
|
||||
}
|
||||
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: titleText,
|
||||
font: Font.bold(25.0),
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
textColor: headerTextColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
@ -647,17 +680,25 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
imageIcon = nil
|
||||
}
|
||||
|
||||
var starOriginY: CGFloat = 81.0
|
||||
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(
|
||||
component: GiftAnimationComponent(
|
||||
component: GiftCompositionComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
file: giftAnimation
|
||||
subject: giftAnimationSubject
|
||||
),
|
||||
availableSize: CGSize(width: 128.0, height: 128.0),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: animationHeight),
|
||||
transition: .immediate
|
||||
)
|
||||
starOriginY = animationHeight / 2.0
|
||||
} else if isBoost {
|
||||
starChild = activeStar.update(
|
||||
component: PremiumStarComponent(
|
||||
@ -721,6 +762,16 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
let tableLinkColor = theme.list.itemAccentColor
|
||||
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 {
|
||||
tableItems.append(.init(
|
||||
id: "from",
|
||||
@ -746,7 +797,9 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
))
|
||||
} else if let toPeer, !isRefProgram {
|
||||
let title: String
|
||||
if isSubscription {
|
||||
if isGiftUpgrade {
|
||||
title = strings.Stars_Transaction_GiftFrom
|
||||
} else if isSubscription {
|
||||
if isBotSubscription {
|
||||
title = strings.Stars_Transaction_Subscription_Bot
|
||||
} else if isBusinessSubscription {
|
||||
@ -759,10 +812,56 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
} else {
|
||||
title = count < StarsAmount.zero || countIsGeneric ? strings.Stars_Transaction_To : strings.Stars_Transaction_From
|
||||
}
|
||||
tableItems.append(.init(
|
||||
id: "to",
|
||||
title: title,
|
||||
component: AnyComponent(
|
||||
|
||||
let toComponent: AnyComponent<Empty>
|
||||
if let _ = giftAnimationSubject, !toPeer.isDeleted && !isGiftUpgrade {
|
||||
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(
|
||||
content: AnyComponent(
|
||||
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 {
|
||||
tableItems.append(.init(
|
||||
@ -1001,6 +1105,7 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
Button(
|
||||
content: AnyComponent(
|
||||
TransactionCellComponent(
|
||||
backgroundColor: theme.actionSheet.opaqueItemBackgroundColor,
|
||||
textColor: tableTextColor,
|
||||
accentColor: tableLinkColor,
|
||||
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)))
|
||||
)
|
||||
))
|
||||
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 {
|
||||
tableItems.append(.init(
|
||||
@ -1103,15 +1219,17 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
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
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 31.0 + 125.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY))
|
||||
)
|
||||
|
||||
var originY: CGFloat = 0.0
|
||||
originY += 200.0 - 23.0
|
||||
originY += 21.0
|
||||
|
||||
var descriptionSize: CGSize = .zero
|
||||
if !descriptionText.isEmpty {
|
||||
@ -1237,6 +1355,10 @@ private final class StarsTransactionSheetContent: CombinedComponent {
|
||||
context.add(amountStar
|
||||
.position(CGPoint(x: amountStarOriginX, y: amountOrigin + amountStar.size.height / 2.0 - UIScreenPixel + amountStarOffsetY))
|
||||
)
|
||||
|
||||
if case .unique = giftAnimationSubject {
|
||||
originY += 21.0
|
||||
}
|
||||
|
||||
context.add(table
|
||||
.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 copyTransactionId: (String) -> Void
|
||||
let updateSubscription: () -> Void
|
||||
let sendGift: (EnginePeer.Id) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
@ -1362,7 +1485,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
openMedia: @escaping ([Media], @escaping (Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?, @escaping (UIView) -> Void) -> Void,
|
||||
openAppExamples: @escaping () -> Void,
|
||||
copyTransactionId: @escaping (String) -> Void,
|
||||
updateSubscription: @escaping () -> Void
|
||||
updateSubscription: @escaping () -> Void,
|
||||
sendGift: @escaping (EnginePeer.Id) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
@ -1372,6 +1496,7 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
self.openAppExamples = openAppExamples
|
||||
self.copyTransactionId = copyTransactionId
|
||||
self.updateSubscription = updateSubscription
|
||||
self.sendGift = sendGift
|
||||
}
|
||||
|
||||
static func ==(lhs: StarsTransactionSheetComponent, rhs: StarsTransactionSheetComponent) -> Bool {
|
||||
@ -1416,7 +1541,8 @@ private final class StarsTransactionSheetComponent: CombinedComponent {
|
||||
openMedia: context.component.openMedia,
|
||||
openAppExamples: context.component.openAppExamples,
|
||||
copyTransactionId: context.component.copyTransactionId,
|
||||
updateSubscription: context.component.updateSubscription
|
||||
updateSubscription: context.component.updateSubscription,
|
||||
sendGift: context.component.sendGift
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
followContentSizeChanges: true,
|
||||
@ -1516,6 +1642,7 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
var openAppExamplesImpl: (() -> Void)?
|
||||
var copyTransactionIdImpl: ((String) -> Void)?
|
||||
var updateSubscriptionImpl: (() -> Void)?
|
||||
var sendGiftImpl: ((EnginePeer.Id) -> Void)?
|
||||
|
||||
super.init(
|
||||
context: context,
|
||||
@ -1539,6 +1666,9 @@ public class StarsTransactionScreen: ViewControllerComponentContainer {
|
||||
},
|
||||
updateSubscription: {
|
||||
updateSubscriptionImpl?()
|
||||
},
|
||||
sendGift: { peerId in
|
||||
sendGiftImpl?(peerId)
|
||||
}
|
||||
),
|
||||
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) {
|
||||
@ -2011,7 +2154,7 @@ private final class PeerCellComponent: Component {
|
||||
let avatarNaturalSize = self.avatar.update(
|
||||
transition: .immediate,
|
||||
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: {},
|
||||
containerSize: CGSize(width: 40.0, height: 40.0)
|
||||
@ -2063,18 +2206,23 @@ private final class PeerCellComponent: Component {
|
||||
}
|
||||
|
||||
private final class TransactionCellComponent: Component {
|
||||
let backgroundColor: UIColor
|
||||
let textColor: UIColor
|
||||
let accentColor: UIColor
|
||||
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.accentColor = accentColor
|
||||
self.transactionId = transactionId
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
if lhs.accentColor != rhs.accentColor {
|
||||
@ -2089,12 +2237,17 @@ private final class TransactionCellComponent: Component {
|
||||
final class View: UIView {
|
||||
private let text = ComponentView<Empty>()
|
||||
private let button = ComponentView<Empty>()
|
||||
private let gradientView = UIImageView()
|
||||
|
||||
private var component: TransactionCellComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
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) {
|
||||
@ -2104,55 +2257,49 @@ private final class TransactionCellComponent: Component {
|
||||
func update(component: TransactionCellComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let spacing: CGFloat = 6.0
|
||||
|
||||
self.gradientView.tintColor = component.backgroundColor
|
||||
|
||||
let buttonSize = self.button.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BundleIconComponent(name: "Chat/Context Menu/Copy", tintColor: component.accentColor)
|
||||
BundleIconComponent(
|
||||
name: "Chat/Context Menu/Copy",
|
||||
tintColor: component.accentColor
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
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(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: text,
|
||||
string: component.transactionId,
|
||||
font: Font.monospace(15.0),
|
||||
textColor: component.textColor,
|
||||
paragraphAlignment: .left
|
||||
)),
|
||||
maximumNumberOfLines: 0,
|
||||
lineSpacing: 0.2
|
||||
maximumNumberOfLines: 1
|
||||
)
|
||||
),
|
||||
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 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)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
@ -2161,13 +2308,7 @@ private final class TransactionCellComponent: Component {
|
||||
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)
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
self.addSubview(textView)
|
||||
}
|
||||
transition.setFrame(view: textView, frame: textFrame)
|
||||
}
|
||||
self.gradientView.frame = CGRect(x: size.width - buttonSize.width - 32.0, y: 0.0, width: 40.0, height: size.height)
|
||||
|
||||
return size
|
||||
}
|
||||
@ -2202,3 +2343,92 @@ private func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor:
|
||||
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/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponentResourceContent",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftAnimationComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -16,7 +16,7 @@ import AvatarNode
|
||||
import BundleIconComponent
|
||||
import PhotoResources
|
||||
import StarsAvatarComponent
|
||||
import LottieComponent
|
||||
import GiftAnimationComponent
|
||||
|
||||
private extension StarsContext.State.Transaction {
|
||||
var extendedId: String {
|
||||
@ -300,13 +300,20 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
var itemDate: String
|
||||
var itemPeer = item.peer
|
||||
var itemFile: TelegramMediaFile?
|
||||
var uniqueGift: StarGift.UniqueGift?
|
||||
switch item.peer {
|
||||
case let .peer(peer):
|
||||
if let starGift = item.starGift {
|
||||
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
|
||||
if item.flags.contains(.isStarGiftUpgrade), case let .unique(gift) = starGift {
|
||||
itemTitle = "\(gift.title) #\(gift.number)"
|
||||
itemSubtitle = environment.strings.Stars_Intro_Transaction_GiftUpgrade
|
||||
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 {
|
||||
itemTitle = peer.displayTitle(strings: environment.strings, displayOrder: .firstLast)
|
||||
@ -394,7 +401,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
itemDate += " – \(environment.strings.Monetization_Transaction_Failed)"
|
||||
itemDateColor = environment.theme.list.itemDestructiveColor
|
||||
}
|
||||
|
||||
|
||||
var titleComponents: [AnyComponentWithIdentity<Empty>] = []
|
||||
titleComponents.append(
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
@ -411,24 +418,23 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
if let itemFile {
|
||||
subtitleComponent = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.ResourceContent(
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(
|
||||
GiftAnimationComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
file: itemFile,
|
||||
attemptSynchronously: false,
|
||||
providesPlaceholder: true
|
||||
),
|
||||
color: nil,
|
||||
placeholderColor: environment.theme.list.mediaPlaceholderColor,
|
||||
size: CGSize(width: 20.0, height: 20.0),
|
||||
loop: false
|
||||
))),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: itemSubtitle,
|
||||
font: Font.regular(fontBaseDisplaySize * 16.0 / 17.0),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
))
|
||||
still: true,
|
||||
size: CGSize(width: 20.0, height: 20.0)
|
||||
)
|
||||
)),
|
||||
AnyComponentWithIdentity(id: AnyHashable(1), component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: itemSubtitle,
|
||||
font: Font.regular(fontBaseDisplaySize * 16.0 / 17.0),
|
||||
textColor: environment.theme.list.itemPrimaryTextColor
|
||||
)
|
||||
)
|
||||
)))
|
||||
], spacing: 2.0)
|
||||
)
|
||||
@ -463,7 +469,7 @@ final class StarsTransactionsListPanelComponent: Component {
|
||||
theme: environment.theme,
|
||||
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),
|
||||
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,
|
||||
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
|
||||
|
@ -765,7 +765,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
if let photo = subscription.photo {
|
||||
nameGroupComponent = AnyComponent(
|
||||
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)
|
||||
], spacing: 6.0)
|
||||
)
|
||||
@ -806,7 +806,7 @@ final class StarsTransactionsScreenComponent: Component {
|
||||
theme: environment.theme,
|
||||
title: AnyComponent(VStack(titleComponents, alignment: .left, spacing: 2.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,
|
||||
accessory: .custom(ListActionItemComponent.CustomAccessory(component: labelComponent, insets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16.0))),
|
||||
action: { [weak self] _ in
|
||||
|
@ -1228,8 +1228,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
controller.videoCompletion = { [weak self] image, url, adjustments, commit in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let _ = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
//settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
settingsController.updateProfileVideo(image, asset: AVURLAsset(url: url), adjustments: adjustments, mode: .accept)
|
||||
commit()
|
||||
}
|
||||
}
|
||||
@ -1263,8 +1263,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, videoCompletion: { [weak self] image, url, adjustments in
|
||||
if let strongSelf = self {
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let _ = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
//settingsController.updateProfileVideo(image, mode: .accept, asset: AVURLAsset(url: url), adjustments: adjustments)
|
||||
if let rootController = strongSelf.effectiveNavigationController as? TelegramRootController, let settingsController = rootController.accountSettingsController as? PeerInfoScreenImpl {
|
||||
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
|
||||
guard let controller, case let .starGiftTransfer(_, messageId, gift, transferStars, _) = source else {
|
||||
guard let controller, case let .starGiftTransfer(_, _, gift, transferStars, _) = source else {
|
||||
return
|
||||
}
|
||||
let alertController = giftTransferAlertController(context: context, gift: gift, peer: peer, transferStars: transferStars, commit: { [weak controller] in
|
||||
controller?.dismiss()
|
||||
let _ = context.engine.payments.transferStarGift(prepaid: transferStars == 0, messageId: messageId, peerId: peer.id).start()
|
||||
completion?([peer.id])
|
||||
|
||||
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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user