Emoji and status improvements

This commit is contained in:
Ali 2022-09-02 20:10:43 +04:00
parent fe3715bd90
commit e4f1c6aa72
34 changed files with 1190 additions and 176 deletions

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -1129,7 +1129,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
customLayout: emojiContentLayout,
externalBackground: EmojiPagerContentComponent.ExternalBackground(
effectContainerView: self.backgroundNode.vibrancyEffectView?.contentView
)
),
useOpaqueTheme: false
)
}

View File

@ -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:

View File

@ -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
}
}
}
}

View File

@ -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)

View File

@ -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() {

View File

@ -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
}

View File

@ -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 {

View File

@ -45,6 +45,7 @@ enum AccountStateUpdatePinnedItemIdsOperation {
enum AccountStateUpdateStickerPacksOperation {
case add(Api.messages.StickerSet)
case reorder(SynchronizeInstalledStickerPacksOperationNamespace, [Int64])
case reorderToTop(SynchronizeInstalledStickerPacksOperationNamespace, [Int64])
case sync
}

View File

@ -66,6 +66,8 @@ extension StickerPackReference {
self = .premiumGifts
case .inputStickerSetEmojiGenericAnimations:
self = .emojiGenericAnimations
case .inputStickerSetEmojiDefaultStatuses:
self = .iconStatusEmoji
}
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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
}
}
}
}

View File

@ -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
}

View File

@ -24,6 +24,8 @@ extension StickerPackReference {
return .inputStickerSetPremiumGifts
case .emojiGenericAnimations:
return .inputStickerSetEmojiGenericAnimations
case .iconStatusEmoji:
return .inputStickerSetEmojiDefaultStatuses
}
}
}

View File

@ -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

View File

@ -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),

View File

@ -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),

View File

@ -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),

View File

@ -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)))

View File

@ -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,

View File

@ -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)

View File

@ -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:

View File

@ -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",

View File

@ -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)
}
}

View File

@ -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() {

View File

@ -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
)
}

View File

@ -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)

View File

@ -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