mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Emoji and status improvements
This commit is contained in:
parent
fe3715bd90
commit
e4f1c6aa72
@ -8013,3 +8013,12 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Premium.PricePerMonth" = "%@/month";
|
||||
"Premium.PricePerYear" = "%@/year";
|
||||
|
||||
"SetTimeoutFor.Minutes_1" = "Set for 1 minute";
|
||||
"SetTimeoutFor.Minutes_any" = "Set for %@ minutes";
|
||||
|
||||
"SetTimeoutFor.Hours_1" = "Set for 1 hour";
|
||||
"SetTimeoutFor.Hours_any" = "Set for %@ hours";
|
||||
|
||||
"SetTimeoutFor.Days_1" = "Set for 1 day";
|
||||
"SetTimeoutFor.Days_any" = "Set for %@ days";
|
||||
|
@ -850,6 +850,10 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
private func openStatusSetup(sourceView: UIView) {
|
||||
self.emojiStatusSelectionController?.dismiss()
|
||||
var selectedItems = Set<MediaId>()
|
||||
if let peerStatus = self.titleView.title.peerStatus, case let .emoji(emojiStatus) = peerStatus {
|
||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||
}
|
||||
let controller = EmojiStatusSelectionController(
|
||||
context: self.context,
|
||||
mode: .statusSelection,
|
||||
@ -864,7 +868,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
topReactionItems: [],
|
||||
areUnicodeEmojiEnabled: false,
|
||||
areCustomEmojiEnabled: true,
|
||||
chatPeerId: self.context.account.peerId
|
||||
chatPeerId: self.context.account.peerId,
|
||||
selectedItems: selectedItems
|
||||
),
|
||||
destinationItemView: { [weak sourceView] in
|
||||
return sourceView
|
||||
|
@ -173,7 +173,7 @@ public class ComponentGesture {
|
||||
}
|
||||
|
||||
public class AnyComponent<EnvironmentType>: _TypeErasedComponent, Equatable {
|
||||
fileprivate let wrapped: _TypeErasedComponent
|
||||
public let wrapped: _TypeErasedComponent
|
||||
|
||||
public init<ComponentType: Component>(_ component: ComponentType) where ComponentType.EnvironmentType == EnvironmentType {
|
||||
self.wrapped = component
|
||||
|
@ -487,7 +487,7 @@ private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
|
||||
subtree.position = sublayer.position
|
||||
subtree.bounds = sublayer.bounds
|
||||
subtree.anchorPoint = sublayer.anchorPoint
|
||||
layer.addSublayer(subtree)
|
||||
view.addSublayer(subtree)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ final class PeerTable: Table {
|
||||
}
|
||||
|
||||
private let reverseAssociatedTable: ReverseAssociatedPeerTable
|
||||
//private let peerTimeoutPropertiesTable: PeerTimeoutPropertiesTable
|
||||
|
||||
private let sharedEncoder = PostboxEncoder()
|
||||
private let sharedKey = ValueBoxKey(length: 8)
|
||||
|
@ -1129,7 +1129,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
customLayout: emojiContentLayout,
|
||||
externalBackground: EmojiPagerContentComponent.ExternalBackground(
|
||||
effectContainerView: self.backgroundNode.vibrancyEffectView?.contentView
|
||||
)
|
||||
),
|
||||
useOpaqueTheme: false
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -355,6 +355,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
|
||||
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
|
||||
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
|
||||
dict[701560302] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultStatuses($0) }
|
||||
dict[80008398] = { return Api.InputStickerSet.parse_inputStickerSetEmojiGenericAnimations($0) }
|
||||
dict[-4838507] = { return Api.InputStickerSet.parse_inputStickerSetEmpty($0) }
|
||||
dict[-1645763991] = { return Api.InputStickerSet.parse_inputStickerSetID($0) }
|
||||
@ -697,6 +698,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1954007928] = { return Api.SecureValueType.parse_secureValueTypeRentalAgreement($0) }
|
||||
dict[-368907213] = { return Api.SecureValueType.parse_secureValueTypeTemporaryRegistration($0) }
|
||||
dict[-63531698] = { return Api.SecureValueType.parse_secureValueTypeUtilityBill($0) }
|
||||
dict[-1206095820] = { return Api.SendAsPeer.parse_sendAsPeer($0) }
|
||||
dict[-44119819] = { return Api.SendMessageAction.parse_sendMessageCancelAction($0) }
|
||||
dict[1653390447] = { return Api.SendMessageAction.parse_sendMessageChooseContactAction($0) }
|
||||
dict[-1336228175] = { return Api.SendMessageAction.parse_sendMessageChooseStickerAction($0) }
|
||||
@ -938,7 +940,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-541588713] = { return Api.channels.ChannelParticipant.parse_channelParticipant($0) }
|
||||
dict[-1699676497] = { return Api.channels.ChannelParticipants.parse_channelParticipants($0) }
|
||||
dict[-266911767] = { return Api.channels.ChannelParticipants.parse_channelParticipantsNotModified($0) }
|
||||
dict[-2091463255] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) }
|
||||
dict[-191450938] = { return Api.channels.SendAsPeers.parse_sendAsPeers($0) }
|
||||
dict[182326673] = { return Api.contacts.Blocked.parse_blocked($0) }
|
||||
dict[-513392236] = { return Api.contacts.Blocked.parse_blockedSlice($0) }
|
||||
dict[-353862078] = { return Api.contacts.Contacts.parse_contacts($0) }
|
||||
@ -1556,6 +1558,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureValueType:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SendAsPeer:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SendMessageAction:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ShippingOption:
|
||||
|
@ -1,3 +1,45 @@
|
||||
public extension Api {
|
||||
enum SendAsPeer: TypeConstructorDescription {
|
||||
case sendAsPeer(flags: Int32, peer: Api.Peer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .sendAsPeer(let flags, let peer):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1206095820)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .sendAsPeer(let flags, let peer):
|
||||
return ("sendAsPeer", [("flags", String(describing: flags)), ("peer", String(describing: peer))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_sendAsPeer(_ reader: BufferReader) -> SendAsPeer? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.SendAsPeer.sendAsPeer(flags: _1!, peer: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum SendMessageAction: TypeConstructorDescription {
|
||||
case sendMessageCancelAction
|
||||
@ -842,49 +884,3 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerPack: TypeConstructorDescription {
|
||||
case stickerPack(emoticon: String, documents: [Int64])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(313694676)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
return ("stickerPack", [("emoticon", String(describing: emoticon)), ("documents", String(describing: documents))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,49 @@
|
||||
public extension Api {
|
||||
enum StickerPack: TypeConstructorDescription {
|
||||
case stickerPack(emoticon: String, documents: [Int64])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
if boxed {
|
||||
buffer.appendInt32(313694676)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(documents.count))
|
||||
for item in documents {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerPack(let emoticon, let documents):
|
||||
return ("stickerPack", [("emoticon", String(describing: emoticon)), ("documents", String(describing: documents))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: [Int64]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerSet: TypeConstructorDescription {
|
||||
case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, thumbVersion: Int32?, thumbDocumentId: Int64?, count: Int32, hash: Int32)
|
||||
|
@ -692,13 +692,13 @@ public extension Api.channels {
|
||||
}
|
||||
public extension Api.channels {
|
||||
enum SendAsPeers: TypeConstructorDescription {
|
||||
case sendAsPeers(peers: [Api.Peer], chats: [Api.Chat], users: [Api.User])
|
||||
case sendAsPeers(peers: [Api.SendAsPeer], chats: [Api.Chat], users: [Api.User])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .sendAsPeers(let peers, let chats, let users):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2091463255)
|
||||
buffer.appendInt32(-191450938)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(peers.count))
|
||||
@ -727,9 +727,9 @@ public extension Api.channels {
|
||||
}
|
||||
|
||||
public static func parse_sendAsPeers(_ reader: BufferReader) -> SendAsPeers? {
|
||||
var _1: [Api.Peer]?
|
||||
var _1: [Api.SendAsPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Peer.self)
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SendAsPeer.self)
|
||||
}
|
||||
var _2: [Api.Chat]?
|
||||
if let _ = reader.readInt32() {
|
||||
|
@ -677,6 +677,7 @@ public extension Api {
|
||||
case inputStickerSetAnimatedEmoji
|
||||
case inputStickerSetAnimatedEmojiAnimations
|
||||
case inputStickerSetDice(emoticon: String)
|
||||
case inputStickerSetEmojiDefaultStatuses
|
||||
case inputStickerSetEmojiGenericAnimations
|
||||
case inputStickerSetEmpty
|
||||
case inputStickerSetID(id: Int64, accessHash: Int64)
|
||||
@ -702,6 +703,12 @@ public extension Api {
|
||||
buffer.appendInt32(-427863538)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
if boxed {
|
||||
buffer.appendInt32(701560302)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
if boxed {
|
||||
@ -745,6 +752,8 @@ public extension Api {
|
||||
return ("inputStickerSetAnimatedEmojiAnimations", [])
|
||||
case .inputStickerSetDice(let emoticon):
|
||||
return ("inputStickerSetDice", [("emoticon", String(describing: emoticon))])
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
return ("inputStickerSetEmojiDefaultStatuses", [])
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
return ("inputStickerSetEmojiGenericAnimations", [])
|
||||
case .inputStickerSetEmpty:
|
||||
@ -775,6 +784,9 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiDefaultStatuses
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiGenericAnimations(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiGenericAnimations
|
||||
}
|
||||
|
@ -1182,6 +1182,8 @@ public class Account {
|
||||
self.managedOperationsDisposable.add(managedRecentReactions(postbox: self.postbox, network: self.network).start())
|
||||
self.managedTopReactionsDisposable.set(managedTopReactions(postbox: self.postbox, network: self.network).start())
|
||||
self.managedOperationsDisposable.add(self.managedTopReactionsDisposable)
|
||||
|
||||
self.managedOperationsDisposable.add(_internal_loadedStickerPack(postbox: self.postbox, network: self.network, reference: .iconStatusEmoji, forceActualized: true).start())
|
||||
}
|
||||
|
||||
if !supplementary {
|
||||
|
@ -45,6 +45,7 @@ enum AccountStateUpdatePinnedItemIdsOperation {
|
||||
enum AccountStateUpdateStickerPacksOperation {
|
||||
case add(Api.messages.StickerSet)
|
||||
case reorder(SynchronizeInstalledStickerPacksOperationNamespace, [Int64])
|
||||
case reorderToTop(SynchronizeInstalledStickerPacksOperationNamespace, [Int64])
|
||||
case sync
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,8 @@ extension StickerPackReference {
|
||||
self = .premiumGifts
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
self = .emojiGenericAnimations
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
self = .iconStatusEmoji
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1392,6 +1392,16 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
namespace = .stickers
|
||||
}
|
||||
updatedState.addUpdateInstalledStickerPacks(.reorder(namespace, order))
|
||||
case let .updateMoveStickerSetToTop(flags, stickerset):
|
||||
let namespace: SynchronizeInstalledStickerPacksOperationNamespace
|
||||
if (flags & (1 << 0)) != 0 {
|
||||
namespace = .masks
|
||||
} else if (flags & (1 << 1)) != 0 {
|
||||
namespace = .emoji
|
||||
} else {
|
||||
namespace = .stickers
|
||||
}
|
||||
updatedState.addUpdateInstalledStickerPacks(.reorderToTop(namespace, [stickerset]))
|
||||
case .updateStickerSets:
|
||||
updatedState.addUpdateInstalledStickerPacks(.sync)
|
||||
case .updateSavedGifs:
|
||||
@ -3666,6 +3676,33 @@ func replayFinalState(
|
||||
}
|
||||
transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) })
|
||||
}
|
||||
case let .reorderToTop(namespace, ids):
|
||||
let collectionNamespace: ItemCollectionId.Namespace
|
||||
switch namespace {
|
||||
case .stickers:
|
||||
collectionNamespace = Namespaces.ItemCollection.CloudStickerPacks
|
||||
case .masks:
|
||||
collectionNamespace = Namespaces.ItemCollection.CloudMaskPacks
|
||||
case .emoji:
|
||||
collectionNamespace = Namespaces.ItemCollection.CloudEmojiPacks
|
||||
}
|
||||
let currentInfos = transaction.getItemCollectionsInfos(namespace: collectionNamespace).map { $0.1 as! StickerPackCollectionInfo }
|
||||
|
||||
var currentDict: [ItemCollectionId: StickerPackCollectionInfo] = [:]
|
||||
for info in currentInfos {
|
||||
currentDict[info.id] = info
|
||||
}
|
||||
var updatedInfos: [StickerPackCollectionInfo] = []
|
||||
for id in ids {
|
||||
let currentInfo = currentDict[ItemCollectionId(namespace: collectionNamespace, id: id)]!
|
||||
updatedInfos.append(currentInfo)
|
||||
}
|
||||
for info in currentInfos {
|
||||
if !updatedInfos.contains(where: { $0.id == info.id }) {
|
||||
updatedInfos.append(info)
|
||||
}
|
||||
}
|
||||
transaction.replaceItemCollectionInfos(namespace: collectionNamespace, itemCollectionInfos: updatedInfos.map { ($0.id, $0) })
|
||||
case .sync:
|
||||
syncStickers = true
|
||||
syncMasks = true
|
||||
|
@ -61,7 +61,7 @@ public func addSavedSticker(postbox: Postbox, network: Network, file: TelegramMe
|
||||
if !found {
|
||||
fetchReference = packReference
|
||||
}
|
||||
case .animatedEmoji, .animatedEmojiAnimations, .dice, .premiumGifts, .emojiGenericAnimations:
|
||||
case .animatedEmoji, .animatedEmojiAnimations, .dice, .premiumGifts, .emojiGenericAnimations, .iconStatusEmoji:
|
||||
break
|
||||
}
|
||||
if let fetchReference = fetchReference {
|
||||
|
@ -49,6 +49,7 @@ public struct Namespaces {
|
||||
public static let CloudPremiumGifts: Int32 = 7
|
||||
public static let CloudEmojiPacks: Int32 = 8
|
||||
public static let CloudEmojiGenericAnimations: Int32 = 9
|
||||
public static let CloudIconStatusEmoji: Int32 = 10
|
||||
}
|
||||
|
||||
public struct OrderedItemList {
|
||||
|
@ -21,6 +21,7 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable, Codable {
|
||||
case animatedEmojiAnimations
|
||||
case premiumGifts
|
||||
case emojiGenericAnimations
|
||||
case iconStatusEmoji
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
||||
@ -83,7 +84,7 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable, Codable {
|
||||
encoder.encodeInt32(4, forKey: "r")
|
||||
case .premiumGifts:
|
||||
encoder.encodeInt32(5, forKey: "r")
|
||||
case .emojiGenericAnimations:
|
||||
case .emojiGenericAnimations, .iconStatusEmoji:
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
@ -108,7 +109,7 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable, Codable {
|
||||
try container.encode(4 as Int32, forKey: "r")
|
||||
case .premiumGifts:
|
||||
try container.encode(5 as Int32, forKey: "r")
|
||||
case .emojiGenericAnimations:
|
||||
case .emojiGenericAnimations, .iconStatusEmoji:
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
@ -157,6 +158,12 @@ public enum StickerPackReference: PostboxCoding, Hashable, Equatable, Codable {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .iconStatusEmoji:
|
||||
if case .iconStatusEmoji = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,16 @@ func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo,
|
||||
case let .dice(emoji):
|
||||
namespace = Namespaces.ItemCollection.CloudDice
|
||||
id = Int64(murMurHashString32(emoji))
|
||||
case .emojiGenericAnimations:
|
||||
namespace = Namespaces.ItemCollection.CloudEmojiGenericAnimations
|
||||
id = 0
|
||||
case .iconStatusEmoji:
|
||||
namespace = Namespaces.ItemCollection.CloudIconStatusEmoji
|
||||
id = 0
|
||||
case .id:
|
||||
break
|
||||
default:
|
||||
assertionFailure()
|
||||
break
|
||||
}
|
||||
if let namespace = namespace, let id = id {
|
||||
@ -152,6 +161,20 @@ func _internal_cachedStickerPack(postbox: Postbox, network: Network, reference:
|
||||
} else {
|
||||
return (.fetching, true, nil)
|
||||
}
|
||||
case .iconStatusEmoji:
|
||||
let namespace = Namespaces.ItemCollection.CloudIconStatusEmoji
|
||||
let id: ItemCollectionId.Id = 0
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id))))?.get(CachedStickerPack.self), let info = cached.info {
|
||||
previousHash = cached.hash
|
||||
let current: CachedStickerPackResult = .result(info, cached.items, false)
|
||||
if cached.hash != info.hash {
|
||||
return (current, true, previousHash)
|
||||
} else {
|
||||
return (current, false, previousHash)
|
||||
}
|
||||
} else {
|
||||
return (.fetching, true, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result, loadRemote, previousHash in
|
||||
@ -272,6 +295,18 @@ func cachedStickerPack(transaction: Transaction, reference: StickerPackReference
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id))))?.get(CachedStickerPack.self), let info = cached.info {
|
||||
return (info, cached.items, false)
|
||||
}
|
||||
case .iconStatusEmoji:
|
||||
let namespace = Namespaces.ItemCollection.CloudIconStatusEmoji
|
||||
let id: ItemCollectionId.Id = 0
|
||||
if let currentInfo = transaction.getItemCollectionInfo(collectionId: ItemCollectionId(namespace: namespace, id: id)) as? StickerPackCollectionInfo {
|
||||
let items = transaction.getItemCollectionItems(collectionId: ItemCollectionId(namespace: namespace, id: id))
|
||||
if !items.isEmpty {
|
||||
return (currentInfo, items.compactMap { $0 as? StickerPackItem }, true)
|
||||
}
|
||||
}
|
||||
if let cached = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedStickerPacks, key: CachedStickerPack.cacheKey(ItemCollectionId(namespace: namespace, id: id))))?.get(CachedStickerPack.self), let info = cached.info {
|
||||
return (info, cached.items, false)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ extension StickerPackReference {
|
||||
return .inputStickerSetPremiumGifts
|
||||
case .emojiGenericAnimations:
|
||||
return .inputStickerSetEmojiGenericAnimations
|
||||
case .iconStatusEmoji:
|
||||
return .inputStickerSetEmojiDefaultStatuses
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ func _internal_requestStickerSet(postbox: Postbox, network: Network, reference:
|
||||
case .emojiGenericAnimations:
|
||||
collectionId = nil
|
||||
input = .inputStickerSetEmojiGenericAnimations
|
||||
case .iconStatusEmoji:
|
||||
collectionId = nil
|
||||
input = .inputStickerSetEmojiDefaultStatuses
|
||||
}
|
||||
|
||||
let localSignal: (ItemCollectionId) -> Signal<(ItemCollectionInfo, [ItemCollectionItem])?, NoError> = { collectionId in
|
||||
|
@ -612,6 +612,8 @@ public func makeDefaultDarkPresentationTheme(extendingThemeReference: Presentati
|
||||
panelContentVibrantOverlayColor: UIColor(rgb: 0x808080),
|
||||
panelContentControlVibrantOverlayColor: UIColor(rgb: 0x808080).mixedWith(UIColor(rgb: 0x000000), alpha: 0.35),
|
||||
panelContentControlVibrantSelectionColor: UIColor(white: 1.0, alpha: 0.1),
|
||||
panelContentControlOpaqueOverlayColor: UIColor(white: 1.0, alpha: 0.1),
|
||||
panelContentControlOpaqueSelectionColor: UIColor(white: 1.0, alpha: 0.1),
|
||||
stickersBackgroundColor: UIColor(rgb: 0x000000),
|
||||
stickersSectionTextColor: UIColor(rgb: 0x7b7b7b),
|
||||
stickersSearchBackgroundColor: UIColor(rgb: 0x1c1c1d),
|
||||
|
@ -443,6 +443,8 @@ public func customizeDefaultDarkTintedPresentationTheme(theme: PresentationTheme
|
||||
panelHighlightedIconColor: mainSecondaryTextColor?.withAlphaComponent(0.5).mixedWith(chat.inputPanel.primaryTextColor, alpha: 0.35),
|
||||
panelContentVibrantOverlayColor: mainSecondaryTextColor?.withAlphaComponent(0.5),
|
||||
panelContentControlVibrantOverlayColor: mainSecondaryTextColor?.withAlphaComponent(0.3),
|
||||
panelContentControlOpaqueOverlayColor: mainSecondaryTextColor?.withAlphaComponent(0.3),
|
||||
panelContentControlOpaqueSelectionColor: mainSecondaryTextColor?.withAlphaComponent(0.3),
|
||||
stickersBackgroundColor: additionalBackgroundColor,
|
||||
stickersSectionTextColor: mainSecondaryTextColor?.withAlphaComponent(0.5),
|
||||
stickersSearchBackgroundColor: accentColor?.withMultiplied(hue: 1.009, saturation: 0.621, brightness: 0.15),
|
||||
@ -845,6 +847,8 @@ public func makeDefaultDarkTintedPresentationTheme(extendingThemeReference: Pres
|
||||
panelContentVibrantOverlayColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||
panelContentControlVibrantOverlayColor: mainSecondaryTextColor.withAlphaComponent(0.3),
|
||||
panelContentControlVibrantSelectionColor: mainSecondaryTextColor.withAlphaComponent(0.1),
|
||||
panelContentControlOpaqueOverlayColor: mainSecondaryTextColor.withAlphaComponent(0.1),
|
||||
panelContentControlOpaqueSelectionColor: mainSecondaryTextColor.withAlphaComponent(0.1),
|
||||
stickersBackgroundColor: additionalBackgroundColor,
|
||||
stickersSectionTextColor: mainSecondaryTextColor.withAlphaComponent(0.5),
|
||||
stickersSearchBackgroundColor: accentColor.withMultiplied(hue: 1.009, saturation: 0.621, brightness: 0.15),
|
||||
|
@ -866,6 +866,8 @@ public func makeDefaultDayPresentationTheme(extendingThemeReference: Presentatio
|
||||
panelContentVibrantOverlayColor: UIColor(white: 0.7, alpha: 0.65),
|
||||
panelContentControlVibrantOverlayColor: UIColor(white: 0.85, alpha: 0.65),
|
||||
panelContentControlVibrantSelectionColor: UIColor(white: 0.85, alpha: 0.1),
|
||||
panelContentControlOpaqueOverlayColor: UIColor(white: 0.0, alpha: 0.3),
|
||||
panelContentControlOpaqueSelectionColor: UIColor(white: 0.0, alpha: 0.1),
|
||||
stickersBackgroundColor: UIColor(rgb: 0xe8ebf0),
|
||||
stickersSectionTextColor: UIColor(rgb: 0x9099a2),
|
||||
stickersSearchBackgroundColor: UIColor(rgb: 0xd9dbe1),
|
||||
|
@ -146,6 +146,16 @@ public func muteForIntervalString(strings: PresentationStrings, value: Int32) ->
|
||||
}
|
||||
}
|
||||
|
||||
public func setTimeoutForIntervalString(strings: PresentationStrings, value: Int32) -> String {
|
||||
if value < 60 * 60 {
|
||||
return strings.SetTimeoutFor_Minutes(max(1, value / (60)))
|
||||
} else if value < 60 * 60 * 24 {
|
||||
return strings.SetTimeoutFor_Hours(max(1, value / (60 * 60)))
|
||||
} else {
|
||||
return strings.SetTimeoutFor_Days(max(1, value / (60 * 60 * 24)))
|
||||
}
|
||||
}
|
||||
|
||||
public func mutedForTimeIntervalString(strings: PresentationStrings, value: Int32) -> String {
|
||||
if value < 60 * 60 {
|
||||
return strings.MutedForTime_Minutes(max(1, value / (60)))
|
||||
|
@ -1146,6 +1146,8 @@ public final class PresentationThemeInputMediaPanel {
|
||||
public let panelContentVibrantOverlayColor: UIColor
|
||||
public let panelContentControlVibrantOverlayColor: UIColor
|
||||
public let panelContentControlVibrantSelectionColor: UIColor
|
||||
public let panelContentControlOpaqueOverlayColor: UIColor
|
||||
public let panelContentControlOpaqueSelectionColor: UIColor
|
||||
public let stickersBackgroundColor: UIColor
|
||||
public let stickersSectionTextColor: UIColor
|
||||
public let stickersSearchBackgroundColor: UIColor
|
||||
@ -1163,6 +1165,8 @@ public final class PresentationThemeInputMediaPanel {
|
||||
panelContentVibrantOverlayColor: UIColor,
|
||||
panelContentControlVibrantOverlayColor: UIColor,
|
||||
panelContentControlVibrantSelectionColor: UIColor,
|
||||
panelContentControlOpaqueOverlayColor: UIColor,
|
||||
panelContentControlOpaqueSelectionColor: UIColor,
|
||||
stickersBackgroundColor: UIColor,
|
||||
stickersSectionTextColor: UIColor,
|
||||
stickersSearchBackgroundColor: UIColor,
|
||||
@ -1179,6 +1183,8 @@ public final class PresentationThemeInputMediaPanel {
|
||||
self.panelContentVibrantOverlayColor = panelContentVibrantOverlayColor
|
||||
self.panelContentControlVibrantOverlayColor = panelContentControlVibrantOverlayColor
|
||||
self.panelContentControlVibrantSelectionColor = panelContentControlVibrantSelectionColor
|
||||
self.panelContentControlOpaqueOverlayColor = panelContentControlOpaqueOverlayColor
|
||||
self.panelContentControlOpaqueSelectionColor = panelContentControlOpaqueSelectionColor
|
||||
self.stickersBackgroundColor = stickersBackgroundColor
|
||||
self.stickersSectionTextColor = stickersSectionTextColor
|
||||
self.stickersSearchBackgroundColor = stickersSearchBackgroundColor
|
||||
@ -1197,6 +1203,8 @@ public final class PresentationThemeInputMediaPanel {
|
||||
panelContentVibrantOverlayColor: UIColor? = nil,
|
||||
panelContentControlVibrantOverlayColor: UIColor? = nil,
|
||||
panelContentControlVibrantSelectionColor: UIColor? = nil,
|
||||
panelContentControlOpaqueOverlayColor: UIColor? = nil,
|
||||
panelContentControlOpaqueSelectionColor: UIColor? = nil,
|
||||
stickersBackgroundColor: UIColor? = nil,
|
||||
stickersSectionTextColor: UIColor? = nil,
|
||||
stickersSearchBackgroundColor: UIColor? = nil,
|
||||
@ -1214,6 +1222,8 @@ public final class PresentationThemeInputMediaPanel {
|
||||
panelContentVibrantOverlayColor: panelContentVibrantOverlayColor ?? self.panelContentVibrantOverlayColor,
|
||||
panelContentControlVibrantOverlayColor: panelContentControlVibrantOverlayColor ?? self.panelContentControlVibrantOverlayColor,
|
||||
panelContentControlVibrantSelectionColor: panelContentControlVibrantSelectionColor ?? self.panelContentControlVibrantSelectionColor,
|
||||
panelContentControlOpaqueOverlayColor: panelContentControlOpaqueOverlayColor ?? self.panelContentControlOpaqueOverlayColor,
|
||||
panelContentControlOpaqueSelectionColor: panelContentControlOpaqueSelectionColor ?? self.panelContentControlOpaqueSelectionColor,
|
||||
stickersBackgroundColor: stickersBackgroundColor ?? self.stickersBackgroundColor,
|
||||
stickersSectionTextColor: stickersSectionTextColor ?? self.stickersSectionTextColor,
|
||||
stickersSearchBackgroundColor: stickersSearchBackgroundColor ?? self.stickersSearchBackgroundColor,
|
||||
|
@ -1609,6 +1609,8 @@ extension PresentationThemeInputMediaPanel: Codable {
|
||||
case panelContentVibrantOverlay
|
||||
case panelContentControlVibrantOverlay
|
||||
case panelContentControlVibrantSelection
|
||||
case panelContentControlOpaqueOverlay
|
||||
case panelContentControlOpaqueSelection
|
||||
case stickersBg
|
||||
case stickersSectionText
|
||||
case stickersSearchBg
|
||||
@ -1648,6 +1650,8 @@ extension PresentationThemeInputMediaPanel: Codable {
|
||||
panelContentVibrantOverlayColor: try decodeColor(values, .panelContentVibrantOverlay, fallbackKey: "\(codingPath).stickersSectionText"),
|
||||
panelContentControlVibrantOverlayColor: try decodeColor(values, .panelContentControlVibrantOverlay, fallbackKey: "\(codingPath).stickersSectionText"),
|
||||
panelContentControlVibrantSelectionColor: try decodeColor(values, .panelContentControlVibrantSelection, fallbackKey: "\(codingPath).stickersSectionText"),
|
||||
panelContentControlOpaqueOverlayColor: try decodeColor(values, .panelContentControlOpaqueOverlay, fallbackKey: "\(codingPath).stickersSectionText"),
|
||||
panelContentControlOpaqueSelectionColor: try decodeColor(values, .panelContentControlOpaqueSelection, fallbackKey: "\(codingPath).stickersSectionText"),
|
||||
stickersBackgroundColor: try decodeColor(values, .stickersBg),
|
||||
stickersSectionTextColor: try decodeColor(values, .stickersSectionText),
|
||||
stickersSearchBackgroundColor: try decodeColor(values, .stickersSearchBg),
|
||||
@ -1666,6 +1670,8 @@ extension PresentationThemeInputMediaPanel: Codable {
|
||||
try encodeColor(&values, self.panelContentVibrantOverlayColor, .panelContentVibrantOverlay)
|
||||
try encodeColor(&values, self.panelContentControlVibrantOverlayColor, .panelContentControlVibrantOverlay)
|
||||
try encodeColor(&values, self.panelContentControlVibrantSelectionColor, .panelContentControlVibrantSelection)
|
||||
try encodeColor(&values, self.panelContentControlOpaqueOverlayColor, .panelContentControlOpaqueOverlay)
|
||||
try encodeColor(&values, self.panelContentControlOpaqueSelectionColor, .panelContentControlOpaqueSelection)
|
||||
try encodeColor(&values, self.stickersBackgroundColor, .stickersBg)
|
||||
try encodeColor(&values, self.stickersSectionTextColor, .stickersSectionText)
|
||||
try encodeColor(&values, self.stickersSearchBackgroundColor, .stickersSearchBg)
|
||||
|
@ -390,7 +390,7 @@ public final class EmojiStatusComponent: Component {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 {
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
|
@ -20,8 +20,10 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent:EmojiStatusComponent",
|
||||
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
|
||||
"//submodules/Components/PagerComponent:PagerComponent",
|
||||
"//submodules/Components/MultilineTextComponent:MultilineTextComponent",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/AccountContext:AccountContext",
|
||||
"//submodules/lottie-ios:Lottie",
|
||||
|
@ -0,0 +1,575 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import ComponentFlow
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import ComponentDisplayAdapters
|
||||
import MultilineTextComponent
|
||||
import EmojiStatusComponent
|
||||
|
||||
protocol ContextMenuItemWithAction: AnyObject {
|
||||
func performAction()
|
||||
}
|
||||
|
||||
private final class ContextMenuActionItem: Component, ContextMenuItemWithAction {
|
||||
typealias EnvironmentType = ContextMenuActionItemEnvironment
|
||||
|
||||
let title: String
|
||||
let action: () -> Void
|
||||
|
||||
init(title: String, action: @escaping () -> Void) {
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: ContextMenuActionItem, rhs: ContextMenuActionItem) -> Bool {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
self.action()
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let titleView: ComponentView<Empty>
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.titleView = ComponentView<Empty>()
|
||||
|
||||
super.init(frame: frame)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: ContextMenuActionItem, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
let contextEnvironment = environment[EnvironmentType.self].value
|
||||
|
||||
let sideInset: CGFloat = 16.0
|
||||
let height: CGFloat = 44.0
|
||||
|
||||
let titleSize = self.titleView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: component.title, font: Font.regular(17.0), textColor: contextEnvironment.theme.contextMenu.primaryColor))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: sideInset, y: floor((height - titleSize.height) / 2.0)), size: titleSize)
|
||||
if let view = self.titleView.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
transition.setFrame(view: view, frame: titleFrame)
|
||||
}
|
||||
|
||||
return CGSize(width: sideInset * 2.0 + titleSize.width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContextMenuActionItemEnvironment: Equatable {
|
||||
let theme: PresentationTheme
|
||||
|
||||
init(
|
||||
theme: PresentationTheme
|
||||
) {
|
||||
self.theme = theme
|
||||
}
|
||||
|
||||
static func ==(lhs: ContextMenuActionItemEnvironment, rhs: ContextMenuActionItemEnvironment) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private final class ContextMenuActionsComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let items: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>]
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
items: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>]
|
||||
) {
|
||||
self.theme = theme
|
||||
self.items = items
|
||||
}
|
||||
|
||||
static func ==(lhs: ContextMenuActionsComponent, rhs: ContextMenuActionsComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.items != rhs.items {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIButton {
|
||||
private final class ItemView {
|
||||
let view = ComponentView<ContextMenuActionItemEnvironment>()
|
||||
let separatorView = UIView()
|
||||
}
|
||||
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
private var itemViews: [AnyHashable: ItemView] = [:]
|
||||
private var highligntedBackgroundView: UIView?
|
||||
|
||||
private var component: ContextMenuActionsComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.layer.cornerRadius = 14.0
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
self.setHighlightedItem(id: self.itemAtPoint(point: touch.location(in: self)))
|
||||
|
||||
return super.beginTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||
self.setHighlightedItem(id: self.itemAtPoint(point: touch.location(in: self)))
|
||||
|
||||
return super.continueTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||
if let component = self.component, let touch = touch, let id = self.itemAtPoint(point: touch.location(in: self)) {
|
||||
self.setHighlightedItem(id: id)
|
||||
for item in component.items {
|
||||
if item.id == id {
|
||||
if let itemComponent = item.component.wrapped as? ContextMenuItemWithAction {
|
||||
itemComponent.performAction()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.setHighlightedItem(id: nil)
|
||||
}
|
||||
|
||||
super.endTracking(touch, with: event)
|
||||
}
|
||||
|
||||
override func cancelTracking(with event: UIEvent?) {
|
||||
self.setHighlightedItem(id: nil)
|
||||
|
||||
super.cancelTracking(with: event)
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
self.setHighlightedItem(id: nil)
|
||||
|
||||
super.touchesCancelled(touches, with: event)
|
||||
}
|
||||
|
||||
private func itemAtPoint(point: CGPoint) -> AnyHashable? {
|
||||
for (id, itemView) in self.itemViews {
|
||||
guard let itemComponentView = itemView.view.view else {
|
||||
continue
|
||||
}
|
||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemComponentView.frame.minY), size: CGSize(width: self.bounds.width, height: itemComponentView.bounds.height))
|
||||
if itemFrame.contains(point) {
|
||||
return id
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func setHighlightedItem(id: AnyHashable?) {
|
||||
if let component = self.component, let id = id, let itemView = self.itemViews[id], let itemComponentView = itemView.view.view {
|
||||
let highligntedBackgroundView: UIView
|
||||
if let current = self.highligntedBackgroundView {
|
||||
highligntedBackgroundView = current
|
||||
} else {
|
||||
highligntedBackgroundView = UIView()
|
||||
self.highligntedBackgroundView = highligntedBackgroundView
|
||||
var found = false
|
||||
outer: for subview in self.subviews {
|
||||
for (_, listItemView) in self.itemViews {
|
||||
if subview === listItemView.view.view {
|
||||
self.insertSubview(highligntedBackgroundView, belowSubview: subview)
|
||||
found = true
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
self.insertSubview(highligntedBackgroundView, aboveSubview: self.backgroundView)
|
||||
}
|
||||
|
||||
highligntedBackgroundView.backgroundColor = component.theme.contextMenu.itemHighlightedBackgroundColor
|
||||
}
|
||||
var highlightFrame = CGRect(origin: CGPoint(x: 0.0, y: itemComponentView.frame.minY), size: CGSize(width: self.bounds.width, height: itemComponentView.bounds.height))
|
||||
if id != component.items.last?.id {
|
||||
highlightFrame.size.height += UIScreenPixel
|
||||
}
|
||||
|
||||
highligntedBackgroundView.frame = highlightFrame
|
||||
} else {
|
||||
if let highligntedBackgroundView = self.highligntedBackgroundView {
|
||||
self.highligntedBackgroundView = nil
|
||||
highligntedBackgroundView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if !self.bounds.contains(point) {
|
||||
return nil
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func update(component: ContextMenuActionsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let availableItemSize = availableSize
|
||||
|
||||
var itemsSize = CGSize()
|
||||
var validIds = Set<AnyHashable>()
|
||||
var currentItems: [(id: AnyHashable, itemFrame: CGRect, itemTransition: Transition)] = []
|
||||
for i in 0 ..< component.items.count {
|
||||
let item = component.items[i]
|
||||
validIds.insert(item.id)
|
||||
|
||||
let itemView: ItemView
|
||||
var itemTransition = transition
|
||||
if let current = self.itemViews[item.id] {
|
||||
itemView = current
|
||||
} else {
|
||||
itemTransition = .immediate
|
||||
itemView = ItemView()
|
||||
self.itemViews[item.id] = itemView
|
||||
self.insertSubview(itemView.separatorView, aboveSubview: self.backgroundView)
|
||||
}
|
||||
|
||||
let itemSize = itemView.view.update(
|
||||
transition: itemTransition,
|
||||
component: item.component,
|
||||
environment: {
|
||||
ContextMenuActionItemEnvironment(theme: component.theme)
|
||||
},
|
||||
containerSize: availableItemSize
|
||||
)
|
||||
let itemFrame = CGRect(origin: CGPoint(x: 0.0, y: itemsSize.height), size: itemSize)
|
||||
if let view = itemView.view.view {
|
||||
if view.superview == nil {
|
||||
self.addSubview(view)
|
||||
}
|
||||
itemTransition.setFrame(view: view, frame: itemFrame)
|
||||
}
|
||||
currentItems.append((item.id, itemFrame, itemTransition))
|
||||
itemsSize.width = max(itemsSize.width, itemSize.width)
|
||||
itemsSize.height += itemSize.height
|
||||
}
|
||||
|
||||
itemsSize.width = max(itemsSize.width, 180.0)
|
||||
|
||||
for i in 0 ..< currentItems.count {
|
||||
let item = currentItems[i]
|
||||
guard let itemView = self.itemViews[item.id] else {
|
||||
continue
|
||||
}
|
||||
itemView.separatorView.backgroundColor = component.theme.contextMenu.itemSeparatorColor
|
||||
itemView.separatorView.isHidden = i == currentItems.count - 1
|
||||
item.itemTransition.setFrame(view: itemView.separatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: item.itemFrame.maxY), size: CGSize(width: itemsSize.width, height: UIScreenPixel)))
|
||||
}
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
itemView.view.view?.removeFromSuperview()
|
||||
itemView.separatorView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
self.backgroundView.updateColor(color: component.theme.contextMenu.backgroundColor, transition: .immediate)
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: itemsSize))
|
||||
self.backgroundView.update(size: itemsSize, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
return itemsSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class EmojiStatusPreviewScreenComponent: Component {
|
||||
struct StatusResult {
|
||||
let timeout: Int
|
||||
let sourceView: UIView
|
||||
}
|
||||
|
||||
final class TransitionAnimation {
|
||||
enum TransitionType {
|
||||
case animateIn(sourceLayer: CALayer)
|
||||
}
|
||||
|
||||
let transitionType: TransitionType
|
||||
|
||||
init(transitionType: TransitionType) {
|
||||
self.transitionType = transitionType
|
||||
}
|
||||
}
|
||||
|
||||
typealias EnvironmentType = Empty
|
||||
|
||||
let theme: PresentationTheme
|
||||
let strings: PresentationStrings
|
||||
let item: EmojiStatusComponent
|
||||
let dismiss: (StatusResult?) -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
item: EmojiStatusComponent,
|
||||
dismiss: @escaping (StatusResult?) -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.item = item
|
||||
self.dismiss = dismiss
|
||||
}
|
||||
|
||||
static func ==(lhs: EmojiStatusPreviewScreenComponent, rhs: EmojiStatusPreviewScreenComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.strings !== rhs.strings {
|
||||
return false
|
||||
}
|
||||
if lhs.item != rhs.item {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
private let itemView: ComponentView<Empty>
|
||||
private let actionsView: ComponentView<Empty>
|
||||
|
||||
private var component: EmojiStatusPreviewScreenComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
|
||||
self.itemView = ComponentView<Empty>()
|
||||
self.actionsView = ComponentView<Empty>()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.backgroundTapGesture(_:))))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func backgroundTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.component?.dismiss(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: EmojiStatusPreviewScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let itemSpacing: CGFloat = 12.0
|
||||
|
||||
let itemSize = self.itemView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(component.item),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 128.0, height: 128.0)
|
||||
)
|
||||
|
||||
var menuItems: [AnyComponentWithIdentity<ContextMenuActionItemEnvironment>] = []
|
||||
let delayDurations: [Int] = [
|
||||
1 * 60 * 60,
|
||||
2 * 60 * 60,
|
||||
8 * 60 * 60,
|
||||
2 * 24 * 60 * 60
|
||||
]
|
||||
for duration in delayDurations {
|
||||
menuItems.append(AnyComponentWithIdentity(id: duration, component: AnyComponent(ContextMenuActionItem(
|
||||
title: setTimeoutForIntervalString(strings: component.strings, value: Int32(duration)),
|
||||
action: { [weak self] in
|
||||
guard let strongSelf = self, let component = strongSelf.component else {
|
||||
return
|
||||
}
|
||||
guard let itemComponentView = strongSelf.itemView.view else {
|
||||
return
|
||||
}
|
||||
component.dismiss(StatusResult(timeout: duration, sourceView: itemComponentView))
|
||||
}
|
||||
))))
|
||||
}
|
||||
let actionsSize = self.actionsView.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ContextMenuActionsComponent(
|
||||
theme: component.theme,
|
||||
items: menuItems
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let totalContentHeight = itemSize.height + itemSpacing + actionsSize.height
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((availableSize.height - totalContentHeight) / 2.0)), size: CGSize(width: availableSize.width, height: totalContentHeight))
|
||||
|
||||
let itemFrame = CGRect(origin: CGPoint(x: contentFrame.minX + floor((contentFrame.width - itemSize.width) / 2.0), y: contentFrame.minY), size: itemSize)
|
||||
let actionsFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - actionsSize.width) / 2.0), y: itemFrame.maxY + itemSpacing), size: actionsSize)
|
||||
|
||||
if let itemComponentView = self.itemView.view {
|
||||
if itemComponentView.superview == nil {
|
||||
self.addSubview(itemComponentView)
|
||||
}
|
||||
transition.setFrame(view: itemComponentView, frame: itemFrame)
|
||||
}
|
||||
|
||||
if let actionsComponentView = self.actionsView.view {
|
||||
if actionsComponentView.superview == nil {
|
||||
self.addSubview(actionsComponentView)
|
||||
}
|
||||
transition.setFrame(view: actionsComponentView, frame: actionsFrame)
|
||||
}
|
||||
|
||||
self.backgroundView.updateColor(color: component.theme.contextMenu.dimColor, transition: .immediate)
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
self.backgroundView.update(size: availableSize, transition: transition.containedViewLayoutTransition)
|
||||
|
||||
if let transitionAnimation = transition.userData(TransitionAnimation.self) {
|
||||
switch transitionAnimation.transitionType {
|
||||
case let .animateIn(sourceLayer):
|
||||
var additionalPositionDifference = CGPoint()
|
||||
if let copyLayer = sourceLayer.snapshotContentTree(), let itemComponentView = self.itemView.view {
|
||||
sourceLayer.isHidden = true
|
||||
copyLayer.frame = sourceLayer.convert(sourceLayer.bounds, to: self.layer)
|
||||
self.layer.addSublayer(copyLayer)
|
||||
|
||||
copyLayer.animatePosition(from: copyLayer.frame.center, to: itemComponentView.frame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
copyLayer.animateScale(from: 1.0, to: itemComponentView.bounds.width / copyLayer.bounds.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
copyLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak copyLayer] _ in
|
||||
copyLayer?.removeFromSuperlayer()
|
||||
})
|
||||
|
||||
additionalPositionDifference = CGPoint(x: itemComponentView.frame.center.x - copyLayer.frame.center.x, y: itemComponentView.frame.center.y - copyLayer.frame.center.y)
|
||||
itemComponentView.layer.animatePosition(from: copyLayer.frame.center, to: itemComponentView.frame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
itemComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
|
||||
itemComponentView.layer.animateScale(from: copyLayer.bounds.width / itemComponentView.bounds.width, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
|
||||
self.backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
|
||||
if let actionsComponentView = self.actionsView.view {
|
||||
actionsComponentView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
actionsComponentView.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.6)
|
||||
actionsComponentView.layer.animateSpring(from: (-actionsComponentView.bounds.height / 2.0) as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.translation.y", duration: 0.6)
|
||||
|
||||
let _ = additionalPositionDifference
|
||||
//actionsComponentView.layer.animatePosition(from: CGPoint(x: -additionalPositionDifference.x, y: -additionalPositionDifference.y), to: CGPoint(), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
func animateOut(targetLayer: CALayer?, completion: @escaping () -> Void) {
|
||||
if let targetLayer = targetLayer, let itemComponentView = self.itemView.view {
|
||||
targetLayer.isHidden = false
|
||||
let targetLayerPosition = targetLayer.position
|
||||
let targetLayerSuperlayer = targetLayer.superlayer
|
||||
var targetLayerIndexPosition: UInt32?
|
||||
if let targetLayerSuperlayer = targetLayerSuperlayer {
|
||||
if let index = targetLayerSuperlayer.sublayers?.firstIndex(of: targetLayer) {
|
||||
targetLayerIndexPosition = UInt32(index)
|
||||
}
|
||||
}
|
||||
|
||||
let localTargetPosition = targetLayer.convert(targetLayer.bounds.center, to: self.layer)
|
||||
self.layer.addSublayer(targetLayer)
|
||||
targetLayer.position = localTargetPosition
|
||||
|
||||
targetLayer.animatePosition(from: itemComponentView.frame.center, to: localTargetPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
targetLayer.animateScale(from: itemComponentView.bounds.width / targetLayer.bounds.width, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, completion: { [weak targetLayer, weak targetLayerSuperlayer] _ in
|
||||
if let targetLayer = targetLayer, let targetLayerSuperlayer = targetLayerSuperlayer {
|
||||
if let targetLayerIndexPosition = targetLayerIndexPosition {
|
||||
targetLayerSuperlayer.insertSublayer(targetLayer, at: targetLayerIndexPosition)
|
||||
targetLayer.position = targetLayerPosition
|
||||
}
|
||||
}
|
||||
completion()
|
||||
})
|
||||
targetLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
|
||||
|
||||
itemComponentView.layer.animatePosition(from: itemComponentView.frame.center, to: localTargetPosition, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
itemComponentView.layer.animateScale(from: 1.0, to: targetLayer.bounds.width / itemComponentView.bounds.width, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
itemComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
|
||||
self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if let actionsComponentView = self.actionsView.view {
|
||||
actionsComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
})
|
||||
}
|
||||
} else {
|
||||
self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
if let actionsComponentView = self.actionsView.view {
|
||||
actionsComponentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import EmojiTextAttachmentView
|
||||
import TextFormat
|
||||
import AppBundle
|
||||
import GZip
|
||||
import EmojiStatusComponent
|
||||
|
||||
private func randomGenericReactionEffect(context: AccountContext) -> Signal<String?, NoError> {
|
||||
return context.engine.stickers.loadedStickerPack(reference: .emojiGenericAnimations, forceActualized: false)
|
||||
@ -29,7 +30,6 @@ private func randomGenericReactionEffect(context: AccountContext) -> Signal<Stri
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> mapToSignal { items -> Signal<String?, NoError> in
|
||||
guard let items = items else {
|
||||
@ -227,6 +227,10 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
private var freezeUpdates: Bool = false
|
||||
private var scheduledEmojiContentAnimationHint: EmojiPagerContentComponent.ContentAnimation?
|
||||
|
||||
private var previewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
||||
private var dismissedPreviewItem: (groupId: AnyHashable, item: EmojiPagerContentComponent.Item)?
|
||||
private var previewScreenView: ComponentView<Empty>?
|
||||
|
||||
private var availableReactions: AvailableReactions?
|
||||
private var availableReactionsDisposable: Disposable?
|
||||
|
||||
@ -289,11 +293,11 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
}
|
||||
|
||||
emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
|
||||
performItemAction: { groupId, item, _, _, _, _ in
|
||||
performItemAction: { groupId, item, _, _, _, isPreview in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.applyItem(groupId: groupId, item: item)
|
||||
strongSelf.applyItem(groupId: groupId, item: item, isPreview: isPreview)
|
||||
},
|
||||
deleteBackwards: {
|
||||
},
|
||||
@ -340,7 +344,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
chatPeerId: nil,
|
||||
peekBehavior: nil,
|
||||
customLayout: nil,
|
||||
externalBackground: nil
|
||||
externalBackground: nil,
|
||||
useOpaqueTheme: true
|
||||
)
|
||||
|
||||
strongSelf.refreshLayout(transition: .immediate)
|
||||
@ -374,29 +379,26 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
self.containerLayoutUpdated(layout: layout, transition: transition)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
func animateOut(completion: @escaping () -> Void, fromBackground: Bool) {
|
||||
if self.isAnimatingOut {
|
||||
return
|
||||
}
|
||||
self.isAnimatingOut = true
|
||||
|
||||
self.componentShadowLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.componentHost.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
let duration: Double = fromBackground ? 0.1 : 0.25
|
||||
|
||||
self.componentShadowLayer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.componentHost.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
||||
self.cloudLayer0.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.cloudShadowLayer0.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.cloudLayer1.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.cloudShadowLayer1.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||
self.cloudLayer0.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.cloudShadowLayer0.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.cloudLayer1.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.cloudShadowLayer1.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
func animateOutToStatus(groupId: AnyHashable, item: EmojiPagerContentComponent.Item, customEffectFile: String?, destinationView: UIView) {
|
||||
guard let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View, let sourceLayer = emojiView.layerForItem( groupId: groupId, item: item) else {
|
||||
self.controller?.dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
func animateOutToStatus(item: EmojiPagerContentComponent.Item, sourceLayer: CALayer, customEffectFile: String?, destinationView: UIView, fromBackground: Bool) {
|
||||
self.isUserInteractionEnabled = false
|
||||
destinationView.isHidden = true
|
||||
|
||||
@ -435,7 +437,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 {
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
useCleanEffect = true
|
||||
}
|
||||
default:
|
||||
@ -517,7 +519,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
var midPointY: CGFloat = localDestinationFrame.center.y - 30.0
|
||||
if let layout = self.validLayout {
|
||||
if midPointY < layout.safeInsets.top + 8.0 {
|
||||
midPointY = max(localDestinationFrame.center.y, layout.safeInsets.top + 8.0)
|
||||
midPointY = max(localDestinationFrame.center.y, layout.safeInsets.top + 20.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,10 +563,24 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
destinationView.isHidden = false
|
||||
}
|
||||
|
||||
if let previewScreenView = self.previewScreenView {
|
||||
self.previewItem = nil
|
||||
self.dismissedPreviewItem = nil
|
||||
self.previewScreenView = nil
|
||||
|
||||
if let previewScreenComponentView = previewScreenView.view as? EmojiStatusPreviewScreenComponent.View {
|
||||
previewScreenComponentView.animateOut(targetLayer: nil, completion: { [weak previewScreenComponentView] in
|
||||
previewScreenComponentView?.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
previewScreenView.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
self.animateOut(completion: {
|
||||
contentCompleted = true
|
||||
completion()
|
||||
})
|
||||
}, fromBackground: fromBackground)
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(layout: ContainerViewLayout, transition: Transition) {
|
||||
@ -707,6 +723,133 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
self.cloudShadowLayer1.animateScale(from: 0.01, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
}
|
||||
}
|
||||
|
||||
if let previewItem = self.previewItem, let itemFile = previewItem.item.itemFile {
|
||||
let previewScreenView: ComponentView<Empty>
|
||||
var previewScreenTransition = transition
|
||||
if let current = self.previewScreenView {
|
||||
previewScreenView = current
|
||||
} else {
|
||||
previewScreenTransition = Transition(animation: .none)
|
||||
if let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View, let sourceLayer = emojiView.layerForItem(groupId: previewItem.groupId, item: previewItem.item) {
|
||||
previewScreenTransition = previewScreenTransition.withUserData(EmojiStatusPreviewScreenComponent.TransitionAnimation(
|
||||
transitionType: .animateIn(sourceLayer: sourceLayer)
|
||||
))
|
||||
}
|
||||
previewScreenView = ComponentView<Empty>()
|
||||
self.previewScreenView = previewScreenView
|
||||
}
|
||||
let _ = previewScreenView.update(
|
||||
transition: previewScreenTransition,
|
||||
component: AnyComponent(EmojiStatusPreviewScreenComponent(
|
||||
theme: self.presentationData.theme,
|
||||
strings: self.presentationData.strings,
|
||||
item: EmojiStatusComponent(
|
||||
context: self.context,
|
||||
animationCache: self.context.animationCache,
|
||||
animationRenderer: self.context.animationRenderer,
|
||||
content: .animation(
|
||||
content: .file(file: itemFile),
|
||||
size: CGSize(width: 128.0, height: 128.0),
|
||||
placeholderColor: self.presentationData.theme.list.plainBackgroundColor.withMultipliedAlpha(0.1),
|
||||
themeColor: self.presentationData.theme.list.itemAccentColor,
|
||||
loopMode: .forever
|
||||
),
|
||||
isVisibleForAnimations: true,
|
||||
useSharedAnimation: false,
|
||||
action: nil
|
||||
),
|
||||
dismiss: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let result = result, let previewItem = strongSelf.previewItem {
|
||||
var emojiString: String?
|
||||
if let itemFile = previewItem.item.itemFile {
|
||||
attributeLoop: for attribute in itemFile.attributes {
|
||||
switch attribute {
|
||||
case let .CustomEmoji(_, alt, _):
|
||||
emojiString = alt
|
||||
break attributeLoop
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let context = strongSelf.context
|
||||
let _ = (context.engine.stickers.availableReactions()
|
||||
|> take(1)
|
||||
|> mapToSignal { availableReactions -> Signal<String?, NoError> in
|
||||
guard let emojiString = emojiString, let availableReactions = availableReactions else {
|
||||
return .single(nil)
|
||||
}
|
||||
for reaction in availableReactions.reactions {
|
||||
if case let .builtin(value) = reaction.value, value == emojiString {
|
||||
if let aroundAnimation = reaction.aroundAnimation {
|
||||
return context.account.postbox.mediaBox.resourceData(aroundAnimation.resource)
|
||||
|> take(1)
|
||||
|> map { data -> String? in
|
||||
if data.complete {
|
||||
return data.path
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
return .single(nil)
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { filePath in
|
||||
guard let strongSelf = self, let previewItem = strongSelf.previewItem, let destinationView = strongSelf.controller?.destinationItemView() else {
|
||||
return
|
||||
}
|
||||
|
||||
var expirationDate: Int32?
|
||||
if result.timeout > 0 {
|
||||
expirationDate = Int32(Date().timeIntervalSince1970) + Int32(result.timeout)
|
||||
}
|
||||
let _ = (strongSelf.context.engine.accountData.setEmojiStatus(file: previewItem.item.itemFile, expirationDate: expirationDate)
|
||||
|> deliverOnMainQueue).start()
|
||||
|
||||
strongSelf.animateOutToStatus(item: previewItem.item, sourceLayer: result.sourceView.layer, customEffectFile: filePath, destinationView: destinationView, fromBackground: true)
|
||||
})
|
||||
} else {
|
||||
strongSelf.dismissedPreviewItem = strongSelf.previewItem
|
||||
strongSelf.previewItem = nil
|
||||
strongSelf.refreshLayout(transition: .immediate)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: layout.size
|
||||
)
|
||||
if let view = previewScreenView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
}
|
||||
} else if let previewScreenView = self.previewScreenView {
|
||||
self.previewScreenView = nil
|
||||
|
||||
if let previewScreenComponentView = previewScreenView.view as? EmojiStatusPreviewScreenComponent.View {
|
||||
var targetLayer: CALayer?
|
||||
if let previewItem = self.dismissedPreviewItem, let emojiView = self.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View, let sourceLayer = emojiView.layerForItem(groupId: previewItem.groupId, item: previewItem.item) {
|
||||
targetLayer = sourceLayer
|
||||
}
|
||||
|
||||
previewScreenComponentView.animateOut(targetLayer: targetLayer, completion: { [weak previewScreenComponentView] in
|
||||
previewScreenComponentView?.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
previewScreenView.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
@ -725,102 +868,114 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func applyItem(groupId: AnyHashable, item: EmojiPagerContentComponent.Item?) {
|
||||
private func applyItem(groupId: AnyHashable, item: EmojiPagerContentComponent.Item?, isPreview: Bool) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
|
||||
self.freezeUpdates = true
|
||||
|
||||
if let _ = item, let destinationView = controller.destinationItemView() {
|
||||
if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = destinationView.frame
|
||||
destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView)
|
||||
snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
if isPreview {
|
||||
guard let item = item else {
|
||||
return
|
||||
}
|
||||
destinationView.isHidden = true
|
||||
}
|
||||
|
||||
switch controller.mode {
|
||||
case .statusSelection:
|
||||
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
case let .quickReactionSelection(completion):
|
||||
if let item = item, let itemFile = item.itemFile {
|
||||
var selectedReaction: MessageReaction.Reaction?
|
||||
self.previewItem = (groupId, item)
|
||||
self.refreshLayout(transition: .immediate)
|
||||
} else {
|
||||
self.freezeUpdates = true
|
||||
|
||||
if let _ = item, let destinationView = controller.destinationItemView() {
|
||||
if let snapshotView = destinationView.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.frame = destinationView.frame
|
||||
destinationView.superview?.insertSubview(snapshotView, belowSubview: destinationView)
|
||||
snapshotView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||
snapshotView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
destinationView.isHidden = true
|
||||
}
|
||||
|
||||
switch controller.mode {
|
||||
case .statusSelection:
|
||||
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
case let .quickReactionSelection(completion):
|
||||
if let item = item, let itemFile = item.itemFile {
|
||||
var selectedReaction: MessageReaction.Reaction?
|
||||
|
||||
if let availableReactions = self.availableReactions {
|
||||
for reaction in availableReactions.reactions {
|
||||
if reaction.selectAnimation.fileId == itemFile.fileId {
|
||||
selectedReaction = reaction.value
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedReaction == nil {
|
||||
selectedReaction = .custom(itemFile.fileId.id)
|
||||
}
|
||||
|
||||
if let selectedReaction = selectedReaction {
|
||||
let _ = context.engine.stickers.updateQuickReaction(reaction: selectedReaction).start()
|
||||
}
|
||||
}
|
||||
|
||||
if let availableReactions = self.availableReactions {
|
||||
for reaction in availableReactions.reactions {
|
||||
if reaction.selectAnimation.fileId == itemFile.fileId {
|
||||
selectedReaction = reaction.value
|
||||
completion()
|
||||
}
|
||||
|
||||
if let item = item, let destinationView = controller.destinationItemView() {
|
||||
var emojiString: String?
|
||||
if let itemFile = item.itemFile {
|
||||
attributeLoop: for attribute in itemFile.attributes {
|
||||
switch attribute {
|
||||
case let .CustomEmoji(_, alt, _):
|
||||
emojiString = alt
|
||||
break attributeLoop
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedReaction == nil {
|
||||
selectedReaction = .custom(itemFile.fileId.id)
|
||||
}
|
||||
|
||||
if let selectedReaction = selectedReaction {
|
||||
let _ = context.engine.stickers.updateQuickReaction(reaction: selectedReaction).start()
|
||||
}
|
||||
}
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
if let item = item, let destinationView = controller.destinationItemView() {
|
||||
var emojiString: String?
|
||||
if let itemFile = item.itemFile {
|
||||
attributeLoop: for attribute in itemFile.attributes {
|
||||
switch attribute {
|
||||
case let .CustomEmoji(_, alt, _):
|
||||
emojiString = alt
|
||||
break attributeLoop
|
||||
default:
|
||||
break
|
||||
let context = self.context
|
||||
let _ = (context.engine.stickers.availableReactions()
|
||||
|> take(1)
|
||||
|> mapToSignal { availableReactions -> Signal<String?, NoError> in
|
||||
guard let emojiString = emojiString, let availableReactions = availableReactions else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
let _ = (context.engine.stickers.availableReactions()
|
||||
|> take(1)
|
||||
|> mapToSignal { availableReactions -> Signal<String?, NoError> in
|
||||
guard let emojiString = emojiString, let availableReactions = availableReactions else {
|
||||
return .single(nil)
|
||||
}
|
||||
for reaction in availableReactions.reactions {
|
||||
if case let .builtin(value) = reaction.value, value == emojiString {
|
||||
if let aroundAnimation = reaction.aroundAnimation {
|
||||
return context.account.postbox.mediaBox.resourceData(aroundAnimation.resource)
|
||||
|> take(1)
|
||||
|> map { data -> String? in
|
||||
if data.complete {
|
||||
return data.path
|
||||
} else {
|
||||
return nil
|
||||
for reaction in availableReactions.reactions {
|
||||
if case let .builtin(value) = reaction.value, value == emojiString {
|
||||
if let aroundAnimation = reaction.aroundAnimation {
|
||||
return context.account.postbox.mediaBox.resourceData(aroundAnimation.resource)
|
||||
|> take(1)
|
||||
|> map { data -> String? in
|
||||
if data.complete {
|
||||
return data.path
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
return .single(nil)
|
||||
}
|
||||
return .single(nil)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] filePath in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
guard let emojiView = strongSelf.componentHost.findTaggedView(tag: EmojiPagerContentComponent.Tag(id: AnyHashable("emoji"))) as? EmojiPagerContentComponent.View, let sourceLayer = emojiView.layerForItem( groupId: groupId, item: item) else {
|
||||
strongSelf.controller?.dismiss()
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.animateOutToStatus(item: item, sourceLayer: sourceLayer, customEffectFile: filePath, destinationView: destinationView, fromBackground: false)
|
||||
})
|
||||
} else {
|
||||
controller.dismiss()
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] filePath in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.animateOutToStatus(groupId: groupId, item: item, customEffectFile: filePath, destinationView: destinationView)
|
||||
})
|
||||
} else {
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -871,7 +1026,7 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
(self.displayNode as! Node).animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}, fromBackground: false)
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
|
@ -1451,11 +1451,15 @@ private final class GroupExpandActionButton: UIButton {
|
||||
super.touchesCancelled(touches, with: event)
|
||||
}
|
||||
|
||||
func update(theme: PresentationTheme, title: String) -> CGSize {
|
||||
func update(theme: PresentationTheme, title: String, useOpaqueTheme: Bool) -> CGSize {
|
||||
let textConstrainedWidth: CGFloat = 100.0
|
||||
let color = theme.list.itemCheckColors.foregroundColor
|
||||
|
||||
self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor
|
||||
if useOpaqueTheme {
|
||||
self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor
|
||||
} else {
|
||||
self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor.cgColor
|
||||
}
|
||||
self.tintContainerLayer.backgroundColor = UIColor.white.cgColor
|
||||
|
||||
let textSize: CGSize
|
||||
@ -1576,6 +1580,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
public let peekBehavior: EmojiContentPeekBehavior?
|
||||
public let customLayout: CustomLayout?
|
||||
public let externalBackground: ExternalBackground?
|
||||
public let useOpaqueTheme: Bool
|
||||
|
||||
public init(
|
||||
performItemAction: @escaping (AnyHashable, Item, UIView, CGRect, CALayer, Bool) -> Void,
|
||||
@ -1592,7 +1597,8 @@ public final class EmojiPagerContentComponent: Component {
|
||||
chatPeerId: PeerId?,
|
||||
peekBehavior: EmojiContentPeekBehavior?,
|
||||
customLayout: CustomLayout?,
|
||||
externalBackground: ExternalBackground?
|
||||
externalBackground: ExternalBackground?,
|
||||
useOpaqueTheme: Bool
|
||||
) {
|
||||
self.performItemAction = performItemAction
|
||||
self.deleteBackwards = deleteBackwards
|
||||
@ -1609,6 +1615,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
self.peekBehavior = peekBehavior
|
||||
self.customLayout = customLayout
|
||||
self.externalBackground = externalBackground
|
||||
self.useOpaqueTheme = useOpaqueTheme
|
||||
}
|
||||
}
|
||||
|
||||
@ -3385,8 +3392,9 @@ public final class EmojiPagerContentComponent: Component {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
}
|
||||
|
||||
let transition = Transition(animation: .curve(duration: longPressDuration, curve: .easeInOut))
|
||||
transition.setScale(layer: itemLayer, scale: 1.3)
|
||||
let _ = itemLayer
|
||||
//let transition = Transition(animation: .curve(duration: longPressDuration, curve: .easeInOut))
|
||||
//transition.setScale(layer: itemLayer, scale: 1.3)
|
||||
|
||||
self.longPressTimer?.invalidate()
|
||||
self.longPressTimer = SwiftSignalKit.Timer(timeout: longPressDuration, repeat: false, completion: { [weak self] in
|
||||
@ -3576,6 +3584,8 @@ public final class EmojiPagerContentComponent: Component {
|
||||
return
|
||||
}
|
||||
|
||||
let useOpaqueTheme = component.inputInteractionHolder.inputInteraction?.useOpaqueTheme ?? false
|
||||
|
||||
var topVisibleGroupId: AnyHashable?
|
||||
var topVisibleSubgroupId: AnyHashable?
|
||||
|
||||
@ -3869,7 +3879,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
}
|
||||
|
||||
let baseItemFrame = itemLayout.frame(groupIndex: groupItems.groupIndex, itemIndex: collapsedItemIndex)
|
||||
let buttonSize = groupExpandActionButton.update(theme: keyboardChildEnvironment.theme, title: collapsedItemText)
|
||||
let buttonSize = groupExpandActionButton.update(theme: keyboardChildEnvironment.theme, title: collapsedItemText, useOpaqueTheme: useOpaqueTheme)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: baseItemFrame.minX + floor((baseItemFrame.width - buttonSize.width) / 2.0), y: baseItemFrame.minY + floor((baseItemFrame.height - buttonSize.height) / 2.0)), size: buttonSize)
|
||||
groupExpandActionButtonTransition.setFrame(view: groupExpandActionButton, frame: buttonFrame)
|
||||
}
|
||||
@ -4050,8 +4060,19 @@ public final class EmojiPagerContentComponent: Component {
|
||||
self.visibleItemSelectionLayers[itemId] = itemSelectionLayer
|
||||
}
|
||||
|
||||
itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor
|
||||
itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor
|
||||
if item.accentTint {
|
||||
itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.list.itemAccentColor.withMultipliedAlpha(0.1).cgColor
|
||||
itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor.clear.cgColor
|
||||
} else {
|
||||
if useOpaqueTheme {
|
||||
itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor
|
||||
itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor.clear.cgColor
|
||||
} else {
|
||||
itemSelectionLayer.backgroundColor = keyboardChildEnvironment.theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor
|
||||
itemSelectionLayer.tintContainerLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor
|
||||
}
|
||||
}
|
||||
|
||||
itemSelectionLayer.frame = baseItemFrame
|
||||
|
||||
itemLayer.transform = CATransform3DMakeScale(0.8, 0.8, 1.0)
|
||||
@ -4771,9 +4792,22 @@ public final class EmojiPagerContentComponent: Component {
|
||||
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.LocalRecentEmoji)
|
||||
|
||||
var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([])
|
||||
|
||||
if isStatusSelection {
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji)
|
||||
|
||||
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false)
|
||||
|> map { result -> [TelegramMediaFile] in
|
||||
switch result {
|
||||
case let .result(_, items, _):
|
||||
return items.map(\.file)
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|> take(1)
|
||||
} else if isReactionSelection {
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions)
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions)
|
||||
@ -4790,9 +4824,10 @@ public final class EmojiPagerContentComponent: Component {
|
||||
context.account.postbox.itemCollectionsView(orderedItemListCollectionIds: orderedItemListCollectionIds, namespaces: [Namespaces.ItemCollection.CloudEmojiPacks], aroundIndex: nil, count: 10000000),
|
||||
hasPremium(context: context, chatPeerId: chatPeerId, premiumIfSavedMessages: true),
|
||||
context.account.viewTracker.featuredEmojiPacks(),
|
||||
availableReactions
|
||||
availableReactions,
|
||||
iconStatusEmoji
|
||||
)
|
||||
|> map { view, hasPremium, featuredEmojiPacks, availableReactions -> EmojiPagerContentComponent in
|
||||
|> map { view, hasPremium, featuredEmojiPacks, availableReactions, iconStatusEmoji -> EmojiPagerContentComponent in
|
||||
struct ItemGroup {
|
||||
var supergroupId: AnyHashable
|
||||
var id: AnyHashable
|
||||
@ -4842,10 +4877,49 @@ public final class EmojiPagerContentComponent: Component {
|
||||
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]))
|
||||
//TODO:localize
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: "Long tap to set a timer".uppercased(), subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem]))
|
||||
}
|
||||
|
||||
var existingIds = Set<MediaId>()
|
||||
|
||||
for file in iconStatusEmoji {
|
||||
if existingIds.contains(file.fileId) {
|
||||
continue
|
||||
}
|
||||
existingIds.insert(file.fileId)
|
||||
|
||||
var accentTint = false
|
||||
for attribute in file.attributes {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let resultItem: EmojiPagerContentComponent.Item
|
||||
|
||||
let animationData = EntityKeyboardAnimationData(file: file)
|
||||
resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: file,
|
||||
subgroupId: nil,
|
||||
icon: .none,
|
||||
accentTint: accentTint
|
||||
)
|
||||
|
||||
if let groupIndex = itemGroupIndexById[groupId] {
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
}
|
||||
}
|
||||
|
||||
if let recentStatusEmoji = recentStatusEmoji {
|
||||
for item in recentStatusEmoji.items {
|
||||
guard let item = item.contents.get(RecentMediaItem.self) else {
|
||||
@ -4863,7 +4937,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 {
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
@ -4908,7 +4982,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
if case let .CustomEmoji(_, _, packReference) = attribute {
|
||||
switch packReference {
|
||||
case let .id(id, _):
|
||||
if id == 773947703670341676 {
|
||||
if id == 773947703670341676 || id == 2964141614563343 {
|
||||
accentTint = true
|
||||
}
|
||||
default:
|
||||
@ -5377,7 +5451,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
},
|
||||
itemLayoutType: .compact,
|
||||
warpContentsOnEdges: isReactionSelection || isStatusSelection,
|
||||
enableLongPress: isReactionSelection && !isQuickReactionSelection,
|
||||
enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection,
|
||||
selectedItems: selectedItems
|
||||
)
|
||||
}
|
||||
|
@ -1104,7 +1104,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
chatPeerId: chatPeerId,
|
||||
peekBehavior: nil,
|
||||
customLayout: nil,
|
||||
externalBackground: nil
|
||||
externalBackground: nil,
|
||||
useOpaqueTheme: false
|
||||
)
|
||||
|
||||
var stickerPeekBehavior: EmojiContentPeekBehaviorImpl?
|
||||
@ -1308,7 +1309,8 @@ final class ChatEntityKeyboardInputNode: ChatInputNode {
|
||||
chatPeerId: chatPeerId,
|
||||
peekBehavior: stickerPeekBehavior,
|
||||
customLayout: nil,
|
||||
externalBackground: nil
|
||||
externalBackground: nil,
|
||||
useOpaqueTheme: false
|
||||
)
|
||||
|
||||
self.inputDataDisposable = (combineLatest(queue: .mainQueue(),
|
||||
@ -2017,7 +2019,8 @@ final class EntityInputView: UIView, AttachmentTextInputPanelInputView, UIInputV
|
||||
chatPeerId: nil,
|
||||
peekBehavior: nil,
|
||||
customLayout: nil,
|
||||
externalBackground: nil
|
||||
externalBackground: nil,
|
||||
useOpaqueTheme: false
|
||||
)
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
|
@ -3105,6 +3105,12 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
let animationRenderer = context.animationRenderer
|
||||
|
||||
strongSelf.emojiStatusSelectionController?.dismiss()
|
||||
var selectedItems = Set<MediaId>()
|
||||
if let peer = strongSelf.data?.peer {
|
||||
if let user = peer as? TelegramUser, let emojiStatus = user.emojiStatus {
|
||||
selectedItems.insert(MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId))
|
||||
}
|
||||
}
|
||||
let emojiStatusSelectionController = EmojiStatusSelectionController(
|
||||
context: strongSelf.context,
|
||||
mode: .statusSelection,
|
||||
@ -3119,7 +3125,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
topReactionItems: [],
|
||||
areUnicodeEmojiEnabled: false,
|
||||
areCustomEmojiEnabled: true,
|
||||
chatPeerId: strongSelf.context.account.peerId
|
||||
chatPeerId: strongSelf.context.account.peerId,
|
||||
selectedItems: selectedItems
|
||||
),
|
||||
destinationItemView: { [weak sourceView] in
|
||||
return sourceView
|
||||
|
Loading…
x
Reference in New Issue
Block a user