diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 830b5aad11..4bee0cde1e 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10141,5 +10141,8 @@ Sorry for the inconvenience."; "NameColor.ChatPreview.Description.Channel" = "You can choose an individual color to tint your channel's name, the links it sends, and replies to its messages."; "NameColor.ApplyColor" = "Apply Color"; +"NameColor.ApplyColorAndBackgroundEmoji" = "Apply Color and Icon"; "NameColor.TooltipPremium.Account" = "Subscribe to [Telegram Premium]() to choose a custom color for your name."; + +"NameColor.BackgroundEmoji.Title" = "ADD ICONS TO REPLIES"; diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 61f902a9c8..d4038c73b4 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -847,9 +847,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController animationCache: self.animationCache, animationRenderer: self.animationRenderer, isStandalone: false, - isStatusSelection: true, - isReactionSelection: false, - isEmojiSelection: false, + subject: .status, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: false, diff --git a/submodules/DrawingUI/Sources/DrawingScreen.swift b/submodules/DrawingUI/Sources/DrawingScreen.swift index ae8323a708..0e709a0282 100644 --- a/submodules/DrawingUI/Sources/DrawingScreen.swift +++ b/submodules/DrawingUI/Sources/DrawingScreen.swift @@ -737,9 +737,7 @@ private final class DrawingScreenComponent: CombinedComponent { animationCache: context.animationCache, animationRenderer: context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isEmojiSelection: true, + subject: .emoji, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: true, diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift index 441d475912..0aafe33323 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntity.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntity.swift @@ -449,9 +449,7 @@ public final class DrawingStickerEntityView: DrawingEntityView { animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: true, - isEmojiSelection: false, + subject: .reaction, hasTrending: false, topReactionItems: mappedReactionItems, areUnicodeEmojiEnabled: false, diff --git a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift b/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift index 6d9eb02631..5c3ddeec9e 100644 --- a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift +++ b/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift @@ -345,11 +345,8 @@ public func quickReactionSetupController( animationCache: context.animationCache, animationRenderer: context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: true, - isEmojiSelection: false, + subject: .quickReaction, hasTrending: false, - isQuickReactionSelection: true, topReactionItems: [], areUnicodeEmojiEnabled: false, areCustomEmojiEnabled: true, diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift index 4978f0b836..3d629bea08 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift @@ -422,6 +422,8 @@ final class StickerPackEmojisItemNode: GridItemNode { itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor case .primary: itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor + case let .custom(color): + itemLayer.layerTintColor = color.cgColor } var itemFrame = itemLayout.frame(itemIndex: index) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index b9f848ceb6..1baaf00d8c 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -166,7 +166,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) } dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) } dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) } - dict[609791884] = { return Api.Chat.parse_channel($0) } + dict[427944574] = { return Api.Chat.parse_channel($0) } dict[399807445] = { return Api.Chat.parse_channelForbidden($0) } dict[1103884886] = { return Api.Chat.parse_chat($0) } dict[693512293] = { return Api.Chat.parse_chatEmpty($0) } @@ -176,7 +176,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) } dict[1915758525] = { return Api.ChatFull.parse_channelFull($0) } dict[-908914376] = { return Api.ChatFull.parse_chatFull($0) } - dict[806110401] = { return Api.ChatInvite.parse_chatInvite($0) } + dict[808708181] = { return Api.ChatInvite.parse_chatInvite($0) } dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) } dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) } dict[-1940201511] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) } @@ -578,7 +578,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[2129714567] = { return Api.MessagesFilter.parse_inputMessagesFilterUrl($0) } dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) } dict[1358283666] = { return Api.MessagesFilter.parse_inputMessagesFilterVoice($0) } - dict[1267991078] = { return Api.MyBoost.parse_myBoost($0) } + dict[-1001897636] = { return Api.MyBoost.parse_myBoost($0) } dict[-1910892683] = { return Api.NearestDc.parse_nearestDc($0) } dict[-1746354498] = { return Api.NotificationSound.parse_notificationSoundDefault($0) } dict[-2096391452] = { return Api.NotificationSound.parse_notificationSoundLocal($0) } @@ -962,7 +962,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1886646706] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) } dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) } dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) } - dict[1876877535] = { return Api.User.parse_user($0) } + dict[-346018011] = { return Api.User.parse_user($0) } dict[-742634630] = { return Api.User.parse_userEmpty($0) } dict[-1179571092] = { return Api.UserFull.parse_userFull($0) } dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } @@ -1181,6 +1181,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1916114267] = { return Api.photos.Photos.parse_photos($0) } dict[352657236] = { return Api.photos.Photos.parse_photosSlice($0) } dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) } + dict[1029548774] = { return Api.premium.BoostsStatus.parse_boostsStatus($0) } dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) } dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) } dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) } @@ -1198,7 +1199,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) } dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) } dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) } - dict[-869070685] = { return Api.stories.BoostsStatus.parse_boostsStatus($0) } dict[-890861720] = { return Api.stories.PeerStories.parse_peerStories($0) } dict[1574486984] = { return Api.stories.Stories.parse_stories($0) } dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) } @@ -2082,6 +2082,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.premium.BoostsList: _1.serialize(buffer, boxed) + case let _1 as Api.premium.BoostsStatus: + _1.serialize(buffer, boxed) case let _1 as Api.premium.MyBoosts: _1.serialize(buffer, boxed) case let _1 as Api.stats.BroadcastStats: @@ -2096,8 +2098,6 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.stories.AllStories: _1.serialize(buffer, boxed) - case let _1 as Api.stories.BoostsStatus: - _1.serialize(buffer, boxed) case let _1 as Api.stories.PeerStories: _1.serialize(buffer, boxed) case let _1 as Api.stories.Stories: diff --git a/submodules/TelegramApi/Sources/Api14.swift b/submodules/TelegramApi/Sources/Api14.swift index 19781ad8f5..6ccc340764 100644 --- a/submodules/TelegramApi/Sources/Api14.swift +++ b/submodules/TelegramApi/Sources/Api14.swift @@ -700,17 +700,18 @@ public extension Api { } public extension Api { enum MyBoost: TypeConstructorDescription { - case myBoost(flags: Int32, slot: Int32, peer: Api.Peer?, expires: Int32, cooldownUntilDate: Int32?) + case myBoost(flags: Int32, slot: Int32, peer: Api.Peer?, date: Int32, expires: Int32, cooldownUntilDate: Int32?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .myBoost(let flags, let slot, let peer, let expires, let cooldownUntilDate): + case .myBoost(let flags, let slot, let peer, let date, let expires, let cooldownUntilDate): if boxed { - buffer.appendInt32(1267991078) + buffer.appendInt32(-1001897636) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(slot, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)} + serializeInt32(date, buffer: buffer, boxed: false) serializeInt32(expires, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {serializeInt32(cooldownUntilDate!, buffer: buffer, boxed: false)} break @@ -719,8 +720,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .myBoost(let flags, let slot, let peer, let expires, let cooldownUntilDate): - return ("myBoost", [("flags", flags as Any), ("slot", slot as Any), ("peer", peer as Any), ("expires", expires as Any), ("cooldownUntilDate", cooldownUntilDate as Any)]) + case .myBoost(let flags, let slot, let peer, let date, let expires, let cooldownUntilDate): + return ("myBoost", [("flags", flags as Any), ("slot", slot as Any), ("peer", peer as Any), ("date", date as Any), ("expires", expires as Any), ("cooldownUntilDate", cooldownUntilDate as Any)]) } } @@ -736,14 +737,17 @@ public extension Api { var _4: Int32? _4 = reader.readInt32() var _5: Int32? - if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } + _5 = reader.readInt32() + var _6: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_6 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 { - return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, expires: _4!, cooldownUntilDate: _5) + let _c5 = _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, date: _4!, expires: _5!, cooldownUntilDate: _6) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api22.swift b/submodules/TelegramApi/Sources/Api22.swift index 7c1f359232..b99155ae2c 100644 --- a/submodules/TelegramApi/Sources/Api22.swift +++ b/submodules/TelegramApi/Sources/Api22.swift @@ -452,14 +452,14 @@ public extension Api { } public extension Api { enum User: TypeConstructorDescription { - case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32, backgroundEmojiId: Int64?) + case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?) case userEmpty(id: Int64) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let backgroundEmojiId): if boxed { - buffer.appendInt32(1876877535) + buffer.appendInt32(-346018011) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -486,7 +486,7 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} - serializeInt32(color, buffer: buffer, boxed: false) + if Int(flags2) & Int(1 << 7) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} break case .userEmpty(let id): @@ -553,7 +553,7 @@ public extension Api { var _17: Int32? if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt32() } var _18: Int32? - _18 = reader.readInt32() + if Int(_2!) & Int(1 << 7) != 0 {_18 = reader.readInt32() } var _19: Int64? if Int(_2!) & Int(1 << 6) != 0 {_19 = reader.readInt64() } let _c1 = _1 != nil @@ -573,10 +573,10 @@ public extension Api { let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil - let _c18 = _18 != nil + let _c18 = (Int(_2!) & Int(1 << 7) == 0) || _18 != nil let _c19 = (Int(_2!) & Int(1 << 6) == 0) || _19 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 { - return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18!, backgroundEmojiId: _19) + return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, backgroundEmojiId: _19) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api29.swift b/submodules/TelegramApi/Sources/Api29.swift index 7a173e0b7e..0502381f99 100644 --- a/submodules/TelegramApi/Sources/Api29.swift +++ b/submodules/TelegramApi/Sources/Api29.swift @@ -62,6 +62,92 @@ public extension Api.premium { } } +public extension Api.premium { + enum BoostsStatus: TypeConstructorDescription { + case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, prepaidGiveaways: [Api.PrepaidGiveaway]?, myBoostSlots: [Int32]?) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): + if boxed { + buffer.appendInt32(1029548774) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(level, buffer: buffer, boxed: false) + serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false) + serializeInt32(boosts, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)} + serializeString(boostUrl, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(prepaidGiveaways!.count)) + for item in prepaidGiveaways! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(myBoostSlots!.count)) + for item in myBoostSlots! { + serializeInt32(item, buffer: buffer, boxed: false) + }} + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots): + return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("prepaidGiveaways", prepaidGiveaways as Any), ("myBoostSlots", myBoostSlots as Any)]) + } + } + + public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() } + var _6: Int32? + if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } + var _7: Api.StatsPercentValue? + if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue + } } + var _8: String? + _8 = parseString(reader) + var _9: [Api.PrepaidGiveaway]? + if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() { + _9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrepaidGiveaway.self) + } } + var _10: [Int32]? + if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { + _10 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil + let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil + let _c8 = _8 != nil + let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil + let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { + return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10) + } + else { + return nil + } + } + + } +} public extension Api.premium { enum MyBoosts: TypeConstructorDescription { case myBoosts(myBoosts: [Api.MyBoost], chats: [Api.Chat], users: [Api.User]) @@ -720,82 +806,6 @@ public extension Api.stories { } } -public extension Api.stories { - enum BoostsStatus: TypeConstructorDescription { - case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, myBoostSlots: [Int32]?) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let myBoostSlots): - if boxed { - buffer.appendInt32(-869070685) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt32(level, buffer: buffer, boxed: false) - serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false) - serializeInt32(boosts, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)} - if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)} - serializeString(boostUrl, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) - buffer.appendInt32(Int32(myBoostSlots!.count)) - for item in myBoostSlots! { - serializeInt32(item, buffer: buffer, boxed: false) - }} - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let myBoostSlots): - return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("myBoostSlots", myBoostSlots as Any)]) - } - } - - public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? { - var _1: Int32? - _1 = reader.readInt32() - var _2: Int32? - _2 = reader.readInt32() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() } - var _6: Int32? - if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() } - var _7: Api.StatsPercentValue? - if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue - } } - var _8: String? - _8 = parseString(reader) - var _9: [Int32]? - if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() { - _9 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) - } } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil - let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil - let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil - let _c8 = _8 != nil - let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.stories.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, myBoostSlots: _9) - } - else { - return nil - } - } - - } -} public extension Api.stories { enum PeerStories: TypeConstructorDescription { case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User]) diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index bc06708611..d192bd167e 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -522,7 +522,7 @@ public extension Api { } public extension Api { indirect enum Chat: TypeConstructorDescription { - case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32, backgroundEmojiId: Int64?) + case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?) case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?) case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?) case chatEmpty(id: Int64) @@ -532,7 +532,7 @@ public extension Api { switch self { case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let backgroundEmojiId): if boxed { - buffer.appendInt32(609791884) + buffer.appendInt32(427944574) } serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags2, buffer: buffer, boxed: false) @@ -557,7 +557,7 @@ public extension Api { item.serialize(buffer, true) }} if Int(flags2) & Int(1 << 4) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)} - serializeInt32(color, buffer: buffer, boxed: false) + if Int(flags2) & Int(1 << 6) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} if Int(flags2) & Int(1 << 5) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)} break case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate): @@ -660,7 +660,7 @@ public extension Api { var _15: Int32? if Int(_2!) & Int(1 << 4) != 0 {_15 = reader.readInt32() } var _16: Int32? - _16 = reader.readInt32() + if Int(_2!) & Int(1 << 6) != 0 {_16 = reader.readInt32() } var _17: Int64? if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt64() } let _c1 = _1 != nil @@ -678,10 +678,10 @@ public extension Api { let _c13 = (Int(_1!) & Int(1 << 17) == 0) || _13 != nil let _c14 = (Int(_2!) & Int(1 << 0) == 0) || _14 != nil let _c15 = (Int(_2!) & Int(1 << 4) == 0) || _15 != nil - let _c16 = _16 != nil + let _c16 = (Int(_2!) & Int(1 << 6) == 0) || _16 != nil let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16!, backgroundEmojiId: _17) + return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, backgroundEmojiId: _17) } else { return nil @@ -1238,15 +1238,15 @@ public extension Api { } public extension Api { indirect enum ChatInvite: TypeConstructorDescription { - case chatInvite(flags: Int32, title: String, about: String?, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?) + case chatInvite(flags: Int32, title: String, about: String?, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?, color: Int32?) case chatInviteAlready(chat: Api.Chat) case chatInvitePeek(chat: Api.Chat, expires: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants): + case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants, let color): if boxed { - buffer.appendInt32(806110401) + buffer.appendInt32(808708181) } serializeInt32(flags, buffer: buffer, boxed: false) serializeString(title, buffer: buffer, boxed: false) @@ -1258,6 +1258,7 @@ public extension Api { for item in participants! { item.serialize(buffer, true) }} + if Int(flags) & Int(1 << 10) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)} break case .chatInviteAlready(let chat): if boxed { @@ -1277,8 +1278,8 @@ public extension Api { public func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants): - return ("chatInvite", [("flags", flags as Any), ("title", title as Any), ("about", about as Any), ("photo", photo as Any), ("participantsCount", participantsCount as Any), ("participants", participants as Any)]) + case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants, let color): + return ("chatInvite", [("flags", flags as Any), ("title", title as Any), ("about", about as Any), ("photo", photo as Any), ("participantsCount", participantsCount as Any), ("participants", participants as Any), ("color", color as Any)]) case .chatInviteAlready(let chat): return ("chatInviteAlready", [("chat", chat as Any)]) case .chatInvitePeek(let chat, let expires): @@ -1303,14 +1304,17 @@ public extension Api { if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() { _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) } } + var _7: Int32? + if Int(_1!) & Int(1 << 10) != 0 {_7 = reader.readInt32() } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil let _c4 = _4 != nil let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { - return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6) + let _c7 = (Int(_1!) & Int(1 << 10) == 0) || _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6, color: _7) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index 9bb63bf0a9..daefae63d9 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -373,6 +373,21 @@ public extension Api.functions.account { }) } } +public extension Api.functions.account { + static func getDefaultBackgroundEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1509246514) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.getDefaultBackgroundEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in + let reader = BufferReader(buffer) + var result: Api.EmojiList? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.EmojiList + } + return result + }) + } +} public extension Api.functions.account { static func getDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -8362,13 +8377,17 @@ public extension Api.functions.photos { } } public extension Api.functions.premium { - static func applyBoost(flags: Int32, slot: Int32?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func applyBoost(flags: Int32, slots: [Int32]?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(1186373995) + buffer.appendInt32(407618489) serializeInt32(flags, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {serializeInt32(slot!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(slots!.count)) + for item in slots! { + serializeInt32(item, buffer: buffer, boxed: false) + }} peer.serialize(buffer, true) - return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slot", String(describing: slot)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slots", String(describing: slots)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in let reader = BufferReader(buffer) var result: Api.Bool? if let signature = reader.readInt32() { @@ -8396,6 +8415,21 @@ public extension Api.functions.premium { }) } } +public extension Api.functions.premium { + static func getBoostsStatus(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(70197089) + peer.serialize(buffer, true) + return (FunctionDescription(name: "premium.getBoostsStatus", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsStatus? in + let reader = BufferReader(buffer) + var result: Api.premium.BoostsStatus? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.premium.BoostsStatus + } + return result + }) + } +} public extension Api.functions.premium { static func getMyBoosts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift index bbacedf276..bc0ab4d048 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ApiGroupOrChannel.swift @@ -153,7 +153,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? { } } - return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId) + return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) case let .channelForbidden(flags, id, accessHash, title, untilDate): let info: TelegramChannelInfo if (flags & Int32(1 << 8)) != 0 { @@ -212,7 +212,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? { } } - return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId) + return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) } else { return parseTelegramGroupOrChannel(chat: rhs) } diff --git a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift index 3a32c0361d..ebc1d017af 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TelegramUser.swift @@ -96,7 +96,7 @@ extension TelegramUser { let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:)) - self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId) + self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) case let .userEmpty(id): self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil) } @@ -180,7 +180,7 @@ extension TelegramUser { accessHash = lhs.accessHash ?? rhsAccessHashValue } - return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId) + return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId) } else { return TelegramUser(user: rhs) } diff --git a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift index 48385da8c6..7bc4fd6b4d 100644 --- a/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift +++ b/submodules/TelegramCore/Sources/State/ManagedRecentStickers.swift @@ -318,6 +318,34 @@ func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart } +func managedBackgroundIconEmoji(postbox: Postbox, network: Network) -> Signal { + let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in + return network.request(Api.functions.account.getDefaultBackgroundEmojis(hash: hash)) + |> retryRequest + |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in + switch result { + case .emojiListNotModified: + return .single(nil) + case let .emojiList(_, documentIds): + return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: documentIds) + |> map { files -> [OrderedItemListEntry] in + var items: [OrderedItemListEntry] = [] + for fileId in documentIds { + guard let file = files[fileId] else { + continue + } + if let entry = CodableEntry(RecentMediaItem(file)) { + items.append(OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: entry)) + } + } + return items + } + } + } + }) + return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart +} + func managedRecentReactions(postbox: Postbox, network: Network) -> Signal { let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentReactions, extractItemId: { rawId in switch RecentReactionItemId(rawId).id { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift index 6be99834da..4ea1ac7aa9 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_CachedUserData.swift @@ -128,9 +128,6 @@ public enum PeerNameColor: Int32, CaseIterable { case greenDash case cyanDash case blueDash - case other13 - case other14 - case other15 } public struct PeerEmojiStatus: Equatable, Codable { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index e87c9baea4..a09fe8fc2a 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -80,6 +80,7 @@ public struct Namespaces { public static let CloudFeaturedProfilePhotoEmoji: Int32 = 23 public static let CloudFeaturedGroupPhotoEmoji: Int32 = 24 public static let NewSessionReviews: Int32 = 25 + public static let CloudFeaturedBackgroundIconEmoji: Int32 = 26 } public struct CachedItemCollection { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 62fa27c30a..f2276b1545 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -514,7 +514,7 @@ private class AdMessagesHistoryContextImpl { } } else if let chatInvite = chatInvite, let chatInviteHash = chatInviteHash { switch chatInvite { - case let .chatInvite(flags, title, _, photo, participantsCount, participants): + case let .chatInvite(flags, title, _, photo, participantsCount, participants, nameColor): let photo = telegramMediaImageFromApiPhoto(photo).flatMap({ smallestImageRepresentation($0.representations) }) let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0) @@ -522,6 +522,7 @@ private class AdMessagesHistoryContextImpl { let _ = flags let _ = participantsCount let _ = participants + let _ = nameColor target = .invite(CachedMessage.Target.Invite( title: title, diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift index be5a1648a5..fd0c211218 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/JoinLink.swift @@ -47,6 +47,7 @@ public enum ExternalJoiningChatState { public let photoRepresentation: TelegramMediaImageRepresentation? public let participantsCount: Int32 public let participants: [EnginePeer]? + public let nameColor: PeerNameColor? } case invite(Invite) @@ -105,10 +106,10 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal mapToSignal { result -> Signal in if let result = result { switch result { - case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants): + case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants, nameColor): let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) }) let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0) - return .single(.invite(ExternalJoiningChatState.Invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({ EnginePeer(TelegramUser(user: $0)) })))) + return .single(.invite(ExternalJoiningChatState.Invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({ EnginePeer(TelegramUser(user: $0)) }), nameColor: nameColor.flatMap({ PeerNameColor(rawValue: $0) })))) case let .chatInviteAlready(chat): if let peer = parseTelegramGroupOrChannel(chat: chat) { return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift index 6bc1f5c1f5..64deebf284 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/Peer.swift @@ -509,6 +509,10 @@ public extension EnginePeer { var nameColor: PeerNameColor? { return self._asPeer().nameColor } + + var backgroundEmojiId: Int64? { + return self._asPeer().backgroundEmojiId + } } public extension EnginePeer { diff --git a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift index e73a756b8d..407ffa5194 100644 --- a/submodules/TelegramCore/Sources/Utils/PeerUtils.swift +++ b/submodules/TelegramCore/Sources/Utils/PeerUtils.swift @@ -243,6 +243,17 @@ public extension Peer { } return false } + + var backgroundEmojiId: Int64? { + switch self { + case let user as TelegramUser: + return user.backgroundEmojiId + case let channel as TelegramChannel: + return channel.backgroundEmojiId + default: + return nil + } + } } public extension TelegramPeerUsername { diff --git a/submodules/TelegramPresentationData/Sources/PeerNameColor.swift b/submodules/TelegramPresentationData/Sources/PeerNameColor.swift index e848fc8484..aa13a83bf4 100644 --- a/submodules/TelegramPresentationData/Sources/PeerNameColor.swift +++ b/submodules/TelegramPresentationData/Sources/PeerNameColor.swift @@ -35,12 +35,6 @@ public extension PeerNameColor { return (UIColor(rgb: 0x27ACCE), UIColor(rgb: 0x82E8D6)) case .blueDash: return (UIColor(rgb: 0x3391D4), UIColor(rgb: 0x7DD3F0)) - case .other13: - return (.black, nil) - case .other14: - return (.black, nil) - case .other15: - return (.black, nil) } } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index c38231b967..f262102ddb 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -1323,11 +1323,11 @@ public struct PresentationResourcesChat { let radius: CGFloat = 3.0 let offset: CGFloat = 5.0 - return generateImage(CGSize(width: radius, height: radius * 6.0), rotatedContext: { size, context in + return generateImage(CGSize(width: 8.0, height: radius * 6.0), rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - context.move(to: CGPoint(x: size.width, y: offset)) - context.addLine(to: CGPoint(x: size.width, y: offset + radius * 3.0)) + context.move(to: CGPoint(x: radius, y: offset)) + context.addLine(to: CGPoint(x: radius, y: offset + radius * 3.0)) context.addLine(to: CGPoint(x: 0.0, y: offset + radius * 4.0)) context.addLine(to: CGPoint(x: 0.0, y: offset + radius)) context.closePath() diff --git a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift index 3f7291657a..5cc53a7184 100644 --- a/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift +++ b/submodules/TelegramUI/Components/AvatarEditorScreen/Sources/AvatarEditorScreen.swift @@ -1509,12 +1509,8 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer { animationCache: context.animationCache, animationRenderer: context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isEmojiSelection: false, + subject: isGroup ? .groupPhoto : .profilePhoto, hasTrending: false, - isProfilePhotoEmojiSelection: !isGroup, - isGroupPhotoEmojiSelection: isGroup, topReactionItems: [], areUnicodeEmojiEnabled: false, areCustomEmojiEnabled: true, diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 1ddce31316..d62eaeb2cb 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -53,6 +53,7 @@ public struct ChatMessageAttachedContentNodeMediaFlags: OptionSet { public final class ChatMessageAttachedContentNode: ASDisplayNode { private var backgroundView: UIImageView? + private var lineDashView: UIImageView? private var title: TextNodeWithEntities? private var subtitle: TextNodeWithEntities? @@ -152,29 +153,32 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing let mainColor: UIColor + var secondaryColor: UIColor? if !incoming { mainColor = messageTheme.accentTextColor } else { var authorNameColor: UIColor? let author = message.author if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), author?.id.namespace == Namespaces.Peer.CloudUser { - authorNameColor = author.flatMap { chatMessagePeerIdColors[Int(clamping: $0.id.id._internalGetInt64Value() % 7)] } - if let rawAuthorNameColor = authorNameColor { - var dimColors = false - switch presentationData.theme.theme.name { - case .builtin(.nightAccent), .builtin(.night): - dimColors = true - default: - break - } - if dimColors { - var hue: CGFloat = 0.0 - var saturation: CGFloat = 0.0 - var brightness: CGFloat = 0.0 - rawAuthorNameColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) - authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0) - } - } + authorNameColor = author?.nameColor?.color + secondaryColor = author?.nameColor?.dashColors.1 + +// if let rawAuthorNameColor = authorNameColor { +// var dimColors = false +// switch presentationData.theme.theme.name { +// case .builtin(.nightAccent), .builtin(.night): +// dimColors = true +// default: +// break +// } +// if dimColors { +// var hue: CGFloat = 0.0 +// var saturation: CGFloat = 0.0 +// var brightness: CGFloat = 0.0 +// rawAuthorNameColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) +// authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0) +// } +// } } if let authorNameColor { @@ -392,7 +396,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { cutout = TextNodeCutout(topRight: CGSize(width: cutoutWidth, height: remainingCutoutHeight)) } - let titleString = NSAttributedString(string: title, font: titleFont, textColor: messageTheme.accentTextColor) + let titleString = NSAttributedString(string: title, font: titleFont, textColor: mainColor) let titleLayoutAndApplyValue = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: maxContentsWidth, height: 10000.0), alignment: .natural, lineSpacing: textLineSpacing, cutout: cutout, insets: UIEdgeInsets())) titleLayoutAndApply = titleLayoutAndApplyValue @@ -754,6 +758,26 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } backgroundView.tintColor = mainColor + + if let secondaryColor { + let lineDashView: UIImageView + if let current = self.lineDashView { + lineDashView = current + } else { + lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(presentationData.theme.theme)) + lineDashView.clipsToBounds = true + self.lineDashView = lineDashView + self.view.insertSubview(lineDashView, aboveSubview: backgroundView) + } + lineDashView.tintColor = secondaryColor + lineDashView.frame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 8.0, height: backgroundFrame.height)) + lineDashView.layer.cornerRadius = 4.0 + } else { + if let lineDashView = self.lineDashView { + self.lineDashView = nil + lineDashView.removeFromSuperview() + } + } } else { if let backgroundView = self.backgroundView { self.backgroundView = nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index b92c7e3734..15e1f2514b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -154,7 +154,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode let presentationData = item.context.sharedContext.currentPresentationData.with { $0 } let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can't participate in this giveaway.", timeout: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false }) - item.controllerInteraction.presentController(controller, nil) + item.controllerInteraction.presentControllerInCurrent(controller, nil) } private func removePlaceholder(animated: Bool) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift index d19d6e42f6..d501556b44 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift @@ -654,11 +654,13 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { lineDashView = current } else { lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(arguments.presentationData.theme.theme)) + lineDashView.clipsToBounds = true node.lineDashView = lineDashView node.contentNode.view.addSubview(lineDashView) } lineDashView.tintColor = secondaryColor - lineDashView.frame = CGRect(origin: .zero, size: CGSize(width: 3.0, height: backgroundFrame.height)) + lineDashView.frame = CGRect(origin: .zero, size: CGSize(width: 8.0, height: backgroundFrame.height)) + lineDashView.layer.cornerRadius = 4.0 } else { if let lineDashView = node.lineDashView { node.lineDashView = nil diff --git a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift index e65d21aa0a..9926c8349e 100644 --- a/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/ChatEntityKeyboardInputNode/Sources/ChatEntityKeyboardInputNode.swift @@ -160,7 +160,20 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode { let animationCache = context.animationCache let animationRenderer = context.animationRenderer - let emojiItems = EmojiPagerContentComponent.emojiInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: true, hasTrending: hasTrending, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: chatPeerId, hasSearch: hasSearch, hideBackground: hideBackground) + let emojiItems = EmojiPagerContentComponent.emojiInputData( + context: context, + animationCache: animationCache, + animationRenderer: animationRenderer, + isStandalone: false, + subject: .emoji, + hasTrending: hasTrending, + topReactionItems: [], + areUnicodeEmojiEnabled: true, + areCustomEmojiEnabled: areCustomEmojiEnabled, + chatPeerId: chatPeerId, + hasSearch: hasSearch, + hideBackground: hideBackground + ) let stickerNamespaces: [ItemCollectionId.Namespace] = [Namespaces.ItemCollection.CloudStickerPacks] let stickerOrderedItemListCollectionIds: [Int32] = [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers, Namespaces.OrderedItemList.CloudAllPremiumStickers] @@ -2256,7 +2269,19 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi let semaphore = DispatchSemaphore(value: 0) var emojiComponent: EmojiPagerContentComponent? - let _ = EmojiPagerContentComponent.emojiInputData(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, isStandalone: true, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: false, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: nil, forceHasPremium: forceHasPremium).start(next: { value in + let _ = EmojiPagerContentComponent.emojiInputData( + context: context, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + isStandalone: true, + subject: .generic, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: true, + areCustomEmojiEnabled: areCustomEmojiEnabled, + chatPeerId: nil, + forceHasPremium: forceHasPremium + ).start(next: { value in emojiComponent = value semaphore.signal() }) @@ -2271,7 +2296,20 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi gifs: nil, availableGifSearchEmojies: [] ), - updatedInputData: EmojiPagerContentComponent.emojiInputData(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, isStandalone: true, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: false, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: nil, forceHasPremium: forceHasPremium, hideBackground: hideBackground) |> map { emojiComponent -> ChatEntityKeyboardInputNode.InputData in + updatedInputData: EmojiPagerContentComponent.emojiInputData( + context: context, + animationCache: self.animationCache, + animationRenderer: self.animationRenderer, + isStandalone: true, + subject: .generic, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: true, + areCustomEmojiEnabled: areCustomEmojiEnabled, + chatPeerId: nil, + forceHasPremium: forceHasPremium, + hideBackground: hideBackground + ) |> map { emojiComponent -> ChatEntityKeyboardInputNode.InputData in return ChatEntityKeyboardInputNode.InputData( emoji: emojiComponent, stickers: nil, diff --git a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift index d0f5381b10..347d40036f 100644 --- a/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusSelectionComponent/Sources/EmojiStatusSelectionComponent.swift @@ -822,6 +822,8 @@ public final class EmojiStatusSelectionController: ViewController { dynamicColor: self.presentationData.theme.list.itemAccentColor ) switch item.tintMode { + case let .custom(color): + baseItemLayer.contentTintColor = color case .accent: baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor case .primary: diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index 614b0ee31d..f9b3de8285 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -1328,6 +1328,8 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager } switch item.tintMode { + case let .custom(color): + itemLayer.layerTintColor = color.cgColor case .accent: itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor case .primary: @@ -2443,6 +2445,7 @@ public final class EmojiPagerContentComponent: Component { public enum Icon: Equatable, Hashable { case premiumStar case topic(String, Int32) + case stop } case animation(EntityKeyboardAnimationData) @@ -2468,10 +2471,11 @@ public final class EmojiPagerContentComponent: Component { case premium } - public enum TintMode { + public enum TintMode: Equatable { case none case accent case primary + case custom(UIColor) } public let animationData: EntityKeyboardAnimationData? @@ -2538,6 +2542,7 @@ public final class EmojiPagerContentComponent: Component { public let displayPremiumBadges: Bool public let headerItem: EntityKeyboardAnimationData? public let fillWithLoadingPlaceholders: Bool + public let customTintColor: UIColor? public let items: [Item] public init( @@ -2554,6 +2559,7 @@ public final class EmojiPagerContentComponent: Component { displayPremiumBadges: Bool, headerItem: EntityKeyboardAnimationData?, fillWithLoadingPlaceholders: Bool, + customTintColor: UIColor? = nil, items: [Item] ) { self.supergroupId = supergroupId @@ -2569,6 +2575,7 @@ public final class EmojiPagerContentComponent: Component { self.displayPremiumBadges = displayPremiumBadges self.headerItem = headerItem self.fillWithLoadingPlaceholders = fillWithLoadingPlaceholders + self.customTintColor = customTintColor self.items = items } @@ -2615,6 +2622,9 @@ public final class EmojiPagerContentComponent: Component { if lhs.fillWithLoadingPlaceholders != rhs.fillWithLoadingPlaceholders { return false } + if lhs.customTintColor != rhs.customTintColor { + return false + } if lhs.items != rhs.items { return false } @@ -3400,6 +3410,11 @@ public final class EmojiPagerContentComponent: Component { let imageSize = image.size//.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) } + case .stop: + if let image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/ButtonStop"), color: UIColor(rgb: 0xcdcdcd)) { + let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) + image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) + } } UIGraphicsPopContext() @@ -5838,6 +5853,8 @@ public final class EmojiPagerContentComponent: Component { itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor) switch item.tintMode { + case let .custom(color): + itemLayer.layerTintColor = color.cgColor case .accent: itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemAccentColor.cgColor case .primary: @@ -7088,19 +7105,25 @@ public final class EmojiPagerContentComponent: Component { return hasPremium } + public enum Subject { + case generic + case status + case reaction + case emoji + case topicIcon + case quickReaction + case profilePhoto + case groupPhoto + case backgroundIcon + } + public static func emojiInputData( context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, isStandalone: Bool, - isStatusSelection: Bool, - isReactionSelection: Bool, - isEmojiSelection: Bool, + subject: Subject, hasTrending: Bool, - isTopicIconSelection: Bool = false, - isQuickReactionSelection: Bool = false, - isProfilePhotoEmojiSelection: Bool = false, - isGroupPhotoEmojiSelection: Bool = false, topReactionItems: [EmojiComponentReactionItem], areUnicodeEmojiEnabled: Bool, areCustomEmojiEnabled: Bool, @@ -7109,6 +7132,7 @@ public final class EmojiPagerContentComponent: Component { topStatusTitle: String? = nil, topicTitle: String? = nil, topicColor: Int32? = nil, + backgroundIconColor: UIColor? = nil, hasSearch: Bool = true, forceHasPremium: Bool = false, premiumIfSavedMessages: Bool = true, @@ -7125,7 +7149,7 @@ public final class EmojiPagerContentComponent: Component { var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([]) - if isStatusSelection { + if case .status = subject { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji) @@ -7139,10 +7163,10 @@ public final class EmojiPagerContentComponent: Component { } } |> take(1) - } else if isReactionSelection { + } else if [.reaction, .quickReaction].contains(subject) { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions) orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions) - } else if isTopicIconSelection { + } else if case .topicIcon = subject { iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false) |> map { result -> [TelegramMediaFile] in switch result { @@ -7153,25 +7177,27 @@ public final class EmojiPagerContentComponent: Component { } } |> take(1) - } else if isProfilePhotoEmojiSelection { + } else if case .profilePhoto = subject { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji) - } else if isGroupPhotoEmojiSelection { + } else if case .groupPhoto = subject { orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji) + } else if case .backgroundIcon = subject { + orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji) } let availableReactions: Signal - if isReactionSelection { + if [.reaction, .quickReaction].contains(subject) { availableReactions = context.engine.stickers.availableReactions() } else { availableReactions = .single(nil) } let searchCategories: Signal - if isEmojiSelection || isReactionSelection { + if [.emoji, .reaction].contains(subject) { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji) - } else if isStatusSelection { + } else if case .status = subject { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status) - } else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection { + } else if [.profilePhoto, .groupPhoto].contains(subject) { searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar) } else { searchCategories = .single(nil) @@ -7312,6 +7338,7 @@ public final class EmojiPagerContentComponent: Component { var topReactions: OrderedItemListView? var recentReactions: OrderedItemListView? var featuredAvatarEmoji: OrderedItemListView? + var featuredBackgroundIconEmoji: OrderedItemListView? for orderedView in view.orderedItemListsViews { if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji { recentEmoji = orderedView @@ -7327,10 +7354,12 @@ public final class EmojiPagerContentComponent: Component { featuredAvatarEmoji = orderedView } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji { featuredAvatarEmoji = orderedView + } else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji { + featuredBackgroundIconEmoji = orderedView } } - if isTopicIconSelection { + if case .topicIcon = subject { let resultItem = EmojiPagerContentComponent.Item( animationData: nil, content: .icon(.topic(String((topicTitle ?? "").prefix(1)), topicColor ?? 0)), @@ -7389,7 +7418,7 @@ public final class EmojiPagerContentComponent: Component { itemGroups[groupIndex].items.append(resultItem) } } - } else if isStatusSelection { + } else if case .status = subject { let resultItem = EmojiPagerContentComponent.Item( animationData: nil, content: .icon(.premiumStar), @@ -7549,7 +7578,7 @@ public final class EmojiPagerContentComponent: Component { } } } - } else if isReactionSelection { + } else if [.reaction, .quickReaction].contains(subject) { var existingIds = Set() var topReactionItems = topReactionItems @@ -7676,7 +7705,7 @@ public final class EmojiPagerContentComponent: Component { itemGroups[groupIndex].items.append(resultItem) } else { itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && !isQuickReactionSelection, headerItem: nil, items: [resultItem])) + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) } } else { let groupId = "recent" @@ -7760,11 +7789,11 @@ public final class EmojiPagerContentComponent: Component { popularInsertIndex += 1 } else { itemGroupIndexById[groupId] = itemGroups.count - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && !isQuickReactionSelection, headerItem: nil, items: [resultItem])) + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem])) } } } - } else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection { + } else if [.profilePhoto, .groupPhoto].contains(subject) { var existingIds = Set() let groupId = "recent" @@ -7812,6 +7841,80 @@ public final class EmojiPagerContentComponent: Component { tintMode: tintMode ) + if let groupIndex = itemGroupIndexById[groupId] { + if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { + break + } + + itemGroups[groupIndex].items.append(resultItem) + } + } + } + } else if case .backgroundIcon = subject { + var existingIds = Set() + + let resultItem = EmojiPagerContentComponent.Item( + animationData: nil, + content: .icon(.stop), + itemFile: nil, + subgroupId: nil, + icon: .none, + tintMode: .none + ) + + let groupId = "recent" + if let groupIndex = itemGroupIndexById[groupId] { + itemGroups[groupIndex].items.append(resultItem) + } else { + itemGroupIndexById[groupId] = itemGroups.count + itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem])) + } + + if let featuredBackgroundIconEmoji { + for item in featuredBackgroundIconEmoji.items { + guard let item = item.contents.get(RecentMediaItem.self) else { + continue + } + + let file = item.media + if existingIds.contains(file.fileId) { + continue + } + existingIds.insert(file.fileId) + + let resultItem: EmojiPagerContentComponent.Item + + var tintMode: Item.TintMode = .none + if file.isCustomTemplateEmoji { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } + } + for attribute in file.attributes { + if case let .CustomEmoji(_, _, _, packReference) = attribute { + switch packReference { + case let .id(id, _): + if id == 773947703670341676 || id == 2964141614563343 { + tintMode = .accent + } + default: + break + } + } + } + + let animationData = EntityKeyboardAnimationData(file: file) + resultItem = EmojiPagerContentComponent.Item( + animationData: animationData, + content: .animation(animationData), + itemFile: file, + subgroupId: nil, + icon: .none, + tintMode: tintMode + ) + if let groupIndex = itemGroupIndexById[groupId] { if itemGroups[groupIndex].items.count >= (5 + 8) * 8 { break @@ -7823,7 +7926,9 @@ public final class EmojiPagerContentComponent: Component { } } - if let recentEmoji = recentEmoji, !isReactionSelection, !isStatusSelection, !isProfilePhotoEmojiSelection, !isGroupPhotoEmojiSelection { + let hasRecentEmoji = ![.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon].contains(subject) + + if let recentEmoji = recentEmoji, hasRecentEmoji { for item in recentEmoji.items { guard let item = item.contents.get(RecentEmojiItem.self) else { continue @@ -7878,7 +7983,8 @@ public final class EmojiPagerContentComponent: Component { if !hasPremium { maybeAppendUnicodeEmoji() } - + + var skippedCollectionIds = Set() if areCustomEmojiEnabled { for entry in view.entries { guard let item = entry.item as? StickerPackItem else { @@ -7886,17 +7992,35 @@ public final class EmojiPagerContentComponent: Component { } var icon: EmojiPagerContentComponent.Item.Icon = .none - if isReactionSelection, !hasPremium { + if [.reaction, .quickReaction].contains(subject), !hasPremium { icon = .locked } + let supergroupId = entry.index.collectionId + let groupId: AnyHashable = supergroupId + + if skippedCollectionIds.contains(groupId) { + continue + } + + var isTemplate = false var tintMode: Item.TintMode = .none if item.file.isCustomTemplateEmoji { - if isStatusSelection { - tintMode = .accent + if [.status, .backgroundIcon].contains(subject) { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } } else { tintMode = .primary } + if case .backgroundIcon = subject { + isTemplate = true + } + } else if case .backgroundIcon = subject { + skippedCollectionIds.insert(groupId) + continue } let animationData = EntityKeyboardAnimationData(file: item.file) @@ -7909,8 +8033,6 @@ public final class EmojiPagerContentComponent: Component { tintMode: tintMode ) - let supergroupId = entry.index.collectionId - let groupId: AnyHashable = supergroupId let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium if isPremiumLocked && isPremiumDisabled { continue @@ -7943,7 +8065,7 @@ public final class EmojiPagerContentComponent: Component { dimensions: thumbnail.dimensions.cgSize, immediateThumbnailData: info.immediateThumbnailData, isReaction: false, - isTemplate: false + isTemplate: isTemplate ) } @@ -7959,15 +8081,29 @@ public final class EmojiPagerContentComponent: Component { if installedCollectionIds.contains(featuredEmojiPack.info.id) { continue } + + let supergroupId = featuredEmojiPack.info.id + let groupId: AnyHashable = supergroupId + + if skippedCollectionIds.contains(groupId) { + continue + } for item in featuredEmojiPack.topItems { var tintMode: Item.TintMode = .none if item.file.isCustomTemplateEmoji { - if isStatusSelection { - tintMode = .accent + if [.status, .backgroundIcon].contains(subject) { + if let backgroundIconColor { + tintMode = .custom(backgroundIconColor) + } else { + tintMode = .accent + } } else { tintMode = .primary } + } else if case .backgroundIcon = subject { + skippedCollectionIds.insert(groupId) + continue } let animationData = EntityKeyboardAnimationData(file: item.file) @@ -7980,8 +8116,6 @@ public final class EmojiPagerContentComponent: Component { tintMode: tintMode ) - let supergroupId = featuredEmojiPack.info.id - let groupId: AnyHashable = supergroupId let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium if isPremiumLocked && isPremiumDisabled { continue @@ -8030,13 +8164,13 @@ public final class EmojiPagerContentComponent: Component { var displaySearchWithPlaceholder: String? let searchInitiallyHidden = true if hasSearch { - if isReactionSelection { + if [.reaction, .quickReaction].contains(subject) { displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder - } else if isStatusSelection { + } else if case .status = subject { displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder - } else if isEmojiSelection { + } else if case .emoji = subject { displaySearchWithPlaceholder = strings.EmojiSearch_SearchEmojiPlaceholder - } else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection { + } else if [.profilePhoto, .groupPhoto].contains(subject) { displaySearchWithPlaceholder = strings.Common_Search } } @@ -8080,10 +8214,14 @@ public final class EmojiPagerContentComponent: Component { displayPremiumBadges: false, headerItem: headerItem, fillWithLoadingPlaceholders: false, + customTintColor: backgroundIconColor, items: group.items ) } + let warpContentsOnEdges = [.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject) + let enableLongPress = [.reaction, .status].contains(subject) + return EmojiPagerContentComponent( id: "emoji", context: context, @@ -8096,7 +8234,7 @@ public final class EmojiPagerContentComponent: Component { itemLayoutType: .compact, itemContentUniqueId: nil, searchState: .empty(hasResults: false), - warpContentsOnEdges: isReactionSelection || isStatusSelection || isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection, + warpContentsOnEdges: warpContentsOnEdges, hideBackground: hideBackground, displaySearchWithPlaceholder: displaySearchWithPlaceholder, searchCategories: searchCategories, @@ -8104,7 +8242,7 @@ public final class EmojiPagerContentComponent: Component { searchAlwaysActive: false, searchIsPlaceholderOnly: false, emptySearchResults: nil, - enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection, + enableLongPress: enableLongPress, selectedItems: selectedItems ) } @@ -8287,7 +8425,20 @@ public final class EmojiPagerContentComponent: Component { let trendingIsPremium = featuredStickersConfiguration?.isPremium ?? false let title = trendingIsPremium ? strings.Stickers_TrendingPremiumStickers : strings.StickerPacksSettings_FeaturedPacks - itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem])) + itemGroups.append( + ItemGroup( + supergroupId: groupId, + id: groupId, + title: title, + subtitle: nil, + actionButtonTitle: nil, + isPremiumLocked: false, + isFeatured: false, + displayPremiumBadges: false, + headerItem: nil, + items: [resultItem] + ) + ) } } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift index 7bc60ace4e..1724a2d761 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboard.swift @@ -610,6 +610,7 @@ public final class EntityKeyboardComponent: Component { animationRenderer: emojiContent.animationRenderer, theme: component.theme, title: itemGroup.title ?? "", + customTintColor: itemGroup.customTintColor, pressed: { [weak self] in self?.scrollToItemGroup(contentId: "emoji", groupId: itemGroup.supergroupId, subgroupId: nil) } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index 493ed6d3a3..860a02694f 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -24,6 +24,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { let animationCache: AnimationCache let animationRenderer: MultiAnimationRenderer let theme: PresentationTheme + let customTintColor: UIColor? let title: String let pressed: () -> Void @@ -36,6 +37,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { animationRenderer: MultiAnimationRenderer, theme: PresentationTheme, title: String, + customTintColor: UIColor? = nil, pressed: @escaping () -> Void ) { self.context = context @@ -46,6 +48,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { self.animationRenderer = animationRenderer self.theme = theme self.title = title + self.customTintColor = customTintColor self.pressed = pressed } @@ -74,6 +77,9 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { if lhs.title != rhs.title { return false } + if lhs.customTintColor != rhs.customTintColor { + return false + } return true } @@ -109,6 +115,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { let displaySize = dimensions.aspectFitted(CGSize(width: 44.0, height: 44.0)) if self.itemLayer == nil { + let tintColor: EmojiPagerContentComponent.Item.TintMode = component.customTintColor.flatMap { .custom($0) } ?? .primary let itemLayer = EmojiPagerContentComponent.View.ItemLayer( item: EmojiPagerContentComponent.Item( animationData: component.item, @@ -116,7 +123,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { itemFile: nil, subgroupId: nil, icon: .none, - tintMode: component.item.isTemplate ? .primary : .none + tintMode: component.item.isTemplate ? tintColor : .none ), context: component.context, attemptSynchronousLoad: false, @@ -165,6 +172,8 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor case .accent: itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor + case let .custom(color): + itemLayer.layerTintColor = component.customTintColor?.cgColor ?? color.cgColor } itemLayer.isVisibleForAnimations = itemEnvironment.isContentInFocus && component.context.sharedContext.energyUsageSettings.loopEmoji diff --git a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift index d1f3df7b5b..59e48bdfc0 100644 --- a/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift +++ b/submodules/TelegramUI/Components/ForumCreateTopicScreen/Sources/ForumCreateTopicScreen.swift @@ -513,11 +513,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isEmojiSelection: false, + subject: .topicIcon, hasTrending: false, - isTopicIconSelection: true, topReactionItems: [], areUnicodeEmojiEnabled: false, areCustomEmojiEnabled: true, @@ -582,11 +579,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { animationCache: self.context.animationCache, animationRenderer: self.context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isEmojiSelection: false, + subject: .topicIcon, hasTrending: false, - isTopicIconSelection: true, topReactionItems: [], areUnicodeEmojiEnabled: false, areCustomEmojiEnabled: true, @@ -916,9 +910,6 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent { } for featuredEmojiPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) { if featuredEmojiPack.info.id == collectionId { - // if let strongSelf = self { - // strongSelf.scheduledEmojiContentAnimationHint = EmojiPagerContentComponent.ContentAnimation(type: .groupInstalled(id: collectionId)) - // } let _ = accountContext.engine.stickers.addStickerPackInteractively(info: featuredEmojiPack.info, items: featuredEmojiPack.topItems).start() break diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 60472d13d4..427d27eb05 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -1960,9 +1960,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate animationCache: controller.context.animationCache, animationRenderer: controller.context.animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: false, - isEmojiSelection: true, + subject: .emoji, hasTrending: true, topReactionItems: [], areUnicodeEmojiEnabled: true, diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD index 83c7ebc642..62d50a0c84 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/UndoUI", "//submodules/WallpaperBackgroundNode", "//submodules/TelegramUI/Components/EmojiStatusComponent", + "//submodules/TelegramUI/Components/EntityKeyboard", "//submodules/SolidRoundedButtonNode", "//submodules/AppBundle", ], diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackgroundEmojiItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackgroundEmojiItem.swift deleted file mode 100644 index 6847d92576..0000000000 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/BackgroundEmojiItem.swift +++ /dev/null @@ -1,429 +0,0 @@ -import Foundation -import UIKit -import Display -import AsyncDisplayKit -import SwiftSignalKit -import TelegramPresentationData -import Postbox -import TelegramCore -import ItemListUI -import EmojiStatusComponent -import ComponentFlow -import AccountContext - -enum ItemListReactionArrowStyle { - case arrow - case none -} - -final class BackgroundEmojiItem: ListViewItem, ItemListItem { - let context: AccountContext - let presentationData: ItemListPresentationData - let icon: UIImage? - let title: String - let arrowStyle: ItemListReactionArrowStyle - let reaction: MessageReaction.Reaction - let availableReactions: AvailableReactions? - public let sectionId: ItemListSectionId - let style: ItemListStyle - let action: (() -> Void)? - public let tag: ItemListItemTag? - - public init(context: AccountContext, presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, arrowStyle: ItemListReactionArrowStyle = .none, reaction: MessageReaction.Reaction, availableReactions: AvailableReactions?, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, tag: ItemListItemTag? = nil) { - self.context = context - self.presentationData = presentationData - self.icon = icon - self.title = title - self.arrowStyle = arrowStyle - self.reaction = reaction - self.availableReactions = availableReactions - self.sectionId = sectionId - self.style = style - self.action = action - self.tag = tag - } - - public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { - async { - Queue.mainQueue().async { - let node = BackgroundEmojiItemNode() - let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) - - node.contentSize = layout.contentSize - node.insets = layout.insets - - completion(node, { - return (nil, { _ in apply() }) - }) - } - } - } - - public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { - Queue.mainQueue().async { - if let nodeValue = node() as? BackgroundEmojiItemNode { - let makeLayout = nodeValue.asyncLayout() - - async { - let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) - Queue.mainQueue().async { - completion(layout, { _ in - apply() - }) - } - } - } - } - } - - public var selectable: Bool = true - - public func selected(listView: ListView){ - listView.clearHighlightAnimated(true) - self.action?() - } -} - -private let badgeFont = Font.regular(15.0) - -final class BackgroundEmojiItemNode: ListViewItemNode, ItemListItemNode { - private let backgroundNode: ASDisplayNode - private let topStripeNode: ASDisplayNode - private let bottomStripeNode: ASDisplayNode - private let highlightedBackgroundNode: ASDisplayNode - private let maskNode: ASImageNode - - let iconNode: ASImageNode - let titleNode: TextNode - let arrowNode: ASImageNode - let iconView: ComponentHostView - - private let activateArea: AccessibilityAreaNode - - private var item: BackgroundEmojiItem? - private var fileDisposable: Disposable? - private var file: TelegramMediaFile? - - override var canBeSelected: Bool { - if let item = self.item, let _ = item.action { - return true - } else { - return false - } - } - - var tag: ItemListItemTag? { - return self.item?.tag - } - - init() { - self.backgroundNode = ASDisplayNode() - self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = .white - - self.maskNode = ASImageNode() - self.maskNode.isUserInteractionEnabled = false - - self.topStripeNode = ASDisplayNode() - self.topStripeNode.isLayerBacked = true - - self.bottomStripeNode = ASDisplayNode() - self.bottomStripeNode.isLayerBacked = true - - self.iconNode = ASImageNode() - self.iconNode.isLayerBacked = true - self.iconNode.displaysAsynchronously = false - - self.titleNode = TextNode() - self.titleNode.isUserInteractionEnabled = false - - self.iconView = ComponentHostView() - - self.arrowNode = ASImageNode() - self.arrowNode.displayWithoutProcessing = true - self.arrowNode.displaysAsynchronously = false - self.arrowNode.isLayerBacked = true - - self.highlightedBackgroundNode = ASDisplayNode() - self.highlightedBackgroundNode.isLayerBacked = true - - self.activateArea = AccessibilityAreaNode() - - super.init(layerBacked: false, dynamicBounce: false) - - self.addSubnode(self.titleNode) - self.view.addSubview(self.iconView) - self.addSubnode(self.arrowNode) - - self.addSubnode(self.activateArea) - } - - deinit { - self.fileDisposable?.dispose() - } - - func asyncLayout() -> (_ item: BackgroundEmojiItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { - let makeTitleLayout = TextNode.asyncLayout(self.titleNode) - - let currentItem = self.item - - return { item, params, neighbors in - var rightInset: CGFloat - rightInset = 34.0 + params.rightInset - let _ = rightInset - - let contentSize: CGSize - let insets: UIEdgeInsets - let separatorHeight = UIScreenPixel - let itemBackgroundColor: UIColor - let itemSeparatorColor: UIColor - - var updatedTheme: PresentationTheme? - var updateArrowImage: UIImage? - if currentItem?.presentationData.theme !== item.presentationData.theme { - updatedTheme = item.presentationData.theme - updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme) - } - - var updateIcon = false - if currentItem?.icon != item.icon { - updateIcon = true - } - - var leftInset = 16.0 + params.leftInset - if item.icon != nil { - leftInset += 43.0 - } - - var additionalTextRightInset: CGFloat = 0.0 - additionalTextRightInset += 44.0 - if item.arrowStyle == .arrow { - additionalTextRightInset += 24.0 - } - - let titleColor: UIColor = item.presentationData.theme.list.itemPrimaryTextColor - - let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize) - - let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset - additionalTextRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - - let verticalInset: CGFloat = 11.0 - - let height: CGFloat - height = verticalInset * 2.0 + titleLayout.size.height - - switch item.style { - case .plain: - itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor - itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor - contentSize = CGSize(width: params.width, height: height) - insets = itemListNeighborsPlainInsets(neighbors) - case .blocks: - itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor - itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor - contentSize = CGSize(width: params.width, height: height) - insets = itemListNeighborsGroupedInsets(neighbors, params) - } - - let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) - - return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in - if let strongSelf = self { - strongSelf.item = item - - strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height)) - strongSelf.activateArea.accessibilityLabel = item.title - - strongSelf.activateArea.accessibilityTraits = [] - - if let icon = item.icon { - if strongSelf.iconNode.supernode == nil { - strongSelf.addSubnode(strongSelf.iconNode) - } - if updateIcon { - strongSelf.iconNode.image = icon - } - let iconY = floor((layout.contentSize.height - icon.size.height) / 2.0) - strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size) - } else if strongSelf.iconNode.supernode != nil { - strongSelf.iconNode.image = nil - strongSelf.iconNode.removeFromSupernode() - } - - if let _ = updatedTheme { - strongSelf.topStripeNode.backgroundColor = itemSeparatorColor - strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor - strongSelf.backgroundNode.backgroundColor = itemBackgroundColor - strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor - - } - - if let updateArrowImage = updateArrowImage { - strongSelf.arrowNode.image = updateArrowImage - } - - let _ = titleApply() - - switch item.style { - case .plain: - if strongSelf.backgroundNode.supernode != nil { - strongSelf.backgroundNode.removeFromSupernode() - } - if strongSelf.topStripeNode.supernode != nil { - strongSelf.topStripeNode.removeFromSupernode() - } - if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0) - } - if strongSelf.maskNode.supernode != nil { - strongSelf.maskNode.removeFromSupernode() - } - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight)) - case .blocks: - if strongSelf.backgroundNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) - } - if strongSelf.topStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) - } - if strongSelf.bottomStripeNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) - } - if strongSelf.maskNode.supernode == nil { - strongSelf.insertSubnode(strongSelf.maskNode, at: 3) - } - - let hasCorners = itemListHasRoundedBlockLayout(params) - var hasTopCorners = false - var hasBottomCorners = false - switch neighbors.top { - case .sameSection(false): - strongSelf.topStripeNode.isHidden = true - default: - hasTopCorners = true - strongSelf.topStripeNode.isHidden = hasCorners - } - let bottomStripeInset: CGFloat - switch neighbors.bottom { - case .sameSection(false): - bottomStripeInset = leftInset - strongSelf.bottomStripeNode.isHidden = false - default: - bottomStripeInset = 0.0 - hasBottomCorners = true - strongSelf.bottomStripeNode.isHidden = hasCorners - } - - strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil - - strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) - strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0) - strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight)) - strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight)) - } - - let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 11.0), size: titleLayout.size) - strongSelf.titleNode.frame = titleFrame - - var animationContent: EmojiStatusComponent.AnimationContent? - switch item.reaction { - case .builtin: - if let availableReactions = item.availableReactions { - for reaction in availableReactions.reactions { - if reaction.value == item.reaction { - animationContent = .file(file: reaction.selectAnimation) - break - } - } - } - case let .custom(fileId): - animationContent = .customEmoji(fileId: fileId) - } - - - var rightInset: CGFloat = 0.0 - if let arrowImage = strongSelf.arrowNode.image, item.arrowStyle == .arrow { - let arrowRightOffset: CGFloat = 7.0 - strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - arrowRightOffset - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size) - rightInset += arrowRightOffset + arrowImage.size.width - strongSelf.arrowNode.isHidden = false - } else { - strongSelf.arrowNode.isHidden = true - } - - if let animationContent = animationContent { - let iconBoundingSize = CGSize(width: 28.0, height: 28.0) - let iconOffsetX: CGFloat = 0.0 - let iconSize = strongSelf.iconView.update( - transition: .immediate, - component: AnyComponent(EmojiStatusComponent( - context: item.context, - animationCache: item.context.animationCache, - animationRenderer: item.context.animationRenderer, - content: .animation(content: animationContent, size: iconBoundingSize, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever), - isVisibleForAnimations: true, - action: nil - )), - environment: {}, - containerSize: iconBoundingSize - ) - strongSelf.iconView.isUserInteractionEnabled = false - strongSelf.iconView.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - iconSize.width + iconOffsetX - rightInset, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize) - } - - strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel)) - } - }) - } - } - - override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) { - super.setHighlighted(highlighted, at: point, animated: animated) - - if highlighted { - self.highlightedBackgroundNode.alpha = 1.0 - if self.highlightedBackgroundNode.supernode == nil { - var anchorNode: ASDisplayNode? - if self.bottomStripeNode.supernode != nil { - anchorNode = self.bottomStripeNode - } else if self.topStripeNode.supernode != nil { - anchorNode = self.topStripeNode - } else if self.backgroundNode.supernode != nil { - anchorNode = self.backgroundNode - } - if let anchorNode = anchorNode { - self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode) - } else { - self.addSubnode(self.highlightedBackgroundNode) - } - } - } else { - if self.highlightedBackgroundNode.supernode != nil { - if animated { - self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in - if let strongSelf = self { - if completed { - strongSelf.highlightedBackgroundNode.removeFromSupernode() - } - } - }) - self.highlightedBackgroundNode.alpha = 0.0 - } else { - self.highlightedBackgroundNode.removeFromSupernode() - } - } - } - } - - override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) - } - - override func animateAdded(_ currentTimestamp: Double, duration: Double) { - self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - } - - override func animateRemoved(_ currentTimestamp: Double, duration: Double) { - self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) - } -} diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift new file mode 100644 index 0000000000..db73a18f92 --- /dev/null +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/EmojiPickerItem.swift @@ -0,0 +1,380 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit +import SwiftSignalKit +import ComponentFlow +import ItemListUI +import TelegramPresentationData +import EntityKeyboard +import PagerComponent +import AccountContext + +final class EmojiPickerItem: ListViewItem, ItemListItem { + let context: AccountContext + let theme: PresentationTheme + let strings: PresentationStrings + let emojiContent: EmojiPagerContentComponent + let backgroundIconColor: UIColor + let sectionId: ItemListSectionId + + init( + context: AccountContext, + theme: PresentationTheme, + strings: PresentationStrings, + emojiContent: EmojiPagerContentComponent, + backgroundIconColor: UIColor, + sectionId: ItemListSectionId + ) { + self.context = context + self.theme = theme + self.strings = strings + self.emojiContent = emojiContent + self.backgroundIconColor = backgroundIconColor + self.sectionId = sectionId + } + + func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { + async { + let node = EmojiPickerItemNode() + let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + + node.contentSize = layout.contentSize + node.insets = layout.insets + + Queue.mainQueue().async { + completion(node, { + return (nil, { _ in apply() }) + }) + } + } + } + + func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { + Queue.mainQueue().async { + if let nodeValue = node() as? EmojiPickerItemNode { + let makeLayout = nodeValue.asyncLayout() + + async { + let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem)) + Queue.mainQueue().async { + completion(layout, { _ in + apply() + }) + } + } + } + } + } +} + +final class EmojiPickerItemNode: ListViewItemNode { + private let backgroundNode: ASDisplayNode + private let topStripeNode: ASDisplayNode + private let bottomStripeNode: ASDisplayNode + private let maskNode: ASImageNode + + private let picker = ComponentView() + + private var item: EmojiPickerItem? + + private let disposable = MetaDisposable() + + init() { + self.backgroundNode = ASDisplayNode() + self.backgroundNode.isLayerBacked = true + + self.topStripeNode = ASDisplayNode() + self.topStripeNode.isLayerBacked = true + + self.bottomStripeNode = ASDisplayNode() + self.bottomStripeNode.isLayerBacked = true + + self.maskNode = ASImageNode() + self.maskNode.isUserInteractionEnabled = false + + super.init(layerBacked: false, dynamicBounce: false) + + self.clipsToBounds = true + } + + deinit { + self.disposable.dispose() + } + + func asyncLayout() -> (_ item: EmojiPickerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { + let currentItem = self.item + + return { item, params, neighbors in + let insets: UIEdgeInsets + let separatorHeight = UIScreenPixel + + let contentSize = CGSize(width: params.width, height: 190.0) + insets = itemListNeighborsGroupedInsets(neighbors, params) + + let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets) + let layoutSize = layout.size + + return (layout, { [weak self] in + if let strongSelf = self { + strongSelf.item = item + + if let currentItem, currentItem.backgroundIconColor != item.backgroundIconColor { + if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size) + strongSelf.view.addSubview(snapshot) + snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + snapshot.removeFromSuperview() + }) + } + } + + strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor + strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor + + if strongSelf.backgroundNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0) + } + if strongSelf.topStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1) + } + if strongSelf.bottomStripeNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2) + } + if strongSelf.maskNode.supernode == nil { + strongSelf.insertSubnode(strongSelf.maskNode, at: 3) + } + + let hasCorners = itemListHasRoundedBlockLayout(params) + var hasTopCorners = false + var hasBottomCorners = false + switch neighbors.top { + case .sameSection(false): + strongSelf.topStripeNode.isHidden = true + default: + hasTopCorners = true + strongSelf.topStripeNode.isHidden = hasCorners + } + let bottomStripeInset: CGFloat + let bottomStripeOffset: CGFloat + switch neighbors.bottom { + case .sameSection(false): + bottomStripeInset = 0.0 + bottomStripeOffset = -separatorHeight + strongSelf.bottomStripeNode.isHidden = false + default: + bottomStripeInset = 0.0 + bottomStripeOffset = 0.0 + hasBottomCorners = true + strongSelf.bottomStripeNode.isHidden = hasCorners + } + + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + + strongSelf.backgroundNode.frame = backgroundFrame + strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil + strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) + strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) + strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight)) + + let pickerSize = strongSelf.picker.update( + transition: .immediate, + component: AnyComponent( + EmojiSelectionComponent( + theme: item.theme, + strings: item.strings, + deviceMetrics: .iPhone14ProMax, + emojiContent: item.emojiContent, + backgroundColor: item.theme.list.itemBlocksBackgroundColor, + separatorColor: item.theme.list.itemBlocksSeparatorColor + ) + ), + environment: {}, + containerSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: contentSize.height) + ) + if let view = strongSelf.picker.view { + if view.superview == nil { + view.disablesInteractiveTransitionGestureRecognizer = true + strongSelf.view.insertSubview(view, at: 1) + } + view.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: pickerSize) + } + } + }) + } + } + + override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) + } + + override func animateRemoved(_ currentTimestamp: Double, duration: Double) { + self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) + } +} + +private final class EmojiSelectionComponent: Component { + public typealias EnvironmentType = Empty + + public let theme: PresentationTheme + public let strings: PresentationStrings + public let deviceMetrics: DeviceMetrics + public let emojiContent: EmojiPagerContentComponent + public let backgroundColor: UIColor + public let separatorColor: UIColor + + public init( + theme: PresentationTheme, + strings: PresentationStrings, + deviceMetrics: DeviceMetrics, + emojiContent: EmojiPagerContentComponent, + backgroundColor: UIColor, + separatorColor: UIColor + ) { + self.theme = theme + self.strings = strings + self.deviceMetrics = deviceMetrics + self.emojiContent = emojiContent + self.backgroundColor = backgroundColor + self.separatorColor = separatorColor + } + + public static func ==(lhs: EmojiSelectionComponent, rhs: EmojiSelectionComponent) -> Bool { + if lhs.theme !== rhs.theme { + return false + } + if lhs.strings != rhs.strings { + return false + } + if lhs.deviceMetrics != rhs.deviceMetrics { + return false + } + if lhs.emojiContent != rhs.emojiContent { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.separatorColor != rhs.separatorColor { + return false + } + return true + } + + public final class View: UIView { + private let keyboardView: ComponentView + private let keyboardClippingView: UIView + private let panelHostView: PagerExternalTopPanelContainer + private let panelBackgroundView: BlurredBackgroundView + private let panelSeparatorView: UIView + + private var component: EmojiSelectionComponent? + private weak var state: EmptyComponentState? + + override init(frame: CGRect) { + self.keyboardView = ComponentView() + self.keyboardClippingView = UIView() + self.panelHostView = PagerExternalTopPanelContainer() + self.panelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) + self.panelSeparatorView = UIView() + + super.init(frame: frame) + + self.addSubview(self.keyboardClippingView) + self.addSubview(self.panelBackgroundView) + self.addSubview(self.panelSeparatorView) + self.addSubview(self.panelHostView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + func update(component: EmojiSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + self.backgroundColor = component.backgroundColor + let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85) + self.panelBackgroundView.updateColor(color: panelBackgroundColor, transition: .immediate) + self.panelSeparatorView.backgroundColor = component.separatorColor + + self.component = component + self.state = state + + let topPanelHeight: CGFloat = 42.0 + + let keyboardSize = self.keyboardView.update( + transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)), + component: AnyComponent(EntityKeyboardComponent( + theme: component.theme, + strings: component.strings, + isContentInFocus: false, + containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0), + topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0), + emojiContent: component.emojiContent, + stickerContent: nil, + maskContent: nil, + gifContent: nil, + hasRecentGifs: false, + availableGifSearchEmojies: [], + defaultToEmojiTab: true, + externalTopPanelContainer: self.panelHostView, + externalBottomPanelContainer: nil, + displayTopPanelBackground: .blur, + topPanelExtensionUpdated: { _, _ in }, + topPanelScrollingOffset: { _, _ in }, + hideInputUpdated: { _, _, _ in }, + hideTopPanelUpdated: { _, _ in }, + switchToTextInput: {}, + switchToGifSubject: { _ in }, + reorderItems: { _, _ in }, + makeSearchContainerNode: { _ in return nil }, + contentIdUpdated: { _ in }, + deviceMetrics: component.deviceMetrics, + hiddenInputHeight: 0.0, + inputHeight: 0.0, + displayBottomPanel: false, + isExpanded: true, + clipContentToTopPanel: false, + useExternalSearchContainer: false + )), + environment: {}, + containerSize: availableSize + ) + if let keyboardComponentView = self.keyboardView.view { + if keyboardComponentView.superview == nil { + self.keyboardClippingView.addSubview(keyboardComponentView) + } + + if panelBackgroundColor.alpha < 0.01 { + self.keyboardClippingView.clipsToBounds = true + } else { + self.keyboardClippingView.clipsToBounds = false + } + + transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: availableSize.width, height: availableSize.height - topPanelHeight))) + + transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight), size: keyboardSize)) + transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0))) + + transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight))) + self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition) + + transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel))) + transition.setAlpha(view: self.panelSeparatorView, alpha: 1.0) + } + + return availableSize + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index 57146fdb98..dfa5a96e61 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -12,7 +12,7 @@ import PresentationDataUtils import AccountContext import WallpaperBackgroundNode -class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem { +final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem { struct MessageItem: Equatable { static func == (lhs: MessageItem, rhs: MessageItem) -> Bool { if lhs.outgoing != rhs.outgoing { @@ -131,7 +131,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { private var itemHeaderNodes: [ListViewItemNode.HeaderId: ListViewItemHeaderNode] = [:] private var item: PeerNameColorChatPreviewItem? - private var finalImage = true private let disposable = MetaDisposable() @@ -255,6 +254,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor { if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) { + snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size) strongSelf.view.addSubview(snapshot) snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in snapshot.removeFromSuperview() @@ -347,7 +347,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil - let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) + let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) let displayMode: WallpaperDisplayMode if abs(params.availableHeight - params.width) < 100.0, params.availableHeight > 700.0 { @@ -365,7 +365,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { } if let backgroundNode = strongSelf.backgroundNode { - backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) + backgroundNode.frame = backgroundFrame backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate) } strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift index 5f10c6c242..45168b1b10 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorScreen.swift @@ -11,20 +11,21 @@ import ItemListUI import PresentationDataUtils import AccountContext import UndoUI +import EntityKeyboard private final class PeerNameColorScreenArguments { let context: AccountContext let updateNameColor: (PeerNameColor) -> Void - let openBackgroundEmoji: () -> Void + let updateBackgroundEmojiId: (Int64?) -> Void init( context: AccountContext, updateNameColor: @escaping (PeerNameColor) -> Void, - openBackgroundEmoji: @escaping () -> Void + updateBackgroundEmojiId: @escaping (Int64?) -> Void ) { self.context = context self.updateNameColor = updateNameColor - self.openBackgroundEmoji = openBackgroundEmoji + self.updateBackgroundEmojiId = updateBackgroundEmojiId } } @@ -39,22 +40,22 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { case colorMessage case colorPicker case colorDescription + case backgroundEmojiHeader case backgroundEmoji - case backgroundEmojiDescription } case colorHeader(String) case colorMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, items: [PeerNameColorChatPreviewItem.MessageItem]) case colorPicker(colors: [PeerNameColor], currentColor: PeerNameColor) case colorDescription(String) - case backgroundEmoji(String, MessageReaction.Reaction, AvailableReactions) - case backgroundEmojiDescription(String) + case backgroundEmojiHeader(String) + case backgroundEmoji(EmojiPagerContentComponent, UIColor) var section: ItemListSectionId { switch self { case .colorHeader, .colorMessage, .colorPicker, .colorDescription: return PeerNameColorScreenSection.nameColor.rawValue - case .backgroundEmoji, .backgroundEmojiDescription: + case .backgroundEmojiHeader, .backgroundEmoji: return PeerNameColorScreenSection.backgroundEmoji.rawValue } } @@ -69,10 +70,10 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { return .colorPicker case .colorDescription: return .colorDescription + case .backgroundEmojiHeader: + return .backgroundEmojiHeader case .backgroundEmoji: return .backgroundEmoji - case .backgroundEmojiDescription: - return .backgroundEmojiDescription } } @@ -86,9 +87,9 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { return 2 case .colorDescription: return 3 - case .backgroundEmoji: + case .backgroundEmojiHeader: return 4 - case .backgroundEmojiDescription: + case .backgroundEmoji: return 5 } } @@ -119,14 +120,14 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { } else { return false } - case let .backgroundEmoji(lhsText, lhsReaction, lhsAvailableReactions): - if case let .backgroundEmoji(rhsText, rhsReaction, rhsAvailableReactions) = rhs, lhsText == rhsText, lhsReaction == rhsReaction, lhsAvailableReactions == rhsAvailableReactions { + case let .backgroundEmojiHeader(text): + if case .backgroundEmojiHeader(text) = rhs { return true } else { return false } - case let .backgroundEmojiDescription(text): - if case .backgroundEmojiDescription(text) = rhs { + case let .backgroundEmoji(lhsEmojiContent, lhsBackgroundIconColor): + if case let .backgroundEmoji(rhsEmojiContent, rhsBackgroundIconColor) = rhs, lhsEmojiContent == rhsEmojiContent, lhsBackgroundIconColor == rhsBackgroundIconColor { return true } else { return false @@ -168,33 +169,25 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry { ) case let .colorDescription(text): return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) - case let .backgroundEmoji(title, reaction, availableReactions): - return BackgroundEmojiItem( - context: arguments.context, - presentationData: presentationData, - title: title, - reaction: reaction, - availableReactions: availableReactions, - sectionId: self.section, - style: .blocks, - action: { - arguments.openBackgroundEmoji() - }) - case let .backgroundEmojiDescription(text): - return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section) + case let .backgroundEmojiHeader(text): + return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) + case let .backgroundEmoji(emojiContent, backgroundIconColor): + return EmojiPickerItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, emojiContent: emojiContent, backgroundIconColor: backgroundIconColor, sectionId: self.section) } } } private struct PeerNameColorScreenState: Equatable { var updatedNameColor: PeerNameColor? + var updatedBackgroundEmojiId: Int64? } private func peerNameColorScreenEntries( presentationData: PresentationData, state: PeerNameColorScreenState, peer: EnginePeer?, - isPremium: Bool + isPremium: Bool, + emojiContent: EmojiPagerContentComponent ) -> [PeerNameColorScreenEntry] { var entries: [PeerNameColorScreenEntry] = [] @@ -203,7 +196,6 @@ private func peerNameColorScreenEntries( .blue ] allColors.append(contentsOf: PeerNameColor.allCases.filter { $0 != .blue}) - allColors.removeLast(3) let nameColor: PeerNameColor if let updatedNameColor = state.updatedNameColor { @@ -214,6 +206,19 @@ private func peerNameColorScreenEntries( nameColor = .blue } + let backgroundEmojiId: Int64? + if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId { + if updatedBackgroundEmojiId == 0 { + backgroundEmojiId = nil + } else { + backgroundEmojiId = updatedBackgroundEmojiId + } + } else if let emojiId = peer.backgroundEmojiId { + backgroundEmojiId = emojiId + } else { + backgroundEmojiId = nil + } + let replyText: String let messageText: String if case .channel = peer { @@ -229,13 +234,11 @@ private func peerNameColorScreenEntries( author: peer.compactDisplayTitle, photo: peer.profileImageRepresentations, nameColor: nameColor, - backgroundEmojiId: nil, + backgroundEmojiId: backgroundEmojiId, reply: (peer.compactDisplayTitle, replyText), linkPreview: (presentationData.strings.NameColor_ChatPreview_LinkSite, presentationData.strings.NameColor_ChatPreview_LinkTitle, presentationData.strings.NameColor_ChatPreview_LinkText), text: messageText ) - - entries.append(.colorHeader(presentationData.strings.NameColor_ChatPreview_Title)) entries.append(.colorMessage( wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, @@ -249,11 +252,11 @@ private func peerNameColorScreenEntries( currentColor: nameColor )) entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account)) + + entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title)) + entries.append(.backgroundEmoji(emojiContent, nameColor.color)) } -// entries.append(.backgroundEmoji(presentationData.strings.Settings_QuickReactionSetup_ChooseQuickReaction, reactionSettings.quickReaction, availableReactions)) -// entries.append(.backgroundEmojiDescription(presentationData.strings.Settings_QuickReactionSetup_ChooseQuickReactionInfo)) - return entries } @@ -290,8 +293,12 @@ public func PeerNameColorScreen( return updatedState } }, - openBackgroundEmoji: { - + updateBackgroundEmojiId: { emojiId in + updateState { state in + var updatedState = state + updatedState.updatedBackgroundEmojiId = emojiId + return updatedState + } } ) @@ -303,17 +310,52 @@ public func PeerNameColorScreen( peerId = channelId } + let emojiContent = combineLatest( + statePromise.get(), + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + ) + |> mapToSignal { state, peer in + var selectedEmojiId: Int64? + if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId { + selectedEmojiId = updatedBackgroundEmojiId + } else { + selectedEmojiId = peer?.backgroundEmojiId + } + let nameColor: UIColor + if let updatedNameColor = state.updatedNameColor { + nameColor = updatedNameColor.color + } else { + nameColor = (peer?.nameColor ?? .blue).color + } + return EmojiPagerContentComponent.emojiInputData( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + isStandalone: false, + subject: .backgroundIcon, + hasTrending: false, + topReactionItems: [], + areUnicodeEmojiEnabled: false, + areCustomEmojiEnabled: true, + chatPeerId: context.account.peerId, + selectedItems: Set(selectedEmojiId.flatMap { [EngineMedia.Id(namespace: Namespaces.Media.CloudFile, id: $0)] } ?? []), + backgroundIconColor: nameColor + ) + } + let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData let signal = combineLatest(queue: .mainQueue(), presentationData, statePromise.get(), context.engine.stickers.availableReactions(), - context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)), + emojiContent ) |> deliverOnMainQueue - |> map { presentationData, state, availableReactions, peer -> (ItemListControllerState, (ItemListNodeState, Any)) in + |> map { presentationData, state, availableReactions, peer, emojiContent -> (ItemListControllerState, (ItemListNodeState, Any)) in let isPremium = peer?.isPremium ?? false let title: String + let buttonTitle: String let isLocked: Bool switch subject { case .account: @@ -324,16 +366,33 @@ public func PeerNameColorScreen( isLocked = false } + let backgroundEmojiId: Int64 + if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId { + backgroundEmojiId = updatedBackgroundEmojiId + } else if let emojiId = peer?.backgroundEmojiId { + backgroundEmojiId = emojiId + } else { + backgroundEmojiId = 0 + } + if backgroundEmojiId != 0 { + buttonTitle = presentationData.strings.NameColor_ApplyColorAndBackgroundEmoji + } else { + buttonTitle = presentationData.strings.NameColor_ApplyColor + } + let footerItem = ApplyColorFooterItem( theme: presentationData.theme, - title: presentationData.strings.NameColor_ApplyColor, + title: buttonTitle, locked: isLocked, action: { if isPremium { let state = stateValue.with { $0 } - if let nameColor = state.updatedNameColor { - let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor, backgroundEmojiId: nil).startStandalone() - } + + let nameColor = state.updatedNameColor ?? peer?.nameColor + let backgroundEmojiId = state.updatedBackgroundEmojiId ?? peer?.backgroundEmojiId + + let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0).startStandalone() + dismissImpl?() } else { HapticFeedback().error() @@ -360,11 +419,81 @@ public func PeerNameColorScreen( } ) + emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction( + performItemAction: { _, item, _, _, _, _ in + var selectedFileId: Int64? + if let fileId = item.itemFile?.fileId.id { + selectedFileId = fileId + } else { + selectedFileId = 0 + } + arguments.updateBackgroundEmojiId(selectedFileId) + }, + deleteBackwards: { + }, + openStickerSettings: { + }, + openFeatured: { + }, + openSearch: { + }, + addGroupAction: { groupId, isPremiumLocked, _ in + guard let collectionId = groupId.base as? ItemCollectionId else { + return + } + + let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedEmojiPacks) + let _ = (context.account.postbox.combinedView(keys: [viewKey]) + |> take(1) + |> deliverOnMainQueue).start(next: { views in + guard let view = views.views[viewKey] as? OrderedItemListView else { + return + } + for featuredEmojiPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) { + if featuredEmojiPack.info.id == collectionId { + let _ = context.engine.stickers.addStickerPackInteractively(info: featuredEmojiPack.info, items: featuredEmojiPack.topItems).start() + + break + } + } + }) + }, + clearGroup: { _ in + }, + pushController: { c in + }, + presentController: { c in + }, + presentGlobalOverlayController: { c in + }, + navigationController: { + return nil + }, + requestUpdate: { _ in + }, + updateSearchQuery: { _ in + }, + updateScrollingToItemGroup: { + }, + onScroll: {}, + chatPeerId: nil, + peekBehavior: nil, + customLayout: nil, + externalBackground: nil, + externalExpansionView: nil, + customContentView: nil, + useOpaqueTheme: true, + hideBackground: false, + stateContext: nil, + addImage: nil + ) + let entries = peerNameColorScreenEntries( presentationData: presentationData, state: state, peer: peer, - isPremium: isPremium + isPremium: isPremium, + emojiContent: emojiContent ) let controllerState = ItemListControllerState( @@ -390,77 +519,6 @@ public func PeerNameColorScreen( } let controller = ItemListController(context: context, state: signal) -// openQuickReactionImpl = { [weak controller] in -// let _ = (combineLatest(queue: .mainQueue(), -// settings, -// context.engine.stickers.availableReactions() -// ) -// |> take(1) -// |> deliverOnMainQueue).start(next: { settings, availableReactions in -// var currentSelectedFileId: MediaId? -// switch settings.quickReaction { -// case .builtin: -// if let availableReactions = availableReactions { -// if let reaction = availableReactions.reactions.first(where: { $0.value == settings.quickReaction }) { -// currentSelectedFileId = reaction.selectAnimation.fileId -// break -// } -// } -// case let .custom(fileId): -// currentSelectedFileId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId) -// } -// -// var selectedItems = Set() -// if let currentSelectedFileId = currentSelectedFileId { -// selectedItems.insert(currentSelectedFileId) -// } -// -// guard let controller = controller else { -// return -// } -// var sourceItemNode: ItemListReactionItemNode? -// controller.forEachItemNode { itemNode in -// if let itemNode = itemNode as? ItemListReactionItemNode { -// sourceItemNode = itemNode -// } -// } -// -// if let sourceItemNode = sourceItemNode { -// controller.present(EmojiStatusSelectionController( -// context: context, -// mode: .quickReactionSelection(completion: { -// updateState { state in -// var state = state -// state.hasReaction = false -// return state -// } -// }), -// sourceView: sourceItemNode.iconView, -// emojiContent: EmojiPagerContentComponent.emojiInputData( -// context: context, -// animationCache: context.animationCache, -// animationRenderer: context.animationRenderer, -// isStandalone: false, -// isStatusSelection: false, -// isReactionSelection: true, -// isEmojiSelection: false, -// hasTrending: false, -// isQuickReactionSelection: true, -// topReactionItems: [], -// areUnicodeEmojiEnabled: false, -// areCustomEmojiEnabled: true, -// chatPeerId: context.account.peerId, -// selectedItems: selectedItems -// ), -// currentSelection: nil, -// destinationItemView: { [weak sourceItemNode] in -// return sourceItemNode?.iconView -// } -// ), in: .window(.root)) -// } -// }) -// } - presentImpl = { [weak controller] c in guard let controller else { return diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 4d0af55aa4..774fd6bb0f 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -4261,9 +4261,7 @@ public final class StoryItemSetContainerComponent: Component { animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: true, - isEmojiSelection: false, + subject: .reaction, hasTrending: false, topReactionItems: mappedReactionItems, areUnicodeEmojiEnabled: false, diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 5e53b0390e..513f796e3c 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -1495,9 +1495,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, - isStatusSelection: false, - isReactionSelection: true, - isEmojiSelection: false, + subject: .reaction, hasTrending: false, topReactionItems: reactionItems, areUnicodeEmojiEnabled: false, diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index a2d8b69a9a..50ff185678 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3754,9 +3754,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, - isStatusSelection: true, - isReactionSelection: false, - isEmojiSelection: false, + subject: .status, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: false,