mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-08 15:23:12 +00:00
Merge remote-tracking branch 'refs/remotes/origin/master'
This commit is contained in:
commit
980ced37a8
@ -14924,3 +14924,87 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"Gift.Upgrade.GiftUpgrade" = "Pay %@ for Upgrade";
|
"Gift.Upgrade.GiftUpgrade" = "Pay %@ for Upgrade";
|
||||||
"Gift.View.GiftUpgrade" = "Gift an Upgrade";
|
"Gift.View.GiftUpgrade" = "Gift an Upgrade";
|
||||||
|
|
||||||
|
"Gift.View.OpenChatTheme" = "This gift is the chat's theme. [Change Theme >]()";
|
||||||
|
|
||||||
|
"Notification.ChatTheme.Text" = "%1$@ set **%2$@** as a new theme for this chat.";
|
||||||
|
"Notification.ChatTheme.TextYou" = "You set **%@** as a new theme for this chat.";
|
||||||
|
|
||||||
|
"Notification.ChangedThemeGift" = "%1$@ changed chat theme to %2$@";
|
||||||
|
"Notification.YouChangedThemeGift" = "You changed chat theme to %@";
|
||||||
|
|
||||||
|
"Conversation.Theme.GiftTransfer.Text" = "This gift is already your theme in the chat with **%@**. Remove it there and use it here instead?";
|
||||||
|
"Conversation.Theme.GiftTransfer.Proceed" = "Yes";
|
||||||
|
|
||||||
|
"PeerInfo.Tabs.SetMainTab" = "Set as Main Tab";
|
||||||
|
"PeerInfo.Tabs.SetMainTab.Succeed" = "Tab order changed.";
|
||||||
|
|
||||||
|
"MediaPlayer.SavedMusic.AddToProfile" = "Add to Profile";
|
||||||
|
"MediaPlayer.SavedMusic.RemoveFromProfile" = "This audio is visible on your profile. [Remove >]()";
|
||||||
|
|
||||||
|
"MediaPlayer.SavedMusic.AddedToProfile.View" = "View";
|
||||||
|
"MediaPlayer.SavedMusic.AddedToProfile" = "Audio added to your profile.";
|
||||||
|
"MediaPlayer.SavedMusic.RemovedFromProfile" = "Audio removed from your profile.";
|
||||||
|
|
||||||
|
"MediaPlayer.ContextMenu.SaveToFiles" = "Save to Files";
|
||||||
|
"MediaPlayer.ContextMenu.SaveTo" = "Save to...";
|
||||||
|
"MediaPlayer.ContextMenu.SaveTo.Profile" = "Profile";
|
||||||
|
"MediaPlayer.ContextMenu.SaveTo.SavedMessages" = "Saved Messages";
|
||||||
|
"MediaPlayer.ContextMenu.SaveTo.Files" = "Files";
|
||||||
|
"MediaPlayer.ContextMenu.SaveTo.Info" = "Choose where you want this audio to be saved.";
|
||||||
|
"MediaPlayer.ContextMenu.ShowInChat" = "Show in Chat";
|
||||||
|
"MediaPlayer.ContextMenu.Forward" = "Forward";
|
||||||
|
"MediaPlayer.ContextMenu.Delete" = "Delete";
|
||||||
|
"MediaPlayer.ContextMenu.Remove" = "Remove";
|
||||||
|
|
||||||
|
"MediaPlayer.Playlist.ThisChat" = "AUDIO IN THIS CHAT";
|
||||||
|
"MediaPlayer.Playlist.SavedMusic" = "%@'S PLAYLIST";
|
||||||
|
"MediaPlayer.Playlist.SavedMusicYou" = "YOUR PLAYLIST";
|
||||||
|
|
||||||
|
"Notification.PremiumGift.Stars_1" = "%@ Star";
|
||||||
|
"Notification.PremiumGift.Stars_any" = "%@ Stars";
|
||||||
|
|
||||||
|
"Ton.ProceedsOverview" = "PROCEEDS OVERVIEW";
|
||||||
|
"Ton.AvailableBalance" = "Balance Available to Withdraw";
|
||||||
|
"Ton.LifetimeProceeds" = "Total Lifetime Proceeds";
|
||||||
|
|
||||||
|
"Ton.WithdrawViaFragment" = "Withdraw via Fragment";
|
||||||
|
"Ton.WithdrawViaFragment.Info" = "Collect your TON using Fragment. [Learn More >]()";
|
||||||
|
"Ton.WithdrawViaFragment.Info_URL" = "https://telegram.org/tos/bot-developers#6-2-2-tpa-balance";
|
||||||
|
|
||||||
|
"MESSAGE_GIFT_THEME" = "%1$@ changed theme to %2$@";
|
||||||
|
|
||||||
|
"Gift.Upgrade.Skip" = "Skip";
|
||||||
|
"Gift.Upgrade.UpgradeNext" = "Upgrade Next Gift";
|
||||||
|
|
||||||
|
"Gift.Value.DescriptionAveragePrice" = "This is the average sale price of **%@** on Telegram and Fragment over the past month.";
|
||||||
|
"Gift.Value.DescriptionLastPriceFragment" = "This is the last price at which **%@** was last sold on Fragment.";
|
||||||
|
"Gift.Value.DescriptionLastPriceTelegram" = "This is the last price at which **%@** was last sold on Telegram.";
|
||||||
|
|
||||||
|
"Gift.Value.LastPriceInfo" = "**%1$@** is the last price for %2$@ gifts listed on Telegram and Fragment.";
|
||||||
|
"Gift.Value.MinimumPriceInfo" = "**%1$@** is the floor price for %2$@ gifts listed on Telegram and Fragment.";
|
||||||
|
"Gift.Value.AveragePriceInfo" = "**%1$@** is the average sale price for %2$@ gifts listed on Telegram and Fragment over the past month.";
|
||||||
|
|
||||||
|
"Gift.Value.AveragePrice" = "Last Sale";
|
||||||
|
"Gift.Value.InitialSale" = "Initial Sale";
|
||||||
|
"Gift.Value.InitialPrice" = "Initial Price";
|
||||||
|
"Gift.Value.LastSale" = "Last Sale";
|
||||||
|
"Gift.Value.LastPrice" = "Last Price";
|
||||||
|
"Gift.Value.MinimumPrice" = "Minimum Price";
|
||||||
|
"Gift.Value.AveragePrice" = "Average Price";
|
||||||
|
"Gift.Value.ForSaleOnTelegram" = "for sale on Telegram";
|
||||||
|
"Gift.Value.ForSaleOnFragment" = "for sale on Fragment";
|
||||||
|
|
||||||
|
"Gift.View.Context.SetAsTheme" = "Set as Theme in...";
|
||||||
|
|
||||||
|
"Notification.GiftStars" = "Gift for %@";
|
||||||
|
"Notification.GiftStars.Stars_1" = "%@ Star";
|
||||||
|
"Notification.GiftStars.Stars_any" = "%@ Stars";
|
||||||
|
|
||||||
|
"Notification.PrepaidGiftUpgrade" = "Gift Upgrade for %@";
|
||||||
|
"Notification.PrepaidGiftUpgrade.Stars_1" = "%@ Star";
|
||||||
|
"Notification.PrepaidGiftUpgrade.Stars_any" = "%@ Stars";
|
||||||
|
|
||||||
|
"ProfileLevelInfo.RatingTitle" = "Rating";
|
||||||
|
"ProfileLevelInfo.FutureRatingTitle" = "Future Rating";
|
||||||
|
|
||||||
|
|||||||
@ -1326,7 +1326,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController
|
func makeStarsGiveawayBoostScreen(context: AccountContext, peerId: EnginePeer.Id, boost: ChannelBoostersContext.State.Boost) -> ViewController
|
||||||
func makeStarsIntroScreen(context: AccountContext) -> ViewController
|
func makeStarsIntroScreen(context: AccountContext) -> ViewController
|
||||||
func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: ((StarGift.UniqueGift) -> Void)?) -> ViewController
|
func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: ((StarGift.UniqueGift) -> Void)?) -> ViewController
|
||||||
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, dismissed: (() -> Void)?) -> ViewController
|
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController
|
||||||
func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
||||||
|
|
||||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||||
|
|||||||
@ -2503,7 +2503,7 @@ public final class ChatListNode: ListView {
|
|||||||
case let .user(userType):
|
case let .user(userType):
|
||||||
if case let .user(user) = peer {
|
if case let .user(user) = peer {
|
||||||
match = true
|
match = true
|
||||||
if user.id.isVerificationCodes {
|
if user.id.isVerificationCodes || user.id.isTelegramNotifications {
|
||||||
match = false
|
match = false
|
||||||
}
|
}
|
||||||
if let isBot = userType.isBot {
|
if let isBot = userType.isBot {
|
||||||
|
|||||||
@ -65,6 +65,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||||||
public let clipsContent: Bool
|
public let clipsContent: Bool
|
||||||
public let isScrollEnabled: Bool
|
public let isScrollEnabled: Bool
|
||||||
public let hasDimView: Bool
|
public let hasDimView: Bool
|
||||||
|
public let autoAnimateOut: Bool
|
||||||
public let externalState: ExternalState?
|
public let externalState: ExternalState?
|
||||||
public let animateOut: ActionSlot<Action<()>>
|
public let animateOut: ActionSlot<Action<()>>
|
||||||
public let onPan: () -> Void
|
public let onPan: () -> Void
|
||||||
@ -77,6 +78,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||||||
clipsContent: Bool = false,
|
clipsContent: Bool = false,
|
||||||
isScrollEnabled: Bool = true,
|
isScrollEnabled: Bool = true,
|
||||||
hasDimView: Bool = true,
|
hasDimView: Bool = true,
|
||||||
|
autoAnimateOut: Bool = true,
|
||||||
externalState: ExternalState? = nil,
|
externalState: ExternalState? = nil,
|
||||||
animateOut: ActionSlot<Action<()>>,
|
animateOut: ActionSlot<Action<()>>,
|
||||||
onPan: @escaping () -> Void = {},
|
onPan: @escaping () -> Void = {},
|
||||||
@ -88,6 +90,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||||||
self.clipsContent = clipsContent
|
self.clipsContent = clipsContent
|
||||||
self.isScrollEnabled = isScrollEnabled
|
self.isScrollEnabled = isScrollEnabled
|
||||||
self.hasDimView = hasDimView
|
self.hasDimView = hasDimView
|
||||||
|
self.autoAnimateOut = autoAnimateOut
|
||||||
self.externalState = externalState
|
self.externalState = externalState
|
||||||
self.animateOut = animateOut
|
self.animateOut = animateOut
|
||||||
self.onPan = onPan
|
self.onPan = onPan
|
||||||
@ -110,6 +113,9 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||||||
if lhs.hasDimView != rhs.hasDimView {
|
if lhs.hasDimView != rhs.hasDimView {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.autoAnimateOut != rhs.autoAnimateOut {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.animateOut != rhs.animateOut {
|
if lhs.animateOut != rhs.animateOut {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -430,9 +436,15 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||||||
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {
|
if environment[SheetComponentEnvironment.self].value.isDisplaying, !self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateInTransition.self) {
|
||||||
self.animateIn()
|
self.animateIn()
|
||||||
} else if !environment[SheetComponentEnvironment.self].value.isDisplaying, self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) {
|
} else if !environment[SheetComponentEnvironment.self].value.isDisplaying, self.previousIsDisplaying, let _ = transition.userData(ViewControllerComponentContainer.AnimateOutTransition.self) {
|
||||||
self.animateOut(completion: {})
|
if component.autoAnimateOut {
|
||||||
|
self.animateOut(completion: {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !sheetEnvironment.isDisplaying && !component.autoAnimateOut {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.previousIsDisplaying = sheetEnvironment.isDisplaying
|
||||||
}
|
}
|
||||||
self.previousIsDisplaying = sheetEnvironment.isDisplaying
|
|
||||||
self.dismiss = sheetEnvironment.dismiss
|
self.dismiss = sheetEnvironment.dismiss
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
|
|||||||
@ -349,6 +349,8 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
|
|||||||
public final var beganInteractiveDragging: (CGPoint) -> Void = { _ in }
|
public final var beganInteractiveDragging: (CGPoint) -> Void = { _ in }
|
||||||
public final var endedInteractiveDragging: (CGPoint) -> Void = { _ in }
|
public final var endedInteractiveDragging: (CGPoint) -> Void = { _ in }
|
||||||
public final var didEndScrolling: ((Bool) -> Void)?
|
public final var didEndScrolling: ((Bool) -> Void)?
|
||||||
|
public final var didEndScrollingWithOverscroll: (() -> Void)?
|
||||||
|
|
||||||
|
|
||||||
private var currentGeneralScrollDirection: GeneralScrollDirection?
|
private var currentGeneralScrollDirection: GeneralScrollDirection?
|
||||||
public final var generalScrollDirectionUpdated: (GeneralScrollDirection) -> Void = { _ in }
|
public final var generalScrollDirectionUpdated: (GeneralScrollDirection) -> Void = { _ in }
|
||||||
@ -891,6 +893,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
|
|||||||
self.resetScrollIndicatorFlashTimer(start: false)
|
self.resetScrollIndicatorFlashTimer(start: false)
|
||||||
|
|
||||||
self.isAuxiliaryDisplayLinkEnabled = true
|
self.isAuxiliaryDisplayLinkEnabled = true
|
||||||
|
|
||||||
|
if scrollView.contentOffset.y < -48.0 {
|
||||||
|
self.didEndScrollingWithOverscroll?()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.isDeceleratingAfterTracking = false
|
self.isDeceleratingAfterTracking = false
|
||||||
self.resetHeaderItemsFlashTimer(start: true)
|
self.resetHeaderItemsFlashTimer(start: true)
|
||||||
|
|||||||
@ -4,8 +4,28 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface GiftPatternRect : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic) CGPoint center;
|
||||||
|
@property (nonatomic) CGFloat side;
|
||||||
|
@property (nonatomic) CGFloat rotation;
|
||||||
|
@property (nonatomic) CGFloat scale;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GiftPatternData : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic) CGSize size;
|
||||||
|
@property (nonatomic, strong) NSArray<GiftPatternRect *> * _Nonnull rects;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data, bool pattern);
|
NSData * _Nullable prepareSvgImage(NSData * _Nonnull data, bool pattern);
|
||||||
|
|
||||||
|
GiftPatternData * _Nullable getGiftPatternData(NSData * _Nonnull data);
|
||||||
|
|
||||||
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit);
|
UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit);
|
||||||
|
UIImage * _Nullable renderPreparedImageWithSymbol(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit, UIImage * _Nullable symbolImage, int32_t modelRectIndex);
|
||||||
|
|
||||||
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, CGFloat scale, bool opaque);
|
UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, CGFloat scale, bool opaque);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -959,7 +959,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
|
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
|
||||||
dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) }
|
dict[-963180333] = { return Api.SponsoredPeer.parse_sponsoredPeer($0) }
|
||||||
dict[-2136190013] = { return Api.StarGift.parse_starGift($0) }
|
dict[-2136190013] = { return Api.StarGift.parse_starGift($0) }
|
||||||
dict[648369470] = { return Api.StarGift.parse_starGiftUnique($0) }
|
dict[468707429] = { return Api.StarGift.parse_starGiftUnique($0) }
|
||||||
dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
|
dict[-650279524] = { return Api.StarGiftAttribute.parse_starGiftAttributeBackdrop($0) }
|
||||||
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
|
dict[970559507] = { return Api.StarGiftAttribute.parse_starGiftAttributeModel($0) }
|
||||||
dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }
|
dict[-524291476] = { return Api.StarGiftAttribute.parse_starGiftAttributeOriginalDetails($0) }
|
||||||
@ -1230,7 +1230,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) }
|
dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) }
|
||||||
dict[1279133341] = { return Api.account.AutoSaveSettings.parse_autoSaveSettings($0) }
|
dict[1279133341] = { return Api.account.AutoSaveSettings.parse_autoSaveSettings($0) }
|
||||||
dict[-331111727] = { return Api.account.BusinessChatLinks.parse_businessChatLinks($0) }
|
dict[-331111727] = { return Api.account.BusinessChatLinks.parse_businessChatLinks($0) }
|
||||||
dict[1271855483] = { return Api.account.ChatThemes.parse_chatThemes($0) }
|
dict[373835863] = { return Api.account.ChatThemes.parse_chatThemes($0) }
|
||||||
dict[-535699004] = { return Api.account.ChatThemes.parse_chatThemesNotModified($0) }
|
dict[-535699004] = { return Api.account.ChatThemes.parse_chatThemesNotModified($0) }
|
||||||
dict[400029819] = { return Api.account.ConnectedBots.parse_connectedBots($0) }
|
dict[400029819] = { return Api.account.ConnectedBots.parse_connectedBots($0) }
|
||||||
dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) }
|
dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) }
|
||||||
|
|||||||
@ -289,7 +289,7 @@ public extension Api {
|
|||||||
public extension Api {
|
public extension Api {
|
||||||
enum StarGift: TypeConstructorDescription {
|
enum StarGift: TypeConstructorDescription {
|
||||||
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?)
|
case starGift(flags: Int32, id: Int64, sticker: Api.Document, stars: Int64, availabilityRemains: Int32?, availabilityTotal: Int32?, availabilityResale: Int64?, convertStars: Int64, firstSaleDate: Int32?, lastSaleDate: Int32?, upgradeStars: Int64?, resellMinStars: Int64?, title: String?, releasedBy: Api.Peer?, perUserTotal: Int32?, perUserRemains: Int32?, lockedUntilDate: Int32?)
|
||||||
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?)
|
case starGiftUnique(flags: Int32, id: Int64, giftId: Int64, title: String, slug: String, num: Int32, ownerId: Api.Peer?, ownerName: String?, ownerAddress: String?, attributes: [Api.StarGiftAttribute], availabilityIssued: Int32, availabilityTotal: Int32, giftAddress: String?, resellAmount: [Api.StarsAmount]?, releasedBy: Api.Peer?, valueAmount: Int64?, valueCurrency: String?, themePeer: Api.Peer?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -315,9 +315,9 @@ public extension Api {
|
|||||||
if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 8) != 0 {serializeInt32(perUserRemains!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(lockedUntilDate!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 9) != 0 {serializeInt32(lockedUntilDate!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency):
|
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(648369470)
|
buffer.appendInt32(468707429)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(id, buffer: buffer, boxed: false)
|
serializeInt64(id, buffer: buffer, boxed: false)
|
||||||
@ -344,6 +344,7 @@ public extension Api {
|
|||||||
if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 5) != 0 {releasedBy!.serialize(buffer, true)}
|
||||||
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(valueAmount!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 8) != 0 {serializeInt64(valueAmount!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 8) != 0 {serializeString(valueCurrency!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 8) != 0 {serializeString(valueCurrency!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 10) != 0 {themePeer!.serialize(buffer, true)}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -352,8 +353,8 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate):
|
case .starGift(let flags, let id, let sticker, let stars, let availabilityRemains, let availabilityTotal, let availabilityResale, let convertStars, let firstSaleDate, let lastSaleDate, let upgradeStars, let resellMinStars, let title, let releasedBy, let perUserTotal, let perUserRemains, let lockedUntilDate):
|
||||||
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any)])
|
return ("starGift", [("flags", flags as Any), ("id", id as Any), ("sticker", sticker as Any), ("stars", stars as Any), ("availabilityRemains", availabilityRemains as Any), ("availabilityTotal", availabilityTotal as Any), ("availabilityResale", availabilityResale as Any), ("convertStars", convertStars as Any), ("firstSaleDate", firstSaleDate as Any), ("lastSaleDate", lastSaleDate as Any), ("upgradeStars", upgradeStars as Any), ("resellMinStars", resellMinStars as Any), ("title", title as Any), ("releasedBy", releasedBy as Any), ("perUserTotal", perUserTotal as Any), ("perUserRemains", perUserRemains as Any), ("lockedUntilDate", lockedUntilDate as Any)])
|
||||||
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency):
|
case .starGiftUnique(let flags, let id, let giftId, let title, let slug, let num, let ownerId, let ownerName, let ownerAddress, let attributes, let availabilityIssued, let availabilityTotal, let giftAddress, let resellAmount, let releasedBy, let valueAmount, let valueCurrency, let themePeer):
|
||||||
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any)])
|
return ("starGiftUnique", [("flags", flags as Any), ("id", id as Any), ("giftId", giftId as Any), ("title", title as Any), ("slug", slug as Any), ("num", num as Any), ("ownerId", ownerId as Any), ("ownerName", ownerName as Any), ("ownerAddress", ownerAddress as Any), ("attributes", attributes as Any), ("availabilityIssued", availabilityIssued as Any), ("availabilityTotal", availabilityTotal as Any), ("giftAddress", giftAddress as Any), ("resellAmount", resellAmount as Any), ("releasedBy", releasedBy as Any), ("valueAmount", valueAmount as Any), ("valueCurrency", valueCurrency as Any), ("themePeer", themePeer as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,6 +464,10 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt64() }
|
if Int(_1!) & Int(1 << 8) != 0 {_16 = reader.readInt64() }
|
||||||
var _17: String?
|
var _17: String?
|
||||||
if Int(_1!) & Int(1 << 8) != 0 {_17 = parseString(reader) }
|
if Int(_1!) & Int(1 << 8) != 0 {_17 = parseString(reader) }
|
||||||
|
var _18: Api.Peer?
|
||||||
|
if Int(_1!) & Int(1 << 10) != 0 {if let signature = reader.readInt32() {
|
||||||
|
_18 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||||
|
} }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
@ -480,8 +485,9 @@ public extension Api {
|
|||||||
let _c15 = (Int(_1!) & Int(1 << 5) == 0) || _15 != nil
|
let _c15 = (Int(_1!) & Int(1 << 5) == 0) || _15 != nil
|
||||||
let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil
|
let _c16 = (Int(_1!) & Int(1 << 8) == 0) || _16 != nil
|
||||||
let _c17 = (Int(_1!) & Int(1 << 8) == 0) || _17 != nil
|
let _c17 = (Int(_1!) & Int(1 << 8) == 0) || _17 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
|
let _c18 = (Int(_1!) & Int(1 << 10) == 0) || _18 != nil
|
||||||
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
|
||||||
|
return Api.StarGift.starGiftUnique(flags: _1!, id: _2!, giftId: _3!, title: _4!, slug: _5!, num: _6!, ownerId: _7, ownerName: _8, ownerAddress: _9, attributes: _10!, availabilityIssued: _11!, availabilityTotal: _12!, giftAddress: _13, resellAmount: _14, releasedBy: _15, valueAmount: _16, valueCurrency: _17, themePeer: _18)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -570,14 +570,14 @@ public extension Api.account {
|
|||||||
}
|
}
|
||||||
public extension Api.account {
|
public extension Api.account {
|
||||||
enum ChatThemes: TypeConstructorDescription {
|
enum ChatThemes: TypeConstructorDescription {
|
||||||
case chatThemes(flags: Int32, hash: Int64, themes: [Api.ChatTheme], nextOffset: Int32?)
|
case chatThemes(flags: Int32, hash: Int64, themes: [Api.ChatTheme], chats: [Api.Chat], users: [Api.User], nextOffset: Int32?)
|
||||||
case chatThemesNotModified
|
case chatThemesNotModified
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .chatThemes(let flags, let hash, let themes, let nextOffset):
|
case .chatThemes(let flags, let hash, let themes, let chats, let users, let nextOffset):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1271855483)
|
buffer.appendInt32(373835863)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||||
@ -586,6 +586,16 @@ public extension Api.account {
|
|||||||
for item in themes {
|
for item in themes {
|
||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}
|
}
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(chats.count))
|
||||||
|
for item in chats {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}
|
||||||
|
buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(users.count))
|
||||||
|
for item in users {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextOffset!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextOffset!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .chatThemesNotModified:
|
case .chatThemesNotModified:
|
||||||
@ -599,8 +609,8 @@ public extension Api.account {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .chatThemes(let flags, let hash, let themes, let nextOffset):
|
case .chatThemes(let flags, let hash, let themes, let chats, let users, let nextOffset):
|
||||||
return ("chatThemes", [("flags", flags as Any), ("hash", hash as Any), ("themes", themes as Any), ("nextOffset", nextOffset as Any)])
|
return ("chatThemes", [("flags", flags as Any), ("hash", hash as Any), ("themes", themes as Any), ("chats", chats as Any), ("users", users as Any), ("nextOffset", nextOffset as Any)])
|
||||||
case .chatThemesNotModified:
|
case .chatThemesNotModified:
|
||||||
return ("chatThemesNotModified", [])
|
return ("chatThemesNotModified", [])
|
||||||
}
|
}
|
||||||
@ -615,14 +625,24 @@ public extension Api.account {
|
|||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatTheme.self)
|
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.ChatTheme.self)
|
||||||
}
|
}
|
||||||
var _4: Int32?
|
var _4: [Api.Chat]?
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
|
if let _ = reader.readInt32() {
|
||||||
|
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
|
||||||
|
}
|
||||||
|
var _5: [Api.User]?
|
||||||
|
if let _ = reader.readInt32() {
|
||||||
|
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
|
}
|
||||||
|
var _6: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = _3 != nil
|
let _c3 = _3 != nil
|
||||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
let _c4 = _4 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 {
|
let _c5 = _5 != nil
|
||||||
return Api.account.ChatThemes.chatThemes(flags: _1!, hash: _2!, themes: _3!, nextOffset: _4)
|
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||||
|
return Api.account.ChatThemes.chatThemes(flags: _1!, hash: _2!, themes: _3!, chats: _4!, users: _5!, nextOffset: _6)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -359,7 +359,9 @@ func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Ne
|
|||||||
resellForTonOnly: false,
|
resellForTonOnly: false,
|
||||||
releasedBy: nil,
|
releasedBy: nil,
|
||||||
valueAmount: nil,
|
valueAmount: nil,
|
||||||
valueCurrency: nil
|
valueCurrency: nil,
|
||||||
|
flags: [],
|
||||||
|
themePeerId: nil
|
||||||
)
|
)
|
||||||
if let entry = CodableEntry(RecentStarGiftItem(gift)) {
|
if let entry = CodableEntry(RecentStarGiftItem(gift)) {
|
||||||
items.append(OrderedItemListEntry(id: RecentStarGiftItemId(id).rawValue, contents: entry))
|
items.append(OrderedItemListEntry(id: RecentStarGiftItemId(id).rawValue, contents: entry))
|
||||||
|
|||||||
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||||||
|
|
||||||
public class Serialization: NSObject, MTSerialization {
|
public class Serialization: NSObject, MTSerialization {
|
||||||
public func currentLayer() -> UInt {
|
public func currentLayer() -> UInt {
|
||||||
return 213
|
return 214
|
||||||
}
|
}
|
||||||
|
|
||||||
public func parseMessage(_ data: Data!) -> Any! {
|
public func parseMessage(_ data: Data!) -> Any! {
|
||||||
|
|||||||
@ -230,6 +230,37 @@ public enum TelegramWallpaper: Equatable {
|
|||||||
self.file = file
|
self.file = file
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: File, rhs: File) -> Bool {
|
||||||
|
if lhs.id != rhs.id {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.accessHash != rhs.accessHash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isCreator != rhs.isCreator {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isDefault != rhs.isDefault {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isPattern != rhs.isPattern {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isDark != rhs.isDark {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.slug != rhs.slug {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.file.fileId != rhs.file.fileId {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.settings != rhs.settings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case builtin(WallpaperSettings)
|
case builtin(WallpaperSettings)
|
||||||
|
|||||||
@ -2303,6 +2303,39 @@ public extension TelegramEngine.EngineData.Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct CopyProtectionEnabled: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||||
|
public typealias Result = Bool
|
||||||
|
|
||||||
|
fileprivate var id: EnginePeer.Id
|
||||||
|
public var mapKey: EnginePeer.Id {
|
||||||
|
return self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(id: EnginePeer.Id) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
var key: PostboxViewKey {
|
||||||
|
return .basicPeer(self.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extract(view: PostboxView) -> Result {
|
||||||
|
guard let view = view as? BasicPeerView else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
guard let peer = view.peer else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if let group = peer as? TelegramGroup {
|
||||||
|
return group.flags.contains(.copyProtectionEnabled)
|
||||||
|
} else if let channel = peer as? TelegramChannel {
|
||||||
|
return channel.flags.contains(.copyProtectionEnabled)
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct BotPreview: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
public struct BotPreview: TelegramEngineDataItem, TelegramEngineMapKeyDataItem, PostboxViewDataItem {
|
||||||
public typealias Result = CachedUserData.BotPreview?
|
public typealias Result = CachedUserData.BotPreview?
|
||||||
|
|
||||||
|
|||||||
@ -318,6 +318,18 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
case releasedBy
|
case releasedBy
|
||||||
case valueAmount
|
case valueAmount
|
||||||
case valueCurrency
|
case valueCurrency
|
||||||
|
case flags
|
||||||
|
case themePeerId
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Flags: OptionSet {
|
||||||
|
public var rawValue: Int32
|
||||||
|
|
||||||
|
public init(rawValue: Int32) {
|
||||||
|
self.rawValue = rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public static let isThemeAvailable = Flags(rawValue: 1 << 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Attribute: Equatable, Codable, PostboxCoding {
|
public enum Attribute: Equatable, Codable, PostboxCoding {
|
||||||
@ -593,8 +605,10 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
public let releasedBy: EnginePeer.Id?
|
public let releasedBy: EnginePeer.Id?
|
||||||
public let valueAmount: Int64?
|
public let valueAmount: Int64?
|
||||||
public let valueCurrency: String?
|
public let valueCurrency: String?
|
||||||
|
public let flags: Flags
|
||||||
|
public let themePeerId: EnginePeer.Id?
|
||||||
|
|
||||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?) {
|
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, flags: Flags, themePeerId: EnginePeer.Id?) {
|
||||||
self.id = id
|
self.id = id
|
||||||
self.giftId = giftId
|
self.giftId = giftId
|
||||||
self.title = title
|
self.title = title
|
||||||
@ -609,6 +623,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
self.releasedBy = releasedBy
|
self.releasedBy = releasedBy
|
||||||
self.valueAmount = valueAmount
|
self.valueAmount = valueAmount
|
||||||
self.valueCurrency = valueCurrency
|
self.valueCurrency = valueCurrency
|
||||||
|
self.flags = flags
|
||||||
|
self.themePeerId = themePeerId
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -641,6 +657,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy)
|
self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy)
|
||||||
self.valueAmount = try container.decodeIfPresent(Int64.self, forKey: .valueAmount)
|
self.valueAmount = try container.decodeIfPresent(Int64.self, forKey: .valueAmount)
|
||||||
self.valueCurrency = try container.decodeIfPresent(String.self, forKey: .valueCurrency)
|
self.valueCurrency = try container.decodeIfPresent(String.self, forKey: .valueCurrency)
|
||||||
|
self.flags = try container.decodeIfPresent(Int32.self, forKey: .flags).flatMap { Flags(rawValue: $0) } ?? []
|
||||||
|
self.themePeerId = try container.decodeIfPresent(Int64.self, forKey: .themePeerId).flatMap { EnginePeer.Id($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
@ -672,6 +690,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) }
|
self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) }
|
||||||
self.valueAmount = decoder.decodeOptionalInt64ForKey(CodingKeys.valueAmount.rawValue)
|
self.valueAmount = decoder.decodeOptionalInt64ForKey(CodingKeys.valueAmount.rawValue)
|
||||||
self.valueCurrency = decoder.decodeOptionalStringForKey(CodingKeys.valueCurrency.rawValue)
|
self.valueCurrency = decoder.decodeOptionalStringForKey(CodingKeys.valueCurrency.rawValue)
|
||||||
|
self.flags = decoder.decodeOptionalInt32ForKey(CodingKeys.flags.rawValue).flatMap { Flags(rawValue: $0) } ?? []
|
||||||
|
self.themePeerId = decoder.decodeOptionalInt64ForKey(CodingKeys.themePeerId.rawValue).flatMap { EnginePeer.Id($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -697,6 +717,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy)
|
try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy)
|
||||||
try container.encodeIfPresent(self.valueAmount, forKey: .valueAmount)
|
try container.encodeIfPresent(self.valueAmount, forKey: .valueAmount)
|
||||||
try container.encodeIfPresent(self.valueCurrency, forKey: .valueCurrency)
|
try container.encodeIfPresent(self.valueCurrency, forKey: .valueCurrency)
|
||||||
|
try container.encode(self.flags.rawValue, forKey: .flags)
|
||||||
|
try container.encodeIfPresent(self.themePeerId?.toInt64(), forKey: .themePeerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
@ -738,6 +760,12 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
encoder.encodeNil(forKey: CodingKeys.valueAmount.rawValue)
|
encoder.encodeNil(forKey: CodingKeys.valueAmount.rawValue)
|
||||||
encoder.encodeNil(forKey: CodingKeys.valueCurrency.rawValue)
|
encoder.encodeNil(forKey: CodingKeys.valueCurrency.rawValue)
|
||||||
}
|
}
|
||||||
|
encoder.encodeInt32(self.flags.rawValue, forKey: CodingKeys.flags.rawValue)
|
||||||
|
if let themePeerId = self.themePeerId {
|
||||||
|
encoder.encodeInt64(themePeerId.toInt64(), forKey: CodingKeys.themePeerId.rawValue)
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: CodingKeys.themePeerId.rawValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift {
|
public func withResellAmounts(_ resellAmounts: [CurrencyAmount]?) -> UniqueGift {
|
||||||
@ -755,7 +783,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
resellForTonOnly: self.resellForTonOnly,
|
resellForTonOnly: self.resellForTonOnly,
|
||||||
releasedBy: self.releasedBy,
|
releasedBy: self.releasedBy,
|
||||||
valueAmount: self.valueAmount,
|
valueAmount: self.valueAmount,
|
||||||
valueCurrency: self.valueCurrency
|
valueCurrency: self.valueCurrency,
|
||||||
|
flags: self.flags,
|
||||||
|
themePeerId: self.themePeerId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -774,7 +804,30 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||||||
resellForTonOnly: resellForTonOnly,
|
resellForTonOnly: resellForTonOnly,
|
||||||
releasedBy: self.releasedBy,
|
releasedBy: self.releasedBy,
|
||||||
valueAmount: self.valueAmount,
|
valueAmount: self.valueAmount,
|
||||||
valueCurrency: self.valueCurrency
|
valueCurrency: self.valueCurrency,
|
||||||
|
flags: self.flags,
|
||||||
|
themePeerId: self.themePeerId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withThemePeerId(_ themePeerId: EnginePeer.Id?) -> UniqueGift {
|
||||||
|
return UniqueGift(
|
||||||
|
id: self.id,
|
||||||
|
giftId: self.giftId,
|
||||||
|
title: self.title,
|
||||||
|
number: self.number,
|
||||||
|
slug: self.slug,
|
||||||
|
owner: self.owner,
|
||||||
|
attributes: self.attributes,
|
||||||
|
availability: self.availability,
|
||||||
|
giftAddress: self.giftAddress,
|
||||||
|
resellAmounts: self.resellAmounts,
|
||||||
|
resellForTonOnly: self.resellForTonOnly,
|
||||||
|
releasedBy: self.releasedBy,
|
||||||
|
valueAmount: self.valueAmount,
|
||||||
|
valueCurrency: self.valueCurrency,
|
||||||
|
flags: self.flags,
|
||||||
|
themePeerId: themePeerId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -884,7 +937,7 @@ extension StarGift {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit, lockedUntilDate: lockedUntilDate))
|
self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit, lockedUntilDate: lockedUntilDate))
|
||||||
case let .starGiftUnique(flags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency):
|
case let .starGiftUnique(apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellAmounts, releasedBy, valueAmount, valueCurrency, themePeer):
|
||||||
let owner: StarGift.UniqueGift.Owner
|
let owner: StarGift.UniqueGift.Owner
|
||||||
if let ownerAddress {
|
if let ownerAddress {
|
||||||
owner = .address(ownerAddress)
|
owner = .address(ownerAddress)
|
||||||
@ -896,7 +949,11 @@ extension StarGift {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let resellAmounts = resellAmounts?.compactMap { CurrencyAmount(apiAmount: $0) }
|
let resellAmounts = resellAmounts?.compactMap { CurrencyAmount(apiAmount: $0) }
|
||||||
self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (flags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency))
|
var flags = StarGift.UniqueGift.Flags()
|
||||||
|
if (apiFlags & (1 << 9)) != 0 {
|
||||||
|
flags.insert(.isThemeAvailable)
|
||||||
|
}
|
||||||
|
self = .unique(StarGift.UniqueGift(id: id, giftId: giftId, title: title, number: num, slug: slug, owner: owner, attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) }, availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal), giftAddress: giftAddress, resellAmounts: resellAmounts, resellForTonOnly: (apiFlags & (1 << 7)) != 0, releasedBy: releasedBy?.peerId, valueAmount: valueAmount, valueCurrency: valueCurrency, flags: flags, themePeerId: themePeer?.peerId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1322,6 +1379,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
|
|
||||||
private var sorting: ProfileGiftsContext.Sorting
|
private var sorting: ProfileGiftsContext.Sorting
|
||||||
private var filter: ProfileGiftsContext.Filters
|
private var filter: ProfileGiftsContext.Filters
|
||||||
|
private var limit: Int32
|
||||||
|
|
||||||
private var gifts: [ProfileGiftsContext.State.StarGift] = []
|
private var gifts: [ProfileGiftsContext.State.StarGift] = []
|
||||||
private var count: Int32?
|
private var count: Int32?
|
||||||
@ -1345,7 +1403,8 @@ private final class ProfileGiftsContextImpl {
|
|||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
collectionId: Int32?,
|
collectionId: Int32?,
|
||||||
sorting: ProfileGiftsContext.Sorting,
|
sorting: ProfileGiftsContext.Sorting,
|
||||||
filter: ProfileGiftsContext.Filters
|
filter: ProfileGiftsContext.Filters,
|
||||||
|
limit: Int32
|
||||||
) {
|
) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -1353,6 +1412,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
self.collectionId = collectionId
|
self.collectionId = collectionId
|
||||||
self.sorting = sorting
|
self.sorting = sorting
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
|
self.limit = limit
|
||||||
|
|
||||||
self.loadMore()
|
self.loadMore()
|
||||||
}
|
}
|
||||||
@ -1377,6 +1437,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
let postbox = self.account.postbox
|
let postbox = self.account.postbox
|
||||||
let filter = self.filter
|
let filter = self.filter
|
||||||
let sorting = self.sorting
|
let sorting = self.sorting
|
||||||
|
let limit = self.limit
|
||||||
|
|
||||||
let isFiltered = self.filter != .All || self.sorting != .date
|
let isFiltered = self.filter != .All || self.sorting != .date
|
||||||
if !isFiltered {
|
if !isFiltered {
|
||||||
@ -1464,7 +1525,7 @@ private final class ProfileGiftsContextImpl {
|
|||||||
if !filter.contains(.unique) {
|
if !filter.contains(.unique) {
|
||||||
flags |= (1 << 4)
|
flags |= (1 << 4)
|
||||||
}
|
}
|
||||||
return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, collectionId: collectionId, offset: initialNextOffset ?? "", limit: 36))
|
return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, collectionId: collectionId, offset: initialNextOffset ?? "", limit: limit))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.payments.SavedStarGifts?, NoError> in
|
|> `catch` { _ -> Signal<Api.payments.SavedStarGifts?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -2363,14 +2424,15 @@ public final class ProfileGiftsContext {
|
|||||||
peerId: EnginePeer.Id,
|
peerId: EnginePeer.Id,
|
||||||
collectionId: Int32? = nil,
|
collectionId: Int32? = nil,
|
||||||
sorting: ProfileGiftsContext.Sorting = .date,
|
sorting: ProfileGiftsContext.Sorting = .date,
|
||||||
filter: ProfileGiftsContext.Filters = .All
|
filter: ProfileGiftsContext.Filters = .All,
|
||||||
|
limit: Int32 = 36
|
||||||
) {
|
) {
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.collectionId = collectionId
|
self.collectionId = collectionId
|
||||||
|
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, collectionId: collectionId, sorting: sorting, filter: filter)
|
return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, collectionId: collectionId, sorting: sorting, filter: filter, limit: limit)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1678,6 +1678,8 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
|
|||||||
return .fail(.alreadyPaid)
|
return .fail(.alreadyPaid)
|
||||||
} else if error.errorDescription == "STARGIFT_USAGE_LIMITED" {
|
} else if error.errorDescription == "STARGIFT_USAGE_LIMITED" {
|
||||||
return .fail(.starGiftOutOfStock)
|
return .fail(.starGiftOutOfStock)
|
||||||
|
} else if error.errorDescription == "STARGIFT_USER_USAGE_LIMITED" {
|
||||||
|
return .fail(.starGiftUserLimit)
|
||||||
}
|
}
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,20 +104,20 @@ public enum ChatTheme: PostboxCoding, Codable, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .gift(lhsGift, _):
|
case let .gift(lhsGift, lhsThemeSettings):
|
||||||
if case let .gift(rhsGift, _) = rhs {
|
if case let .gift(rhsGift, rhsThemeSettings) = rhs {
|
||||||
switch lhsGift {
|
switch lhsGift {
|
||||||
case .generic(let lhsGeneric):
|
case let .generic(lhsGeneric):
|
||||||
switch rhsGift {
|
switch rhsGift {
|
||||||
case .generic(let rhsGeneric):
|
case let .generic(rhsGeneric):
|
||||||
return lhsGeneric == rhsGeneric
|
return lhsGeneric == rhsGeneric && lhsThemeSettings == rhsThemeSettings
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case .unique(let lhsUnique):
|
case let .unique(lhsUnique):
|
||||||
switch rhsGift {
|
switch rhsGift {
|
||||||
case .unique(let rhsUnique):
|
case let .unique(rhsUnique):
|
||||||
return lhsUnique.id == rhsUnique.id
|
return lhsUnique.slug == rhsUnique.slug && lhsThemeSettings == rhsThemeSettings
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -148,6 +148,20 @@ public enum ChatTheme: PostboxCoding, Codable, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func withThemePeerId(_ themePeerId: EnginePeer.Id?) -> ChatTheme {
|
||||||
|
switch self {
|
||||||
|
case .emoticon:
|
||||||
|
return self
|
||||||
|
case let .gift(gift, themeSettings):
|
||||||
|
switch gift {
|
||||||
|
case let .unique(uniqueGift):
|
||||||
|
return .gift(.unique(uniqueGift.withThemePeerId(themePeerId)), themeSettings)
|
||||||
|
case .generic:
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ChatTheme {
|
extension ChatTheme {
|
||||||
@ -235,6 +249,22 @@ func _internal_setChatTheme(account: Account, peerId: PeerId, chatTheme: ChatThe
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||||
|
var chatTheme = chatTheme
|
||||||
|
if case let .gift(gift, _) = chatTheme, case let .unique(uniqueGift) = gift, let previousThemePeerId = uniqueGift.themePeerId {
|
||||||
|
transaction.updatePeerCachedData(peerIds: Set([previousThemePeerId]), update: { _, current in
|
||||||
|
if let current = current as? CachedUserData {
|
||||||
|
return current.withUpdatedChatTheme(nil)
|
||||||
|
} else if let current = current as? CachedGroupData {
|
||||||
|
return current.withUpdatedChatTheme(nil)
|
||||||
|
} else if let current = current as? CachedChannelData {
|
||||||
|
return current.withUpdatedChatTheme(nil)
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
chatTheme = chatTheme?.withThemePeerId(peerId)
|
||||||
|
|
||||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
|
||||||
if let current = current as? CachedUserData {
|
if let current = current as? CachedUserData {
|
||||||
return current.withUpdatedChatTheme(chatTheme)
|
return current.withUpdatedChatTheme(chatTheme)
|
||||||
@ -246,6 +276,7 @@ func _internal_setChatTheme(account: Account, peerId: PeerId, chatTheme: ChatThe
|
|||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let inputTheme: Api.InputChatTheme
|
let inputTheme: Api.InputChatTheme
|
||||||
if let chatTheme {
|
if let chatTheme {
|
||||||
inputTheme = chatTheme.apiChatTheme
|
inputTheme = chatTheme.apiChatTheme
|
||||||
@ -466,7 +497,7 @@ public final class UniqueGiftChatThemesContext {
|
|||||||
private let cacheDisposable = MetaDisposable()
|
private let cacheDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var themes: [ChatTheme] = []
|
private var themes: [ChatTheme] = []
|
||||||
private var nextOffset: Int32?
|
private var nextOffset: Int32 = 0
|
||||||
private var dataState: UniqueGiftChatThemesContext.State.DataState = .ready(canLoadMore: true)
|
private var dataState: UniqueGiftChatThemesContext.State.DataState = .ready(canLoadMore: true)
|
||||||
|
|
||||||
private let stateValue = Promise<State>()
|
private let stateValue = Promise<State>()
|
||||||
@ -487,7 +518,7 @@ public final class UniqueGiftChatThemesContext {
|
|||||||
|
|
||||||
public func reload() {
|
public func reload() {
|
||||||
self.themes = []
|
self.themes = []
|
||||||
self.nextOffset = nil
|
self.nextOffset = 0
|
||||||
self.dataState = .ready(canLoadMore: true)
|
self.dataState = .ready(canLoadMore: true)
|
||||||
self.loadMore(reload: true)
|
self.loadMore(reload: true)
|
||||||
}
|
}
|
||||||
@ -495,6 +526,7 @@ public final class UniqueGiftChatThemesContext {
|
|||||||
public func loadMore(reload: Bool = false) {
|
public func loadMore(reload: Bool = false) {
|
||||||
let network = self.account.network
|
let network = self.account.network
|
||||||
let postbox = self.account.postbox
|
let postbox = self.account.postbox
|
||||||
|
let accountPeerId = self.account.peerId
|
||||||
let dataState = self.dataState
|
let dataState = self.dataState
|
||||||
let offset = self.nextOffset
|
let offset = self.nextOffset
|
||||||
|
|
||||||
@ -520,13 +552,24 @@ public final class UniqueGiftChatThemesContext {
|
|||||||
self.pushState()
|
self.pushState()
|
||||||
}
|
}
|
||||||
|
|
||||||
let signal = network.request(Api.functions.account.getUniqueGiftChatThemes(offset: offset ?? 0, limit: 32, hash: 0))
|
let signal = network.request(Api.functions.account.getUniqueGiftChatThemes(offset: offset, limit: 50, hash: 0))
|
||||||
|> map { result -> ([ChatTheme], Int32?) in
|
|> map(Optional.init)
|
||||||
switch result {
|
|> `catch` { error in
|
||||||
case let .chatThemes(_, _, themes, nextOffset):
|
return .single(nil)
|
||||||
return (themes.compactMap { ChatTheme(apiChatTheme: $0) }, nextOffset)
|
}
|
||||||
case .chatThemesNotModified:
|
|> mapToSignal { result -> Signal<([ChatTheme], Int32?), NoError> in
|
||||||
return ([], nil)
|
guard let result else {
|
||||||
|
return .single(([], nil))
|
||||||
|
}
|
||||||
|
return postbox.transaction { transaction -> ([ChatTheme], Int32?) in
|
||||||
|
switch result {
|
||||||
|
case let .chatThemes(_, _, themes, chats, users, nextOffset):
|
||||||
|
let parsedPeers = AccumulatedPeers(transaction: transaction, chats: chats, users: users)
|
||||||
|
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
|
||||||
|
return (themes.compactMap { ChatTheme(apiChatTheme: $0) }, nextOffset)
|
||||||
|
case .chatThemesNotModified:
|
||||||
|
return ([], nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +588,9 @@ public final class UniqueGiftChatThemesContext {
|
|||||||
} else {
|
} else {
|
||||||
self.themes.append(contentsOf: themes)
|
self.themes.append(contentsOf: themes)
|
||||||
}
|
}
|
||||||
|
if let nextOffset {
|
||||||
|
self.nextOffset = nextOffset
|
||||||
|
}
|
||||||
self.dataState = .ready(canLoadMore: nextOffset != nil)
|
self.dataState = .ready(canLoadMore: nextOffset != nil)
|
||||||
self.pushState()
|
self.pushState()
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -47,13 +47,36 @@ public func makePresentationTheme(cloudTheme: TelegramTheme, dark: Bool = false)
|
|||||||
} else {
|
} else {
|
||||||
settings = nil
|
settings = nil
|
||||||
}
|
}
|
||||||
guard let settings = settings else {
|
guard let settings else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: .cloud(PresentationCloudTheme(theme: cloudTheme, resolvedWallpaper: nil, creatorAccountId: nil)), serviceBackgroundColor: nil, preview: false)
|
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), extendingThemeReference: .cloud(PresentationCloudTheme(theme: cloudTheme, resolvedWallpaper: nil, creatorAccountId: nil)), serviceBackgroundColor: nil, preview: false)
|
||||||
return customizePresentationTheme(defaultTheme, editing: true, accentColor: UIColor(argb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(argb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
|
return customizePresentationTheme(defaultTheme, editing: true, accentColor: UIColor(argb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(argb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makePresentationTheme(chatTheme: ChatTheme, dark: Bool = false) -> PresentationTheme? {
|
||||||
|
guard case let .gift(_, themeSettings) = chatTheme else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let settings: TelegramThemeSettings?
|
||||||
|
if let exactSettings = themeSettings.first(where: { dark ? ($0.baseTheme == .night || $0.baseTheme == .tinted) : ($0.baseTheme == .classic || $0.baseTheme == .day) }) {
|
||||||
|
settings = exactSettings
|
||||||
|
} else if let firstSettings = themeSettings.first {
|
||||||
|
settings = firstSettings
|
||||||
|
} else {
|
||||||
|
settings = nil
|
||||||
|
}
|
||||||
|
guard let settings else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let defaultTheme = makeDefaultPresentationTheme(reference: PresentationBuiltinThemeReference(baseTheme: settings.baseTheme), serviceBackgroundColor: nil, preview: false)
|
||||||
|
let theme = customizePresentationTheme(defaultTheme, editing: false, accentColor: UIColor(rgb: settings.accentColor), outgoingAccentColor: settings.outgoingAccentColor.flatMap { UIColor(rgb: $0) }, backgroundColors: [], bubbleColors: settings.messageColors, animateBubbleColors: settings.animateMessageColors, wallpaper: settings.wallpaper)
|
||||||
|
if case let .gift(starGiftValue, _) = chatTheme {
|
||||||
|
theme.starGift = starGiftValue
|
||||||
|
}
|
||||||
|
return theme
|
||||||
|
}
|
||||||
|
|
||||||
public func makePresentationTheme(cloudTheme: TelegramTheme, baseTheme: TelegramBaseTheme? = nil) -> PresentationTheme? {
|
public func makePresentationTheme(cloudTheme: TelegramTheme, baseTheme: TelegramBaseTheme? = nil) -> PresentationTheme? {
|
||||||
let settings: TelegramThemeSettings?
|
let settings: TelegramThemeSettings?
|
||||||
if let exactSettings = cloudTheme.settings?.first(where: { $0.baseTheme == baseTheme }) {
|
if let exactSettings = cloudTheme.settings?.first(where: { $0.baseTheme == baseTheme }) {
|
||||||
|
|||||||
@ -1574,6 +1574,7 @@ public final class PresentationTheme: Equatable {
|
|||||||
public let chart: PresentationThemeChart
|
public let chart: PresentationThemeChart
|
||||||
public let preview: Bool
|
public let preview: Bool
|
||||||
public var forceSync: Bool = false
|
public var forceSync: Bool = false
|
||||||
|
public var starGift: StarGift?
|
||||||
|
|
||||||
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
|
public let resourceCache: PresentationsResourceCache = PresentationsResourceCache()
|
||||||
|
|
||||||
|
|||||||
@ -781,12 +781,15 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
} else {
|
} else {
|
||||||
var emoji = ""
|
var emoji = ""
|
||||||
var additionalAttributes: [String: Any] = [:]
|
var additionalAttributes: [String: Any] = [:]
|
||||||
|
var giftTitle: String?
|
||||||
switch chatTheme {
|
switch chatTheme {
|
||||||
case let .emoticon(emoticon):
|
case let .emoticon(emoticon):
|
||||||
emoji = emoticon
|
emoji = emoticon
|
||||||
case let .gift(starGift, _):
|
case let .gift(starGift, _):
|
||||||
var file: TelegramMediaFile?
|
var file: TelegramMediaFile?
|
||||||
|
|
||||||
if case let .unique(uniqueGift) = starGift {
|
if case let .unique(uniqueGift) = starGift {
|
||||||
|
giftTitle = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: dateTimeFormat))"
|
||||||
for attribute in uniqueGift.attributes {
|
for attribute in uniqueGift.attributes {
|
||||||
if case let .model(_, fileValue, _) = attribute {
|
if case let .model(_, fileValue, _) = attribute {
|
||||||
file = fileValue
|
file = fileValue
|
||||||
@ -802,11 +805,21 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
|
if message.author?.id.namespace == Namespaces.Peer.CloudChannel {
|
||||||
attributedString = NSAttributedString(string: strings.Notification_ChannelChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_ChannelChangedTheme(emoji).string, font: titleFont, textColor: primaryTextColor)
|
||||||
} else if message.author?.id == accountPeerId {
|
} else if message.author?.id == accountPeerId {
|
||||||
let resultTitleString = strings.Notification_YouChangedTheme(emoji)
|
if let giftTitle {
|
||||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: emojiAttributes])
|
let resultTitleString = strings.Notification_YouChangedThemeGift(giftTitle)
|
||||||
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [:])
|
||||||
|
} else {
|
||||||
|
let resultTitleString = strings.Notification_YouChangedTheme(emoji)
|
||||||
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: emojiAttributes])
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let resultTitleString = strings.Notification_ChangedTheme(compactAuthorName, emoji)
|
if let giftTitle {
|
||||||
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: emojiAttributes])
|
let resultTitleString = strings.Notification_ChangedThemeGift(compactAuthorName, giftTitle)
|
||||||
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes])
|
||||||
|
} else {
|
||||||
|
let resultTitleString = strings.Notification_ChangedTheme(compactAuthorName, emoji)
|
||||||
|
attributedString = addAttributesToStringWithRanges(resultTitleString._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: emojiAttributes])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .webViewData(text):
|
case let .webViewData(text):
|
||||||
@ -817,8 +830,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
} else {
|
} else {
|
||||||
let price: String
|
let price: String
|
||||||
if currency == "XTR" {
|
if currency == "XTR" {
|
||||||
//TODO:localize
|
price = strings.Notification_PremiumGift_Stars(Int32(clamping: amount))
|
||||||
price = "\(amount) Stars"
|
|
||||||
} else {
|
} else {
|
||||||
price = formatCurrencyAmount(amount, currency: currency)
|
price = formatCurrencyAmount(amount, currency: currency)
|
||||||
}
|
}
|
||||||
@ -833,7 +845,8 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
case let .giftStars(currency, amount, count, _, _, _):
|
case let .giftStars(currency, amount, count, _, _, _):
|
||||||
let _ = count
|
let _ = count
|
||||||
if !forAdditionalServiceMessage {
|
if !forAdditionalServiceMessage {
|
||||||
attributedString = NSAttributedString(string: strings.Notification_Gift, font: titleFont, textColor: primaryTextColor)
|
let starsPrice = strings.Notification_GiftStars_Stars(Int32(clamping: count))
|
||||||
|
attributedString = NSAttributedString(string: strings.Notification_GiftStars(starsPrice).string, font: titleFont, textColor: primaryTextColor)
|
||||||
} else {
|
} else {
|
||||||
let price = formatCurrencyAmount(amount, currency: currency)
|
let price = formatCurrencyAmount(amount, currency: currency)
|
||||||
if message.author?.id == accountPeerId {
|
if message.author?.id == accountPeerId {
|
||||||
@ -1155,17 +1168,22 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||||||
attributedString = mutableString
|
attributedString = mutableString
|
||||||
case .prizeStars:
|
case .prizeStars:
|
||||||
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
attributedString = NSAttributedString(string: strings.Notification_StarsPrize, font: titleFont, textColor: primaryTextColor)
|
||||||
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, isPrepaidUpgrade, _, peerId, senderId, _, _, _, _):
|
case let .starGift(gift, _, text, entities, _, _, _, _, _, upgradeStars, _, isPrepaidUpgrade, _, peerId, senderId, _, _, _, upgradeSeparate):
|
||||||
if !forAdditionalServiceMessage {
|
if !forAdditionalServiceMessage {
|
||||||
if let text {
|
if let text {
|
||||||
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
|
let mutableAttributedString = NSMutableAttributedString(attributedString: stringWithAppliedEntities(text, entities: entities ?? [], baseColor: primaryTextColor, linkColor: primaryTextColor, baseFont: titleFont, linkFont: titleBoldFont, boldFont: titleBoldFont, italicFont: titleFont, boldItalicFont: titleBoldFont, fixedFont: titleFont, blockQuoteFont: titleFont, underlineLinks: false, message: message._asMessage()))
|
||||||
attributedString = mutableAttributedString
|
attributedString = mutableAttributedString
|
||||||
} else {
|
} else {
|
||||||
attributedString = NSAttributedString(string: strings.Notification_Gift, font: titleFont, textColor: primaryTextColor)
|
if isPrepaidUpgrade {
|
||||||
|
let starsPrice = strings.Notification_PrepaidGiftUpgrade_Stars(Int32(clamping: upgradeStars ?? 0))
|
||||||
|
attributedString = NSAttributedString(string: strings.Notification_PrepaidGiftUpgrade(starsPrice).string, font: titleFont, textColor: primaryTextColor)
|
||||||
|
} else {
|
||||||
|
attributedString = NSAttributedString(string: strings.Notification_Gift, font: titleFont, textColor: primaryTextColor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if case let .generic(gift) = gift {
|
} else if case let .generic(gift) = gift {
|
||||||
var finalPrice = gift.price
|
var finalPrice = gift.price
|
||||||
if let upgradeStars {
|
if let upgradeStars, !upgradeSeparate {
|
||||||
finalPrice += upgradeStars
|
finalPrice += upgradeStars
|
||||||
}
|
}
|
||||||
let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: finalPrice))
|
let starsPrice = strings.Notification_StarsGift_Stars(Int32(clamping: finalPrice))
|
||||||
|
|||||||
@ -485,6 +485,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
||||||
"//submodules/TelegramUI/Components/FaceScanScreen",
|
"//submodules/TelegramUI/Components/FaceScanScreen",
|
||||||
"//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist",
|
"//submodules/TelegramUI/Components/MediaManager/PeerMessagesMediaPlaylist",
|
||||||
|
"//submodules/TelegramUI/Components/ChatThemeScreen",
|
||||||
"//submodules/ContactsHelper",
|
"//submodules/ContactsHelper",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
|
|||||||
@ -241,6 +241,8 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
|||||||
} else if case .joinedChannel = action.action {
|
} else if case .joinedChannel = action.action {
|
||||||
result.append((message, ChatMessageJoinedChannelBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
result.append((message, ChatMessageJoinedChannelBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
needReactions = false
|
needReactions = false
|
||||||
|
} else if case let .setChatTheme(chatTheme) = action.action, case .gift = chatTheme {
|
||||||
|
result.append((message, ChatMessageGiftBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .text, neighborSpacing: .default)))
|
||||||
} else {
|
} else {
|
||||||
if !canAddMessageReactions(message: message) {
|
if !canAddMessageReactions(message: message) {
|
||||||
needReactions = false
|
needReactions = false
|
||||||
|
|||||||
@ -33,6 +33,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
"//submodules/TelegramUI/Components/TextNodeWithEntities",
|
||||||
"//submodules/InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode",
|
||||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||||
|
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import ChatMessageItemCommon
|
|||||||
import TextNodeWithEntities
|
import TextNodeWithEntities
|
||||||
import InvisibleInkDustNode
|
import InvisibleInkDustNode
|
||||||
import PeerInfoCoverComponent
|
import PeerInfoCoverComponent
|
||||||
|
import GiftItemComponent
|
||||||
|
|
||||||
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? {
|
private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: EngineMessage, accountPeerId: EnginePeer.Id) -> NSAttributedString? {
|
||||||
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true)
|
return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: false, forForumOverview: false, forAdditionalServiceMessage: true)
|
||||||
@ -45,6 +46,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
private var dustNode: InvisibleInkDustNode?
|
private var dustNode: InvisibleInkDustNode?
|
||||||
private let placeholderNode: StickerShimmerEffectNode
|
private let placeholderNode: StickerShimmerEffectNode
|
||||||
private let animationNode: AnimatedStickerNode
|
private let animationNode: AnimatedStickerNode
|
||||||
|
private let giftIcon = ComponentView<Empty>()
|
||||||
|
|
||||||
private let modelTitleTextNode: TextNode
|
private let modelTitleTextNode: TextNode
|
||||||
private let modelValueTextNode: TextNode
|
private let modelValueTextNode: TextNode
|
||||||
@ -416,6 +418,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
var months: Int32 = 3
|
var months: Int32 = 3
|
||||||
var animationName: String = ""
|
var animationName: String = ""
|
||||||
var animationFile: TelegramMediaFile?
|
var animationFile: TelegramMediaFile?
|
||||||
|
var uniqueGift: StarGift.UniqueGift?
|
||||||
var title = item.presentationData.strings.Notification_PremiumGift_Title
|
var title = item.presentationData.strings.Notification_PremiumGift_Title
|
||||||
var text = ""
|
var text = ""
|
||||||
var subtitleColor = primaryTextColor
|
var subtitleColor = primaryTextColor
|
||||||
@ -708,6 +711,20 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
text = item.presentationData.strings.Notification_StarGift_Subtitle_Refunded
|
text = item.presentationData.strings.Notification_StarGift_Subtitle_Refunded
|
||||||
animationFile = gift.file
|
animationFile = gift.file
|
||||||
}
|
}
|
||||||
|
case let .setChatTheme(chatTheme):
|
||||||
|
title = ""
|
||||||
|
var giftTitle = ""
|
||||||
|
if case let .gift(gift, _) = chatTheme, case let .unique(uniqueGiftValue) = gift {
|
||||||
|
giftTitle = "\(uniqueGiftValue.title) #\(formatCollectibleNumber(uniqueGiftValue.number, dateTimeFormat: item.presentationData.dateTimeFormat))"
|
||||||
|
uniqueGift = uniqueGiftValue
|
||||||
|
}
|
||||||
|
if incoming {
|
||||||
|
let authorName = item.message.author.flatMap { EnginePeer($0) }?.compactDisplayTitle ?? ""
|
||||||
|
text = item.presentationData.strings.Notification_ChatTheme_Text(authorName, giftTitle).string
|
||||||
|
} else {
|
||||||
|
text = item.presentationData.strings.Notification_ChatTheme_TextYou(giftTitle).string
|
||||||
|
}
|
||||||
|
hasServiceMessage = false
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -866,6 +883,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
giftSize.height += 12.0
|
giftSize.height += 12.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let _ = uniqueGift {
|
||||||
|
giftSize.height -= 31.0
|
||||||
|
}
|
||||||
|
|
||||||
var labelRects = labelLayout.linesRects()
|
var labelRects = labelLayout.linesRects()
|
||||||
if labelRects.count > 1 {
|
if labelRects.count > 1 {
|
||||||
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
let sortedIndices = (0 ..< labelRects.count).sorted(by: { labelRects[$0].width > labelRects[$1].width })
|
||||||
@ -944,7 +965,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.creatorButtonNode.isUserInteractionEnabled = !item.presentationData.isPreview
|
strongSelf.creatorButtonNode.isUserInteractionEnabled = !item.presentationData.isPreview
|
||||||
strongSelf.creatorButtonTitleNode.isHidden = creatorButtonTitle.isEmpty
|
strongSelf.creatorButtonTitleNode.isHidden = creatorButtonTitle.isEmpty
|
||||||
|
|
||||||
if strongSelf.item == nil && !isStoryEntity {
|
if strongSelf.item == nil && !isStoryEntity && uniqueGift == nil {
|
||||||
strongSelf.animationNode.started = { [weak self] in
|
strongSelf.animationNode.started = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let current = CACurrentMediaTime()
|
let current = CACurrentMediaTime()
|
||||||
@ -1009,7 +1030,10 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
|
let titleFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - titleLayout.size.width) / 2.0) , y: mediaBackgroundFrame.minY + 151.0), size: titleLayout.size)
|
||||||
strongSelf.titleNode.frame = titleFrame
|
strongSelf.titleNode.frame = titleFrame
|
||||||
|
|
||||||
let clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight))
|
var clippingTextFrame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - subtitleLayout.size.width) / 2.0), y: titleFrame.maxY + textSpacing), size: CGSize(width: subtitleLayout.size.width, height: clippedTextHeight))
|
||||||
|
if let _ = uniqueGift {
|
||||||
|
clippingTextFrame.origin.y -= 23.0
|
||||||
|
}
|
||||||
|
|
||||||
var attributesOffsetY: CGFloat = 0.0
|
var attributesOffsetY: CGFloat = 0.0
|
||||||
|
|
||||||
@ -1315,6 +1339,31 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let uniqueGift {
|
||||||
|
let iconSize = CGSize(width: 94.0, height: 94.0)
|
||||||
|
let _ = strongSelf.giftIcon.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(GiftItemComponent(
|
||||||
|
context: item.context,
|
||||||
|
theme: item.presentationData.theme.theme,
|
||||||
|
strings: item.presentationData.strings,
|
||||||
|
peer: nil,
|
||||||
|
subject: .uniqueGift(gift: uniqueGift, price: nil),
|
||||||
|
mode: .thumbnail
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: iconSize
|
||||||
|
)
|
||||||
|
if let giftIconView = strongSelf.giftIcon.view {
|
||||||
|
if giftIconView.superview == nil {
|
||||||
|
// backgroundView.layer.cornerRadius = 20.0
|
||||||
|
//backgroundView.clipsToBounds = true
|
||||||
|
strongSelf.view.addSubview(giftIconView)
|
||||||
|
}
|
||||||
|
giftIconView.frame = CGRect(origin: CGPoint(x: mediaBackgroundFrame.minX + floorToScreenPixels((mediaBackgroundFrame.width - iconSize.width) / 2.0), y: mediaBackgroundFrame.minY + 17.0), size: iconSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
|
let baseBackgroundFrame = labelFrame.offsetBy(dx: 0.0, dy: -11.0)
|
||||||
if let (offset, image) = backgroundMaskImage {
|
if let (offset, image) = backgroundMaskImage {
|
||||||
if strongSelf.backgroundNode == nil {
|
if strongSelf.backgroundNode == nil {
|
||||||
@ -1524,7 +1573,9 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
if isPlaying {
|
if isPlaying {
|
||||||
var alreadySeen = true
|
var alreadySeen = true
|
||||||
|
|
||||||
if item.message.flags.contains(.Incoming) {
|
if let action = item.message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case .setChatTheme = action.action {
|
||||||
|
|
||||||
|
} else if item.message.flags.contains(.Incoming) {
|
||||||
if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] {
|
if let unreadRange = item.controllerInteraction.unreadMessageRange[UnreadMessageRangeKey(peerId: item.message.id.peerId, namespace: item.message.id.namespace)] {
|
||||||
if unreadRange.contains(item.message.id.id) {
|
if unreadRange.contains(item.message.id.id) {
|
||||||
alreadySeen = false
|
alreadySeen = false
|
||||||
|
|||||||
@ -1656,7 +1656,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
|
|||||||
return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .screen)
|
return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .screen)
|
||||||
|> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
|> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
||||||
if let value = value {
|
if let value = value {
|
||||||
return .single(value)
|
return .single(value.generator)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -387,7 +387,7 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
updateImageSignal = patternWallpaperImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, representations: representations, mode: .thumbnail)
|
updateImageSignal = patternWallpaperImage(account: item.context.account, accountManager: item.context.sharedContext.accountManager, representations: representations, mode: .thumbnail)
|
||||||
|> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
|> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
||||||
if let value {
|
if let value {
|
||||||
return .single(value)
|
return .single(value.generator)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|||||||
44
submodules/TelegramUI/Components/ChatThemeScreen/BUILD
Normal file
44
submodules/TelegramUI/Components/ChatThemeScreen/BUILD
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "ChatThemeScreen",
|
||||||
|
module_name = "ChatThemeScreen",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit",
|
||||||
|
"//submodules/AsyncDisplayKit",
|
||||||
|
"//submodules/Display",
|
||||||
|
"//submodules/Postbox",
|
||||||
|
"//submodules/TelegramCore",
|
||||||
|
"//submodules/AccountContext",
|
||||||
|
"//submodules/ComponentFlow",
|
||||||
|
"//submodules/TelegramPresentationData",
|
||||||
|
"//submodules/TelegramUIPreferences",
|
||||||
|
"//submodules/PresentationDataUtils",
|
||||||
|
"//submodules/TelegramNotices",
|
||||||
|
"//submodules/AnimationUI",
|
||||||
|
"//submodules/MergeLists",
|
||||||
|
"//submodules/MediaResources",
|
||||||
|
"//submodules/StickerResources",
|
||||||
|
"//submodules/WallpaperResources",
|
||||||
|
"//submodules/TooltipUI",
|
||||||
|
"//submodules/SolidRoundedButtonNode",
|
||||||
|
"//submodules/AnimatedStickerNode",
|
||||||
|
"//submodules/TelegramAnimatedStickerNode",
|
||||||
|
"//submodules/ShimmerEffect",
|
||||||
|
"//submodules/AttachmentUI",
|
||||||
|
"//submodules/AvatarNode",
|
||||||
|
"//submodules/Markdown",
|
||||||
|
"//submodules/AppBundle",
|
||||||
|
"//submodules/ActivityIndicator",
|
||||||
|
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
||||||
@ -21,20 +21,22 @@ import AnimatedStickerNode
|
|||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import ShimmerEffect
|
import ShimmerEffect
|
||||||
import AttachmentUI
|
import AttachmentUI
|
||||||
|
import AvatarNode
|
||||||
|
|
||||||
private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
||||||
let index: Int
|
let index: Int
|
||||||
let chatTheme: ChatTheme?
|
let chatTheme: ChatTheme?
|
||||||
let emojiFile: TelegramMediaFile?
|
let emojiFile: TelegramMediaFile?
|
||||||
let themeReference: PresentationThemeReference?
|
let themeReference: PresentationThemeReference?
|
||||||
|
let peer: EnginePeer?
|
||||||
let nightMode: Bool
|
let nightMode: Bool
|
||||||
var selected: Bool
|
var selected: Bool
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let wallpaper: TelegramWallpaper?
|
let wallpaper: TelegramWallpaper?
|
||||||
|
|
||||||
var stableId: Int {
|
var stableId: String {
|
||||||
return index
|
return self.chatTheme?.id ?? "\(self.index)"
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ==(lhs: ThemeSettingsThemeEntry, rhs: ThemeSettingsThemeEntry) -> Bool {
|
static func ==(lhs: ThemeSettingsThemeEntry, rhs: ThemeSettingsThemeEntry) -> Bool {
|
||||||
@ -47,6 +49,9 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
|||||||
if lhs.themeReference?.index != rhs.themeReference?.index {
|
if lhs.themeReference?.index != rhs.themeReference?.index {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.peer != rhs.peer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.nightMode != rhs.nightMode {
|
if lhs.nightMode != rhs.nightMode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -70,16 +75,16 @@ private struct ThemeSettingsThemeEntry: Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func item(context: AccountContext, action: @escaping (ChatTheme?) -> Void) -> ListViewItem {
|
func item(context: AccountContext, action: @escaping (ChatTheme?) -> Void) -> ListViewItem {
|
||||||
return ThemeSettingsThemeIconItem(context: context, chatTheme: self.chatTheme, emojiFile: self.emojiFile, themeReference: self.themeReference, nightMode: self.nightMode, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action)
|
return ThemeSettingsThemeIconItem(context: context, chatTheme: self.chatTheme, emojiFile: self.emojiFile, themeReference: self.themeReference, peer: self.peer, nightMode: self.nightMode, selected: self.selected, theme: self.theme, strings: self.strings, wallpaper: self.wallpaper, action: action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class ThemeSettingsThemeIconItem: ListViewItem {
|
private class ThemeSettingsThemeIconItem: ListViewItem {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let chatTheme: ChatTheme?
|
let chatTheme: ChatTheme?
|
||||||
let emojiFile: TelegramMediaFile?
|
let emojiFile: TelegramMediaFile?
|
||||||
let themeReference: PresentationThemeReference?
|
let themeReference: PresentationThemeReference?
|
||||||
|
let peer: EnginePeer?
|
||||||
let nightMode: Bool
|
let nightMode: Bool
|
||||||
let selected: Bool
|
let selected: Bool
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
@ -87,11 +92,24 @@ private class ThemeSettingsThemeIconItem: ListViewItem {
|
|||||||
let wallpaper: TelegramWallpaper?
|
let wallpaper: TelegramWallpaper?
|
||||||
let action: (ChatTheme?) -> Void
|
let action: (ChatTheme?) -> Void
|
||||||
|
|
||||||
public init(context: AccountContext, chatTheme: ChatTheme?, emojiFile: TelegramMediaFile?, themeReference: PresentationThemeReference?, nightMode: Bool, selected: Bool, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper?, action: @escaping (ChatTheme?) -> Void) {
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
chatTheme: ChatTheme?,
|
||||||
|
emojiFile: TelegramMediaFile?,
|
||||||
|
themeReference: PresentationThemeReference?,
|
||||||
|
peer: EnginePeer?,
|
||||||
|
nightMode: Bool,
|
||||||
|
selected: Bool,
|
||||||
|
theme: PresentationTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
|
wallpaper: TelegramWallpaper?,
|
||||||
|
action: @escaping (ChatTheme?) -> Void
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.chatTheme = chatTheme
|
self.chatTheme = chatTheme
|
||||||
self.emojiFile = emojiFile
|
self.emojiFile = emojiFile
|
||||||
self.themeReference = themeReference
|
self.themeReference = themeReference
|
||||||
|
self.peer = peer
|
||||||
self.nightMode = nightMode
|
self.nightMode = nightMode
|
||||||
self.selected = selected
|
self.selected = selected
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
@ -240,6 +258,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
private let emojiImageNode: TransformImageNode
|
private let emojiImageNode: TransformImageNode
|
||||||
private var animatedStickerNode: AnimatedStickerNode?
|
private var animatedStickerNode: AnimatedStickerNode?
|
||||||
private var placeholderNode: StickerShimmerEffectNode
|
private var placeholderNode: StickerShimmerEffectNode
|
||||||
|
private var bubbleNode: ASImageNode?
|
||||||
|
private var avatarNode: AvatarNode?
|
||||||
|
private var replaceNode: ASImageNode?
|
||||||
var snapshotView: UIView?
|
var snapshotView: UIView?
|
||||||
|
|
||||||
var item: ThemeSettingsThemeIconItem?
|
var item: ThemeSettingsThemeIconItem?
|
||||||
@ -489,6 +510,75 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
animatedStickerNode.frame = emojiFrame
|
animatedStickerNode.frame = emojiFrame
|
||||||
animatedStickerNode.updateLayout(size: emojiFrame.size)
|
animatedStickerNode.updateLayout(size: emojiFrame.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let _ = item.peer {
|
||||||
|
let bubbleNode: ASImageNode
|
||||||
|
if let current = strongSelf.bubbleNode {
|
||||||
|
bubbleNode = current
|
||||||
|
} else {
|
||||||
|
bubbleNode = ASImageNode()
|
||||||
|
strongSelf.insertSubnode(bubbleNode, belowSubnode: strongSelf.emojiContainerNode)
|
||||||
|
strongSelf.bubbleNode = bubbleNode
|
||||||
|
|
||||||
|
var bubbleColor: UIColor?
|
||||||
|
if let theme = item.chatTheme, case let .gift(_, themeSettings) = theme {
|
||||||
|
if item.nightMode {
|
||||||
|
if let theme = themeSettings.first(where: { $0.baseTheme == .night || $0.baseTheme == .tinted }) {
|
||||||
|
let color = theme.wallpaper?.settings?.colors.first ?? theme.accentColor
|
||||||
|
bubbleColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let theme = themeSettings.first(where: { $0.baseTheme == .classic || $0.baseTheme == .day }) {
|
||||||
|
let color = theme.wallpaper?.settings?.colors.first ?? theme.accentColor
|
||||||
|
bubbleColor = UIColor(rgb: UInt32(bitPattern: color))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let bubbleColor {
|
||||||
|
bubbleNode.image = generateFilledRoundedRectImage(size: CGSize(width: 24.0, height: 48.0), cornerRadius: 12.0, color: bubbleColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bubbleNode.frame = CGRect(origin: CGPoint(x: 50.0, y: 12.0), size: CGSize(width: 24.0, height: 48.0))
|
||||||
|
} else if let bubbleNode = strongSelf.bubbleNode {
|
||||||
|
strongSelf.bubbleNode = nil
|
||||||
|
bubbleNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let peer = item.peer {
|
||||||
|
let avatarNode: AvatarNode
|
||||||
|
if let current = strongSelf.avatarNode {
|
||||||
|
avatarNode = current
|
||||||
|
} else {
|
||||||
|
avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 8.0))
|
||||||
|
strongSelf.insertSubnode(avatarNode, belowSubnode: strongSelf.emojiContainerNode)
|
||||||
|
strongSelf.avatarNode = avatarNode
|
||||||
|
avatarNode.setPeer(context: item.context, theme: item.theme, peer: peer, displayDimensions: CGSize(width: 20.0, height: 20.0))
|
||||||
|
}
|
||||||
|
avatarNode.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
avatarNode.frame = CGRect(origin: CGPoint(x: 52.0, y: 14.0), size: CGSize(width: 20.0, height: 20.0))
|
||||||
|
} else if let avatarNode = strongSelf.avatarNode {
|
||||||
|
strongSelf.avatarNode = nil
|
||||||
|
avatarNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let _ = item.peer {
|
||||||
|
let replaceNode: ASImageNode
|
||||||
|
if let current = strongSelf.replaceNode {
|
||||||
|
replaceNode = current
|
||||||
|
} else {
|
||||||
|
replaceNode = ASImageNode()
|
||||||
|
strongSelf.insertSubnode(replaceNode, belowSubnode: strongSelf.emojiContainerNode)
|
||||||
|
strongSelf.replaceNode = replaceNode
|
||||||
|
replaceNode.image = generateTintedImage(image: UIImage(bundleImageName: "Settings/Refresh"), color: .white)
|
||||||
|
}
|
||||||
|
replaceNode.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0)
|
||||||
|
if let image = replaceNode.image {
|
||||||
|
replaceNode.frame = CGRect(origin: CGPoint(x: 53.0, y: 37.0), size: image.size)
|
||||||
|
}
|
||||||
|
} else if let replaceNode = strongSelf.replaceNode {
|
||||||
|
strongSelf.replaceNode = nil
|
||||||
|
replaceNode.removeFromSupernode()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -525,9 +615,9 @@ private final class ThemeSettingsThemeItemIconNode : ListViewItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ChatThemeScreen: ViewController {
|
public final class ChatThemeScreen: ViewController {
|
||||||
static let themeCrossfadeDuration: Double = 0.3
|
public static let themeCrossfadeDuration: Double = 0.3
|
||||||
static let themeCrossfadeDelay: Double = 0.25
|
public static let themeCrossfadeDelay: Double = 0.25
|
||||||
|
|
||||||
private var controllerNode: ChatThemeScreenNode {
|
private var controllerNode: ChatThemeScreenNode {
|
||||||
return self.displayNode as! ChatThemeScreenNode
|
return self.displayNode as! ChatThemeScreenNode
|
||||||
@ -539,7 +629,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
private let animatedEmojiStickers: [String: [StickerPackItem]]
|
private let animatedEmojiStickers: [String: [StickerPackItem]]
|
||||||
private let initiallySelectedTheme: ChatTheme?
|
private let initiallySelectedTheme: ChatTheme?
|
||||||
private let peerName: String
|
private let peerName: String
|
||||||
let canResetWallpaper: Bool
|
fileprivate let canResetWallpaper: Bool
|
||||||
private let previewTheme: (ChatTheme?, Bool?) -> Void
|
private let previewTheme: (ChatTheme?, Bool?) -> Void
|
||||||
fileprivate let changeWallpaper: () -> Void
|
fileprivate let changeWallpaper: () -> Void
|
||||||
fileprivate let resetWallpaper: () -> Void
|
fileprivate let resetWallpaper: () -> Void
|
||||||
@ -548,9 +638,9 @@ final class ChatThemeScreen: ViewController {
|
|||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
var dismissed: (() -> Void)?
|
public var dismissed: (() -> Void)?
|
||||||
|
|
||||||
var passthroughHitTestImpl: ((CGPoint) -> UIView?)? {
|
public var passthroughHitTestImpl: ((CGPoint) -> UIView?)? {
|
||||||
didSet {
|
didSet {
|
||||||
if self.isNodeLoaded {
|
if self.isNodeLoaded {
|
||||||
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
|
self.controllerNode.passthroughHitTestImpl = self.passthroughHitTestImpl
|
||||||
@ -558,7 +648,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(
|
public init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>),
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>),
|
||||||
animatedEmojiStickers: [String: [StickerPackItem]],
|
animatedEmojiStickers: [String: [StickerPackItem]],
|
||||||
@ -657,7 +747,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
override public func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
self.forEachController({ controller in
|
self.forEachController({ controller in
|
||||||
if let controller = controller as? TooltipScreen {
|
if let controller = controller as? TooltipScreen {
|
||||||
controller.dismiss()
|
controller.dismiss()
|
||||||
@ -683,7 +773,7 @@ final class ChatThemeScreen: ViewController {
|
|||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dimTapped() {
|
public func dimTapped() {
|
||||||
self.controllerNode.dimTapped()
|
self.controllerNode.dimTapped()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -743,6 +833,7 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
private var initialized = false
|
private var initialized = false
|
||||||
|
|
||||||
private let uniqueGiftChatThemesContext: UniqueGiftChatThemesContext
|
private let uniqueGiftChatThemesContext: UniqueGiftChatThemesContext
|
||||||
|
private var currentUniqueGiftChatThemesState: UniqueGiftChatThemesContext.State?
|
||||||
|
|
||||||
private let peerName: String
|
private let peerName: String
|
||||||
|
|
||||||
@ -875,9 +966,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
self.cancelButtonNode.buttonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
self.cancelButtonNode.buttonNode.addTarget(self, action: #selector(self.cancelButtonPressed), forControlEvents: .touchUpInside)
|
||||||
self.doneButton.pressed = { [weak self] in
|
self.doneButton.pressed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.doneButton.isUserInteractionEnabled = false
|
|
||||||
if strongSelf.doneButton.font == .bold {
|
if strongSelf.doneButton.font == .bold {
|
||||||
strongSelf.completion?(strongSelf.selectedTheme)
|
strongSelf.complete()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.controller?.changeWallpaper()
|
strongSelf.controller?.changeWallpaper()
|
||||||
}
|
}
|
||||||
@ -888,13 +978,37 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
self.disposable.set(combineLatest(
|
self.disposable.set(combineLatest(
|
||||||
queue: Queue.mainQueue(),
|
queue: Queue.mainQueue(),
|
||||||
self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager),
|
self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager),
|
||||||
self.uniqueGiftChatThemesContext.state,
|
self.uniqueGiftChatThemesContext.state
|
||||||
|
|> mapToSignal { state -> Signal<(UniqueGiftChatThemesContext.State, [EnginePeer.Id: EnginePeer]), NoError> in
|
||||||
|
var peerIds: [EnginePeer.Id] = []
|
||||||
|
for theme in state.themes {
|
||||||
|
if case let .gift(gift, _) = theme, case let .unique(uniqueGift) = gift, let themePeerId = uniqueGift.themePeerId {
|
||||||
|
peerIds.append(themePeerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return combineLatest(
|
||||||
|
.single(state),
|
||||||
|
context.engine.data.get(
|
||||||
|
EngineDataMap(peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init))
|
||||||
|
) |> map { peers in
|
||||||
|
var result: [EnginePeer.Id: EnginePeer] = [:]
|
||||||
|
for peerId in peerIds {
|
||||||
|
if let maybePeer = peers[peerId], let peer = maybePeer {
|
||||||
|
result[peerId] = peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
self.selectedThemePromise.get(),
|
self.selectedThemePromise.get(),
|
||||||
self.isDarkAppearancePromise.get()
|
self.isDarkAppearancePromise.get()
|
||||||
).startStrict(next: { [weak self] themes, uniqueGiftChatThemes, selectedTheme, isDarkAppearance in
|
).startStrict(next: { [weak self] themes, uniqueGiftChatThemesStateAndPeers, selectedTheme, isDarkAppearance in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let (uniqueGiftChatThemesState, peers) = uniqueGiftChatThemesStateAndPeers
|
||||||
|
strongSelf.currentUniqueGiftChatThemesState = uniqueGiftChatThemesState
|
||||||
|
|
||||||
let isFirstTime = strongSelf.entries == nil
|
let isFirstTime = strongSelf.entries == nil
|
||||||
let presentationData = strongSelf.presentationData
|
let presentationData = strongSelf.presentationData
|
||||||
@ -905,51 +1019,81 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
chatTheme: nil,
|
chatTheme: nil,
|
||||||
emojiFile: nil,
|
emojiFile: nil,
|
||||||
themeReference: nil,
|
themeReference: nil,
|
||||||
|
peer: nil,
|
||||||
nightMode: false,
|
nightMode: false,
|
||||||
selected: selectedTheme == nil,
|
selected: selectedTheme == nil,
|
||||||
theme: presentationData.theme,
|
theme: presentationData.theme,
|
||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
wallpaper: nil
|
wallpaper: nil
|
||||||
))
|
))
|
||||||
for theme in themes {
|
|
||||||
guard let emoticon = theme.emoticon else {
|
var giftThemes = uniqueGiftChatThemesState.themes
|
||||||
continue
|
var existingIds = Set<String>()
|
||||||
|
if let initiallySelectedTheme, case .gift = initiallySelectedTheme {
|
||||||
|
let initialThemeIndex = giftThemes.firstIndex(where: { $0.id == initiallySelectedTheme.id })
|
||||||
|
if initialThemeIndex == nil || initialThemeIndex! > 50 {
|
||||||
|
giftThemes.insert(initiallySelectedTheme, at: 0)
|
||||||
}
|
}
|
||||||
entries.append(ThemeSettingsThemeEntry(
|
|
||||||
index: entries.count,
|
|
||||||
chatTheme: .emoticon(emoticon),
|
|
||||||
emojiFile: animatedEmojiStickers[emoticon]?.first?.file._parse(),
|
|
||||||
themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)),
|
|
||||||
nightMode: isDarkAppearance,
|
|
||||||
selected: selectedTheme?.id == ChatTheme.emoticon(emoticon).id,
|
|
||||||
theme: presentationData.theme,
|
|
||||||
strings: presentationData.strings,
|
|
||||||
wallpaper: nil
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
for theme in uniqueGiftChatThemes.themes {
|
|
||||||
guard case let .gift(gift, wallpaperFile) = theme else {
|
for theme in giftThemes {
|
||||||
|
guard case let .gift(gift, themeSettings) = theme, !existingIds.contains(theme.id) else {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var emojiFile: TelegramMediaFile?
|
var emojiFile: TelegramMediaFile?
|
||||||
|
var peer: EnginePeer?
|
||||||
if case let .unique(uniqueGift) = gift {
|
if case let .unique(uniqueGift) = gift {
|
||||||
for attribute in uniqueGift.attributes {
|
for attribute in uniqueGift.attributes {
|
||||||
if case let .model(_, file, _) = attribute {
|
if case let .model(_, file, _) = attribute {
|
||||||
emojiFile = file
|
emojiFile = file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let themePeerId = uniqueGift.themePeerId, theme.id != initiallySelectedTheme?.id {
|
||||||
|
peer = peers[themePeerId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let themeReference: PresentationThemeReference
|
||||||
|
let wallpaper: TelegramWallpaper?
|
||||||
|
if isDarkAppearance {
|
||||||
|
wallpaper = themeSettings.first(where: { $0.baseTheme == .night || $0.baseTheme == .tinted })?.wallpaper
|
||||||
|
themeReference = .builtin(.night)
|
||||||
|
} else {
|
||||||
|
wallpaper = themeSettings.first(where: { $0.baseTheme == .classic || $0.baseTheme == .day })?.wallpaper
|
||||||
|
themeReference = .builtin(.dayClassic)
|
||||||
}
|
}
|
||||||
entries.append(ThemeSettingsThemeEntry(
|
entries.append(ThemeSettingsThemeEntry(
|
||||||
index: entries.count,
|
index: entries.count,
|
||||||
chatTheme: theme,
|
chatTheme: theme,
|
||||||
emojiFile: emojiFile,
|
emojiFile: emojiFile,
|
||||||
themeReference: nil,
|
themeReference: themeReference,
|
||||||
|
peer: peer,
|
||||||
nightMode: isDarkAppearance,
|
nightMode: isDarkAppearance,
|
||||||
selected: selectedTheme?.id == theme.id,
|
selected: selectedTheme?.id == theme.id,
|
||||||
theme: presentationData.theme,
|
theme: presentationData.theme,
|
||||||
strings: presentationData.strings,
|
strings: presentationData.strings,
|
||||||
wallpaper: .file(TelegramWallpaper.File(id: wallpaperFile.fileId.id, accessHash: 0, isCreator: false, isDefault: false, isPattern: true, isDark: false, slug: "", file: wallpaperFile, settings: WallpaperSettings(blur: false, motion: false, colors: [], intensity: 100, rotation: 0)))
|
wallpaper: wallpaper
|
||||||
))
|
))
|
||||||
|
existingIds.insert(theme.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if uniqueGiftChatThemesState.themes.count == 0 || uniqueGiftChatThemesState.dataState == .ready(canLoadMore: false) {
|
||||||
|
for theme in themes {
|
||||||
|
guard let emoticon = theme.emoticon else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entries.append(ThemeSettingsThemeEntry(
|
||||||
|
index: entries.count,
|
||||||
|
chatTheme: .emoticon(emoticon),
|
||||||
|
emojiFile: animatedEmojiStickers[emoticon]?.first?.file._parse(),
|
||||||
|
themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)),
|
||||||
|
peer: nil,
|
||||||
|
nightMode: isDarkAppearance,
|
||||||
|
selected: selectedTheme?.id == ChatTheme.emoticon(emoticon).id,
|
||||||
|
theme: presentationData.theme,
|
||||||
|
strings: presentationData.strings,
|
||||||
|
wallpaper: nil
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let action: (ChatTheme?) -> Void = { [weak self] chatTheme in
|
let action: (ChatTheme?) -> Void = { [weak self] chatTheme in
|
||||||
@ -958,8 +1102,8 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let previousEntries = strongSelf.entries ?? []
|
let previousEntries = strongSelf.entries ?? []
|
||||||
let crossfade = previousEntries.count != entries.count
|
//let crossfade = previousEntries.count != entries.count
|
||||||
let transition = preparedTransition(context: strongSelf.context, action: action, from: previousEntries, to: entries, crossfade: crossfade)
|
let transition = preparedTransition(context: strongSelf.context, action: action, from: previousEntries, to: entries, crossfade: false)
|
||||||
strongSelf.enqueueTransition(transition)
|
strongSelf.enqueueTransition(transition)
|
||||||
|
|
||||||
strongSelf.entries = entries
|
strongSelf.entries = entries
|
||||||
@ -1008,6 +1152,15 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
|
||||||
|
guard let self, let state = self.currentUniqueGiftChatThemesState, case .ready(true) = state.dataState else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if case let .known(value) = offset, value < 100.0 {
|
||||||
|
self.uniqueGiftChatThemesContext.loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.updateCancelButton()
|
self.updateCancelButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1195,13 +1348,39 @@ private class ChatThemeScreenNode: ViewControllerTracingNode, ASScrollViewDelega
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func complete() {
|
||||||
|
let proceed = {
|
||||||
|
self.doneButton.isUserInteractionEnabled = false
|
||||||
|
self.completion?(self.selectedTheme)
|
||||||
|
}
|
||||||
|
if case let .gift(gift, _) = self.selectedTheme, case let .unique(uniqueGift) = gift, let themePeerId = uniqueGift.themePeerId {
|
||||||
|
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: themePeerId))
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||||
|
guard let self, let peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = giftThemeTransferAlertController(
|
||||||
|
context: self.context,
|
||||||
|
gift: uniqueGift,
|
||||||
|
previousPeer: peer,
|
||||||
|
commit: {
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.controller?.present(controller, in: .window(.root))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func dimTapped() {
|
func dimTapped() {
|
||||||
if self.selectedTheme?.id == self.initiallySelectedTheme?.id {
|
if self.selectedTheme?.id == self.initiallySelectedTheme?.id {
|
||||||
self.cancelButtonPressed()
|
self.cancelButtonPressed()
|
||||||
} else {
|
} else {
|
||||||
let alertController = textAlertController(context: self.context, updatedPresentationData: (self.presentationData, .single(self.presentationData)), title: nil, text: self.presentationData.strings.Conversation_Theme_DismissAlert, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_Theme_DismissAlertApply, action: { [weak self] in
|
let alertController = textAlertController(context: self.context, updatedPresentationData: (self.presentationData, .single(self.presentationData)), title: nil, text: self.presentationData.strings.Conversation_Theme_DismissAlert, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Conversation_Theme_DismissAlertApply, action: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.completion?(strongSelf.selectedTheme)
|
self.complete()
|
||||||
}
|
}
|
||||||
})], actionLayout: .horizontal, dismissOnOutsideTap: true)
|
})], actionLayout: .horizontal, dismissOnOutsideTap: true)
|
||||||
self.present?(alertController)
|
self.present?(alertController)
|
||||||
@ -0,0 +1,295 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
import ComponentFlow
|
||||||
|
import Postbox
|
||||||
|
import TelegramCore
|
||||||
|
import TelegramPresentationData
|
||||||
|
import TelegramUIPreferences
|
||||||
|
import AccountContext
|
||||||
|
import AppBundle
|
||||||
|
import AvatarNode
|
||||||
|
import Markdown
|
||||||
|
import GiftItemComponent
|
||||||
|
import ActivityIndicator
|
||||||
|
|
||||||
|
private final class GiftThemeTransferAlertContentNode: AlertContentNode {
|
||||||
|
private let context: AccountContext
|
||||||
|
private let strings: PresentationStrings
|
||||||
|
private var presentationTheme: PresentationTheme
|
||||||
|
private let title: String
|
||||||
|
private let text: String
|
||||||
|
private let gift: StarGift.UniqueGift
|
||||||
|
|
||||||
|
private let titleNode: ASTextNode
|
||||||
|
private let giftView = ComponentView<Empty>()
|
||||||
|
private let textNode: ASTextNode
|
||||||
|
private let arrowNode: ASImageNode
|
||||||
|
private let avatarNode: AvatarNode
|
||||||
|
|
||||||
|
private let actionNodesSeparator: ASDisplayNode
|
||||||
|
private let actionNodes: [TextAlertContentActionNode]
|
||||||
|
private let actionVerticalSeparators: [ASDisplayNode]
|
||||||
|
|
||||||
|
private var activityIndicator: ActivityIndicator?
|
||||||
|
|
||||||
|
private var validLayout: CGSize?
|
||||||
|
|
||||||
|
var inProgress = false {
|
||||||
|
didSet {
|
||||||
|
if let size = self.validLayout {
|
||||||
|
let _ = self.updateLayout(size: size, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dismissOnOutsideTap: Bool {
|
||||||
|
return self.isUserInteractionEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
theme: AlertControllerTheme,
|
||||||
|
ptheme: PresentationTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
|
gift: StarGift.UniqueGift,
|
||||||
|
peer: EnginePeer,
|
||||||
|
title: String,
|
||||||
|
text: String,
|
||||||
|
actions: [TextAlertAction]
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
self.strings = strings
|
||||||
|
self.presentationTheme = ptheme
|
||||||
|
self.title = title
|
||||||
|
self.text = text
|
||||||
|
self.gift = gift
|
||||||
|
|
||||||
|
self.titleNode = ASTextNode()
|
||||||
|
self.titleNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
|
self.textNode = ASTextNode()
|
||||||
|
self.textNode.maximumNumberOfLines = 0
|
||||||
|
|
||||||
|
self.arrowNode = ASImageNode()
|
||||||
|
self.arrowNode.displaysAsynchronously = false
|
||||||
|
self.arrowNode.displayWithoutProcessing = true
|
||||||
|
|
||||||
|
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||||
|
|
||||||
|
self.actionNodesSeparator = ASDisplayNode()
|
||||||
|
self.actionNodesSeparator.isLayerBacked = true
|
||||||
|
|
||||||
|
self.actionNodes = actions.map { action -> TextAlertContentActionNode in
|
||||||
|
return TextAlertContentActionNode(theme: theme, action: action)
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionVerticalSeparators: [ASDisplayNode] = []
|
||||||
|
if actions.count > 1 {
|
||||||
|
for _ in 0 ..< actions.count - 1 {
|
||||||
|
let separatorNode = ASDisplayNode()
|
||||||
|
separatorNode.isLayerBacked = true
|
||||||
|
actionVerticalSeparators.append(separatorNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.actionVerticalSeparators = actionVerticalSeparators
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.titleNode)
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
self.addSubnode(self.arrowNode)
|
||||||
|
self.addSubnode(self.avatarNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.actionNodesSeparator)
|
||||||
|
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
self.addSubnode(actionNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
for separatorNode in self.actionVerticalSeparators {
|
||||||
|
self.addSubnode(separatorNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateTheme(theme)
|
||||||
|
|
||||||
|
self.avatarNode.setPeer(context: context, theme: ptheme, peer: peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: self.title, font: Font.semibold(17.0), textColor: theme.primaryColor)
|
||||||
|
self.textNode.attributedText = parseMarkdownIntoAttributedString(self.text, attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||||
|
bold: MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor),
|
||||||
|
link: MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor),
|
||||||
|
linkAttribute: { url in
|
||||||
|
return ("URL", url)
|
||||||
|
}
|
||||||
|
), textAlignment: .center)
|
||||||
|
self.arrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Media Editor/CutoutUndo"), color: theme.secondaryColor.withAlphaComponent(0.9))
|
||||||
|
|
||||||
|
self.actionNodesSeparator.backgroundColor = theme.separatorColor
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
actionNode.updateTheme(theme)
|
||||||
|
}
|
||||||
|
for separatorNode in self.actionVerticalSeparators {
|
||||||
|
separatorNode.backgroundColor = theme.separatorColor
|
||||||
|
}
|
||||||
|
|
||||||
|
if let size = self.validLayout {
|
||||||
|
_ = self.updateLayout(size: size, transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||||
|
var size = size
|
||||||
|
size.width = min(size.width, 270.0)
|
||||||
|
|
||||||
|
self.validLayout = size
|
||||||
|
|
||||||
|
var origin: CGPoint = CGPoint(x: 0.0, y: 20.0)
|
||||||
|
|
||||||
|
let avatarSize = CGSize(width: 60.0, height: 60.0)
|
||||||
|
self.avatarNode.updateSize(size: avatarSize)
|
||||||
|
|
||||||
|
let giftFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) - 52.0, y: origin.y), size: avatarSize)
|
||||||
|
|
||||||
|
let _ = self.giftView.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(
|
||||||
|
GiftItemComponent(
|
||||||
|
context: self.context,
|
||||||
|
theme: self.presentationTheme,
|
||||||
|
strings: self.strings,
|
||||||
|
peer: nil,
|
||||||
|
subject: .uniqueGift(gift: self.gift, price: nil),
|
||||||
|
mode: .thumbnail
|
||||||
|
)
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: avatarSize
|
||||||
|
)
|
||||||
|
if let view = self.giftView.view {
|
||||||
|
if view.superview == nil {
|
||||||
|
self.view.addSubview(view)
|
||||||
|
}
|
||||||
|
view.frame = giftFrame
|
||||||
|
}
|
||||||
|
|
||||||
|
if let arrowImage = self.arrowNode.image {
|
||||||
|
let arrowFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - arrowImage.size.width) / 2.0), y: origin.y + floorToScreenPixels((avatarSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||||
|
transition.updateFrame(node: self.arrowNode, frame: arrowFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
let avatarFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0) + 52.0, y: origin.y), size: avatarSize)
|
||||||
|
transition.updateFrame(node: self.avatarNode, frame: avatarFrame)
|
||||||
|
|
||||||
|
origin.y += avatarSize.height + 17.0
|
||||||
|
|
||||||
|
let titleSize = self.titleNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||||
|
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||||
|
origin.y += titleSize.height + 5.0
|
||||||
|
|
||||||
|
let textSize = self.textNode.measure(CGSize(width: size.width - 32.0, height: size.height))
|
||||||
|
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - textSize.width) / 2.0), y: origin.y), size: textSize))
|
||||||
|
origin.y += textSize.height + 10.0
|
||||||
|
|
||||||
|
let actionButtonHeight: CGFloat = 44.0
|
||||||
|
var minActionsWidth: CGFloat = 0.0
|
||||||
|
let maxActionWidth: CGFloat = floor(size.width / CGFloat(self.actionNodes.count))
|
||||||
|
let actionTitleInsets: CGFloat = 8.0
|
||||||
|
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
let actionTitleSize = actionNode.titleNode.updateLayout(CGSize(width: maxActionWidth, height: actionButtonHeight))
|
||||||
|
minActionsWidth += actionTitleSize.width + actionTitleInsets
|
||||||
|
}
|
||||||
|
|
||||||
|
let insets = UIEdgeInsets(top: 18.0, left: 18.0, bottom: 18.0, right: 18.0)
|
||||||
|
|
||||||
|
let contentWidth = max(size.width, minActionsWidth)
|
||||||
|
|
||||||
|
let actionsHeight = actionButtonHeight
|
||||||
|
|
||||||
|
let resultSize = CGSize(width: contentWidth, height: avatarSize.height + titleSize.height + textSize.height + actionsHeight + 24.0 + insets.top + insets.bottom)
|
||||||
|
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))
|
||||||
|
|
||||||
|
var actionOffset: CGFloat = 0.0
|
||||||
|
let actionWidth: CGFloat = floor(resultSize.width / CGFloat(self.actionNodes.count))
|
||||||
|
var separatorIndex = -1
|
||||||
|
var nodeIndex = 0
|
||||||
|
for actionNode in self.actionNodes {
|
||||||
|
if separatorIndex >= 0 {
|
||||||
|
let separatorNode = self.actionVerticalSeparators[separatorIndex]
|
||||||
|
transition.updateFrame(node: separatorNode, frame: CGRect(origin: CGPoint(x: actionOffset - UIScreenPixel, y: resultSize.height - actionsHeight), size: CGSize(width: UIScreenPixel, height: actionsHeight - UIScreenPixel)))
|
||||||
|
}
|
||||||
|
separatorIndex += 1
|
||||||
|
|
||||||
|
let currentActionWidth: CGFloat
|
||||||
|
if nodeIndex == self.actionNodes.count - 1 {
|
||||||
|
currentActionWidth = resultSize.width - actionOffset
|
||||||
|
} else {
|
||||||
|
currentActionWidth = actionWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
let actionNodeFrame: CGRect
|
||||||
|
actionNodeFrame = CGRect(origin: CGPoint(x: actionOffset, y: resultSize.height - actionsHeight), size: CGSize(width: currentActionWidth, height: actionButtonHeight))
|
||||||
|
actionOffset += currentActionWidth
|
||||||
|
|
||||||
|
transition.updateFrame(node: actionNode, frame: actionNodeFrame)
|
||||||
|
|
||||||
|
nodeIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.inProgress {
|
||||||
|
let activityIndicator: ActivityIndicator
|
||||||
|
if let current = self.activityIndicator {
|
||||||
|
activityIndicator = current
|
||||||
|
} else {
|
||||||
|
activityIndicator = ActivityIndicator(type: .custom(self.presentationTheme.list.freeInputField.controlColor, 18.0, 1.5, false))
|
||||||
|
self.addSubnode(activityIndicator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let actionNode = self.actionNodes.first {
|
||||||
|
actionNode.isHidden = true
|
||||||
|
|
||||||
|
let indicatorSize = CGSize(width: 22.0, height: 22.0)
|
||||||
|
transition.updateFrame(node: activityIndicator, frame: CGRect(origin: CGPoint(x: actionNode.frame.minX + floor((actionNode.frame.width - indicatorSize.width) / 2.0), y: actionNode.frame.minY + floor((actionNode.frame.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func giftThemeTransferAlertController(
|
||||||
|
context: AccountContext,
|
||||||
|
gift: StarGift.UniqueGift,
|
||||||
|
previousPeer: EnginePeer,
|
||||||
|
commit: @escaping () -> Void
|
||||||
|
) -> AlertController {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let strings = presentationData.strings
|
||||||
|
|
||||||
|
var contentNode: GiftThemeTransferAlertContentNode?
|
||||||
|
var dismissImpl: ((Bool) -> Void)?
|
||||||
|
let actions: [TextAlertAction] = [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
dismissImpl?(true)
|
||||||
|
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Conversation_Theme_GiftTransfer_Proceed, action: {
|
||||||
|
dismissImpl?(true)
|
||||||
|
commit()
|
||||||
|
})]
|
||||||
|
|
||||||
|
let text = strings.Conversation_Theme_GiftTransfer_Text(previousPeer.compactDisplayTitle).string
|
||||||
|
contentNode = GiftThemeTransferAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: strings, gift: gift, peer: previousPeer, title: "", text: text, actions: actions)
|
||||||
|
|
||||||
|
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode!)
|
||||||
|
dismissImpl = { [weak controller] animated in
|
||||||
|
if animated {
|
||||||
|
controller?.dismissAnimated()
|
||||||
|
} else {
|
||||||
|
controller?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
@ -477,7 +477,12 @@ final class GiftSetupScreenComponent: Component {
|
|||||||
case .starGiftUserLimit:
|
case .starGiftUserLimit:
|
||||||
if let perUserLimit, let giftFile {
|
if let perUserLimit, let giftFile {
|
||||||
let text = presentationData.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit)
|
let text = presentationData.strings.Gift_Options_Gift_BuyLimitReached(perUserLimit)
|
||||||
let undoController = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: giftFile, loop: true, title: nil, text: text, undoText: nil, customAction: nil), action: { _ in return false })
|
let undoController = UndoOverlayController(
|
||||||
|
presentationData: presentationData,
|
||||||
|
content: .sticker(context: component.context, file: giftFile, loop: true, title: nil, text: text, undoText: nil, customAction: nil),
|
||||||
|
elevatedLayout: true,
|
||||||
|
action: { _ in return false }
|
||||||
|
)
|
||||||
controller.present(undoController, in: .current)
|
controller.present(undoController, in: .current)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -277,7 +277,7 @@ final class GiftStoreScreenComponent: Component {
|
|||||||
buyGift: { slug, peerId, price in
|
buyGift: { slug, peerId, price in
|
||||||
return self.state?.starGiftsContext.buyStarGift(slug: slug, peerId: peerId, price: price) ?? .complete()
|
return self.state?.starGiftsContext.buyStarGift(slug: slug, peerId: peerId, price: price) ?? .complete()
|
||||||
},
|
},
|
||||||
updateResellStars: { price in
|
updateResellStars: { _, price in
|
||||||
return self.state?.starGiftsContext.updateStarGiftResellPrice(slug: uniqueGift.slug, price: price) ?? .complete()
|
return self.state?.starGiftsContext.updateStarGiftResellPrice(slug: uniqueGift.slug, price: price) ?? .complete()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -53,6 +53,7 @@ swift_library(
|
|||||||
"//submodules/ActivityIndicator",
|
"//submodules/ActivityIndicator",
|
||||||
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
"//submodules/TelegramUI/Components/TabSelectorComponent",
|
||||||
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
||||||
|
"//submodules/TelegramUI/Components/ChatThemeScreen",
|
||||||
"//submodules/ImageBlur",
|
"//submodules/ImageBlur",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
|
|||||||
@ -149,10 +149,6 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
gift: gift
|
gift: gift
|
||||||
)
|
)
|
||||||
controller.push(storeController)
|
controller.push(storeController)
|
||||||
|
|
||||||
Queue.mainQueue().after(2.0, {
|
|
||||||
controller.dismiss(animated: false)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openGiftFragmentResale(url: String) {
|
func openGiftFragmentResale(url: String) {
|
||||||
@ -205,8 +201,6 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
let theme = environment.theme
|
let theme = environment.theme
|
||||||
let strings = environment.strings
|
let strings = environment.strings
|
||||||
let dateTimeFormat = environment.dateTimeFormat
|
let dateTimeFormat = environment.dateTimeFormat
|
||||||
//let nameDisplayOrder = component.context.sharedContext.currentPresentationData.with { $0 }.nameDisplayOrder
|
|
||||||
//let controller = environment.controller
|
|
||||||
|
|
||||||
let state = context.state
|
let state = context.state
|
||||||
|
|
||||||
@ -319,12 +313,12 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
var descriptionText: String
|
var descriptionText: String
|
||||||
if component.valueInfo.valueIsAverage {
|
if component.valueInfo.valueIsAverage {
|
||||||
descriptionText = "This is the average sale price of **\(giftCollectionTitle)** on Telegram and Fragment over the past month."
|
descriptionText = strings.Gift_Value_DescriptionAveragePrice(giftCollectionTitle).string
|
||||||
} else {
|
} else {
|
||||||
if component.valueInfo.isLastSaleOnFragment {
|
if component.valueInfo.isLastSaleOnFragment {
|
||||||
descriptionText = "This is the last price at which **\(giftTitle)** was last sold on Fragment."
|
descriptionText = strings.Gift_Value_DescriptionLastPriceFragment(giftTitle).string
|
||||||
} else {
|
} else {
|
||||||
descriptionText = "This is the last price at which **\(giftTitle)** was last sold on Telegram."
|
descriptionText = strings.Gift_Value_DescriptionLastPriceTelegram(giftTitle).string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !descriptionText.isEmpty {
|
if !descriptionText.isEmpty {
|
||||||
@ -394,7 +388,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "initialDate",
|
id: "initialDate",
|
||||||
title: "Initial Sale",
|
title: strings.Gift_Value_InitialSale,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: component.valueInfo.initialSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: component.valueInfo.initialSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||||
)
|
)
|
||||||
@ -410,7 +404,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "initialPrice",
|
id: "initialPrice",
|
||||||
title: "Initial Price",
|
title: strings.Gift_Value_InitialPrice,
|
||||||
component: AnyComponent(MultilineTextWithEntitiesComponent(
|
component: AnyComponent(MultilineTextWithEntitiesComponent(
|
||||||
context: component.context,
|
context: component.context,
|
||||||
animationCache: component.context.animationCache,
|
animationCache: component.context.animationCache,
|
||||||
@ -425,7 +419,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
if let lastSaleDate = component.valueInfo.lastSaleDate {
|
if let lastSaleDate = component.valueInfo.lastSaleDate {
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "lastDate",
|
id: "lastDate",
|
||||||
title: "Last Sale",
|
title: strings.Gift_Value_LastSale,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: lastSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: stringForMediumDate(timestamp: lastSaleDate, strings: strings, dateTimeFormat: dateTimeFormat), font: tableFont, textColor: tableTextColor)))
|
||||||
)
|
)
|
||||||
@ -457,7 +451,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
color: theme.list.itemAccentColor
|
color: theme.list.itemAccentColor
|
||||||
)),
|
)),
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
state?.showAttributeInfo(tag: tag, text: "**\(lastSalePriceString)** is the last price for \(giftCollectionTitle) gifts listed on Telegram and Fragment.")
|
state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_LastPriceInfo(lastSalePriceString, giftCollectionTitle).string)
|
||||||
|
|
||||||
}
|
}
|
||||||
).tagged(tag))
|
).tagged(tag))
|
||||||
@ -467,7 +461,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "lastPrice",
|
id: "lastPrice",
|
||||||
title: "Last Price",
|
title: strings.Gift_Value_LastPrice,
|
||||||
hasBackground: false,
|
hasBackground: false,
|
||||||
component: itemComponent
|
component: itemComponent
|
||||||
))
|
))
|
||||||
@ -494,8 +488,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
color: theme.list.itemAccentColor
|
color: theme.list.itemAccentColor
|
||||||
)),
|
)),
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
state?.showAttributeInfo(tag: tag, text: "**\(floorPriceString)** is the floor price for \(giftCollectionTitle) gifts listed on Telegram and Fragment.")
|
state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_MinimumPriceInfo(floorPriceString, giftCollectionTitle).string)
|
||||||
|
|
||||||
}
|
}
|
||||||
).tagged(tag))
|
).tagged(tag))
|
||||||
))
|
))
|
||||||
@ -504,7 +497,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "floorPrice",
|
id: "floorPrice",
|
||||||
title: "Minumum Price",
|
title: strings.Gift_Value_MinimumPrice,
|
||||||
hasBackground: false,
|
hasBackground: false,
|
||||||
component: itemComponent
|
component: itemComponent
|
||||||
))
|
))
|
||||||
@ -531,7 +524,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
color: theme.list.itemAccentColor
|
color: theme.list.itemAccentColor
|
||||||
)),
|
)),
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
state?.showAttributeInfo(tag: tag, text: "**\(averagePriceString)** is the average sale price of \(giftCollectionTitle) on Telegram and Fragment over the past month.")
|
state?.showAttributeInfo(tag: tag, text: strings.Gift_Value_AveragePriceInfo(averagePriceString, giftCollectionTitle).string)
|
||||||
}
|
}
|
||||||
).tagged(tag))
|
).tagged(tag))
|
||||||
))
|
))
|
||||||
@ -540,7 +533,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
tableItems.append(.init(
|
tableItems.append(.init(
|
||||||
id: "averagePrice",
|
id: "averagePrice",
|
||||||
title: "Average Price",
|
title: strings.Gift_Value_AveragePrice,
|
||||||
hasBackground: false,
|
hasBackground: false,
|
||||||
component: itemComponent
|
component: itemComponent
|
||||||
))
|
))
|
||||||
@ -587,7 +580,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: " for sale on Telegram", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnTelegram)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
||||||
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
||||||
@ -639,7 +632,7 @@ private final class GiftValueSheetContent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: " for sale on Fragment", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: " \(strings.Gift_Value_ForSaleOnFragment)", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
AnyComponentWithIdentity(id: "arrow", component: AnyComponent(
|
||||||
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
BundleIconComponent(name: "Chat/Context Menu/Arrow", tintColor: theme.actionSheet.controlAccentColor)
|
||||||
@ -724,6 +717,7 @@ final class GiftValueSheetComponent: CombinedComponent {
|
|||||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||||
followContentSizeChanges: true,
|
followContentSizeChanges: true,
|
||||||
clipsContent: true,
|
clipsContent: true,
|
||||||
|
autoAnimateOut: false,
|
||||||
externalState: sheetExternalState,
|
externalState: sheetExternalState,
|
||||||
animateOut: animateOut,
|
animateOut: animateOut,
|
||||||
onPan: {
|
onPan: {
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import StarsBalanceOverlayComponent
|
|||||||
import BalanceNeededScreen
|
import BalanceNeededScreen
|
||||||
import GiftItemComponent
|
import GiftItemComponent
|
||||||
import GiftAnimationComponent
|
import GiftAnimationComponent
|
||||||
|
import ChatThemeScreen
|
||||||
|
|
||||||
private final class GiftViewSheetContent: CombinedComponent {
|
private final class GiftViewSheetContent: CombinedComponent {
|
||||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||||
@ -104,6 +105,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
var cachedHiddenImage: (UIImage, PresentationTheme)?
|
var cachedHiddenImage: (UIImage, PresentationTheme)?
|
||||||
|
|
||||||
var inProgress = false
|
var inProgress = false
|
||||||
|
var canSkip = false
|
||||||
|
|
||||||
var testUpgradeAnimation = !"".isEmpty
|
var testUpgradeAnimation = !"".isEmpty
|
||||||
|
|
||||||
@ -152,7 +154,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
if let arguments = subject.arguments {
|
if let arguments = subject.arguments {
|
||||||
if let upgradeStars = arguments.upgradeStars, upgradeStars > 0, !arguments.nameHidden {
|
if let upgradeStars = arguments.upgradeStars, upgradeStars > 0, !arguments.nameHidden && !arguments.upgradeSeparate {
|
||||||
self.keepOriginalInfo = true
|
self.keepOriginalInfo = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,7 +597,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let convertToStars = controller?.convertToStars {
|
if let convertToStars = controller?.convertToStars {
|
||||||
convertToStars()
|
convertToStars(reference)
|
||||||
} else {
|
} else {
|
||||||
let _ = (self.context.engine.payments.convertStarGift(reference: reference)
|
let _ = (self.context.engine.payments.convertStarGift(reference: reference)
|
||||||
|> deliverOnMainQueue).startStandalone()
|
|> deliverOnMainQueue).startStandalone()
|
||||||
@ -668,14 +670,34 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isOpeningValue = true
|
self.isOpeningValue = true
|
||||||
|
|
||||||
|
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let _ = (self.context.engine.payments.getUniqueStarGiftValueInfo(slug: uniqueGift.slug)
|
let _ = (self.context.engine.payments.getUniqueStarGiftValueInfo(slug: uniqueGift.slug)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] valueInfo in
|
|> deliverOnMainQueue).start(next: { [weak self] valueInfo in
|
||||||
guard let self, let valueInfo else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.isOpeningValue = false
|
Queue.mainQueue().after(0.2) {
|
||||||
let valueController = GiftValueScreen(context: self.context, gift: gift, valueInfo: valueInfo)
|
self.isOpeningValue = false
|
||||||
controller.push(valueController)
|
}
|
||||||
|
if let valueInfo {
|
||||||
|
let valueController = GiftValueScreen(context: self.context, gift: gift, valueInfo: valueInfo)
|
||||||
|
controller.push(valueController)
|
||||||
|
} else {
|
||||||
|
guard let controller = self.getController() as? GiftViewScreen else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let alertController = textAlertController(
|
||||||
|
context: self.context,
|
||||||
|
title: nil,
|
||||||
|
text: presentationData.strings.Login_UnknownError,
|
||||||
|
actions: [
|
||||||
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})
|
||||||
|
],
|
||||||
|
parseMarkdown: true
|
||||||
|
)
|
||||||
|
controller.present(alertController, in: .window(.root))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,6 +804,83 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
controller.present(shareController, in: .window(.root))
|
controller.present(shareController, in: .window(.root))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setAsGiftTheme() {
|
||||||
|
guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, let navigationController = controller.navigationController as? NavigationController, case let .unique(gift) = arguments.gift else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let context = self.context
|
||||||
|
|
||||||
|
let themePeerId = Promise<EnginePeer.Id?>()
|
||||||
|
themePeerId.set(
|
||||||
|
.single(gift.themePeerId)
|
||||||
|
|> then(
|
||||||
|
context.engine.payments.getUniqueStarGift(slug: gift.slug)
|
||||||
|
|> map { gift in
|
||||||
|
return gift?.themePeerId
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let peerController = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: [.user(.init(isBot: false, isPremium: nil))], hasContactSelector: false, hasCreation: false))
|
||||||
|
peerController.peerSelected = { [weak peerController, weak navigationController] peer, _ in
|
||||||
|
if let navigationController {
|
||||||
|
let proceed = {
|
||||||
|
let _ = context.engine.themes.setChatWallpaper(peerId: peer.id, wallpaper: nil, forBoth: true).startStandalone()
|
||||||
|
let _ = context.engine.themes.setChatTheme(peerId: peer.id, chatTheme: .gift(.unique(gift), [])).startStandalone()
|
||||||
|
|
||||||
|
peerController?.dismiss()
|
||||||
|
|
||||||
|
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(
|
||||||
|
navigationController: navigationController,
|
||||||
|
chatController: nil,
|
||||||
|
context: context,
|
||||||
|
chatLocation: .peer(peer),
|
||||||
|
subject: nil,
|
||||||
|
botStart: nil,
|
||||||
|
updateTextInputState: nil,
|
||||||
|
keepStack: .always,
|
||||||
|
useExisting: true,
|
||||||
|
purposefulAction: nil,
|
||||||
|
scrollToEndIfExists: false,
|
||||||
|
activateMessageSearch: nil,
|
||||||
|
animated: true
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = (themePeerId.get()
|
||||||
|
|> deliverOnMainQueue
|
||||||
|
|> take(1)).start(next: { [weak navigationController] themePeerId in
|
||||||
|
if let themePeerId, themePeerId != peer.id {
|
||||||
|
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: themePeerId))
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak navigationController] peer in
|
||||||
|
guard let peer else {
|
||||||
|
proceed()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = giftThemeTransferAlertController(
|
||||||
|
context: context,
|
||||||
|
gift: gift,
|
||||||
|
previousPeer: peer,
|
||||||
|
commit: {
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
(navigationController?.viewControllers.last as? ViewController)?.present(controller, in: .window(.root))
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
proceed()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dismiss(animated: true)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.4) {
|
||||||
|
navigationController.pushViewController(peerController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func transferGift() {
|
func transferGift() {
|
||||||
guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, case let .unique(gift) = arguments.gift, let reference = arguments.reference, let transferStars = arguments.transferStars else {
|
guard let arguments = self.subject.arguments, let controller = self.getController() as? GiftViewScreen, case let .unique(gift) = arguments.gift, let reference = arguments.reference, let transferStars = arguments.transferStars else {
|
||||||
return
|
return
|
||||||
@ -882,7 +981,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
guard let self, let controller else {
|
guard let self, let controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = ((controller.updateResellStars?(nil) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: nil))
|
let _ = ((controller.updateResellStars?(reference, nil) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: nil))
|
||||||
|> deliverOnMainQueue).startStandalone(error: { error in
|
|> deliverOnMainQueue).startStandalone(error: { error in
|
||||||
|
|
||||||
}, completed: { [weak self, weak controller] in
|
}, completed: { [weak self, weak controller] in
|
||||||
@ -932,7 +1031,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = ((controller.updateResellStars?(price) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: price))
|
let _ = ((controller.updateResellStars?(reference, price) ?? context.engine.payments.updateStarGiftResalePrice(reference: reference, price: price))
|
||||||
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak controller] error in
|
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak controller] error in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
@ -1061,7 +1160,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
let strings = presentationData.strings
|
let strings = presentationData.strings
|
||||||
|
|
||||||
if let _ = arguments.reference, case .unique = arguments.gift, let togglePinnedToTop = controller.togglePinnedToTop, let pinnedToTop = arguments.pinnedToTop {
|
if let reference = arguments.reference, case .unique = arguments.gift, let togglePinnedToTop = controller.togglePinnedToTop, let pinnedToTop = arguments.pinnedToTop {
|
||||||
items.append(.action(ContextMenuActionItem(text: pinnedToTop ? strings.PeerInfo_Gifts_Context_Unpin : strings.PeerInfo_Gifts_Context_Pin , icon: { theme in generateTintedImage(image: UIImage(bundleImageName: pinnedToTop ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
items.append(.action(ContextMenuActionItem(text: pinnedToTop ? strings.PeerInfo_Gifts_Context_Unpin : strings.PeerInfo_Gifts_Context_Pin , icon: { theme in generateTintedImage(image: UIImage(bundleImageName: pinnedToTop ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in
|
||||||
c?.dismiss(completion: { [weak self, weak controller] in
|
c?.dismiss(completion: { [weak self, weak controller] in
|
||||||
guard let self, let controller else {
|
guard let self, let controller else {
|
||||||
@ -1069,7 +1168,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pinnedToTop = !pinnedToTop
|
let pinnedToTop = !pinnedToTop
|
||||||
if togglePinnedToTop(pinnedToTop) {
|
if togglePinnedToTop(reference, pinnedToTop) {
|
||||||
if pinnedToTop {
|
if pinnedToTop {
|
||||||
controller.dismissAnimated()
|
controller.dismissAnimated()
|
||||||
} else {
|
} else {
|
||||||
@ -1114,6 +1213,16 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
self?.shareGift()
|
self?.shareGift()
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
if gift.flags.contains(.isThemeAvailable) {
|
||||||
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Gift_View_Context_SetAsTheme, icon: { theme in
|
||||||
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ApplyTheme"), color: theme.contextMenu.primaryColor)
|
||||||
|
}, action: { [weak self] c, _ in
|
||||||
|
c?.dismiss(completion: nil)
|
||||||
|
|
||||||
|
self?.setAsGiftTheme()
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
if let _ = arguments.transferStars {
|
if let _ = arguments.transferStars {
|
||||||
if case let .channel(channel) = peer, !channel.flags.contains(.isCreator) {
|
if case let .channel(channel) = peer, !channel.flags.contains(.isCreator) {
|
||||||
|
|
||||||
@ -1528,7 +1637,28 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func skipAnimation() {
|
||||||
|
guard let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.canSkip = false
|
||||||
|
self.revealedNumberDigits = "\(uniqueGift.number)".count
|
||||||
|
self.revealedAttributes.insert(.backdrop)
|
||||||
|
self.revealedAttributes.insert(.pattern)
|
||||||
|
self.revealedAttributes.insert(.model)
|
||||||
|
|
||||||
|
self.updated(transition: .easeInOut(duration: 0.2))
|
||||||
|
}
|
||||||
|
|
||||||
func commitUpgrade() {
|
func commitUpgrade() {
|
||||||
|
let duration = Double.random(in: 0.85 ..< 2.25)
|
||||||
|
let firstFraction = Double.random(in: 0.2 ..< 0.4)
|
||||||
|
let secondFraction = Double.random(in: 0.2 ..< 0.4)
|
||||||
|
let thirdFraction = 1.0 - firstFraction - secondFraction
|
||||||
|
let firstDuration = duration * firstFraction
|
||||||
|
let secondDuration = duration * secondFraction
|
||||||
|
let thirdDuration = duration * thirdFraction
|
||||||
|
|
||||||
if self.testUpgradeAnimation, let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift {
|
if self.testUpgradeAnimation, let arguments = self.subject.arguments, case let .unique(uniqueGift) = arguments.gift {
|
||||||
self.inProgress = true
|
self.inProgress = true
|
||||||
self.updated()
|
self.updated()
|
||||||
@ -1538,8 +1668,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Queue.mainQueue().after(0.5, {
|
Queue.mainQueue().after(0.5, {
|
||||||
self.inUpgradePreview = false
|
self.canSkip = true
|
||||||
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
self.inProgress = false
|
self.inProgress = false
|
||||||
|
self.inUpgradePreview = false
|
||||||
|
|
||||||
self.justUpgraded = true
|
self.justUpgraded = true
|
||||||
self.revealedNumberDigits = -1
|
self.revealedNumberDigits = -1
|
||||||
@ -1550,20 +1683,24 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.updated(transition: .spring(duration: 0.4))
|
|
||||||
|
|
||||||
Queue.mainQueue().after(1.2) {
|
Queue.mainQueue().after(firstDuration) {
|
||||||
self.revealedAttributes.insert(.backdrop)
|
self.revealedAttributes.insert(.backdrop)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.7) {
|
Queue.mainQueue().after(secondDuration) {
|
||||||
self.revealedAttributes.insert(.pattern)
|
self.revealedAttributes.insert(.pattern)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.7) {
|
Queue.mainQueue().after(thirdDuration) {
|
||||||
self.revealedAttributes.insert(.model)
|
self.revealedAttributes.insert(.model)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.55) {
|
||||||
|
self.canSkip = false
|
||||||
|
self.updated(transition: .easeInOut(duration: 0.2))
|
||||||
|
}
|
||||||
|
|
||||||
Queue.mainQueue().after(0.6) {
|
Queue.mainQueue().after(0.6) {
|
||||||
if let controller = self.getController() as? GiftViewScreen {
|
if let controller = self.getController() as? GiftViewScreen {
|
||||||
controller.animateSuccess()
|
controller.animateSuccess()
|
||||||
@ -1572,6 +1709,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updated(transition: .spring(duration: 0.4))
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1592,8 +1731,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let context = self.context
|
let context = self.context
|
||||||
let upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
let upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||||
if let upgradeGift = controller.upgradeGift {
|
if let upgradeGift = controller.upgradeGift {
|
||||||
|
guard let reference = arguments.reference else {
|
||||||
|
return
|
||||||
|
}
|
||||||
upgradeGiftImpl = { formId, keepOriginalInfo in
|
upgradeGiftImpl = { formId, keepOriginalInfo in
|
||||||
return upgradeGift(formId, keepOriginalInfo)
|
return upgradeGift(formId, reference, keepOriginalInfo)
|
||||||
|> afterCompleted {
|
|> afterCompleted {
|
||||||
if formId != nil {
|
if formId != nil {
|
||||||
context.starsContext?.load(force: true)
|
context.starsContext?.load(force: true)
|
||||||
@ -1619,6 +1761,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
guard let self, let controller = self.getController() as? GiftViewScreen else {
|
guard let self, let controller = self.getController() as? GiftViewScreen else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.canSkip = true
|
||||||
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
self.inProgress = false
|
self.inProgress = false
|
||||||
self.inUpgradePreview = false
|
self.inUpgradePreview = false
|
||||||
|
|
||||||
@ -1639,18 +1784,23 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Queue.mainQueue().after(1.2) {
|
Queue.mainQueue().after(firstDuration) {
|
||||||
self.revealedAttributes.insert(.backdrop)
|
self.revealedAttributes.insert(.backdrop)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.7) {
|
Queue.mainQueue().after(secondDuration) {
|
||||||
self.revealedAttributes.insert(.pattern)
|
self.revealedAttributes.insert(.pattern)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
Queue.mainQueue().after(0.7) {
|
Queue.mainQueue().after(thirdDuration) {
|
||||||
self.revealedAttributes.insert(.model)
|
self.revealedAttributes.insert(.model)
|
||||||
self.updated(transition: .immediate)
|
self.updated(transition: .immediate)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.55) {
|
||||||
|
self.canSkip = false
|
||||||
|
self.updated(transition: .easeInOut(duration: 0.2))
|
||||||
|
}
|
||||||
|
|
||||||
Queue.mainQueue().after(0.6) {
|
Queue.mainQueue().after(0.6) {
|
||||||
if let controller = self.getController() as? GiftViewScreen {
|
if let controller = self.getController() as? GiftViewScreen {
|
||||||
controller.animateSuccess()
|
controller.animateSuccess()
|
||||||
@ -1661,7 +1811,6 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.subject = .profileGift(peerId, result)
|
self.subject = .profileGift(peerId, result)
|
||||||
controller.animateSuccess()
|
|
||||||
self.updated(transition: .spring(duration: 0.4))
|
self.updated(transition: .spring(duration: 0.4))
|
||||||
|
|
||||||
Queue.mainQueue().after(0.5) {
|
Queue.mainQueue().after(0.5) {
|
||||||
@ -2267,13 +2416,13 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
availableSize: CGSize(width: context.availableSize.width - perksSideInset * 2.0, height: 10000.0),
|
availableSize: CGSize(width: context.availableSize.width - perksSideInset * 2.0, height: 10000.0),
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
headerComponents.append({
|
|
||||||
context.add(wearPerks
|
context.add(wearPerks
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + wearPerks.size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + wearPerks.size.height / 2.0))
|
||||||
.appear(.default(alpha: true))
|
.appear(.default(alpha: true))
|
||||||
.disappear(.default(alpha: true))
|
.disappear(.default(alpha: true))
|
||||||
)
|
)
|
||||||
})
|
|
||||||
originY += wearPerks.size.height
|
originY += wearPerks.size.height
|
||||||
originY += 16.0
|
originY += 16.0
|
||||||
} else if showUpgradePreview {
|
} else if showUpgradePreview {
|
||||||
@ -3166,8 +3315,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
component: PlainButtonComponent(
|
component: PlainButtonComponent(
|
||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
HeaderButtonComponent(
|
HeaderButtonComponent(
|
||||||
title: uniqueGift.resellAmounts == nil ? strings.Gift_View_Sell : strings.Gift_View_Unlist,
|
title: (uniqueGift.resellAmounts ?? []).isEmpty ? strings.Gift_View_Sell : strings.Gift_View_Unlist,
|
||||||
iconName: uniqueGift.resellAmounts == nil ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist"
|
iconName: (uniqueGift.resellAmounts ?? []).isEmpty ? "Premium/Collectible/Sell" : "Premium/Collectible/Unlist"
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
effectAlignment: .center,
|
effectAlignment: .center,
|
||||||
@ -3629,7 +3778,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
} else {
|
} else {
|
||||||
resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })
|
resellAmount = uniqueGift.resellAmounts?.first(where: { $0.currency == .stars })
|
||||||
}
|
}
|
||||||
if let resellAmount {
|
if let resellAmount, wearPeerNameChild == nil {
|
||||||
if incoming || ownerPeerId == component.context.account.peerId {
|
if incoming || ownerPeerId == component.context.account.peerId {
|
||||||
let priceButton = priceButton.update(
|
let priceButton = priceButton.update(
|
||||||
component: PlainButtonComponent(
|
component: PlainButtonComponent(
|
||||||
@ -3658,7 +3807,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((incoming && !converted && !upgraded) || exported || selling) && (!showUpgradePreview && !showWearPreview) {
|
|
||||||
|
var isChatTheme = false
|
||||||
|
if let controller = controller() as? GiftViewScreen, controller.openChatTheme != nil {
|
||||||
|
isChatTheme = true
|
||||||
|
}
|
||||||
|
if ((incoming && !converted && !upgraded) || exported || selling || isChatTheme) && (!showUpgradePreview && !showWearPreview) {
|
||||||
let textFont = Font.regular(13.0)
|
let textFont = Font.regular(13.0)
|
||||||
let textColor = theme.list.itemSecondaryTextColor
|
let textColor = theme.list.itemSecondaryTextColor
|
||||||
let linkColor = theme.actionSheet.controlAccentColor
|
let linkColor = theme.actionSheet.controlAccentColor
|
||||||
@ -3672,7 +3826,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
|
|
||||||
var addressToOpen: String?
|
var addressToOpen: String?
|
||||||
var descriptionText: String
|
var descriptionText: String
|
||||||
if let uniqueGift, selling {
|
if isChatTheme {
|
||||||
|
descriptionText = strings.Gift_View_OpenChatTheme
|
||||||
|
} else if let uniqueGift, selling {
|
||||||
let ownerName: String
|
let ownerName: String
|
||||||
if case let .peerId(peerId) = uniqueGift.owner {
|
if case let .peerId(peerId) = uniqueGift.owner {
|
||||||
ownerName = state.peerMap[peerId]?.compactDisplayTitle ?? ""
|
ownerName = state.peerMap[peerId]?.compactDisplayTitle ?? ""
|
||||||
@ -3731,7 +3887,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
},
|
},
|
||||||
tapAction: { [weak state] attributes, _ in
|
tapAction: { [weak state] attributes, _ in
|
||||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||||
if let addressToOpen {
|
if isChatTheme, let controller = controller() as? GiftViewScreen {
|
||||||
|
state?.dismiss(animated: true)
|
||||||
|
controller.openChatTheme?()
|
||||||
|
} else if let addressToOpen {
|
||||||
state?.openAddress(addressToOpen)
|
state?.openAddress(addressToOpen)
|
||||||
} else {
|
} else {
|
||||||
state?.updateSavedToProfile(!savedToProfile)
|
state?.updateSavedToProfile(!savedToProfile)
|
||||||
@ -3761,7 +3920,25 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||||
)
|
)
|
||||||
let buttonChild: _UpdatedChildComponent
|
let buttonChild: _UpdatedChildComponent
|
||||||
if showWearPreview, let uniqueGift {
|
if state.canSkip {
|
||||||
|
buttonChild = button.update(
|
||||||
|
component: ButtonComponent(
|
||||||
|
background: buttonBackground,
|
||||||
|
content: AnyComponentWithIdentity(
|
||||||
|
id: AnyHashable("skip"),
|
||||||
|
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_Skip, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||||
|
),
|
||||||
|
isEnabled: true,
|
||||||
|
displaysProgress: state.inProgress,
|
||||||
|
action: { [weak state] in
|
||||||
|
if let state {
|
||||||
|
state.skipAnimation()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
availableSize: buttonSize,
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
} else if showWearPreview, let uniqueGift {
|
||||||
let buttonContent: AnyComponentWithIdentity<Empty>
|
let buttonContent: AnyComponentWithIdentity<Empty>
|
||||||
|
|
||||||
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
let premiumConfiguration = PremiumConfiguration.with(appConfiguration: component.context.currentAppConfiguration.with { $0 })
|
||||||
@ -4081,7 +4258,9 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
displaysProgress: state.inProgress,
|
displaysProgress: state.inProgress,
|
||||||
action: { [weak state] in
|
action: { [weak state] in
|
||||||
state?.dismiss(animated: true)
|
if let state {
|
||||||
|
state.dismiss(animated: true)
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
availableSize: buttonSize,
|
availableSize: buttonSize,
|
||||||
transition: context.transition
|
transition: context.transition
|
||||||
@ -4090,7 +4269,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: buttonChild.size)
|
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: originY), size: buttonChild.size)
|
||||||
|
|
||||||
var buttonAlpha: CGFloat = 1.0
|
var buttonAlpha: CGFloat = 1.0
|
||||||
if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift {
|
if let nextGiftToUpgrade = state.nextGiftToUpgrade, case let .generic(gift) = nextGiftToUpgrade.gift, !state.canSkip {
|
||||||
buttonAlpha = 0.0
|
buttonAlpha = 0.0
|
||||||
|
|
||||||
let upgradeNextButton = upgradeNextButton.update(
|
let upgradeNextButton = upgradeNextButton.update(
|
||||||
@ -4098,7 +4277,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(
|
content: AnyComponent(
|
||||||
HStack([
|
HStack([
|
||||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Upgrade Next Gift", font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Upgrade_UpgradeNext, font: Font.regular(17.0), textColor: theme.actionSheet.controlAccentColor)))
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
AnyComponentWithIdentity(id: "icon", component: AnyComponent(
|
||||||
GiftItemComponent(
|
GiftItemComponent(
|
||||||
@ -4278,12 +4457,12 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
case upgradePreview([StarGift.UniqueGift.Attribute], String)
|
case upgradePreview([StarGift.UniqueGift.Attribute], String)
|
||||||
case wearPreview(StarGift.UniqueGift)
|
case wearPreview(StarGift.UniqueGift)
|
||||||
|
|
||||||
var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?, prepaidUpgradeHash: String?)? {
|
var arguments: (peerId: EnginePeer.Id?, fromPeerId: EnginePeer.Id?, fromPeerName: String?, messageId: EngineMessage.Id?, reference: StarGiftReference?, incoming: Bool, gift: StarGift, date: Int32, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, pinnedToTop: Bool?, converted: Bool, upgraded: Bool, refunded: Bool, canUpgrade: Bool, upgradeStars: Int64?, transferStars: Int64?, resellAmounts: [CurrencyAmount]?, canExportDate: Int32?, upgradeMessageId: Int32?, canTransferDate: Int32?, canResaleDate: Int32?, prepaidUpgradeHash: String?, upgradeSeparate: Bool)? {
|
||||||
switch self {
|
switch self {
|
||||||
case let .message(message):
|
case let .message(message):
|
||||||
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
if let action = message.media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction {
|
||||||
switch action.action {
|
switch action.action {
|
||||||
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, _):
|
case let .starGift(gift, convertStars, text, entities, nameHidden, savedToProfile, converted, upgraded, canUpgrade, upgradeStars, isRefunded, _, upgradeMessageId, peerId, senderId, savedId, prepaidUpgradeHash, giftMessageId, upgradeSeparate):
|
||||||
var reference: StarGiftReference
|
var reference: StarGiftReference
|
||||||
if let peerId, let giftMessageId {
|
if let peerId, let giftMessageId {
|
||||||
reference = .message(messageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: giftMessageId))
|
reference = .message(messageId: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: giftMessageId))
|
||||||
@ -4292,7 +4471,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
} else {
|
} else {
|
||||||
reference = .message(messageId: message.id)
|
reference = .message(messageId: message.id)
|
||||||
}
|
}
|
||||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil, prepaidUpgradeHash)
|
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, message.flags.contains(.Incoming), gift, message.timestamp, convertStars, text, entities, nameHidden, savedToProfile, nil, converted, upgraded, isRefunded, canUpgrade, upgradeStars, nil, nil, nil, upgradeMessageId, nil, nil, prepaidUpgradeHash, upgradeSeparate)
|
||||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, _, peerId, senderId, savedId, _, canTransferDate, canResaleDate):
|
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, _, _, peerId, senderId, savedId, _, canTransferDate, canResaleDate):
|
||||||
var reference: StarGiftReference
|
var reference: StarGiftReference
|
||||||
if let peerId, let savedId {
|
if let peerId, let savedId {
|
||||||
@ -4317,13 +4496,13 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
if case let .unique(uniqueGift) = gift {
|
if case let .unique(uniqueGift) = gift {
|
||||||
resellAmounts = uniqueGift.resellAmounts
|
resellAmounts = uniqueGift.resellAmounts
|
||||||
}
|
}
|
||||||
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate, nil)
|
return (message.id.peerId, senderId ?? message.author?.id, message.author?.compactDisplayTitle, message.id, reference, incoming, gift, message.timestamp, nil, nil, nil, false, savedToProfile, nil, false, false, false, false, nil, transferStars, resellAmounts, canExportDate, nil, canTransferDate, canResaleDate, nil, false)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .uniqueGift(gift, _), let .wearPreview(gift):
|
case let .uniqueGift(gift, _), let .wearPreview(gift):
|
||||||
return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil)
|
return (nil, nil, nil, nil, nil, false, .unique(gift), 0, nil, nil, nil, false, false, nil, false, false, false, false, nil, nil, gift.resellAmounts, nil, nil, nil, nil, nil, false)
|
||||||
case let .profileGift(peerId, gift):
|
case let .profileGift(peerId, gift):
|
||||||
var messageId: EngineMessage.Id?
|
var messageId: EngineMessage.Id?
|
||||||
if case let .message(messageIdValue) = gift.reference {
|
if case let .message(messageIdValue) = gift.reference {
|
||||||
@ -4333,7 +4512,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
if case let .unique(uniqueGift) = gift.gift {
|
if case let .unique(uniqueGift) = gift.gift {
|
||||||
resellAmounts = uniqueGift.resellAmounts
|
resellAmounts = uniqueGift.resellAmounts
|
||||||
}
|
}
|
||||||
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash)
|
return (peerId, gift.fromPeer?.id, gift.fromPeer?.compactDisplayTitle, messageId, gift.reference, false, gift.gift, gift.date, gift.convertStars, gift.text, gift.entities, gift.nameHidden, gift.savedToProfile, gift.pinnedToTop, false, false, false, gift.canUpgrade, gift.upgradeStars, gift.transferStars, resellAmounts, gift.canExportDate, nil, gift.canTransferDate, gift.canResaleDate, gift.prepaidUpgradeHash, gift.upgradeSeparate)
|
||||||
case .soldOutGift:
|
case .soldOutGift:
|
||||||
return nil
|
return nil
|
||||||
case .upgradePreview:
|
case .upgradePreview:
|
||||||
@ -4374,13 +4553,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
fileprivate let balanceOverlay = ComponentView<Empty>()
|
fileprivate let balanceOverlay = ComponentView<Empty>()
|
||||||
|
|
||||||
fileprivate let updateSavedToProfile: ((StarGiftReference, Bool) -> Void)?
|
fileprivate let updateSavedToProfile: ((StarGiftReference, Bool) -> Void)?
|
||||||
fileprivate let convertToStars: (() -> Void)?
|
fileprivate let convertToStars: ((StarGiftReference) -> Void)?
|
||||||
fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)?
|
fileprivate let transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)?
|
||||||
fileprivate let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
|
fileprivate let upgradeGift: ((Int64?, StarGiftReference, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
|
||||||
fileprivate let buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)?
|
fileprivate let buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)?
|
||||||
fileprivate let updateResellStars: ((CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)?
|
fileprivate let updateResellStars: ((StarGiftReference, CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)?
|
||||||
fileprivate let togglePinnedToTop: ((Bool) -> Bool)?
|
fileprivate let togglePinnedToTop: ((StarGiftReference, Bool) -> Bool)?
|
||||||
fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)?
|
fileprivate let shareStory: ((StarGift.UniqueGift) -> Void)?
|
||||||
|
fileprivate let openChatTheme: (() -> Void)?
|
||||||
|
|
||||||
public var disposed: () -> Void = {}
|
public var disposed: () -> Void = {}
|
||||||
|
|
||||||
@ -4391,13 +4571,14 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
index: Int? = nil,
|
index: Int? = nil,
|
||||||
forceDark: Bool = false,
|
forceDark: Bool = false,
|
||||||
updateSavedToProfile: ((StarGiftReference, Bool) -> Void)? = nil,
|
updateSavedToProfile: ((StarGiftReference, Bool) -> Void)? = nil,
|
||||||
convertToStars: (() -> Void)? = nil,
|
convertToStars: ((StarGiftReference) -> Void)? = nil,
|
||||||
transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? = nil,
|
transferGift: ((Bool, EnginePeer.Id) -> Signal<Never, TransferStarGiftError>)? = nil,
|
||||||
upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil,
|
upgradeGift: ((Int64?, StarGiftReference, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)? = nil,
|
||||||
buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)? = nil,
|
buyGift: ((String, EnginePeer.Id, CurrencyAmount?) -> Signal<Never, BuyStarGiftError>)? = nil,
|
||||||
updateResellStars: ((CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil,
|
updateResellStars: ((StarGiftReference, CurrencyAmount?) -> Signal<Never, UpdateStarGiftPriceError>)? = nil,
|
||||||
togglePinnedToTop: ((Bool) -> Bool)? = nil,
|
togglePinnedToTop: ((StarGiftReference, Bool) -> Bool)? = nil,
|
||||||
shareStory: ((StarGift.UniqueGift) -> Void)? = nil
|
shareStory: ((StarGift.UniqueGift) -> Void)? = nil,
|
||||||
|
openChatTheme: (() -> Void)? = nil
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.subject = subject
|
self.subject = subject
|
||||||
@ -4410,6 +4591,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
self.updateResellStars = updateResellStars
|
self.updateResellStars = updateResellStars
|
||||||
self.togglePinnedToTop = togglePinnedToTop
|
self.togglePinnedToTop = togglePinnedToTop
|
||||||
self.shareStory = shareStory
|
self.shareStory = shareStory
|
||||||
|
self.openChatTheme = openChatTheme
|
||||||
|
|
||||||
if case let .unique(gift) = subject.arguments?.gift, gift.resellForTonOnly {
|
if case let .unique(gift) = subject.arguments?.gift, gift.resellForTonOnly {
|
||||||
self.balanceCurrency = .ton
|
self.balanceCurrency = .ton
|
||||||
@ -4453,8 +4635,8 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
self.navigationPresentation = .flatModal
|
self.navigationPresentation = .flatModal
|
||||||
self.automaticallyControlPresentationContextLayout = false
|
self.automaticallyControlPresentationContextLayout = false
|
||||||
|
|
||||||
if "".isEmpty {
|
if let gift = subject.arguments?.gift, case .generic = gift {
|
||||||
let upgradableGiftsContext = ProfileGiftsContext(account: context.account, peerId: context.account.peerId, collectionId: nil, sorting: .date, filter: [.displayed, .hidden, .limitedUpgradable])
|
let upgradableGiftsContext = ProfileGiftsContext(account: context.account, peerId: context.account.peerId, collectionId: nil, sorting: .date, filter: [.displayed, .hidden, .limitedUpgradable], limit: 50)
|
||||||
self.upgradableDisposable = (upgradableGiftsContext.state
|
self.upgradableDisposable = (upgradableGiftsContext.state
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] state in
|
|> deliverOnMainQueue).start(next: { [weak self] state in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -4529,7 +4711,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
|||||||
|
|
||||||
self.view.disablesInteractiveModalDismiss = true
|
self.view.disablesInteractiveModalDismiss = true
|
||||||
|
|
||||||
if let arguments = self.subject.arguments, let _ = self.subject.arguments?.resellAmounts {
|
if let arguments = self.subject.arguments, let resellAmounts = self.subject.arguments?.resellAmounts, !resellAmounts.isEmpty {
|
||||||
if case let .unique(uniqueGift) = arguments.gift, case .peerId(self.context.account.peerId) = uniqueGift.owner {
|
if case let .unique(uniqueGift) = arguments.gift, case .peerId(self.context.account.peerId) = uniqueGift.owner {
|
||||||
} else {
|
} else {
|
||||||
self.showBalance = true
|
self.showBalance = true
|
||||||
|
|||||||
@ -127,7 +127,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
let presence: EnginePeer.Presence
|
var presence: EnginePeer.Presence
|
||||||
if member.peer.id == context.account.peerId {
|
if member.peer.id == context.account.peerId {
|
||||||
presence = EnginePeer.Presence(status: .present(until: Int32.max), lastActivity: 0)
|
presence = EnginePeer.Presence(status: .present(until: Int32.max), lastActivity: 0)
|
||||||
} else if let value = member.presence {
|
} else if let value = member.presence {
|
||||||
@ -136,6 +136,17 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||||||
presence = EnginePeer.Presence(status: .longTimeAgo, lastActivity: 0)
|
presence = EnginePeer.Presence(status: .longTimeAgo, lastActivity: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var status: ContactsPeerItemStatus = .presence(presence, presentationData.dateTimeFormat)
|
||||||
|
if let user = member.peer as? TelegramUser, let botInfo = user.botInfo {
|
||||||
|
let botStatus: String
|
||||||
|
if botInfo.flags.contains(.hasAccessToChatHistory) {
|
||||||
|
botStatus = presentationData.strings.Bot_GroupStatusReadsHistory
|
||||||
|
} else {
|
||||||
|
botStatus = presentationData.strings.Bot_GroupStatusDoesNotReadHistory
|
||||||
|
}
|
||||||
|
status = .custom(string: NSAttributedString(string: botStatus, font: Font.regular(floor(presentationData.listsFontSize.itemListBaseFontSize * 14.0 / 17.0)), textColor: presentationData.theme.list.itemSecondaryTextColor), multiline: false, isActive: false, icon: nil)
|
||||||
|
}
|
||||||
|
|
||||||
return ContactsPeerItem(
|
return ContactsPeerItem(
|
||||||
presentationData: ItemListPresentationData(presentationData),
|
presentationData: ItemListPresentationData(presentationData),
|
||||||
style: .plain,
|
style: .plain,
|
||||||
@ -145,7 +156,7 @@ private enum PeerMembersListEntry: Comparable, Identifiable {
|
|||||||
context: context,
|
context: context,
|
||||||
peerMode: .memberList,
|
peerMode: .memberList,
|
||||||
peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)),
|
peer: .peer(peer: EnginePeer(member.peer), chatPeer: EnginePeer(member.peer)),
|
||||||
status: .presence(presence, presentationData.dateTimeFormat),
|
status: status,
|
||||||
rightLabelText: label,
|
rightLabelText: label,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
selection: .none,
|
selection: .none,
|
||||||
|
|||||||
@ -589,14 +589,14 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentSavedMusic: TelegramMediaFile?
|
var currentSavedMusic: TelegramMediaFile?
|
||||||
if !self.isSettings, let screenData {
|
if let peer, peer.id != self.context.account.peerId || self.isMyProfile, let screenData {
|
||||||
if let savedMusicState = screenData.savedMusicState {
|
if let savedMusicState = screenData.savedMusicState {
|
||||||
currentSavedMusic = savedMusicState.files.first
|
currentSavedMusic = savedMusicState.files.first
|
||||||
} else if let cachedUserData = screenData.cachedData as? CachedUserData {
|
} else if let cachedUserData = screenData.cachedData as? CachedUserData {
|
||||||
currentSavedMusic = cachedUserData.savedMusic
|
currentSavedMusic = cachedUserData.savedMusic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let musicHeight: CGFloat = hasBackground ? 24.0 : 16.0
|
let musicHeight: CGFloat = hasBackground || self.isAvatarExpanded ? 24.0 : 16.0
|
||||||
let bottomInset: CGFloat = currentSavedMusic != nil ? musicHeight : 0.0
|
let bottomInset: CGFloat = currentSavedMusic != nil ? musicHeight : 0.0
|
||||||
|
|
||||||
let isLandscape = containerInset > 16.0
|
let isLandscape = containerInset > 16.0
|
||||||
@ -2640,7 +2640,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
return musicBackground
|
return musicBackground
|
||||||
}()
|
}()
|
||||||
musicTransition.updateFrame(view: musicBackground, frame: CGRect(origin: CGPoint(x: 0.0, y: backgroundHeight - musicHeight - buttonRightOrigin.y), size: CGSize(width: backgroundFrame.width, height: musicHeight)))
|
musicTransition.updateFrame(view: musicBackground, frame: CGRect(origin: CGPoint(x: 0.0, y: backgroundHeight - 24.0 - buttonRightOrigin.y), size: CGSize(width: backgroundFrame.width, height: 24.0)))
|
||||||
|
|
||||||
if let _ = self.navigationTransition {
|
if let _ = self.navigationTransition {
|
||||||
transition.updateAlpha(layer: musicBackground.layer, alpha: 1.0 - transitionFraction)
|
transition.updateAlpha(layer: musicBackground.layer, alpha: 1.0 - transitionFraction)
|
||||||
@ -2698,7 +2698,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
|||||||
environment: {},
|
environment: {},
|
||||||
containerSize: CGSize(width: backgroundFrame.width, height: musicHeight)
|
containerSize: CGSize(width: backgroundFrame.width, height: musicHeight)
|
||||||
)
|
)
|
||||||
let musicFrame = CGRect(origin: CGPoint(x: 0.0, y: (apparentBackgroundHeight - backgroundHeight) + backgroundHeight - musicHeight - (hasBackground ? 0.0 : 4.0)), size: musicSize)
|
let musicFrame = CGRect(origin: CGPoint(x: 0.0, y: (apparentBackgroundHeight - backgroundHeight) + backgroundHeight - musicHeight - (hasBackground || self.isAvatarExpanded ? 0.0 : 4.0)), size: musicSize)
|
||||||
if let musicView = music.view {
|
if let musicView = music.view {
|
||||||
if musicView.superview == nil {
|
if musicView.superview == nil {
|
||||||
self.regularContentNode.view.addSubview(musicView)
|
self.regularContentNode.view.addSubview(musicView)
|
||||||
|
|||||||
@ -1163,9 +1163,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
items.append(.action(ContextMenuActionItem(text: "Set as Main Tab", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: params.presentationData.strings.PeerInfo_Tabs_SetMainTab, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/ReorderItems"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -1183,7 +1182,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = UndoOverlayController(presentationData: params.presentationData, content: .actionSucceeded(title: nil, text: "Tab order changed.", cancel: nil, destructive: false), action: { _ in return true })
|
let controller = UndoOverlayController(presentationData: params.presentationData, content: .actionSucceeded(title: nil, text: params.presentationData.strings.PeerInfo_Tabs_SetMainTab_Succeed, cancel: nil, destructive: false), action: { _ in return true })
|
||||||
self.parentController?.present(controller, in: .current)
|
self.parentController?.present(controller, in: .current)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5012,8 +5012,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
||||||
},
|
},
|
||||||
convertToStars: { [weak profileGifts] in
|
convertToStars: { [weak profileGifts] reference in
|
||||||
guard let profileGifts, let reference = gift.reference else {
|
guard let profileGifts else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
profileGifts.convertStarGift(reference: reference)
|
profileGifts.convertStarGift(reference: reference)
|
||||||
@ -5024,8 +5024,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
}
|
}
|
||||||
return profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
|
return profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
|
||||||
},
|
},
|
||||||
upgradeGift: { [weak profileGifts] formId, keepOriginalInfo in
|
upgradeGift: { [weak profileGifts] formId, reference, keepOriginalInfo in
|
||||||
guard let profileGifts, let reference = gift.reference else {
|
guard let profileGifts else {
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
return profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
|
return profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
|
||||||
|
|||||||
@ -611,8 +611,8 @@ final class GiftsListView: UIView {
|
|||||||
}
|
}
|
||||||
self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
self.profileGifts.updateStarGiftAddedToProfile(reference: reference, added: added)
|
||||||
},
|
},
|
||||||
convertToStars: { [weak self] in
|
convertToStars: { [weak self] reference in
|
||||||
guard let self, let reference = product.reference else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.profileGifts.convertStarGift(reference: reference)
|
self.profileGifts.convertStarGift(reference: reference)
|
||||||
@ -623,8 +623,8 @@ final class GiftsListView: UIView {
|
|||||||
}
|
}
|
||||||
return self.profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
|
return self.profileGifts.transferStarGift(prepaid: prepaid, reference: reference, peerId: peerId)
|
||||||
},
|
},
|
||||||
upgradeGift: { [weak self] formId, keepOriginalInfo in
|
upgradeGift: { [weak self] formId, reference, keepOriginalInfo in
|
||||||
guard let self, let reference = product.reference else {
|
guard let self else {
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
|
return self.profileGifts.upgradeStarGift(formId: formId, reference: reference, keepOriginalInfo: keepOriginalInfo)
|
||||||
@ -635,36 +635,34 @@ final class GiftsListView: UIView {
|
|||||||
}
|
}
|
||||||
return self.profileGifts.buyStarGift(slug: slug, peerId: peerId, price: price)
|
return self.profileGifts.buyStarGift(slug: slug, peerId: peerId, price: price)
|
||||||
},
|
},
|
||||||
updateResellStars: { [weak self] price in
|
updateResellStars: { [weak self] reference, price in
|
||||||
guard let self, let reference = product.reference else {
|
guard let self else {
|
||||||
return .never()
|
return .never()
|
||||||
}
|
}
|
||||||
return self.profileGifts.updateStarGiftResellPrice(reference: reference, price: price)
|
return self.profileGifts.updateStarGiftResellPrice(reference: reference, price: price)
|
||||||
},
|
},
|
||||||
togglePinnedToTop: { [weak self] pinnedToTop in
|
togglePinnedToTop: { [weak self] reference, pinnedToTop in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if let reference = product.reference {
|
if pinnedToTop && self.pinnedReferences.count >= self.maxPinnedCount {
|
||||||
if pinnedToTop && self.pinnedReferences.count >= self.maxPinnedCount {
|
self.displayUnpinScreen?(product, {
|
||||||
self.displayUnpinScreen?(product, {
|
dismissImpl?()
|
||||||
dismissImpl?()
|
})
|
||||||
})
|
return false
|
||||||
return false
|
}
|
||||||
}
|
self.profileGifts.updateStarGiftPinnedToTop(reference: reference, pinnedToTop: pinnedToTop)
|
||||||
self.profileGifts.updateStarGiftPinnedToTop(reference: reference, pinnedToTop: pinnedToTop)
|
|
||||||
|
|
||||||
var title = ""
|
var title = ""
|
||||||
if case let .unique(uniqueGift) = product.gift {
|
if case let .unique(uniqueGift) = product.gift {
|
||||||
title = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: params.presentationData.dateTimeFormat))"
|
title = "\(uniqueGift.title) #\(formatCollectibleNumber(uniqueGift.number, dateTimeFormat: params.presentationData.dateTimeFormat))"
|
||||||
}
|
}
|
||||||
|
|
||||||
if pinnedToTop {
|
if pinnedToTop {
|
||||||
Queue.mainQueue().after(0.35) {
|
Queue.mainQueue().after(0.35) {
|
||||||
let toastTitle = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string
|
let toastTitle = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_TitleNew(title).string
|
||||||
let toastText = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_Text
|
let toastText = params.presentationData.strings.PeerInfo_Gifts_ToastPinned_Text
|
||||||
self.parentController?.present(UndoOverlayController(presentationData: params.presentationData, content: .universal(animation: "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
self.parentController?.present(UndoOverlayController(presentationData: params.presentationData, content: .universal(animation: "anim_toastpin", scale: 0.06, colors: [:], title: toastTitle, text: toastText, customUndoText: nil, timeout: 5), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -996,7 +994,7 @@ final class GiftsListView: UIView {
|
|||||||
|
|
||||||
fadeTransition.setAlpha(view: self.emptyResultsClippingView, alpha: visibleHeight < 300.0 ? 0.0 : 1.0)
|
fadeTransition.setAlpha(view: self.emptyResultsClippingView, alpha: visibleHeight < 300.0 ? 0.0 : 1.0)
|
||||||
|
|
||||||
if self.peerId == self.context.account.peerId, !self.canSelect && !self.filteredResultsAreEmpty && self.profileGifts.collectionId == nil {
|
if self.peerId == self.context.account.peerId, !self.canSelect && !self.filteredResultsAreEmpty && self.profileGifts.collectionId == nil && self.emptyResultsClippingView.isHidden {
|
||||||
let footerText: ComponentView<Empty>
|
let footerText: ComponentView<Empty>
|
||||||
if let current = self.footerText {
|
if let current = self.footerText {
|
||||||
footerText = current
|
footerText = current
|
||||||
@ -1024,6 +1022,13 @@ final class GiftsListView: UIView {
|
|||||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: floor((size.width - footerTextSize.width) / 2.0), y: contentHeight), size: footerTextSize))
|
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: floor((size.width - footerTextSize.width) / 2.0), y: contentHeight), size: footerTextSize))
|
||||||
}
|
}
|
||||||
contentHeight += footerTextSize.height
|
contentHeight += footerTextSize.height
|
||||||
|
} else if let footerText = self.footerText {
|
||||||
|
self.footerText = nil
|
||||||
|
if let view = footerText.view {
|
||||||
|
fadeTransition.setAlpha(view: view, alpha: 0.0, completion: { _ in
|
||||||
|
view.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return contentHeight
|
return contentHeight
|
||||||
|
|||||||
@ -90,7 +90,6 @@ public final class PeerInfoGiftsPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScr
|
|||||||
private let tabSelector = ComponentView<Empty>()
|
private let tabSelector = ComponentView<Empty>()
|
||||||
public private(set) var currentCollection: GiftCollection = .all
|
public private(set) var currentCollection: GiftCollection = .all
|
||||||
|
|
||||||
private var footerText: ComponentView<Empty>?
|
|
||||||
private var panelBackground: NavigationBackgroundNode?
|
private var panelBackground: NavigationBackgroundNode?
|
||||||
private var panelSeparator: ASDisplayNode?
|
private var panelSeparator: ASDisplayNode?
|
||||||
private var panelButton: ComponentView<Empty>?
|
private var panelButton: ComponentView<Empty>?
|
||||||
|
|||||||
@ -377,24 +377,46 @@ private final class ProfileLevelInfoScreenComponent: Component {
|
|||||||
descriptionTextString = environment.strings.ProfileLevelInfo_OtherDescription(component.peer.compactDisplayTitle).string
|
descriptionTextString = environment.strings.ProfileLevelInfo_OtherDescription(component.peer.compactDisplayTitle).string
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
var titleItems: [AnimatedTextComponent.Item] = []
|
var titleItems: [AnimatedTextComponent.Item] = []
|
||||||
|
|
||||||
|
let ratingTitle = environment.strings.ProfileLevelInfo_RatingTitle
|
||||||
|
let futureTitle = environment.strings.ProfileLevelInfo_FutureRatingTitle
|
||||||
|
|
||||||
if self.isPreviewingPendingRating {
|
if self.isPreviewingPendingRating {
|
||||||
titleItems.append(AnimatedTextComponent.Item(
|
if let range = futureTitle.range(of: ratingTitle) {
|
||||||
id: AnyHashable(0),
|
if !futureTitle[..<range.lowerBound].isEmpty {
|
||||||
isUnbreakable: false,
|
titleItems.append(AnimatedTextComponent.Item(
|
||||||
content: .text("Future ")
|
id: AnyHashable(0),
|
||||||
))
|
isUnbreakable: false,
|
||||||
titleItems.append(AnimatedTextComponent.Item(
|
content: .text(String(futureTitle[..<range.lowerBound]))
|
||||||
id: AnyHashable(1),
|
))
|
||||||
isUnbreakable: true,
|
}
|
||||||
content: .text("Rating")
|
|
||||||
))
|
titleItems.append(AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(1),
|
||||||
|
isUnbreakable: true,
|
||||||
|
content: .text(ratingTitle)
|
||||||
|
))
|
||||||
|
|
||||||
|
if !futureTitle[range.upperBound...].isEmpty {
|
||||||
|
titleItems.append(AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(2),
|
||||||
|
isUnbreakable: false,
|
||||||
|
content: .text(String(futureTitle[range.upperBound...]))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
titleItems.append(AnimatedTextComponent.Item(
|
||||||
|
id: AnyHashable(0),
|
||||||
|
isUnbreakable: true,
|
||||||
|
content: .text(futureTitle)
|
||||||
|
))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
titleItems.append(AnimatedTextComponent.Item(
|
titleItems.append(AnimatedTextComponent.Item(
|
||||||
id: AnyHashable(1),
|
id: AnyHashable(1),
|
||||||
isUnbreakable: true,
|
isUnbreakable: true,
|
||||||
content: .text("Rating")
|
content: .text(ratingTitle)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -465,7 +465,9 @@ final class UserAppearanceScreenComponent: Component {
|
|||||||
resellForTonOnly: false,
|
resellForTonOnly: false,
|
||||||
releasedBy: nil,
|
releasedBy: nil,
|
||||||
valueAmount: nil,
|
valueAmount: nil,
|
||||||
valueCurrency: nil
|
valueCurrency: nil,
|
||||||
|
flags: [],
|
||||||
|
themePeerId: nil
|
||||||
)
|
)
|
||||||
signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate)
|
signal = component.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: emojiStatus.expirationDate)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -273,9 +273,9 @@ public final class SettingsThemeWallpaperNode: ASDisplayNode {
|
|||||||
self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white)
|
self.arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white)
|
||||||
}
|
}
|
||||||
imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true)
|
imageSignal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .thumbnail, autoFetchFullSize: true)
|
||||||
|> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
|> mapToSignal { generatorAndRects -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in
|
||||||
if let value = value {
|
if let (generator, _) = generatorAndRects {
|
||||||
return .single(value)
|
return .single(generator)
|
||||||
} else {
|
} else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -673,14 +673,13 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
let withdrawAvailable = (self.revenueState?.balances.overallRevenue.amount.value ?? 0) > 0
|
let withdrawAvailable = (self.revenueState?.balances.overallRevenue.amount.value ?? 0) > 0
|
||||||
|
|
||||||
if component.starsContext.ton {
|
if component.starsContext.ton {
|
||||||
//TODO:localize
|
|
||||||
let proceedsSize = self.proceedsView.update(
|
let proceedsSize = self.proceedsView.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(ListSectionComponent(
|
component: AnyComponent(ListSectionComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
header: AnyComponent(MultilineTextComponent(
|
header: AnyComponent(MultilineTextComponent(
|
||||||
text: .plain(NSAttributedString(
|
text: .plain(NSAttributedString(
|
||||||
string: "Proceeds Overview".uppercased(),
|
string: environment.strings.Ton_ProceedsOverview.uppercased(),
|
||||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||||
textColor: environment.theme.list.freeTextColor
|
textColor: environment.theme.list.freeTextColor
|
||||||
)),
|
)),
|
||||||
@ -691,14 +690,14 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
AnyComponentWithIdentity(id: 0, component: AnyComponent(StarsOverviewItemComponent(
|
AnyComponentWithIdentity(id: 0, component: AnyComponent(StarsOverviewItemComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
dateTimeFormat: environment.dateTimeFormat,
|
dateTimeFormat: environment.dateTimeFormat,
|
||||||
title: "Balance Available to Withdraw",
|
title: environment.strings.Ton_AvailableBalance,
|
||||||
value: self.revenueState?.balances.availableBalance ?? CurrencyAmount(amount: .zero, currency: .ton),
|
value: self.revenueState?.balances.availableBalance ?? CurrencyAmount(amount: .zero, currency: .ton),
|
||||||
rate: self.revenueState?.usdRate ?? 0.0
|
rate: self.revenueState?.usdRate ?? 0.0
|
||||||
))),
|
))),
|
||||||
AnyComponentWithIdentity(id: 1, component: AnyComponent(StarsOverviewItemComponent(
|
AnyComponentWithIdentity(id: 1, component: AnyComponent(StarsOverviewItemComponent(
|
||||||
theme: environment.theme,
|
theme: environment.theme,
|
||||||
dateTimeFormat: environment.dateTimeFormat,
|
dateTimeFormat: environment.dateTimeFormat,
|
||||||
title: "Total Lifetime Proceeds",
|
title: environment.strings.Ton_LifetimeProceeds,
|
||||||
value: self.revenueState?.balances.overallRevenue ?? CurrencyAmount(amount: .zero, currency: .ton),
|
value: self.revenueState?.balances.overallRevenue ?? CurrencyAmount(amount: .zero, currency: .ton),
|
||||||
rate: self.revenueState?.usdRate ?? 0.0
|
rate: self.revenueState?.usdRate ?? 0.0
|
||||||
)))
|
)))
|
||||||
@ -725,7 +724,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
|
|
||||||
let balanceInfoRawString = "Collect your TON using Fragment. [Learn More >]()"
|
let balanceInfoRawString = environment.strings.Ton_WithdrawViaFragment_Info
|
||||||
let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(balanceInfoRawString, attributes: termsMarkdownAttributes, textAlignment: .natural))
|
let balanceInfoString = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(balanceInfoRawString, attributes: termsMarkdownAttributes, textAlignment: .natural))
|
||||||
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme {
|
if self.cachedChevronImage == nil || self.cachedChevronImage?.1 !== environment.theme {
|
||||||
self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
|
self.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Contact List/SubtitleArrow"), color: environment.theme.list.itemAccentColor)!, environment.theme)
|
||||||
@ -754,7 +753,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
},
|
},
|
||||||
tapAction: { [weak self] attributes, _ in
|
tapAction: { [weak self] attributes, _ in
|
||||||
if let controller = self?.controller?() as? StarsTransactionsScreen, let navigationController = controller.navigationController as? NavigationController {
|
if let controller = self?.controller?() as? StarsTransactionsScreen, let navigationController = controller.navigationController as? NavigationController {
|
||||||
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: environment.strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
component.context.sharedContext.openExternalUrl(context: component.context, urlContext: .generic, url: component.starsContext.ton ? environment.strings.Ton_WithdrawViaFragment_Info_URL : environment.strings.Stars_BotRevenue_Withdraw_Info_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)) : nil,
|
)) : nil,
|
||||||
@ -766,7 +765,7 @@ final class StarsTransactionsScreenComponent: Component {
|
|||||||
count: self.starsState?.balance ?? StarsAmount.zero,
|
count: self.starsState?.balance ?? StarsAmount.zero,
|
||||||
currency: component.starsContext.ton ? .ton : .stars,
|
currency: component.starsContext.ton ? .ton : .stars,
|
||||||
rate: nil,
|
rate: nil,
|
||||||
actionTitle: component.starsContext.ton ? "Withdraw via Fragment" : (withdrawAvailable ? environment.strings.Stars_Intro_BuyShort : environment.strings.Stars_Intro_Buy),
|
actionTitle: component.starsContext.ton ? environment.strings.Ton_WithdrawViaFragment : (withdrawAvailable ? environment.strings.Stars_Intro_BuyShort : environment.strings.Stars_Intro_Buy),
|
||||||
actionAvailable: (!premiumConfiguration.areStarsDisabled && !premiumConfiguration.isPremiumDisabled),
|
actionAvailable: (!premiumConfiguration.areStarsDisabled && !premiumConfiguration.isPremiumDisabled),
|
||||||
actionIsEnabled: true,
|
actionIsEnabled: true,
|
||||||
actionIcon: component.starsContext.ton ? nil : PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme),
|
actionIcon: component.starsContext.ton ? nil : PresentationResourcesItemList.itemListRoundTopupIcon(environment.theme),
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "rotate_18.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/rotate_18.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Settings/Refresh.imageset/rotate_18.pdf
vendored
Normal file
Binary file not shown.
@ -101,9 +101,22 @@ func openWebAppImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.BotAppSettings(id: botPeer.id))
|
var botPeer = botPeer
|
||||||
|> deliverOnMainQueue).start(next: { appSettings in
|
if case let .inline(bot) = source {
|
||||||
let openWebView = { [weak parentController] in
|
botPeer = bot
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = combineLatest(queue: Queue.mainQueue(),
|
||||||
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.BotAppSettings(id: botPeer.id)),
|
||||||
|
ApplicationSpecificNotice.getBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id),
|
||||||
|
context.engine.messages.attachMenuBots(),
|
||||||
|
context.engine.messages.getAttachMenuBot(botId: botPeer.id, cached: true)
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<AttachMenuBot?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
).start(next: { appSettings, noticed, attachMenuBots, attachMenuBot in
|
||||||
|
let openWebView: (Bool) -> Void = { [weak parentController] justInstalled in
|
||||||
guard let parentController else {
|
guard let parentController else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -305,6 +318,11 @@ func openWebAppImpl(
|
|||||||
presentImpl = { [weak controller] c, a in
|
presentImpl = { [weak controller] c, a in
|
||||||
controller?.present(c, in: .window(.root), with: a)
|
controller?.present(c, in: .window(.root), with: a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if justInstalled {
|
||||||
|
let content: UndoOverlayContent = .succeed(text: presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil)
|
||||||
|
controller.present(UndoOverlayController(presentationData: presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
|
||||||
|
}
|
||||||
}, error: { [weak parentController] error in
|
}, error: { [weak parentController] error in
|
||||||
if let parentController {
|
if let parentController {
|
||||||
parentController.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
parentController.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
|
||||||
@ -314,25 +332,37 @@ func openWebAppImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipTermsOfService {
|
var isAttachMenuBotInstalled: Bool?
|
||||||
openWebView()
|
if let _ = attachMenuBot {
|
||||||
} else {
|
if let _ = attachMenuBots.first(where: { $0.peer.id == botPeer.id && !$0.flags.contains(.notActivated) }) {
|
||||||
var botPeer = botPeer
|
isAttachMenuBotInstalled = true
|
||||||
if case let .inline(bot) = source {
|
} else {
|
||||||
botPeer = bot
|
isAttachMenuBotInstalled = false
|
||||||
}
|
}
|
||||||
let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id)
|
}
|
||||||
|> deliverOnMainQueue).startStandalone(next: { [weak parentController] value in
|
|
||||||
guard let parentController else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if value {
|
if !noticed || attachMenuBot?.flags.contains(.notActivated) == true || isAttachMenuBotInstalled == false {
|
||||||
openWebView()
|
if let isAttachMenuBotInstalled, let attachMenuBot {
|
||||||
|
if !isAttachMenuBotInstalled {
|
||||||
|
let controller = webAppTermsAlertController(context: context, updatedPresentationData: updatedPresentationData, bot: attachMenuBot, completion: { allowWrite in
|
||||||
|
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
|
||||||
|
let _ = (context.engine.messages.addBotToAttachMenu(botId: botPeer.id, allowWrite: allowWrite)
|
||||||
|
|> deliverOnMainQueue).startStandalone(error: { _ in
|
||||||
|
}, completed: {
|
||||||
|
openWebView(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
parentController.present(controller, in: .window(.root))
|
||||||
|
} else {
|
||||||
|
openWebView(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if skipTermsOfService {
|
||||||
|
openWebView(false)
|
||||||
} else {
|
} else {
|
||||||
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, completion: { _ in
|
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: updatedPresentationData, peer: botPeer, completion: { _ in
|
||||||
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
|
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
|
||||||
openWebView()
|
openWebView(false)
|
||||||
}, showMore: nil, openTerms: {
|
}, showMore: nil, openTerms: {
|
||||||
if let navigationController = parentController.navigationController as? NavigationController {
|
if let navigationController = parentController.navigationController as? NavigationController {
|
||||||
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: presentationData.strings.WebApp_LaunchTermsConfirmation_URL, forceExternal: false, presentationData: presentationData, navigationController: navigationController, dismissInput: {})
|
||||||
@ -340,7 +370,9 @@ func openWebAppImpl(
|
|||||||
})
|
})
|
||||||
parentController.present(controller, in: .window(.root))
|
parentController.present(controller, in: .window(.root))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
} else {
|
||||||
|
openWebView(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -123,6 +123,7 @@ import ChatEmptyNode
|
|||||||
import ChatMediaInputStickerGridItem
|
import ChatMediaInputStickerGridItem
|
||||||
import AdsInfoScreen
|
import AdsInfoScreen
|
||||||
import Photos
|
import Photos
|
||||||
|
import ChatThemeScreen
|
||||||
|
|
||||||
extension ChatControllerImpl {
|
extension ChatControllerImpl {
|
||||||
public func presentThemeSelection() {
|
public func presentThemeSelection() {
|
||||||
@ -182,15 +183,15 @@ extension ChatControllerImpl {
|
|||||||
previewTheme: { [weak self] chatTheme, dark in
|
previewTheme: { [weak self] chatTheme, dark in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentCrossfadeSnapshot()
|
strongSelf.presentCrossfadeSnapshot()
|
||||||
strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme ?? .emoticon(""), dark)))
|
strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme, dark)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
changeWallpaper: { [weak self] in
|
changeWallpaper: { [weak self] in
|
||||||
guard let strongSelf = self, let peerId else {
|
guard let self, let peerId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let themeController = strongSelf.themeScreen {
|
if let themeController = self.themeScreen {
|
||||||
strongSelf.themeScreen = nil
|
self.themeScreen = nil
|
||||||
themeController.dimTapped()
|
themeController.dimTapped()
|
||||||
}
|
}
|
||||||
let dismissControllers = { [weak self] in
|
let dismissControllers = { [weak self] in
|
||||||
@ -206,69 +207,67 @@ extension ChatControllerImpl {
|
|||||||
}
|
}
|
||||||
var openWallpaperPickerImpl: ((Bool) -> Void)?
|
var openWallpaperPickerImpl: ((Bool) -> Void)?
|
||||||
let openWallpaperPicker = { [weak self] animateAppearance in
|
let openWallpaperPicker = { [weak self] animateAppearance in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = wallpaperMediaPickerController(
|
let controller = wallpaperMediaPickerController(
|
||||||
context: strongSelf.context,
|
context: context,
|
||||||
updatedPresentationData: strongSelf.updatedPresentationData,
|
updatedPresentationData: self.updatedPresentationData,
|
||||||
peer: EnginePeer(peer),
|
peer: EnginePeer(peer),
|
||||||
animateAppearance: animateAppearance,
|
animateAppearance: animateAppearance,
|
||||||
completion: { [weak self] _, result in
|
completion: { [weak self] _, result in
|
||||||
guard let strongSelf = self, let asset = result as? PHAsset else {
|
guard let self, let asset = result as? PHAsset else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = WallpaperGalleryController(context: strongSelf.context, source: .asset(asset), mode: .peer(EnginePeer(peer), false))
|
let controller = WallpaperGalleryController(context: context, source: .asset(asset), mode: .peer(EnginePeer(peer), false))
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
controller.apply = { [weak self] wallpaper, options, editedImage, cropRect, brightness, forBoth in
|
controller.apply = { wallpaper, options, editedImage, cropRect, brightness, forBoth in
|
||||||
if let strongSelf = self {
|
uploadCustomPeerWallpaper(context: context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, peerId: peerId, forBoth: forBoth, completion: {
|
||||||
uploadCustomPeerWallpaper(context: strongSelf.context, wallpaper: wallpaper, mode: options, editedImage: editedImage, cropRect: cropRect, brightness: brightness, peerId: peerId, forBoth: forBoth, completion: {
|
Queue.mainQueue().after(0.3, {
|
||||||
Queue.mainQueue().after(0.3, {
|
dismissControllers()
|
||||||
dismissControllers()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
},
|
},
|
||||||
openColors: { [weak self] in
|
openColors: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = standaloneColorPickerController(context: strongSelf.context, peer: EnginePeer(peer), push: { [weak self] controller in
|
let controller = standaloneColorPickerController(context: context, peer: EnginePeer(peer), push: { [weak self] controller in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
}
|
}
|
||||||
}, openGallery: {
|
}, openGallery: {
|
||||||
openWallpaperPickerImpl?(false)
|
openWallpaperPickerImpl?(false)
|
||||||
})
|
})
|
||||||
controller.navigationPresentation = .flatModal
|
controller.navigationPresentation = .flatModal
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
controller.navigationPresentation = .flatModal
|
controller.navigationPresentation = .flatModal
|
||||||
strongSelf.push(controller)
|
self.push(controller)
|
||||||
}
|
}
|
||||||
openWallpaperPickerImpl = openWallpaperPicker
|
openWallpaperPickerImpl = openWallpaperPicker
|
||||||
openWallpaperPicker(true)
|
openWallpaperPicker(true)
|
||||||
},
|
},
|
||||||
resetWallpaper: { [weak self] in
|
resetWallpaper: { [weak self] in
|
||||||
guard let strongSelf = self, let peerId else {
|
guard let self, let peerId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let _ = strongSelf.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone()
|
let _ = self.context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone()
|
||||||
},
|
},
|
||||||
completion: { [weak self] chatTheme in
|
completion: { [weak self] chatTheme in
|
||||||
guard let strongSelf = self, let peerId else {
|
guard let self, let peerId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if canResetWallpaper && chatTheme != nil {
|
if canResetWallpaper && chatTheme != nil {
|
||||||
let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: false).startStandalone()
|
let _ = context.engine.themes.setChatWallpaper(peerId: peerId, wallpaper: nil, forBoth: true).startStandalone()
|
||||||
}
|
}
|
||||||
strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme ?? .emoticon(""), nil)))
|
strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((chatTheme ?? .emoticon(""), nil)))
|
||||||
let _ = context.engine.themes.setChatTheme(peerId: peerId, chatTheme: chatTheme ?? .emoticon("")).startStandalone(completed: { [weak self] in
|
let _ = context.engine.themes.setChatTheme(peerId: peerId, chatTheme: chatTheme ?? .emoticon("")).startStandalone(completed: { [weak self] in
|
||||||
if let strongSelf = self {
|
if let self {
|
||||||
strongSelf.chatThemeAndDarkAppearancePreviewPromise.set(.single((nil, nil)))
|
self.chatThemeAndDarkAppearancePreviewPromise.set(.single((nil, nil)))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -141,6 +141,7 @@ import SuggestedPostApproveAlert
|
|||||||
import AVFoundation
|
import AVFoundation
|
||||||
import BalanceNeededScreen
|
import BalanceNeededScreen
|
||||||
import FaceScanScreen
|
import FaceScanScreen
|
||||||
|
import ChatThemeScreen
|
||||||
|
|
||||||
public final class ChatControllerOverlayPresentationData {
|
public final class ChatControllerOverlayPresentationData {
|
||||||
public let expandData: (ASDisplayNode?, () -> Void)
|
public let expandData: (ASDisplayNode?, () -> Void)
|
||||||
@ -1110,8 +1111,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.present(BotReceiptController(context: self.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
self.present(BotReceiptController(context: self.context, messageId: message.id), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
case .setChatTheme:
|
case let .setChatTheme(chatTheme):
|
||||||
self.presentThemeSelection()
|
switch chatTheme {
|
||||||
|
case .emoticon:
|
||||||
|
self.presentThemeSelection()
|
||||||
|
case let .gift(gift, _):
|
||||||
|
if case let .unique(uniqueGift) = gift {
|
||||||
|
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, gift: uniqueGift, shareStory: { [weak self] uniqueGift in
|
||||||
|
Queue.mainQueue().after(0.15) {
|
||||||
|
if let self {
|
||||||
|
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||||
|
self.push(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, openChatTheme: { [weak self] in
|
||||||
|
if let self {
|
||||||
|
Queue.mainQueue().after(0.15) {
|
||||||
|
self.presentThemeSelection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, dismissed: nil)
|
||||||
|
self.push(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
case let .setChatWallpaper(wallpaper, _):
|
case let .setChatWallpaper(wallpaper, _):
|
||||||
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
guard let peer = self.presentationInterfaceState.renderedPeer?.peer else {
|
||||||
@ -5808,10 +5830,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .gift(gift, wallpaper):
|
case .gift:
|
||||||
let _ = gift
|
if let darkAppearancePreview = darkAppearancePreview {
|
||||||
let _ = wallpaper
|
useDarkAppearance = darkAppearancePreview
|
||||||
//TODO:release
|
}
|
||||||
|
if let theme = makePresentationTheme(chatTheme: chatTheme, dark: useDarkAppearance) {
|
||||||
|
theme.forceSync = true
|
||||||
|
presentationData = presentationData.withUpdated(theme: theme).withUpdated(chatWallpaper: theme.chat.defaultWallpaper)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
theme.forceSync = false
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let darkAppearancePreview = darkAppearancePreview {
|
} else if let darkAppearancePreview = darkAppearancePreview {
|
||||||
useDarkAppearance = darkAppearancePreview
|
useDarkAppearance = darkAppearancePreview
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import ComponentFlow
|
|||||||
import ChatEmptyNode
|
import ChatEmptyNode
|
||||||
import SpaceWarpView
|
import SpaceWarpView
|
||||||
import ChatSideTopicsPanel
|
import ChatSideTopicsPanel
|
||||||
|
import ChatThemeScreen
|
||||||
|
|
||||||
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||||
let itemNode: OverlayMediaItemNode
|
let itemNode: OverlayMediaItemNode
|
||||||
@ -3582,7 +3583,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
|||||||
|
|
||||||
let themeUpdated = presentationReadyUpdated || (self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme)
|
let themeUpdated = presentationReadyUpdated || (self.chatPresentationInterfaceState.theme !== chatPresentationInterfaceState.theme)
|
||||||
|
|
||||||
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper, animated: true)
|
self.backgroundNode.update(wallpaper: chatPresentationInterfaceState.chatWallpaper, starGift: chatPresentationInterfaceState.theme.starGift, animated: true)
|
||||||
|
|
||||||
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
self.historyNode.verticalScrollIndicatorColor = UIColor(white: 0.5, alpha: 0.8)
|
||||||
if self.pendingSwitchToChatLocation == nil {
|
if self.pendingSwitchToChatLocation == nil {
|
||||||
|
|||||||
@ -2053,7 +2053,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isSuspiciousPeer = false
|
var isSuspiciousPeer = false
|
||||||
if let cachedUserData = data.cachedData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.flags.contains(.canBlock) {
|
if let cachedUserData = data.cachedData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.flags.contains(.canBlock) || peerStatusSettings.flags.contains(.canReport) {
|
||||||
isSuspiciousPeer = true
|
isSuspiciousPeer = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2309,7 +2309,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
|||||||
}
|
}
|
||||||
|
|
||||||
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: !isSavedMusic || forceUpdateAll)
|
let rawTransition = preparedChatHistoryViewTransition(from: previous, to: processedView, reason: reason, reverse: reverse, chatLocation: chatLocation, source: source, controllerInteraction: controllerInteraction, scrollPosition: updatedScrollPosition, scrollAnimationCurve: scrollAnimationCurve, initialData: initialData?.initialData, keyboardButtonsMessage: keyboardButtonsMessage, cachedData: initialData?.cachedData, cachedDataMessages: initialData?.cachedDataMessages, readStateData: initialData?.readStateData, flashIndicators: flashIndicators, updatedMessageSelection: previousSelectedMessages != selectedMessages, messageTransitionNode: messageTransitionNode(), allUpdated: !isSavedMusic || forceUpdateAll)
|
||||||
var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition)
|
var mappedTransition = mappedChatHistoryViewListTransition(context: context, chatLocation: chatLocation, associatedData: associatedData, controllerInteraction: controllerInteraction, mode: mode, lastHeaderId: lastHeaderId, isSavedMusic: isSavedMusic, canReorder: processedView.filteredEntries.count > 1 && canReorder, animateFromPreviousFilter: resetScrolling, transition: rawTransition)
|
||||||
|
|
||||||
if disableAnimations {
|
if disableAnimations {
|
||||||
mappedTransition.options.remove(.AnimateInsertion)
|
mappedTransition.options.remove(.AnimateInsertion)
|
||||||
|
|||||||
@ -1481,7 +1481,7 @@ func openResolvedUrlImpl(
|
|||||||
navigationController?.pushViewController(controller)
|
navigationController?.pushViewController(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, dismissed: {
|
}, openChatTheme: nil, dismissed: {
|
||||||
dismissedImpl?()
|
dismissedImpl?()
|
||||||
})
|
})
|
||||||
navigationController?.pushViewController(controller)
|
navigationController?.pushViewController(controller)
|
||||||
|
|||||||
@ -57,6 +57,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
private var savedIdsPromise = Promise<Set<Int64>?>()
|
private var savedIdsPromise = Promise<Set<Int64>?>()
|
||||||
private var savedIds: Set<Int64>?
|
private var savedIds: Set<Int64>?
|
||||||
|
|
||||||
|
private var copyProtectionEnabled = false
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
chatLocation: ChatLocation,
|
chatLocation: ChatLocation,
|
||||||
@ -275,13 +277,17 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.historyNode.endedInteractiveDragging = { [weak self] _ in
|
self.historyNode.endedInteractiveDragging = { [weak self] _ in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch strongSelf.historyNode.visibleContentOffset() {
|
switch self.historyNode.visibleContentOffset() {
|
||||||
case let .known(value):
|
case let .known(value):
|
||||||
if value <= -10.0 {
|
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
||||||
strongSelf.requestDismiss()
|
|
||||||
|
} else {
|
||||||
|
if value <= -10.0 {
|
||||||
|
self.requestDismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -388,14 +394,25 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.savedIdsDisposable = (context.engine.peers.savedMusicIds()
|
let copyProtectionEnabled: Signal<Bool, NoError>
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] savedIds in
|
if case let .peer(peerId) = self.chatLocation {
|
||||||
|
copyProtectionEnabled = context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.CopyProtectionEnabled(id: peerId))
|
||||||
|
} else {
|
||||||
|
copyProtectionEnabled = .single(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.savedIdsDisposable = combineLatest(
|
||||||
|
queue: Queue.mainQueue(),
|
||||||
|
context.engine.peers.savedMusicIds(),
|
||||||
|
copyProtectionEnabled
|
||||||
|
).start(next: { [weak self] savedIds, copyProtectionEnabled in
|
||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let isFirstTime = self.savedIds == nil
|
let isFirstTime = self.savedIds == nil
|
||||||
self.savedIds = savedIds
|
self.savedIds = savedIds
|
||||||
self.savedIdsPromise.set(.single(savedIds))
|
self.savedIdsPromise.set(.single(savedIds))
|
||||||
|
self.copyProtectionEnabled = copyProtectionEnabled
|
||||||
|
|
||||||
let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : .animated(duration: 0.5, curve: .spring)
|
let transition: ContainedViewLayoutTransition = isFirstTime ? .immediate : .animated(duration: 0.5, curve: .spring)
|
||||||
self.updateFloatingHeaderOffset(offset: self.floatingHeaderOffset ?? 0.0, transition: transition)
|
self.updateFloatingHeaderOffset(offset: self.floatingHeaderOffset ?? 0.0, transition: transition)
|
||||||
@ -439,11 +456,11 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
panRecognizer.delaysTouchesBegan = false
|
panRecognizer.delaysTouchesBegan = false
|
||||||
panRecognizer.cancelsTouchesInView = true
|
panRecognizer.cancelsTouchesInView = true
|
||||||
panRecognizer.shouldBegin = { [weak self] point in
|
panRecognizer.shouldBegin = { [weak self] point in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if strongSelf.controlsNode.bounds.contains(strongSelf.view.convert(point, to: strongSelf.controlsNode.view)) {
|
if self.controlsNode.bounds.contains(self.view.convert(point, to: self.controlsNode.view)) {
|
||||||
if strongSelf.controlsNode.frame.maxY <= strongSelf.historyNode.frame.minY {
|
if self.controlsNode.frame.maxY <= self.historyNode.frame.minY {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -494,8 +511,13 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
return .single(true)
|
return .single(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
self.historyNode.useMainQueueTransactions = false
|
|
||||||
self.historyNode.autoScrollWhenReordering = false
|
self.historyNode.autoScrollWhenReordering = false
|
||||||
|
self.historyNode.didEndScrollingWithOverscroll = { [weak self] in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.requestDismiss()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -557,19 +579,18 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
func addToSavedMusic(file: FileMediaReference) {
|
func addToSavedMusic(file: FileMediaReference) {
|
||||||
self.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
var actionText: String? = "View"
|
var actionText: String? = self.presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile_View
|
||||||
if let itemId = self.controlsNode.currentItemId as? PeerMessagesMediaPlaylistItemId, itemId.messageId.namespace == Namespaces.Message.Local && itemId.messageId.peerId == self.context.account.peerId {
|
if let itemId = self.controlsNode.currentItemId as? PeerMessagesMediaPlaylistItemId, itemId.messageId.namespace == Namespaces.Message.Local && itemId.messageId.peerId == self.context.account.peerId {
|
||||||
actionText = nil
|
actionText = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let controller = UndoOverlayController(
|
let controller = UndoOverlayController(
|
||||||
presentationData: self.presentationData,
|
presentationData: self.presentationData,
|
||||||
content: .universalImage(
|
content: .universalImage(
|
||||||
image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!,
|
image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!,
|
||||||
size: nil,
|
size: nil,
|
||||||
title: nil,
|
title: nil,
|
||||||
text: "Audio added to your profile.",
|
text: self.presentationData.strings.MediaPlayer_SavedMusic_AddedToProfile,
|
||||||
customUndoText: actionText,
|
customUndoText: actionText,
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
),
|
),
|
||||||
@ -607,14 +628,13 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
func removeFromSavedMusic(file: FileMediaReference) {
|
func removeFromSavedMusic(file: FileMediaReference) {
|
||||||
self.dismissAllTooltips()
|
self.dismissAllTooltips()
|
||||||
|
|
||||||
//TODO:localize
|
|
||||||
let controller = UndoOverlayController(
|
let controller = UndoOverlayController(
|
||||||
presentationData: self.presentationData,
|
presentationData: self.presentationData,
|
||||||
content: .universalImage(
|
content: .universalImage(
|
||||||
image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!,
|
image: generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SavedMusic"), color: .white)!,
|
||||||
size: nil,
|
size: nil,
|
||||||
title: nil,
|
title: nil,
|
||||||
text: "Audio removed from your profile.",
|
text: self.presentationData.strings.MediaPlayer_SavedMusic_RemovedFromProfile,
|
||||||
customUndoText: nil,
|
customUndoText: nil,
|
||||||
timeout: 3.0
|
timeout: 3.0
|
||||||
),
|
),
|
||||||
@ -638,6 +658,12 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var isSaved: Bool? {
|
private var isSaved: Bool? {
|
||||||
|
if self .copyProtectionEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
guard let fileReference = self.controlsNode.currentFileReference else {
|
guard let fileReference = self.controlsNode.currentFileReference else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -672,7 +698,6 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
||||||
|
|
||||||
var itemOffsetInsets = insets
|
var itemOffsetInsets = insets
|
||||||
|
|
||||||
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
||||||
itemOffsetInsets.top = 0.0
|
itemOffsetInsets.top = 0.0
|
||||||
itemOffsetInsets.bottom = 0.0
|
itemOffsetInsets.bottom = 0.0
|
||||||
@ -981,9 +1006,16 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
|
|
||||||
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
insets.top = max(0.0, listNodeSize.height - floor(56.0 * 3.5))
|
||||||
|
|
||||||
|
var itemOffsetInsets = insets
|
||||||
|
if let playlistLocation = self.playlistLocation as? PeerMessagesPlaylistLocation, case let .savedMusic(_, _, canReorder) = playlistLocation, canReorder {
|
||||||
|
itemOffsetInsets.top = 0.0
|
||||||
|
itemOffsetInsets.bottom = 0.0
|
||||||
|
insets = itemOffsetInsets
|
||||||
|
}
|
||||||
|
|
||||||
self.historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize)
|
self.historyNode.frame = CGRect(origin: CGPoint(x: 0.0, y: listTopInset), size: listNodeSize)
|
||||||
|
|
||||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, duration: 0.0, curve: .Default(duration: nil))
|
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: listNodeSize, insets: insets, itemOffsetInsets: itemOffsetInsets, duration: 0.0, curve: .Default(duration: nil))
|
||||||
self.historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets)
|
self.historyNode.updateLayout(transition: .immediate, updateSizeAndInsets: updateSizeAndInsets)
|
||||||
|
|
||||||
self.historyNode.recursivelyEnsureDisplaySynchronously(true)
|
self.historyNode.recursivelyEnsureDisplaySynchronously(true)
|
||||||
@ -1010,10 +1042,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
//TODO:localize
|
|
||||||
if canSaveToProfile || canSaveToSavedMessages {
|
if canSaveToProfile || canSaveToSavedMessages {
|
||||||
items.append(
|
items.append(
|
||||||
.action(ContextMenuActionItem(text: "Save to...", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/DownloadTone"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, _ in
|
||||||
if let self {
|
if let self {
|
||||||
var subActions: [ContextMenuItem] = []
|
var subActions: [ContextMenuItem] = []
|
||||||
subActions.append(
|
subActions.append(
|
||||||
@ -1025,7 +1056,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
|
|
||||||
if canSaveToProfile {
|
if canSaveToProfile {
|
||||||
subActions.append(
|
subActions.append(
|
||||||
.action(ContextMenuActionItem(text: "Profile", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Profile, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
if let self {
|
if let self {
|
||||||
@ -1037,7 +1068,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
|
|
||||||
if canSaveToSavedMessages {
|
if canSaveToSavedMessages {
|
||||||
subActions.append(
|
subActions.append(
|
||||||
.action(ContextMenuActionItem(text: "Saved Messages", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_SavedMessages, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Fave"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
if let self {
|
if let self {
|
||||||
@ -1048,7 +1079,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
|
|
||||||
subActions.append(
|
subActions.append(
|
||||||
.action(ContextMenuActionItem(text: "Files", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Files, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
|
||||||
if let self {
|
if let self {
|
||||||
@ -1072,7 +1103,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
|
|
||||||
let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
let noAction: ((ContextMenuActionItem.Action) -> Void)? = nil
|
||||||
subActions.append(
|
subActions.append(
|
||||||
.action(ContextMenuActionItem(text: "Choose where you want this audio to be saved.", textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: noAction))
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveTo_Info, textLayout: .multiline, textFont: .small, icon: { _ in return nil }, action: noAction))
|
||||||
)
|
)
|
||||||
|
|
||||||
c?.pushItems(items: .single(ContextController.Items(content: .list(subActions))))
|
c?.pushItems(items: .single(ContextController.Items(content: .list(subActions))))
|
||||||
@ -1080,7 +1111,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
items.append(.action(ContextMenuActionItem(text: "Save to Files", icon: { theme in
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_SaveToFiles, icon: { theme in
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
||||||
}, action: { [weak self] _, f in
|
}, action: { [weak self] _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
@ -1112,7 +1143,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
addedSeparator = true
|
addedSeparator = true
|
||||||
}
|
}
|
||||||
items.append(
|
items.append(
|
||||||
.action(ContextMenuActionItem(text: "Show in Chat", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
.action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_ShowInChat, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/GoToMessage"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
f(.dismissWithoutContent)
|
f(.dismissWithoutContent)
|
||||||
|
|
||||||
guard let self else {
|
guard let self else {
|
||||||
@ -1125,7 +1156,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// items.append(
|
// items.append(
|
||||||
// .action(ContextMenuActionItem(text: "Forward", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
// .action(ContextMenuActionItem(text: presentationData.strings.MediaPlayer_ContextMenu_Forward, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Forward"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||||
// f(.default)
|
// f(.default)
|
||||||
//
|
//
|
||||||
// if let _ = self {
|
// if let _ = self {
|
||||||
@ -1162,9 +1193,9 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
|||||||
items.append(.separator)
|
items.append(.separator)
|
||||||
addedSeparator = true
|
addedSeparator = true
|
||||||
}
|
}
|
||||||
var actionTitle = "Delete"
|
var actionTitle = presentationData.strings.MediaPlayer_ContextMenu_Delete
|
||||||
if case .custom = self.source {
|
if case .custom = self.source {
|
||||||
actionTitle = "Remove"
|
actionTitle = presentationData.strings.MediaPlayer_ContextMenu_Remove
|
||||||
}
|
}
|
||||||
items.append(
|
items.append(
|
||||||
.action(ContextMenuActionItem(text: actionTitle, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in
|
.action(ContextMenuActionItem(text: actionTitle, textColor: .destructive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, f in
|
||||||
|
|||||||
@ -1026,13 +1026,12 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
self.separatorNode.isHidden = hasSectionHeader
|
self.separatorNode.isHidden = hasSectionHeader
|
||||||
|
|
||||||
if hasSectionHeader {
|
if hasSectionHeader {
|
||||||
//TODO:localize
|
|
||||||
let sideInset: CGFloat = 16.0
|
let sideInset: CGFloat = 16.0
|
||||||
var sectionTitle = "AUDIO IN THIS CHAT"
|
var sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_ThisChat
|
||||||
if let peerName = self.peerName {
|
if let peerName = self.peerName {
|
||||||
sectionTitle = "\(peerName.uppercased())'S PLAYLIST"
|
sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusic(peerName.uppercased()).string
|
||||||
} else if case .custom = self.source {
|
} else if case .custom = self.source {
|
||||||
sectionTitle = "YOUR PLAYLIST"
|
sectionTitle = self.presentationData.strings.MediaPlayer_Playlist_SavedMusicYou
|
||||||
}
|
}
|
||||||
let sectionTitleSize = self.sectionTitle.update(
|
let sectionTitleSize = self.sectionTitle.update(
|
||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
@ -1096,7 +1095,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
return (TelegramTextAttributes.URL, contents)
|
return (TelegramTextAttributes.URL, contents)
|
||||||
})
|
})
|
||||||
|
|
||||||
let attributedString = parseMarkdownIntoAttributedString("This audio is visible on your profile. [Remove >]()", attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
let attributedString = parseMarkdownIntoAttributedString(self.presentationData.strings.MediaPlayer_SavedMusic_RemoveFromProfile, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||||
if let range = attributedString.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
if let range = attributedString.string.range(of: ">"), let chevronImage = self.cachedChevronImage?.0 {
|
||||||
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string))
|
||||||
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
attributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: attributedString.string))
|
||||||
@ -1125,7 +1124,6 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
))
|
))
|
||||||
profileAudioOffset = 18.0
|
profileAudioOffset = 18.0
|
||||||
} else {
|
} else {
|
||||||
//TODO:localize
|
|
||||||
profileAudioComponent = AnyComponent(ButtonComponent(
|
profileAudioComponent = AnyComponent(ButtonComponent(
|
||||||
background: ButtonComponent.Background(
|
background: ButtonComponent.Background(
|
||||||
color: self.presentationData.theme.list.itemCheckColors.fillColor,
|
color: self.presentationData.theme.list.itemCheckColors.fillColor,
|
||||||
@ -1139,7 +1137,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
BundleIconComponent(name: "Peer Info/SaveMusic", tintColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
BundleIconComponent(name: "Peer Info/SaveMusic", tintColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)
|
||||||
)),
|
)),
|
||||||
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
AnyComponentWithIdentity(id: "label", component: AnyComponent(
|
||||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Add to Profile", font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)))
|
MultilineTextComponent(text: .plain(NSAttributedString(string: self.presentationData.strings.MediaPlayer_SavedMusic_AddToProfile, font: Font.semibold(17.0), textColor: self.presentationData.theme.list.itemCheckColors.foregroundColor)))
|
||||||
))
|
))
|
||||||
], spacing: 8.0)
|
], spacing: 8.0)
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -3768,8 +3768,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
return GiftViewScreen(context: context, subject: .message(message), shareStory: shareStory)
|
return GiftViewScreen(context: context, subject: .message(message), shareStory: shareStory)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, dismissed: (() -> Void)?) -> ViewController {
|
public func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController {
|
||||||
let controller = GiftViewScreen(context: context, subject: .uniqueGift(gift, nil), shareStory: shareStory)
|
let controller = GiftViewScreen(context: context, subject: .uniqueGift(gift, nil), shareStory: shareStory, openChatTheme: openChatTheme)
|
||||||
controller.disposed = {
|
controller.disposed = {
|
||||||
dismissed?()
|
dismissed?()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -8,6 +8,7 @@ import TelegramCore
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import WallpaperResources
|
import WallpaperResources
|
||||||
|
import StickerResources
|
||||||
import FastBlur
|
import FastBlur
|
||||||
import Svg
|
import Svg
|
||||||
import GZip
|
import GZip
|
||||||
@ -85,6 +86,7 @@ public protocol WallpaperBackgroundNode: ASDisplayNode {
|
|||||||
var rotation: CGFloat { get set }
|
var rotation: CGFloat { get set }
|
||||||
|
|
||||||
func update(wallpaper: TelegramWallpaper, animated: Bool)
|
func update(wallpaper: TelegramWallpaper, animated: Bool)
|
||||||
|
func update(wallpaper: TelegramWallpaper, starGift: StarGift?, animated: Bool)
|
||||||
func _internalUpdateIsSettingUpWallpaper()
|
func _internalUpdateIsSettingUpWallpaper()
|
||||||
func updateLayout(size: CGSize, displayMode: WallpaperDisplayMode, transition: ContainedViewLayoutTransition)
|
func updateLayout(size: CGSize, displayMode: WallpaperDisplayMode, transition: ContainedViewLayoutTransition)
|
||||||
func updateIsLooping(_ isLooping: Bool)
|
func updateIsLooping(_ isLooping: Bool)
|
||||||
@ -758,11 +760,20 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
|
|
||||||
private var validLayout: (CGSize, WallpaperDisplayMode)?
|
private var validLayout: (CGSize, WallpaperDisplayMode)?
|
||||||
private var wallpaper: TelegramWallpaper?
|
private var wallpaper: TelegramWallpaper?
|
||||||
|
private var starGift: StarGift?
|
||||||
|
private var modelRectIndex: Int32?
|
||||||
|
|
||||||
|
private var modelStickerNode: DefaultAnimatedStickerNodeImpl?
|
||||||
|
|
||||||
private var isSettingUpWallpaper: Bool = false
|
private var isSettingUpWallpaper: Bool = false
|
||||||
|
|
||||||
private struct CachedValidPatternImage {
|
private struct CachedValidPatternImage {
|
||||||
let generate: (TransformImageArguments) -> DrawingContext?
|
let generate: (TransformImageArguments) -> DrawingContext?
|
||||||
let generated: ValidPatternGeneratedImage
|
let generated: ValidPatternGeneratedImage
|
||||||
|
let rects: [WallpaperGiftPatternRect]
|
||||||
|
let starGift: StarGift?
|
||||||
|
let symbolImage: UIImage?
|
||||||
|
let modelRectIndex: Int32?
|
||||||
let image: UIImage
|
let image: UIImage
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,6 +782,10 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
private struct ValidPatternImage {
|
private struct ValidPatternImage {
|
||||||
let wallpaper: TelegramWallpaper
|
let wallpaper: TelegramWallpaper
|
||||||
let invertPattern: Bool
|
let invertPattern: Bool
|
||||||
|
let rects: [WallpaperGiftPatternRect]
|
||||||
|
let starGift: StarGift?
|
||||||
|
let symbolImage: UIImage?
|
||||||
|
let modelRectIndex: Int32?
|
||||||
let generate: (TransformImageArguments) -> DrawingContext?
|
let generate: (TransformImageArguments) -> DrawingContext?
|
||||||
}
|
}
|
||||||
private var validPatternImage: ValidPatternImage?
|
private var validPatternImage: ValidPatternImage?
|
||||||
@ -781,10 +796,38 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
let patternColor: UInt32
|
let patternColor: UInt32
|
||||||
let backgroundColor: UInt32
|
let backgroundColor: UInt32
|
||||||
let invertPattern: Bool
|
let invertPattern: Bool
|
||||||
|
let starGift: StarGift?
|
||||||
|
let modelRectIndex: Int32?
|
||||||
|
|
||||||
|
public static func ==(lhs: ValidPatternGeneratedImage, rhs: ValidPatternGeneratedImage) -> Bool {
|
||||||
|
if lhs.wallpaper != rhs.wallpaper {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.size != rhs.size {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.patternColor != rhs.patternColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.invertPattern != rhs.invertPattern {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.starGift?.slug != rhs.starGift?.slug {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.modelRectIndex != rhs.modelRectIndex {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private var validPatternGeneratedImage: ValidPatternGeneratedImage?
|
private var validPatternGeneratedImage: ValidPatternGeneratedImage?
|
||||||
|
|
||||||
private let patternImageDisposable = MetaDisposable()
|
private let patternImageDisposable = MetaDisposable()
|
||||||
|
private let symbolImageDisposable = MetaDisposable()
|
||||||
|
|
||||||
private var bubbleTheme: PresentationTheme?
|
private var bubbleTheme: PresentationTheme?
|
||||||
private var bubbleCorners: PresentationChatBubbleCorners?
|
private var bubbleCorners: PresentationChatBubbleCorners?
|
||||||
@ -930,11 +973,26 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func update(wallpaper: TelegramWallpaper, animated: Bool) {
|
public func update(wallpaper: TelegramWallpaper, animated: Bool) {
|
||||||
if self.wallpaper == wallpaper {
|
self.update(wallpaper: wallpaper, starGift: nil, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(wallpaper: TelegramWallpaper, starGift: StarGift?, animated: Bool) {
|
||||||
|
if self.wallpaper == wallpaper && self.starGift == starGift {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let previousWallpaper = self.wallpaper
|
let previousWallpaper = self.wallpaper
|
||||||
|
let previousStarGift = self.starGift
|
||||||
|
|
||||||
self.wallpaper = wallpaper
|
self.wallpaper = wallpaper
|
||||||
|
self.starGift = starGift
|
||||||
|
|
||||||
|
if previousWallpaper != wallpaper || previousStarGift?.slug != starGift?.slug {
|
||||||
|
if let _ = starGift {
|
||||||
|
self.modelRectIndex = Int32.random(in: 0 ..< 10)
|
||||||
|
} else {
|
||||||
|
self.modelRectIndex = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let _ = previousWallpaper, animated {
|
if let _ = previousWallpaper, animated {
|
||||||
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
|
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
|
||||||
@ -1132,6 +1190,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
self.patternImageDisposable.set(nil)
|
self.patternImageDisposable.set(nil)
|
||||||
|
self.symbolImageDisposable.set(nil)
|
||||||
self.validPatternImage = nil
|
self.validPatternImage = nil
|
||||||
self.patternImageLayer.isHidden = true
|
self.patternImageLayer.isHidden = true
|
||||||
self.patternImageLayer.fillWithColorUntilLoaded = nil
|
self.patternImageLayer.fillWithColorUntilLoaded = nil
|
||||||
@ -1147,6 +1206,9 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let starGift = self.starGift
|
||||||
|
let modelRectIndex = self.modelRectIndex
|
||||||
|
|
||||||
var invertPattern: Bool = false
|
var invertPattern: Bool = false
|
||||||
var patternIsLight: Bool = false
|
var patternIsLight: Bool = false
|
||||||
|
|
||||||
@ -1170,12 +1232,19 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let previousStarGift = self.validPatternImage?.starGift, !updated {
|
||||||
|
updated = true
|
||||||
|
if previousStarGift.slug == starGift?.slug {
|
||||||
|
updated = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if updated {
|
if updated {
|
||||||
self.validPatternGeneratedImage = nil
|
self.validPatternGeneratedImage = nil
|
||||||
self.validPatternImage = nil
|
self.validPatternImage = nil
|
||||||
|
|
||||||
if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper && cachedValidPatternImage.generated.invertPattern == invertPattern {
|
if let cachedValidPatternImage = WallpaperBackgroundNodeImpl.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper && cachedValidPatternImage.generated.invertPattern == invertPattern && cachedValidPatternImage.starGift == starGift && cachedValidPatternImage.modelRectIndex == modelRectIndex {
|
||||||
self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, invertPattern: invertPattern, generate: cachedValidPatternImage.generate)
|
self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, invertPattern: invertPattern, rects: cachedValidPatternImage.rects, starGift: cachedValidPatternImage.starGift, symbolImage: cachedValidPatternImage.symbolImage, modelRectIndex: cachedValidPatternImage.modelRectIndex, generate: cachedValidPatternImage.generate)
|
||||||
} else {
|
} else {
|
||||||
func reference(for resource: EngineMediaResource, media: EngineMedia) -> MediaResourceReference {
|
func reference(for resource: EngineMediaResource, media: EngineMedia) -> MediaResourceReference {
|
||||||
return .wallpaper(wallpaper: .slug(file.slug), resource: resource._asResource())
|
return .wallpaper(wallpaper: .slug(file.slug), resource: resource._asResource())
|
||||||
@ -1189,37 +1258,33 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: reference(for: EngineMediaResource(file.file.resource), media: EngineMedia(file.file))))
|
convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: reference(for: EngineMediaResource(file.file.resource), media: EngineMedia(file.file))))
|
||||||
|
|
||||||
let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
|
let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
|
||||||
self.patternImageDisposable.set((signal
|
var symbolImage: Signal<UIImage?, NoError> = .single(nil)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] generator in
|
if let starGift = self.starGift, case let .unique(uniqueGift) = starGift {
|
||||||
guard let strongSelf = self else {
|
for attribute in uniqueGift.attributes {
|
||||||
|
if case let .pattern(_, file, _) = attribute, let dimensions = file.dimensions {
|
||||||
|
let size = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0))
|
||||||
|
symbolImage = chatMessageAnimatedSticker(postbox: self.context.account.postbox, userLocation: .other, file: file, small: false, size: size)
|
||||||
|
|> map { generator -> UIImage? in
|
||||||
|
return generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero))?.generateImage()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.patternImageDisposable.set(combineLatest(queue: Queue.mainQueue(), signal, symbolImage).start(next: { [weak self] generator, symbolImage in
|
||||||
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if let (generator, rects) = generator {
|
||||||
if let generator = generator {
|
self.validPatternImage = ValidPatternImage(wallpaper: wallpaper, invertPattern: invertPattern, rects: rects, starGift: starGift, symbolImage: symbolImage, modelRectIndex: modelRectIndex, generate: generator)
|
||||||
/*generator = { arguments in
|
self.validPatternGeneratedImage = nil
|
||||||
let scale = arguments.scale ?? UIScreenScale
|
if let (size, displayMode) = self.validLayout {
|
||||||
let context = DrawingContext(size: arguments.drawingSize, scale: scale, clear: true)
|
self.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: .immediate)
|
||||||
|
|
||||||
context.withFlippedContext { c in
|
|
||||||
if let path = getAppBundle().path(forResource: "PATTERN_static", ofType: "svg"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
|
|
||||||
if let image = drawSvgImage(data, CGSize(width: arguments.drawingSize.width * scale, height: arguments.drawingSize.height * scale), .clear, .black, false) {
|
|
||||||
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return context
|
|
||||||
}*/
|
|
||||||
|
|
||||||
strongSelf.validPatternImage = ValidPatternImage(wallpaper: wallpaper, invertPattern: invertPattern, generate: generator)
|
|
||||||
strongSelf.validPatternGeneratedImage = nil
|
|
||||||
if let (size, displayMode) = strongSelf.validLayout {
|
|
||||||
strongSelf.loadPatternForSizeIfNeeded(size: size, displayMode: displayMode, transition: .immediate)
|
|
||||||
} else {
|
} else {
|
||||||
strongSelf._isReady.set(true)
|
self._isReady.set(true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strongSelf._isReady.set(true)
|
self._isReady.set(true)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@ -1244,7 +1309,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
self.patternImageLayer.backgroundColor = nil
|
self.patternImageLayer.backgroundColor = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb, invertPattern: invertPattern)
|
let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb, invertPattern: invertPattern, starGift: starGift, modelRectIndex: modelRectIndex)
|
||||||
|
|
||||||
if self.validPatternGeneratedImage != updatedGeneratedImage {
|
if self.validPatternGeneratedImage != updatedGeneratedImage {
|
||||||
self.validPatternGeneratedImage = updatedGeneratedImage
|
self.validPatternGeneratedImage = updatedGeneratedImage
|
||||||
@ -1256,7 +1321,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
self.patternImageLayer.suspendCompositionUpdates = false
|
self.patternImageLayer.suspendCompositionUpdates = false
|
||||||
self.patternImageLayer.updateCompositionIfNeeded()
|
self.patternImageLayer.updateCompositionIfNeeded()
|
||||||
} else {
|
} else {
|
||||||
let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false, displayMode: displayMode.argumentsDisplayMode), scale: min(2.0, UIScreenScale))
|
let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false, displayMode: displayMode.argumentsDisplayMode, symbolImage: generateTintedImage(image: validPatternImage.symbolImage, color: .white), modelRectIndex: self.modelRectIndex), scale: min(2.0, UIScreenScale))
|
||||||
if self.useSharedAnimationPhase || self.patternImageLayer.contents == nil {
|
if self.useSharedAnimationPhase || self.patternImageLayer.contents == nil {
|
||||||
if let drawingContext = validPatternImage.generate(patternArguments) {
|
if let drawingContext = validPatternImage.generate(patternArguments) {
|
||||||
if let image = drawingContext.generateImage() {
|
if let image = drawingContext.generateImage() {
|
||||||
@ -1267,7 +1332,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
self.patternImageLayer.updateCompositionIfNeeded()
|
self.patternImageLayer.updateCompositionIfNeeded()
|
||||||
|
|
||||||
if self.useSharedAnimationPhase {
|
if self.useSharedAnimationPhase {
|
||||||
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
|
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, rects: validPatternImage.rects, starGift: validPatternImage.starGift, symbolImage: validPatternImage.symbolImage, modelRectIndex: validPatternImage.modelRectIndex, image: image)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.updatePatternPresentation()
|
self.updatePatternPresentation()
|
||||||
@ -1288,7 +1353,7 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
strongSelf.updatePatternPresentation()
|
strongSelf.updatePatternPresentation()
|
||||||
|
|
||||||
if let image = image, strongSelf.useSharedAnimationPhase {
|
if let image = image, strongSelf.useSharedAnimationPhase {
|
||||||
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image)
|
WallpaperBackgroundNodeImpl.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, rects: validPatternImage.rects, starGift: validPatternImage.starGift, symbolImage: validPatternImage.symbolImage, modelRectIndex: validPatternImage.modelRectIndex, image: image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1307,6 +1372,68 @@ public final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgrou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var modelFile: TelegramMediaFile?
|
||||||
|
if let validPatternImage = self.validPatternImage, !validPatternImage.rects.isEmpty, let starGift = validPatternImage.starGift {
|
||||||
|
if case let .unique(uniqueGift) = starGift {
|
||||||
|
for attribute in uniqueGift.attributes {
|
||||||
|
if case let .model(_, file, _) = attribute {
|
||||||
|
modelFile = file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let validPatternImage = self.validPatternImage, !validPatternImage.rects.isEmpty, var modelRectIndex = self.modelRectIndex, let modelFile {
|
||||||
|
let filteredRects = validPatternImage.rects.filter { $0.center.y > $0.containerSize.height * 0.1 && $0.center.y < $0.containerSize.height * 0.9 }
|
||||||
|
modelRectIndex = modelRectIndex % Int32(filteredRects.count);
|
||||||
|
|
||||||
|
let rect = filteredRects[Int(modelRectIndex)]
|
||||||
|
|
||||||
|
let modelStickerNode: DefaultAnimatedStickerNodeImpl
|
||||||
|
if let current = self.modelStickerNode {
|
||||||
|
modelStickerNode = current
|
||||||
|
} else {
|
||||||
|
modelStickerNode = DefaultAnimatedStickerNodeImpl()
|
||||||
|
modelStickerNode.setup(source: AnimatedStickerResourceSource(account: self.context.account, resource: modelFile.resource, isVideo: false), width: 96, height: 96, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||||
|
modelStickerNode.visibility = true
|
||||||
|
self.modelStickerNode = modelStickerNode
|
||||||
|
self.addSubnode(modelStickerNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetSize: CGSize = self.bounds.size
|
||||||
|
let containerSize: CGSize = rect.containerSize
|
||||||
|
let useAspectFit: Bool = false
|
||||||
|
|
||||||
|
let renderScale: CGFloat = useAspectFit
|
||||||
|
? min(targetSize.width / containerSize.width, targetSize.height / containerSize.height)
|
||||||
|
: max(targetSize.width / containerSize.width, targetSize.height / containerSize.height)
|
||||||
|
|
||||||
|
let drawingSize = CGSize(width: containerSize.width * renderScale, height: containerSize.height * renderScale)
|
||||||
|
|
||||||
|
let offsetX = (targetSize.width - drawingSize.width) * 0.5
|
||||||
|
let offsetY = (targetSize.height - drawingSize.height) * 0.5
|
||||||
|
|
||||||
|
let onScreenCenter = CGPoint(x: offsetX + rect.center.x * renderScale, y: offsetY + rect.center.y * renderScale)
|
||||||
|
|
||||||
|
let side = rect.side * rect.scale * renderScale
|
||||||
|
modelStickerNode.bounds = CGRect(origin: .zero, size: CGSize(width: side, height: side))
|
||||||
|
modelStickerNode.position = onScreenCenter
|
||||||
|
modelStickerNode.updateLayout(size: modelStickerNode.bounds.size)
|
||||||
|
modelStickerNode.alpha = 0.5
|
||||||
|
|
||||||
|
modelStickerNode.layer.transform = CATransform3DMakeRotation(rect.rotation, 0, 0, 1)
|
||||||
|
} else {
|
||||||
|
if let modelStickerNode = self.modelStickerNode {
|
||||||
|
self.modelStickerNode = nil
|
||||||
|
if transition.isAnimated {
|
||||||
|
modelStickerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { [weak modelStickerNode] _ in
|
||||||
|
modelStickerNode?.removeFromSupernode()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
modelStickerNode.removeFromSupernode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: size))
|
transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: size))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1559,3 +1686,14 @@ private protocol WallpaperComponentView: AnyObject {
|
|||||||
public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode {
|
public func createWallpaperBackgroundNode(context: AccountContext, forChatDisplay: Bool, useSharedAnimationPhase: Bool = false) -> WallpaperBackgroundNode {
|
||||||
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
return WallpaperBackgroundNodeImpl(context: context, useSharedAnimationPhase: useSharedAnimationPhase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension StarGift {
|
||||||
|
var slug: String? {
|
||||||
|
switch self {
|
||||||
|
case let .unique(uniqueGift):
|
||||||
|
return uniqueGift.slug
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -353,14 +353,18 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments {
|
|||||||
let customPatternColor: UIColor?
|
let customPatternColor: UIColor?
|
||||||
let bakePatternAlpha: CGFloat
|
let bakePatternAlpha: CGFloat
|
||||||
let displayMode: DisplayMode
|
let displayMode: DisplayMode
|
||||||
|
let symbolImage: UIImage?
|
||||||
|
let modelRectIndex: Int32?
|
||||||
|
|
||||||
public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0, displayMode: DisplayMode = .aspectFill) {
|
public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0, displayMode: DisplayMode = .aspectFill, symbolImage: UIImage? = nil, modelRectIndex: Int32? = nil) {
|
||||||
self.colors = colors
|
self.colors = colors
|
||||||
self.rotation = rotation
|
self.rotation = rotation
|
||||||
self.customPatternColor = customPatternColor
|
self.customPatternColor = customPatternColor
|
||||||
self.preview = preview
|
self.preview = preview
|
||||||
self.bakePatternAlpha = bakePatternAlpha
|
self.bakePatternAlpha = bakePatternAlpha
|
||||||
self.displayMode = displayMode
|
self.displayMode = displayMode
|
||||||
|
self.symbolImage = symbolImage
|
||||||
|
self.modelRectIndex = modelRectIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public func serialized() -> NSArray {
|
public func serialized() -> NSArray {
|
||||||
@ -373,6 +377,9 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments {
|
|||||||
array.add(NSNumber(value: self.preview))
|
array.add(NSNumber(value: self.preview))
|
||||||
array.add(NSNumber(value: Double(self.bakePatternAlpha)))
|
array.add(NSNumber(value: Double(self.bakePatternAlpha)))
|
||||||
array.add(NSNumber(value: self.displayMode.rawValue))
|
array.add(NSNumber(value: self.displayMode.rawValue))
|
||||||
|
if let symbolImage {
|
||||||
|
array.add(symbolImage)
|
||||||
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,18 +477,34 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func patternWallpaperImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> {
|
public struct WallpaperGiftPatternRect: Equatable {
|
||||||
|
public let containerSize: CGSize
|
||||||
|
public let center: CGPoint
|
||||||
|
public let side: CGFloat
|
||||||
|
public let scale: CGFloat
|
||||||
|
public let rotation: CGFloat
|
||||||
|
|
||||||
|
fileprivate init(containerSize: CGSize, rect: GiftPatternRect) {
|
||||||
|
self.containerSize = containerSize
|
||||||
|
self.center = rect.center
|
||||||
|
self.side = rect.side
|
||||||
|
self.scale = rect.scale
|
||||||
|
self.rotation = rect.rotation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func patternWallpaperImage(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false, forcePrepared: Bool = false) -> Signal<(generator: (TransformImageArguments) -> DrawingContext?, rects: [WallpaperGiftPatternRect])?, NoError> {
|
||||||
return patternWallpaperDatas(account: account, accountManager: accountManager, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize)
|
return patternWallpaperDatas(account: account, accountManager: accountManager, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize)
|
||||||
|> mapToSignal { fullSizeData, fullSizeComplete in
|
|> mapToSignal { fullSizeData, fullSizeComplete in
|
||||||
if !autoFetchFullSize || fullSizeComplete {
|
if !autoFetchFullSize || fullSizeComplete {
|
||||||
return patternWallpaperImageInternal(fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode)
|
return patternWallpaperImageInternal(fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode, forcePrepared: forcePrepared)
|
||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> {
|
private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode, forcePrepared: Bool = false) -> Signal<(generator: (TransformImageArguments) -> DrawingContext?, rects: [WallpaperGiftPatternRect])?, NoError> {
|
||||||
var prominent = false
|
var prominent = false
|
||||||
if case .thumbnail = mode {
|
if case .thumbnail = mode {
|
||||||
prominent = true
|
prominent = true
|
||||||
@ -491,7 +514,11 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete
|
|||||||
|
|
||||||
return .single((fullSizeData, fullSizeComplete))
|
return .single((fullSizeData, fullSizeComplete))
|
||||||
|> map { fullSizeData, fullSizeComplete in
|
|> map { fullSizeData, fullSizeComplete in
|
||||||
return { arguments in
|
var rects: [WallpaperGiftPatternRect] = []
|
||||||
|
if let fullSizeData, let patternData = getGiftPatternData(fullSizeData) {
|
||||||
|
rects = patternData.rects.map { WallpaperGiftPatternRect(containerSize: patternData.size, rect: $0) }
|
||||||
|
}
|
||||||
|
return ({ arguments in
|
||||||
var scale = scale
|
var scale = scale
|
||||||
if scale.isZero {
|
if scale.isZero {
|
||||||
scale = arguments.scale ?? UIScreenScale
|
scale = arguments.scale ?? UIScreenScale
|
||||||
@ -561,7 +588,7 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete
|
|||||||
var image: UIImage?
|
var image: UIImage?
|
||||||
if let fullSizeData = fullSizeData {
|
if let fullSizeData = fullSizeData {
|
||||||
if mode == .screen {
|
if mode == .screen {
|
||||||
image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0, displayMode == .aspectFit)
|
image = renderPreparedImageWithSymbol(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0, displayMode == .aspectFit, customArguments.symbolImage, customArguments.modelRectIndex ?? -1)
|
||||||
} else {
|
} else {
|
||||||
image = UIImage(data: fullSizeData)
|
image = UIImage(data: fullSizeData)
|
||||||
}
|
}
|
||||||
@ -674,7 +701,7 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete
|
|||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}, rects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1452,6 +1479,10 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
|||||||
wallpaperSignal = .single((backgroundColor, incomingColors, outgoingColors, image, options.blur, false, 1.0, rotation))
|
wallpaperSignal = .single((backgroundColor, incomingColors, outgoingColors, image, options.blur, false, 1.0, rotation))
|
||||||
}
|
}
|
||||||
case let .file(file):
|
case let .file(file):
|
||||||
|
if file.settings.intensity == 100 {
|
||||||
|
print()
|
||||||
|
}
|
||||||
|
|
||||||
rotation = file.settings.rotation
|
rotation = file.settings.rotation
|
||||||
if file.isPattern, let intensity = file.settings.intensity, intensity < 0 {
|
if file.isPattern, let intensity = file.settings.intensity, intensity < 0 {
|
||||||
backgroundColor = (.black, nil, [])
|
backgroundColor = (.black, nil, [])
|
||||||
@ -1464,6 +1495,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
|||||||
} else {
|
} else {
|
||||||
backgroundColor = (theme.chatList.backgroundColor, nil, [])
|
backgroundColor = (theme.chatList.backgroundColor, nil, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
wallpaperSignal = cachedWallpaper(account: account, slug: file.slug, settings: file.settings)
|
||||||
|> mapToSignal { wallpaper in
|
|> mapToSignal { wallpaper in
|
||||||
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
|
if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper {
|
||||||
@ -1479,6 +1511,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
|||||||
let convertedPreviewRepresentations : [ImageRepresentationWithReference] = file.file.previewRepresentations.map {
|
let convertedPreviewRepresentations : [ImageRepresentationWithReference] = file.file.previewRepresentations.map {
|
||||||
ImageRepresentationWithReference(representation: $0, reference: .wallpaper(wallpaper: .slug(file.slug), resource: $0.resource))
|
ImageRepresentationWithReference(representation: $0, reference: .wallpaper(wallpaper: .slug(file.slug), resource: $0.resource))
|
||||||
}
|
}
|
||||||
|
let useFallback = convertedPreviewRepresentations.isEmpty
|
||||||
|
|
||||||
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
var convertedRepresentations: [ImageRepresentationWithReference] = []
|
||||||
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
|
convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)))
|
||||||
@ -1511,12 +1544,11 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
|||||||
let isLight = UIColor.average(of: file.settings.colors.map(UIColor.init(rgb:))).hsb.b > 0.3
|
let isLight = UIColor.average(of: file.settings.colors.map(UIColor.init(rgb:))).hsb.b > 0.3
|
||||||
arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white)
|
arguments = PatternWallpaperArguments(colors: [.clear], rotation: nil, customPatternColor: isLight ? .black : .white)
|
||||||
}
|
}
|
||||||
|
return patternWallpaperImage(account: account, accountManager: accountManager, representations: useFallback ? convertedRepresentations : convertedPreviewRepresentations, mode: useFallback ? .screen : .thumbnail, autoFetchFullSize: true)
|
||||||
return patternWallpaperImage(account: account, accountManager: accountManager, representations: convertedPreviewRepresentations, mode: .thumbnail, autoFetchFullSize: true)
|
|> mapToSignal { generatorAndRects -> Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> in
|
||||||
|> mapToSignal { generator -> Signal<((UIColor, UIColor?, [UInt32]), [UIColor], [UIColor], UIImage?, Bool, Bool, CGFloat, Int32?), NoError> in
|
|
||||||
let imageSize = CGSize(width: 148.0, height: 320.0)
|
let imageSize = CGSize(width: 148.0, height: 320.0)
|
||||||
let imageArguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, custom: arguments)
|
let imageArguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, custom: arguments)
|
||||||
let context = generator?(imageArguments)
|
let context = generatorAndRects?.generator(imageArguments)
|
||||||
let image = context?.generateImage()
|
let image = context?.generateImage()
|
||||||
|
|
||||||
if !file.settings.colors.isEmpty {
|
if !file.settings.colors.isEmpty {
|
||||||
@ -1771,7 +1803,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager<Tele
|
|||||||
|
|
||||||
c.restoreGState()
|
c.restoreGState()
|
||||||
} else {
|
} else {
|
||||||
let rect = CGRect(x: 8.0, y: arguments.drawingSize.height - 24.0 - 9.0 - 3.0, width: arguments.drawingSize.width - 8.0 * 2.0, height: 24.0)
|
let rect = CGRect(x: 8.0, y: arguments.drawingSize.height - 24.0 - 9.0 - 3.0, width: 48.0, height: 24.0)
|
||||||
c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath)
|
c.addPath(UIBezierPath(roundedRect: rect, cornerRadius: 12.0).cgPath)
|
||||||
c.clip()
|
c.clip()
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "11.15",
|
"app": "12.0",
|
||||||
"xcode": "16.2",
|
"xcode": "16.2",
|
||||||
"bazel": "8.3.1:0cac3a67dc5429c68272dc6944104952e9e4cf84b29d126a5ff3fbaa59045217",
|
"bazel": "8.3.1:0cac3a67dc5429c68272dc6944104952e9e4cf84b29d126a5ff3fbaa59045217",
|
||||||
"macos": "15"
|
"macos": "15"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user