mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-25 17:43:18 +00:00
Merge commit 'ee0c0ec3e5979ede0ee153aa60801e33ce7d98b6'
This commit is contained in:
commit
9e94faf08b
@ -10141,5 +10141,8 @@ Sorry for the inconvenience.";
|
|||||||
"NameColor.ChatPreview.Description.Channel" = "You can choose an individual color to tint your channel's name, the links it sends, and replies to its messages.";
|
"NameColor.ChatPreview.Description.Channel" = "You can choose an individual color to tint your channel's name, the links it sends, and replies to its messages.";
|
||||||
|
|
||||||
"NameColor.ApplyColor" = "Apply Color";
|
"NameColor.ApplyColor" = "Apply Color";
|
||||||
|
"NameColor.ApplyColorAndBackgroundEmoji" = "Apply Color and Icon";
|
||||||
|
|
||||||
"NameColor.TooltipPremium.Account" = "Subscribe to [Telegram Premium]() to choose a custom color for your name.";
|
"NameColor.TooltipPremium.Account" = "Subscribe to [Telegram Premium]() to choose a custom color for your name.";
|
||||||
|
|
||||||
|
"NameColor.BackgroundEmoji.Title" = "ADD ICONS TO REPLIES";
|
||||||
|
|||||||
@ -663,9 +663,10 @@ public final class ContactSelectionControllerParams {
|
|||||||
public let displayDeviceContacts: Bool
|
public let displayDeviceContacts: Bool
|
||||||
public let displayCallIcons: Bool
|
public let displayCallIcons: Bool
|
||||||
public let multipleSelection: Bool
|
public let multipleSelection: Bool
|
||||||
|
public let requirePhoneNumbers: Bool
|
||||||
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
public let confirmation: (ContactListPeer) -> Signal<Bool, NoError>
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, autoDismiss: Bool = true, title: @escaping (PresentationStrings) -> String, options: [ContactListAdditionalOption] = [], displayDeviceContacts: Bool = false, displayCallIcons: Bool = false, multipleSelection: Bool = false, requirePhoneNumbers: Bool = false, confirmation: @escaping (ContactListPeer) -> Signal<Bool, NoError> = { _ in .single(true) }) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updatedPresentationData = updatedPresentationData
|
self.updatedPresentationData = updatedPresentationData
|
||||||
self.autoDismiss = autoDismiss
|
self.autoDismiss = autoDismiss
|
||||||
@ -674,6 +675,7 @@ public final class ContactSelectionControllerParams {
|
|||||||
self.displayDeviceContacts = displayDeviceContacts
|
self.displayDeviceContacts = displayDeviceContacts
|
||||||
self.displayCallIcons = displayCallIcons
|
self.displayCallIcons = displayCallIcons
|
||||||
self.multipleSelection = multipleSelection
|
self.multipleSelection = multipleSelection
|
||||||
|
self.requirePhoneNumbers = requirePhoneNumbers
|
||||||
self.confirmation = confirmation
|
self.confirmation = confirmation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -72,6 +72,7 @@ public enum ContactMultiselectionControllerMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ContactListFilter {
|
public enum ContactListFilter {
|
||||||
|
case excludeWithoutPhoneNumbers
|
||||||
case excludeSelf
|
case excludeSelf
|
||||||
case exclude([EnginePeer.Id])
|
case exclude([EnginePeer.Id])
|
||||||
case disable([EnginePeer.Id])
|
case disable([EnginePeer.Id])
|
||||||
|
|||||||
@ -113,7 +113,7 @@ private final class ContentNode: ASDisplayNode {
|
|||||||
} else {
|
} else {
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})!
|
})!
|
||||||
self.updateImage(image: image, size: size, spacing: spacing)
|
self.updateImage(image: image, size: size, spacing: spacing)
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ public final class AnimatedAvatarSetView: UIView {
|
|||||||
} else {
|
} else {
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: size, font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})!
|
})!
|
||||||
self.updateImage(image: image, size: size, spacing: spacing)
|
self.updateImage(image: image, size: size, spacing: spacing)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -156,7 +156,7 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En
|
|||||||
iconSignal = Signal { subscriber in
|
iconSignal = Signal { subscriber in
|
||||||
let image = generateImage(size, rotatedContext: { size, context in
|
let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), round: round, font: font, letters: displayLetters, peerId: peerId)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), round: round, font: font, letters: displayLetters, peerId: peerId, nameColor: peer.nameColor)
|
||||||
if blurred {
|
if blurred {
|
||||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
||||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
@ -346,7 +346,7 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: EnginePeer.Id) {
|
public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool = true, font: UIFont, letters: [String], peerId: EnginePeer.Id, nameColor: PeerNameColor?) {
|
||||||
if round {
|
if round {
|
||||||
context.beginPath()
|
context.beginPath()
|
||||||
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height:
|
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height:
|
||||||
@ -365,7 +365,11 @@ public func drawPeerAvatarLetters(context: CGContext, size: CGSize, round: Bool
|
|||||||
if colorIndex == -1 {
|
if colorIndex == -1 {
|
||||||
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
||||||
} else {
|
} else {
|
||||||
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
|
var index = colorIndex % AvatarNode.gradientColors.count
|
||||||
|
if let nameColor {
|
||||||
|
index = Int(nameColor.rawValue) % AvatarNode.gradientColors.count
|
||||||
|
}
|
||||||
|
colorsArray = AvatarNode.gradientColors[index].map(\.cgColor) as NSArray
|
||||||
}
|
}
|
||||||
|
|
||||||
var locations: [CGFloat] = [1.0, 0.0]
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
|
|||||||
@ -847,9 +847,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
animationCache: self.animationCache,
|
animationCache: self.animationCache,
|
||||||
animationRenderer: self.animationRenderer,
|
animationRenderer: self.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: true,
|
subject: .status,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
|
|||||||
@ -1085,15 +1085,18 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
|
|
||||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||||
var excludeSelf = false
|
var excludeSelf = false
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
excludeSelf = true
|
excludeSelf = true
|
||||||
existingPeerIds.insert(context.account.peerId)
|
existingPeerIds.insert(context.account.peerId)
|
||||||
case let .exclude(peerIds):
|
case let .exclude(peerIds):
|
||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1127,8 +1130,13 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
for peer in remotePeers.0 {
|
for peer in remotePeers.0 {
|
||||||
let matches: Bool
|
let matches: Bool
|
||||||
if peer.peer is TelegramUser {
|
if let user = peer.peer as? TelegramUser {
|
||||||
matches = true
|
let phone = user.phone ?? ""
|
||||||
|
if requirePhoneNumbers && phone.isEmpty {
|
||||||
|
matches = false
|
||||||
|
} else {
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
} else if searchGroups || searchChannels {
|
} else if searchGroups || searchChannels {
|
||||||
if peer.peer is TelegramGroup && searchGroups {
|
if peer.peer is TelegramGroup && searchGroups {
|
||||||
matches = true
|
matches = true
|
||||||
@ -1157,8 +1165,13 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
for peer in remotePeers.1 {
|
for peer in remotePeers.1 {
|
||||||
let matches: Bool
|
let matches: Bool
|
||||||
if peer.peer is TelegramUser {
|
if let user = peer.peer as? TelegramUser {
|
||||||
matches = true
|
let phone = user.phone ?? ""
|
||||||
|
if requirePhoneNumbers && phone.isEmpty {
|
||||||
|
matches = false
|
||||||
|
} else {
|
||||||
|
matches = true
|
||||||
|
}
|
||||||
} else if searchGroups || searchChannels {
|
} else if searchGroups || searchChannels {
|
||||||
if peer.peer is TelegramGroup {
|
if peer.peer is TelegramGroup {
|
||||||
matches = searchGroups
|
matches = searchGroups
|
||||||
@ -1270,23 +1283,32 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
var existingPeerIds = Set<EnginePeer.Id>()
|
var existingPeerIds = Set<EnginePeer.Id>()
|
||||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
existingPeerIds.insert(context.account.peerId)
|
existingPeerIds.insert(context.account.peerId)
|
||||||
case let .exclude(peerIds):
|
case let .exclude(peerIds):
|
||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peers = peers.filter { contact in
|
peers = peers.filter { contact in
|
||||||
switch contact {
|
switch contact {
|
||||||
case let .peer(peer, _, _):
|
case let .peer(peer, _, _):
|
||||||
return !existingPeerIds.contains(peer.id)
|
if requirePhoneNumbers, let user = peer as? TelegramUser {
|
||||||
default:
|
let phone = user.phone ?? ""
|
||||||
return true
|
if phone.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !existingPeerIds.contains(peer.id)
|
||||||
|
default:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -322,6 +322,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
var entries: [ContactListSearchEntry] = []
|
var entries: [ContactListSearchEntry] = []
|
||||||
var existingPeerIds = Set<EnginePeer.Id>()
|
var existingPeerIds = Set<EnginePeer.Id>()
|
||||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||||
|
var requirePhoneNumbers = false
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
switch filter {
|
switch filter {
|
||||||
case .excludeSelf:
|
case .excludeSelf:
|
||||||
@ -330,6 +331,8 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
existingPeerIds = existingPeerIds.union(peerIds)
|
existingPeerIds = existingPeerIds.union(peerIds)
|
||||||
case let .disable(peerIds):
|
case let .disable(peerIds):
|
||||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||||
|
case .excludeWithoutPhoneNumbers:
|
||||||
|
requirePhoneNumbers = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||||
@ -338,6 +341,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if existingPeerIds.contains(peer.id) {
|
if existingPeerIds.contains(peer.id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .user(user) = peer, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
existingPeerIds.insert(peer.id)
|
existingPeerIds.insert(peer.id)
|
||||||
var enabled = true
|
var enabled = true
|
||||||
if onlyWriteable {
|
if onlyWriteable {
|
||||||
@ -354,6 +365,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if !(peer.peer is TelegramUser) {
|
if !(peer.peer is TelegramUser) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !existingPeerIds.contains(peer.peer.id) {
|
if !existingPeerIds.contains(peer.peer.id) {
|
||||||
existingPeerIds.insert(peer.peer.id)
|
existingPeerIds.insert(peer.peer.id)
|
||||||
|
|
||||||
@ -373,6 +392,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
|||||||
if !(peer.peer is TelegramUser) {
|
if !(peer.peer is TelegramUser) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||||
|
let phone = user.phone ?? ""
|
||||||
|
if phone.isEmpty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !existingPeerIds.contains(peer.peer.id) {
|
if !existingPeerIds.contains(peer.peer.id) {
|
||||||
existingPeerIds.insert(peer.peer.id)
|
existingPeerIds.insert(peer.peer.id)
|
||||||
|
|
||||||
|
|||||||
@ -737,9 +737,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
|||||||
animationCache: context.animationCache,
|
animationCache: context.animationCache,
|
||||||
animationRenderer: context.animationRenderer,
|
animationRenderer: context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .emoji,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: true,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: true,
|
areUnicodeEmojiEnabled: true,
|
||||||
|
|||||||
@ -449,9 +449,7 @@ public final class DrawingStickerEntityView: DrawingEntityView {
|
|||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .reaction,
|
||||||
isReactionSelection: true,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: mappedReactionItems,
|
topReactionItems: mappedReactionItems,
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
|
|||||||
@ -94,7 +94,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
case timeHeader(PresentationTheme, String)
|
case timeHeader(PresentationTheme, String)
|
||||||
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
||||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?)
|
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?)
|
||||||
case timeInfo(PresentationTheme, String)
|
case timeInfo(PresentationTheme, String)
|
||||||
|
|
||||||
case durationHeader(PresentationTheme, String)
|
case durationHeader(PresentationTheme, String)
|
||||||
@ -282,8 +282,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate):
|
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate, lhsMinDate, lhsMaxDate):
|
||||||
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate {
|
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate, rhsMinDate, rhsMaxDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate, lhsMinDate == rhsMinDate, lhsMaxDate == rhsMaxDate {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -450,8 +450,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
case let .timeCustomPicker(_, dateTimeFormat, date):
|
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate):
|
||||||
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, sectionId: self.section, style: .blocks, updated: { date in
|
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, sectionId: self.section, style: .blocks, updated: { date in
|
||||||
arguments.updateState({ state in
|
arguments.updateState({ state in
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.time = date
|
updatedState.time = date
|
||||||
@ -499,7 +499,18 @@ private struct PremiumGiftProduct: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createGiveawayControllerEntries(peerId: EnginePeer.Id, subject: CreateGiveawaySubject, state: CreateGiveawayControllerState, presentationData: PresentationData, locale: Locale, peers: [EnginePeer.Id: EnginePeer], products: [PremiumGiftProduct], defaultPrice: (Int64, NSDecimalNumber)) -> [CreateGiveawayEntry] {
|
private func createGiveawayControllerEntries(
|
||||||
|
peerId: EnginePeer.Id,
|
||||||
|
subject: CreateGiveawaySubject,
|
||||||
|
state: CreateGiveawayControllerState,
|
||||||
|
presentationData: PresentationData,
|
||||||
|
locale: Locale,
|
||||||
|
peers: [EnginePeer.Id: EnginePeer],
|
||||||
|
products: [PremiumGiftProduct],
|
||||||
|
defaultPrice: (Int64, NSDecimalNumber),
|
||||||
|
minDate: Int32,
|
||||||
|
maxDate: Int32
|
||||||
|
) -> [CreateGiveawayEntry] {
|
||||||
var entries: [CreateGiveawayEntry] = []
|
var entries: [CreateGiveawayEntry] = []
|
||||||
|
|
||||||
switch subject {
|
switch subject {
|
||||||
@ -568,7 +579,7 @@ private func createGiveawayControllerEntries(peerId: EnginePeer.Id, subject: Cre
|
|||||||
entries.append(.timeHeader(presentationData.theme, "DATE WHEN GIVEAWAY ENDS".uppercased()))
|
entries.append(.timeHeader(presentationData.theme, "DATE WHEN GIVEAWAY ENDS".uppercased()))
|
||||||
entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit))
|
entries.append(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, state.pickingTimeLimit))
|
||||||
if state.pickingTimeLimit {
|
if state.pickingTimeLimit {
|
||||||
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time))
|
entries.append(.timeCustomPicker(presentationData.theme, presentationData.dateTimeFormat, state.time, minDate, maxDate))
|
||||||
}
|
}
|
||||||
entries.append(.timeInfo(presentationData.theme, "Choose when \(state.subscriptions) subscribers of your channel will be randomly selected to receive Telegram Premium."))
|
entries.append(.timeInfo(presentationData.theme, "Choose when \(state.subscriptions) subscribers of your channel will be randomly selected to receive Telegram Premium."))
|
||||||
}
|
}
|
||||||
@ -657,7 +668,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
initialSubscriptions = 5
|
initialSubscriptions = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
let expiryTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) + 86400 * 5
|
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||||
|
let expiryTime = currentTime + 86400 * 3
|
||||||
|
let minDate = currentTime + 60 * 10
|
||||||
|
let maxDate = currentTime + context.userLimits.maxGiveawayPeriodSeconds
|
||||||
|
|
||||||
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, channels: [], peers: [], countries: [], onlyNewEligible: false, time: expiryTime)
|
let initialState: CreateGiveawayControllerState = CreateGiveawayControllerState(mode: .giveaway, subscriptions: initialSubscriptions, channels: [], peers: [], countries: [], onlyNewEligible: false, time: expiryTime)
|
||||||
|
|
||||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||||
@ -801,7 +816,7 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(""), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(""), leftNavigationButton: leftNavigationButton, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGiveawayControllerEntries(peerId: peerId, subject: subject, state: state, presentationData: presentationData, locale: locale, peers: peers, products: products, defaultPrice: defaultPrice), style: .blocks, emptyStateItem: nil, headerItem: headerItem, footerItem: footerItem, crossfadeState: false, animateChanges: animateChanges)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: createGiveawayControllerEntries(peerId: peerId, subject: subject, state: state, presentationData: presentationData, locale: locale, peers: peers, products: products, defaultPrice: defaultPrice, minDate: minDate, maxDate: maxDate), style: .blocks, emptyStateItem: nil, headerItem: headerItem, footerItem: footerItem, crossfadeState: false, animateChanges: animateChanges)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let giftCode: PremiumGiftCodeInfo
|
let giftCode: PremiumGiftCodeInfo
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let cancel: () -> Void
|
let cancel: (Bool) -> Void
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
let openMessage: (EngineMessage.Id) -> Void
|
let openMessage: (EngineMessage.Id) -> Void
|
||||||
let copyLink: (String) -> Void
|
let copyLink: (String) -> Void
|
||||||
@ -37,7 +37,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
cancel: @escaping () -> Void,
|
cancel: @escaping (Bool) -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void,
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
copyLink: @escaping (String) -> Void,
|
copyLink: @escaping (String) -> Void,
|
||||||
@ -149,7 +149,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
component: Button(
|
component: Button(
|
||||||
content: AnyComponent(Image(image: closeImage)),
|
content: AnyComponent(Image(image: closeImage)),
|
||||||
action: { [weak component] in
|
action: { [weak component] in
|
||||||
component?.cancel()
|
component?.cancel(true)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
availableSize: CGSize(width: 30.0, height: 30.0),
|
availableSize: CGSize(width: 30.0, height: 30.0),
|
||||||
@ -240,8 +240,10 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: fromPeer)),
|
||||||
action: {
|
action: {
|
||||||
if let peer = fromPeer {
|
if let peer = fromPeer {
|
||||||
component.cancel()
|
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -257,8 +259,10 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
content: AnyComponent(PeerCellComponent(context: context.component.context, textColor: tableLinkColor, peer: toPeer)),
|
||||||
action: {
|
action: {
|
||||||
if let peer = toPeer {
|
if let peer = toPeer {
|
||||||
component.cancel()
|
|
||||||
component.openPeer(peer)
|
component.openPeer(peer)
|
||||||
|
Queue.mainQueue().after(1.0, {
|
||||||
|
component.cancel(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -301,10 +305,12 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: giftReason, font: tableFont, textColor: giftCode.messageId != nil ? tableLinkColor : tableTextColor)))),
|
content: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: giftReason, font: tableFont, textColor: giftCode.messageId != nil ? tableLinkColor : tableTextColor)))),
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
action: {
|
action: {
|
||||||
component.cancel()
|
|
||||||
if let messageId = giftCode.messageId {
|
if let messageId = giftCode.messageId {
|
||||||
component.openMessage(messageId)
|
component.openMessage(messageId)
|
||||||
}
|
}
|
||||||
|
Queue.mainQueue().after(1.0) {
|
||||||
|
component.cancel(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -362,7 +368,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
|||||||
iconPosition: .left,
|
iconPosition: .left,
|
||||||
action: {
|
action: {
|
||||||
if giftCode.isUsed {
|
if giftCode.isUsed {
|
||||||
component.cancel()
|
component.cancel(true)
|
||||||
} else {
|
} else {
|
||||||
component.action()
|
component.action()
|
||||||
}
|
}
|
||||||
@ -425,7 +431,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let giftCode: PremiumGiftCodeInfo
|
let giftCode: PremiumGiftCodeInfo
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let cancel: () -> Void
|
|
||||||
let openPeer: (EnginePeer) -> Void
|
let openPeer: (EnginePeer) -> Void
|
||||||
let openMessage: (EngineMessage.Id) -> Void
|
let openMessage: (EngineMessage.Id) -> Void
|
||||||
let copyLink: (String) -> Void
|
let copyLink: (String) -> Void
|
||||||
@ -435,7 +440,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
cancel: @escaping () -> Void,
|
|
||||||
openPeer: @escaping (EnginePeer) -> Void,
|
openPeer: @escaping (EnginePeer) -> Void,
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||||
copyLink: @escaping (String) -> Void,
|
copyLink: @escaping (String) -> Void,
|
||||||
@ -444,7 +448,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
self.context = context
|
self.context = context
|
||||||
self.giftCode = giftCode
|
self.giftCode = giftCode
|
||||||
self.action = action
|
self.action = action
|
||||||
self.cancel = cancel
|
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
self.openMessage = openMessage
|
self.openMessage = openMessage
|
||||||
self.copyLink = copyLink
|
self.copyLink = copyLink
|
||||||
@ -475,7 +478,7 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
|||||||
context: context.component.context,
|
context: context.component.context,
|
||||||
giftCode: context.component.giftCode,
|
giftCode: context.component.giftCode,
|
||||||
action: context.component.action,
|
action: context.component.action,
|
||||||
cancel: {
|
cancel: { animate in
|
||||||
animateOut.invoke(Action { _ in
|
animateOut.invoke(Action { _ in
|
||||||
if let controller = controller() {
|
if let controller = controller() {
|
||||||
controller.dismiss(completion: nil)
|
controller.dismiss(completion: nil)
|
||||||
@ -535,7 +538,6 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
|||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
giftCode: PremiumGiftCodeInfo,
|
giftCode: PremiumGiftCodeInfo,
|
||||||
forceDark: Bool = false,
|
forceDark: Bool = false,
|
||||||
cancel: @escaping () -> Void = {},
|
|
||||||
action: @escaping () -> Void,
|
action: @escaping () -> Void,
|
||||||
openPeer: @escaping (EnginePeer) -> Void = { _ in },
|
openPeer: @escaping (EnginePeer) -> Void = { _ in },
|
||||||
openMessage: @escaping (EngineMessage.Id) -> Void = { _ in },
|
openMessage: @escaping (EngineMessage.Id) -> Void = { _ in },
|
||||||
@ -544,7 +546,7 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
|||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
var copyLinkImpl: ((String) -> Void)?
|
var copyLinkImpl: ((String) -> Void)?
|
||||||
super.init(context: context, component: PremiumGiftCodeSheetComponent(context: context, giftCode: giftCode, action: action, cancel: cancel, openPeer: openPeer, openMessage: openMessage, copyLink: { link in
|
super.init(context: context, component: PremiumGiftCodeSheetComponent(context: context, giftCode: giftCode, action: action, openPeer: openPeer, openMessage: openMessage, copyLink: { link in
|
||||||
copyLinkImpl?(link)
|
copyLinkImpl?(link)
|
||||||
}, shareLink: shareLink), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
}, shareLink: shareLink), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
||||||
|
|
||||||
|
|||||||
@ -345,11 +345,8 @@ public func quickReactionSetupController(
|
|||||||
animationCache: context.animationCache,
|
animationCache: context.animationCache,
|
||||||
animationRenderer: context.animationRenderer,
|
animationRenderer: context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .quickReaction,
|
||||||
isReactionSelection: true,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
isQuickReactionSelection: true,
|
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
areCustomEmojiEnabled: true,
|
areCustomEmojiEnabled: true,
|
||||||
|
|||||||
@ -422,6 +422,8 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
|
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
|
||||||
case .primary:
|
case .primary:
|
||||||
itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor
|
itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor
|
||||||
|
case let .custom(color):
|
||||||
|
itemLayer.layerTintColor = color.cgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemFrame = itemLayout.frame(itemIndex: index)
|
var itemFrame = itemLayout.frame(itemIndex: index)
|
||||||
|
|||||||
@ -166,7 +166,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
|
dict[-531931925] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsMentions($0) }
|
||||||
dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) }
|
dict[-566281095] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsRecent($0) }
|
||||||
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
|
dict[106343499] = { return Api.ChannelParticipantsFilter.parse_channelParticipantsSearch($0) }
|
||||||
dict[609791884] = { return Api.Chat.parse_channel($0) }
|
dict[427944574] = { return Api.Chat.parse_channel($0) }
|
||||||
dict[399807445] = { return Api.Chat.parse_channelForbidden($0) }
|
dict[399807445] = { return Api.Chat.parse_channelForbidden($0) }
|
||||||
dict[1103884886] = { return Api.Chat.parse_chat($0) }
|
dict[1103884886] = { return Api.Chat.parse_chat($0) }
|
||||||
dict[693512293] = { return Api.Chat.parse_chatEmpty($0) }
|
dict[693512293] = { return Api.Chat.parse_chatEmpty($0) }
|
||||||
@ -176,7 +176,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
|
dict[-1626209256] = { return Api.ChatBannedRights.parse_chatBannedRights($0) }
|
||||||
dict[1915758525] = { return Api.ChatFull.parse_channelFull($0) }
|
dict[1915758525] = { return Api.ChatFull.parse_channelFull($0) }
|
||||||
dict[-908914376] = { return Api.ChatFull.parse_chatFull($0) }
|
dict[-908914376] = { return Api.ChatFull.parse_chatFull($0) }
|
||||||
dict[806110401] = { return Api.ChatInvite.parse_chatInvite($0) }
|
dict[808708181] = { return Api.ChatInvite.parse_chatInvite($0) }
|
||||||
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
|
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
|
||||||
dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) }
|
dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) }
|
||||||
dict[-1940201511] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
|
dict[-1940201511] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
|
||||||
@ -578,7 +578,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[2129714567] = { return Api.MessagesFilter.parse_inputMessagesFilterUrl($0) }
|
dict[2129714567] = { return Api.MessagesFilter.parse_inputMessagesFilterUrl($0) }
|
||||||
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
|
dict[-1614803355] = { return Api.MessagesFilter.parse_inputMessagesFilterVideo($0) }
|
||||||
dict[1358283666] = { return Api.MessagesFilter.parse_inputMessagesFilterVoice($0) }
|
dict[1358283666] = { return Api.MessagesFilter.parse_inputMessagesFilterVoice($0) }
|
||||||
dict[1267991078] = { return Api.MyBoost.parse_myBoost($0) }
|
dict[-1001897636] = { return Api.MyBoost.parse_myBoost($0) }
|
||||||
dict[-1910892683] = { return Api.NearestDc.parse_nearestDc($0) }
|
dict[-1910892683] = { return Api.NearestDc.parse_nearestDc($0) }
|
||||||
dict[-1746354498] = { return Api.NotificationSound.parse_notificationSoundDefault($0) }
|
dict[-1746354498] = { return Api.NotificationSound.parse_notificationSoundDefault($0) }
|
||||||
dict[-2096391452] = { return Api.NotificationSound.parse_notificationSoundLocal($0) }
|
dict[-2096391452] = { return Api.NotificationSound.parse_notificationSoundLocal($0) }
|
||||||
@ -962,7 +962,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1886646706] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) }
|
dict[-1886646706] = { return Api.UrlAuthResult.parse_urlAuthResultAccepted($0) }
|
||||||
dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) }
|
dict[-1445536993] = { return Api.UrlAuthResult.parse_urlAuthResultDefault($0) }
|
||||||
dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) }
|
dict[-1831650802] = { return Api.UrlAuthResult.parse_urlAuthResultRequest($0) }
|
||||||
dict[1876877535] = { return Api.User.parse_user($0) }
|
dict[-346018011] = { return Api.User.parse_user($0) }
|
||||||
dict[-742634630] = { return Api.User.parse_userEmpty($0) }
|
dict[-742634630] = { return Api.User.parse_userEmpty($0) }
|
||||||
dict[-1179571092] = { return Api.UserFull.parse_userFull($0) }
|
dict[-1179571092] = { return Api.UserFull.parse_userFull($0) }
|
||||||
dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) }
|
dict[-2100168954] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) }
|
||||||
@ -1181,6 +1181,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-1916114267] = { return Api.photos.Photos.parse_photos($0) }
|
dict[-1916114267] = { return Api.photos.Photos.parse_photos($0) }
|
||||||
dict[352657236] = { return Api.photos.Photos.parse_photosSlice($0) }
|
dict[352657236] = { return Api.photos.Photos.parse_photosSlice($0) }
|
||||||
dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) }
|
dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) }
|
||||||
|
dict[1029548774] = { return Api.premium.BoostsStatus.parse_boostsStatus($0) }
|
||||||
dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) }
|
dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) }
|
||||||
dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
|
dict[-1107852396] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
|
||||||
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
|
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
|
||||||
@ -1198,7 +1199,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) }
|
dict[276907596] = { return Api.storage.FileType.parse_fileWebp($0) }
|
||||||
dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) }
|
dict[1862033025] = { return Api.stories.AllStories.parse_allStories($0) }
|
||||||
dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) }
|
dict[291044926] = { return Api.stories.AllStories.parse_allStoriesNotModified($0) }
|
||||||
dict[-869070685] = { return Api.stories.BoostsStatus.parse_boostsStatus($0) }
|
|
||||||
dict[-890861720] = { return Api.stories.PeerStories.parse_peerStories($0) }
|
dict[-890861720] = { return Api.stories.PeerStories.parse_peerStories($0) }
|
||||||
dict[1574486984] = { return Api.stories.Stories.parse_stories($0) }
|
dict[1574486984] = { return Api.stories.Stories.parse_stories($0) }
|
||||||
dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) }
|
dict[-560009955] = { return Api.stories.StoryViews.parse_storyViews($0) }
|
||||||
@ -2082,6 +2082,8 @@ public extension Api {
|
|||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.premium.BoostsList:
|
case let _1 as Api.premium.BoostsList:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
|
case let _1 as Api.premium.BoostsStatus:
|
||||||
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.premium.MyBoosts:
|
case let _1 as Api.premium.MyBoosts:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.stats.BroadcastStats:
|
case let _1 as Api.stats.BroadcastStats:
|
||||||
@ -2096,8 +2098,6 @@ public extension Api {
|
|||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.stories.AllStories:
|
case let _1 as Api.stories.AllStories:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.stories.BoostsStatus:
|
|
||||||
_1.serialize(buffer, boxed)
|
|
||||||
case let _1 as Api.stories.PeerStories:
|
case let _1 as Api.stories.PeerStories:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.stories.Stories:
|
case let _1 as Api.stories.Stories:
|
||||||
|
|||||||
@ -700,17 +700,18 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum MyBoost: TypeConstructorDescription {
|
enum MyBoost: TypeConstructorDescription {
|
||||||
case myBoost(flags: Int32, slot: Int32, peer: Api.Peer?, expires: Int32, cooldownUntilDate: Int32?)
|
case myBoost(flags: Int32, slot: Int32, peer: Api.Peer?, date: Int32, expires: Int32, cooldownUntilDate: Int32?)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .myBoost(let flags, let slot, let peer, let expires, let cooldownUntilDate):
|
case .myBoost(let flags, let slot, let peer, let date, let expires, let cooldownUntilDate):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1267991078)
|
buffer.appendInt32(-1001897636)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(slot, buffer: buffer, boxed: false)
|
serializeInt32(slot, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 0) != 0 {peer!.serialize(buffer, true)}
|
||||||
|
serializeInt32(date, buffer: buffer, boxed: false)
|
||||||
serializeInt32(expires, buffer: buffer, boxed: false)
|
serializeInt32(expires, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(cooldownUntilDate!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(cooldownUntilDate!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
@ -719,8 +720,8 @@ public extension Api {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .myBoost(let flags, let slot, let peer, let expires, let cooldownUntilDate):
|
case .myBoost(let flags, let slot, let peer, let date, let expires, let cooldownUntilDate):
|
||||||
return ("myBoost", [("flags", flags as Any), ("slot", slot as Any), ("peer", peer as Any), ("expires", expires as Any), ("cooldownUntilDate", cooldownUntilDate as Any)])
|
return ("myBoost", [("flags", flags as Any), ("slot", slot as Any), ("peer", peer as Any), ("date", date as Any), ("expires", expires as Any), ("cooldownUntilDate", cooldownUntilDate as Any)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,14 +737,17 @@ public extension Api {
|
|||||||
var _4: Int32?
|
var _4: Int32?
|
||||||
_4 = reader.readInt32()
|
_4 = reader.readInt32()
|
||||||
var _5: Int32?
|
var _5: Int32?
|
||||||
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
|
_5 = reader.readInt32()
|
||||||
|
var _6: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 1) != 0 {_6 = reader.readInt32() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
|
let _c5 = _5 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
|
||||||
return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, expires: _4!, cooldownUntilDate: _5)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||||
|
return Api.MyBoost.myBoost(flags: _1!, slot: _2!, peer: _3, date: _4!, expires: _5!, cooldownUntilDate: _6)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -452,14 +452,14 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum User: TypeConstructorDescription {
|
enum User: TypeConstructorDescription {
|
||||||
case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32, backgroundEmojiId: Int64?)
|
case user(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, firstName: String?, lastName: String?, username: String?, phone: String?, photo: Api.UserProfilePhoto?, status: Api.UserStatus?, botInfoVersion: Int32?, restrictionReason: [Api.RestrictionReason]?, botInlinePlaceholder: String?, langCode: String?, emojiStatus: Api.EmojiStatus?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?)
|
||||||
case userEmpty(id: Int64)
|
case userEmpty(id: Int64)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let backgroundEmojiId):
|
case .user(let flags, let flags2, let id, let accessHash, let firstName, let lastName, let username, let phone, let photo, let status, let botInfoVersion, let restrictionReason, let botInlinePlaceholder, let langCode, let emojiStatus, let usernames, let storiesMaxId, let color, let backgroundEmojiId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(1876877535)
|
buffer.appendInt32(-346018011)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||||
@ -486,7 +486,7 @@ public extension Api {
|
|||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)}
|
if Int(flags2) & Int(1 << 5) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)}
|
||||||
serializeInt32(color, buffer: buffer, boxed: false)
|
if Int(flags2) & Int(1 << 7) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)}
|
if Int(flags2) & Int(1 << 6) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .userEmpty(let id):
|
case .userEmpty(let id):
|
||||||
@ -553,7 +553,7 @@ public extension Api {
|
|||||||
var _17: Int32?
|
var _17: Int32?
|
||||||
if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt32() }
|
if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt32() }
|
||||||
var _18: Int32?
|
var _18: Int32?
|
||||||
_18 = reader.readInt32()
|
if Int(_2!) & Int(1 << 7) != 0 {_18 = reader.readInt32() }
|
||||||
var _19: Int64?
|
var _19: Int64?
|
||||||
if Int(_2!) & Int(1 << 6) != 0 {_19 = reader.readInt64() }
|
if Int(_2!) & Int(1 << 6) != 0 {_19 = reader.readInt64() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
@ -573,10 +573,10 @@ public extension Api {
|
|||||||
let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil
|
let _c15 = (Int(_1!) & Int(1 << 30) == 0) || _15 != nil
|
||||||
let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil
|
let _c16 = (Int(_2!) & Int(1 << 0) == 0) || _16 != nil
|
||||||
let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil
|
let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil
|
||||||
let _c18 = _18 != nil
|
let _c18 = (Int(_2!) & Int(1 << 7) == 0) || _18 != nil
|
||||||
let _c19 = (Int(_2!) & Int(1 << 6) == 0) || _19 != nil
|
let _c19 = (Int(_2!) & Int(1 << 6) == 0) || _19 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 {
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 {
|
||||||
return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18!, backgroundEmojiId: _19)
|
return Api.User.user(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, firstName: _5, lastName: _6, username: _7, phone: _8, photo: _9, status: _10, botInfoVersion: _11, restrictionReason: _12, botInlinePlaceholder: _13, langCode: _14, emojiStatus: _15, usernames: _16, storiesMaxId: _17, color: _18, backgroundEmojiId: _19)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -62,6 +62,92 @@ public extension Api.premium {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.premium {
|
||||||
|
enum BoostsStatus: TypeConstructorDescription {
|
||||||
|
case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, prepaidGiveaways: [Api.PrepaidGiveaway]?, myBoostSlots: [Int32]?)
|
||||||
|
|
||||||
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
|
switch self {
|
||||||
|
case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(1029548774)
|
||||||
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(level, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(boosts, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)}
|
||||||
|
if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)}
|
||||||
|
serializeString(boostUrl, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(prepaidGiveaways!.count))
|
||||||
|
for item in prepaidGiveaways! {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}}
|
||||||
|
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(myBoostSlots!.count))
|
||||||
|
for item in myBoostSlots! {
|
||||||
|
serializeInt32(item, buffer: buffer, boxed: false)
|
||||||
|
}}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
|
switch self {
|
||||||
|
case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let prepaidGiveaways, let myBoostSlots):
|
||||||
|
return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("prepaidGiveaways", prepaidGiveaways as Any), ("myBoostSlots", myBoostSlots as Any)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? {
|
||||||
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
var _2: Int32?
|
||||||
|
_2 = reader.readInt32()
|
||||||
|
var _3: Int32?
|
||||||
|
_3 = reader.readInt32()
|
||||||
|
var _4: Int32?
|
||||||
|
_4 = reader.readInt32()
|
||||||
|
var _5: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
|
||||||
|
var _6: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
||||||
|
var _7: Api.StatsPercentValue?
|
||||||
|
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||||
|
_7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue
|
||||||
|
} }
|
||||||
|
var _8: String?
|
||||||
|
_8 = parseString(reader)
|
||||||
|
var _9: [Api.PrepaidGiveaway]?
|
||||||
|
if Int(_1!) & Int(1 << 3) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PrepaidGiveaway.self)
|
||||||
|
} }
|
||||||
|
var _10: [Int32]?
|
||||||
|
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
||||||
|
_10 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||||
|
} }
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = _3 != nil
|
||||||
|
let _c4 = _4 != nil
|
||||||
|
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
|
||||||
|
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||||
|
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||||
|
let _c8 = _8 != nil
|
||||||
|
let _c9 = (Int(_1!) & Int(1 << 3) == 0) || _9 != nil
|
||||||
|
let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||||
|
return Api.premium.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, prepaidGiveaways: _9, myBoostSlots: _10)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.premium {
|
public extension Api.premium {
|
||||||
enum MyBoosts: TypeConstructorDescription {
|
enum MyBoosts: TypeConstructorDescription {
|
||||||
case myBoosts(myBoosts: [Api.MyBoost], chats: [Api.Chat], users: [Api.User])
|
case myBoosts(myBoosts: [Api.MyBoost], chats: [Api.Chat], users: [Api.User])
|
||||||
@ -720,82 +806,6 @@ public extension Api.stories {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.stories {
|
|
||||||
enum BoostsStatus: TypeConstructorDescription {
|
|
||||||
case boostsStatus(flags: Int32, level: Int32, currentLevelBoosts: Int32, boosts: Int32, giftBoosts: Int32?, nextLevelBoosts: Int32?, premiumAudience: Api.StatsPercentValue?, boostUrl: String, myBoostSlots: [Int32]?)
|
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
|
||||||
switch self {
|
|
||||||
case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let myBoostSlots):
|
|
||||||
if boxed {
|
|
||||||
buffer.appendInt32(-869070685)
|
|
||||||
}
|
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
|
||||||
serializeInt32(level, buffer: buffer, boxed: false)
|
|
||||||
serializeInt32(currentLevelBoosts, buffer: buffer, boxed: false)
|
|
||||||
serializeInt32(boosts, buffer: buffer, boxed: false)
|
|
||||||
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(giftBoosts!, buffer: buffer, boxed: false)}
|
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(nextLevelBoosts!, buffer: buffer, boxed: false)}
|
|
||||||
if Int(flags) & Int(1 << 1) != 0 {premiumAudience!.serialize(buffer, true)}
|
|
||||||
serializeString(boostUrl, buffer: buffer, boxed: false)
|
|
||||||
if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261)
|
|
||||||
buffer.appendInt32(Int32(myBoostSlots!.count))
|
|
||||||
for item in myBoostSlots! {
|
|
||||||
serializeInt32(item, buffer: buffer, boxed: false)
|
|
||||||
}}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
|
||||||
switch self {
|
|
||||||
case .boostsStatus(let flags, let level, let currentLevelBoosts, let boosts, let giftBoosts, let nextLevelBoosts, let premiumAudience, let boostUrl, let myBoostSlots):
|
|
||||||
return ("boostsStatus", [("flags", flags as Any), ("level", level as Any), ("currentLevelBoosts", currentLevelBoosts as Any), ("boosts", boosts as Any), ("giftBoosts", giftBoosts as Any), ("nextLevelBoosts", nextLevelBoosts as Any), ("premiumAudience", premiumAudience as Any), ("boostUrl", boostUrl as Any), ("myBoostSlots", myBoostSlots as Any)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func parse_boostsStatus(_ reader: BufferReader) -> BoostsStatus? {
|
|
||||||
var _1: Int32?
|
|
||||||
_1 = reader.readInt32()
|
|
||||||
var _2: Int32?
|
|
||||||
_2 = reader.readInt32()
|
|
||||||
var _3: Int32?
|
|
||||||
_3 = reader.readInt32()
|
|
||||||
var _4: Int32?
|
|
||||||
_4 = reader.readInt32()
|
|
||||||
var _5: Int32?
|
|
||||||
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
|
|
||||||
var _6: Int32?
|
|
||||||
if Int(_1!) & Int(1 << 0) != 0 {_6 = reader.readInt32() }
|
|
||||||
var _7: Api.StatsPercentValue?
|
|
||||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
|
||||||
_7 = Api.parse(reader, signature: signature) as? Api.StatsPercentValue
|
|
||||||
} }
|
|
||||||
var _8: String?
|
|
||||||
_8 = parseString(reader)
|
|
||||||
var _9: [Int32]?
|
|
||||||
if Int(_1!) & Int(1 << 2) != 0 {if let _ = reader.readInt32() {
|
|
||||||
_9 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
|
||||||
} }
|
|
||||||
let _c1 = _1 != nil
|
|
||||||
let _c2 = _2 != nil
|
|
||||||
let _c3 = _3 != nil
|
|
||||||
let _c4 = _4 != nil
|
|
||||||
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
|
|
||||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
|
||||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
|
||||||
let _c8 = _8 != nil
|
|
||||||
let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil
|
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
|
||||||
return Api.stories.BoostsStatus.boostsStatus(flags: _1!, level: _2!, currentLevelBoosts: _3!, boosts: _4!, giftBoosts: _5, nextLevelBoosts: _6, premiumAudience: _7, boostUrl: _8!, myBoostSlots: _9)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public extension Api.stories {
|
public extension Api.stories {
|
||||||
enum PeerStories: TypeConstructorDescription {
|
enum PeerStories: TypeConstructorDescription {
|
||||||
case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User])
|
case peerStories(stories: Api.PeerStories, chats: [Api.Chat], users: [Api.User])
|
||||||
|
|||||||
@ -522,7 +522,7 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum Chat: TypeConstructorDescription {
|
indirect enum Chat: TypeConstructorDescription {
|
||||||
case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32, backgroundEmojiId: Int64?)
|
case channel(flags: Int32, flags2: Int32, id: Int64, accessHash: Int64?, title: String, username: String?, photo: Api.ChatPhoto, date: Int32, restrictionReason: [Api.RestrictionReason]?, adminRights: Api.ChatAdminRights?, bannedRights: Api.ChatBannedRights?, defaultBannedRights: Api.ChatBannedRights?, participantsCount: Int32?, usernames: [Api.Username]?, storiesMaxId: Int32?, color: Int32?, backgroundEmojiId: Int64?)
|
||||||
case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?)
|
case channelForbidden(flags: Int32, id: Int64, accessHash: Int64, title: String, untilDate: Int32?)
|
||||||
case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?)
|
case chat(flags: Int32, id: Int64, title: String, photo: Api.ChatPhoto, participantsCount: Int32, date: Int32, version: Int32, migratedTo: Api.InputChannel?, adminRights: Api.ChatAdminRights?, defaultBannedRights: Api.ChatBannedRights?)
|
||||||
case chatEmpty(id: Int64)
|
case chatEmpty(id: Int64)
|
||||||
@ -532,7 +532,7 @@ public extension Api {
|
|||||||
switch self {
|
switch self {
|
||||||
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let backgroundEmojiId):
|
case .channel(let flags, let flags2, let id, let accessHash, let title, let username, let photo, let date, let restrictionReason, let adminRights, let bannedRights, let defaultBannedRights, let participantsCount, let usernames, let storiesMaxId, let color, let backgroundEmojiId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(609791884)
|
buffer.appendInt32(427944574)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeInt32(flags2, buffer: buffer, boxed: false)
|
serializeInt32(flags2, buffer: buffer, boxed: false)
|
||||||
@ -557,7 +557,7 @@ public extension Api {
|
|||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
if Int(flags2) & Int(1 << 4) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)}
|
if Int(flags2) & Int(1 << 4) != 0 {serializeInt32(storiesMaxId!, buffer: buffer, boxed: false)}
|
||||||
serializeInt32(color, buffer: buffer, boxed: false)
|
if Int(flags2) & Int(1 << 6) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags2) & Int(1 << 5) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)}
|
if Int(flags2) & Int(1 << 5) != 0 {serializeInt64(backgroundEmojiId!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate):
|
case .channelForbidden(let flags, let id, let accessHash, let title, let untilDate):
|
||||||
@ -660,7 +660,7 @@ public extension Api {
|
|||||||
var _15: Int32?
|
var _15: Int32?
|
||||||
if Int(_2!) & Int(1 << 4) != 0 {_15 = reader.readInt32() }
|
if Int(_2!) & Int(1 << 4) != 0 {_15 = reader.readInt32() }
|
||||||
var _16: Int32?
|
var _16: Int32?
|
||||||
_16 = reader.readInt32()
|
if Int(_2!) & Int(1 << 6) != 0 {_16 = reader.readInt32() }
|
||||||
var _17: Int64?
|
var _17: Int64?
|
||||||
if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt64() }
|
if Int(_2!) & Int(1 << 5) != 0 {_17 = reader.readInt64() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
@ -678,10 +678,10 @@ public extension Api {
|
|||||||
let _c13 = (Int(_1!) & Int(1 << 17) == 0) || _13 != nil
|
let _c13 = (Int(_1!) & Int(1 << 17) == 0) || _13 != nil
|
||||||
let _c14 = (Int(_2!) & Int(1 << 0) == 0) || _14 != nil
|
let _c14 = (Int(_2!) & Int(1 << 0) == 0) || _14 != nil
|
||||||
let _c15 = (Int(_2!) & Int(1 << 4) == 0) || _15 != nil
|
let _c15 = (Int(_2!) & Int(1 << 4) == 0) || _15 != nil
|
||||||
let _c16 = _16 != nil
|
let _c16 = (Int(_2!) & Int(1 << 6) == 0) || _16 != nil
|
||||||
let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil
|
let _c17 = (Int(_2!) & Int(1 << 5) == 0) || _17 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
|
||||||
return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16!, backgroundEmojiId: _17)
|
return Api.Chat.channel(flags: _1!, flags2: _2!, id: _3!, accessHash: _4, title: _5!, username: _6, photo: _7!, date: _8!, restrictionReason: _9, adminRights: _10, bannedRights: _11, defaultBannedRights: _12, participantsCount: _13, usernames: _14, storiesMaxId: _15, color: _16, backgroundEmojiId: _17)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
@ -1238,15 +1238,15 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
indirect enum ChatInvite: TypeConstructorDescription {
|
indirect enum ChatInvite: TypeConstructorDescription {
|
||||||
case chatInvite(flags: Int32, title: String, about: String?, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?)
|
case chatInvite(flags: Int32, title: String, about: String?, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?, color: Int32?)
|
||||||
case chatInviteAlready(chat: Api.Chat)
|
case chatInviteAlready(chat: Api.Chat)
|
||||||
case chatInvitePeek(chat: Api.Chat, expires: Int32)
|
case chatInvitePeek(chat: Api.Chat, expires: Int32)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants):
|
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants, let color):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(806110401)
|
buffer.appendInt32(808708181)
|
||||||
}
|
}
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
serializeString(title, buffer: buffer, boxed: false)
|
serializeString(title, buffer: buffer, boxed: false)
|
||||||
@ -1258,6 +1258,7 @@ public extension Api {
|
|||||||
for item in participants! {
|
for item in participants! {
|
||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}}
|
}}
|
||||||
|
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(color!, buffer: buffer, boxed: false)}
|
||||||
break
|
break
|
||||||
case .chatInviteAlready(let chat):
|
case .chatInviteAlready(let chat):
|
||||||
if boxed {
|
if boxed {
|
||||||
@ -1277,8 +1278,8 @@ public extension Api {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants):
|
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants, let color):
|
||||||
return ("chatInvite", [("flags", flags as Any), ("title", title as Any), ("about", about as Any), ("photo", photo as Any), ("participantsCount", participantsCount as Any), ("participants", participants as Any)])
|
return ("chatInvite", [("flags", flags as Any), ("title", title as Any), ("about", about as Any), ("photo", photo as Any), ("participantsCount", participantsCount as Any), ("participants", participants as Any), ("color", color as Any)])
|
||||||
case .chatInviteAlready(let chat):
|
case .chatInviteAlready(let chat):
|
||||||
return ("chatInviteAlready", [("chat", chat as Any)])
|
return ("chatInviteAlready", [("chat", chat as Any)])
|
||||||
case .chatInvitePeek(let chat, let expires):
|
case .chatInvitePeek(let chat, let expires):
|
||||||
@ -1303,14 +1304,17 @@ public extension Api {
|
|||||||
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
|
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
|
||||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
} }
|
} }
|
||||||
|
var _7: Int32?
|
||||||
|
if Int(_1!) & Int(1 << 10) != 0 {_7 = reader.readInt32() }
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
|
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
|
||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
|
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
let _c7 = (Int(_1!) & Int(1 << 10) == 0) || _7 != nil
|
||||||
return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6)
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||||
|
return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6, color: _7)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -373,6 +373,21 @@ public extension Api.functions.account {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.account {
|
||||||
|
static func getDefaultBackgroundEmojis(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.EmojiList>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(-1509246514)
|
||||||
|
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "account.getDefaultBackgroundEmojis", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.EmojiList? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.EmojiList?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.EmojiList
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.account {
|
public extension Api.functions.account {
|
||||||
static func getDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.EmojiStatuses>) {
|
static func getDefaultEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.EmojiStatuses>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
@ -8362,13 +8377,17 @@ public extension Api.functions.photos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.premium {
|
public extension Api.functions.premium {
|
||||||
static func applyBoost(flags: Int32, slot: Int32?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func applyBoost(flags: Int32, slots: [Int32]?, peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(1186373995)
|
buffer.appendInt32(407618489)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(slot!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||||
|
buffer.appendInt32(Int32(slots!.count))
|
||||||
|
for item in slots! {
|
||||||
|
serializeInt32(item, buffer: buffer, boxed: false)
|
||||||
|
}}
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slot", String(describing: slot)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
return (FunctionDescription(name: "premium.applyBoost", parameters: [("flags", String(describing: flags)), ("slots", String(describing: slots)), ("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Bool?
|
var result: Api.Bool?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
@ -8396,6 +8415,21 @@ public extension Api.functions.premium {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api.functions.premium {
|
||||||
|
static func getBoostsStatus(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.premium.BoostsStatus>) {
|
||||||
|
let buffer = Buffer()
|
||||||
|
buffer.appendInt32(70197089)
|
||||||
|
peer.serialize(buffer, true)
|
||||||
|
return (FunctionDescription(name: "premium.getBoostsStatus", parameters: [("peer", String(describing: peer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.premium.BoostsStatus? in
|
||||||
|
let reader = BufferReader(buffer)
|
||||||
|
var result: Api.premium.BoostsStatus?
|
||||||
|
if let signature = reader.readInt32() {
|
||||||
|
result = Api.parse(reader, signature: signature) as? Api.premium.BoostsStatus
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api.functions.premium {
|
public extension Api.functions.premium {
|
||||||
static func getMyBoosts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.premium.MyBoosts>) {
|
static func getMyBoosts() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.premium.MyBoosts>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
|
|||||||
@ -153,7 +153,7 @@ func parseTelegramGroupOrChannel(chat: Api.Chat) -> Peer? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId)
|
return TelegramChannel(id: PeerId(namespace: Namespaces.Peer.CloudChannel, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: date, version: 0, participationStatus: participationStatus, info: info, flags: channelFlags, restrictionInfo: restrictionInfo, adminRights: adminRights.flatMap(TelegramChatAdminRights.init), bannedRights: bannedRights.flatMap(TelegramChatBannedRights.init), defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId)
|
||||||
case let .channelForbidden(flags, id, accessHash, title, untilDate):
|
case let .channelForbidden(flags, id, accessHash, title, untilDate):
|
||||||
let info: TelegramChannelInfo
|
let info: TelegramChannelInfo
|
||||||
if (flags & Int32(1 << 8)) != 0 {
|
if (flags & Int32(1 << 8)) != 0 {
|
||||||
@ -212,7 +212,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId)
|
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, defaultBannedRights: defaultBannedRights.flatMap(TelegramChatBannedRights.init), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId)
|
||||||
} else {
|
} else {
|
||||||
return parseTelegramGroupOrChannel(chat: rhs)
|
return parseTelegramGroupOrChannel(chat: rhs)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,7 +96,7 @@ extension TelegramUser {
|
|||||||
|
|
||||||
let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:))
|
let restrictionInfo: PeerAccessRestrictionInfo? = restrictionReason.flatMap(PeerAccessRestrictionInfo.init(apiReasons:))
|
||||||
|
|
||||||
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId)
|
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: accessHashValue, firstName: firstName, lastName: lastName, username: username, phone: phone, photo: representations, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: usernames?.map(TelegramPeerUsername.init(apiUsername:)) ?? [], storiesHidden: storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId)
|
||||||
case let .userEmpty(id):
|
case let .userEmpty(id):
|
||||||
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)
|
self.init(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(id)), accessHash: nil, firstName: nil, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil)
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ extension TelegramUser {
|
|||||||
accessHash = lhs.accessHash ?? rhsAccessHashValue
|
accessHash = lhs.accessHash ?? rhsAccessHashValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: PeerNameColor(rawValue: nameColor), backgroundEmojiId: backgroundEmojiId)
|
return TelegramUser(id: lhs.id, accessHash: accessHash, firstName: lhs.firstName, lastName: lhs.lastName, username: lhs.username, phone: lhs.phone, photo: telegramPhoto, botInfo: botInfo, restrictionInfo: restrictionInfo, flags: userFlags, emojiStatus: emojiStatus.flatMap(PeerEmojiStatus.init(apiStatus:)), usernames: lhs.usernames, storiesHidden: lhs.storiesHidden, nameColor: nameColor.flatMap { PeerNameColor(rawValue: $0) }, backgroundEmojiId: backgroundEmojiId)
|
||||||
} else {
|
} else {
|
||||||
return TelegramUser(user: rhs)
|
return TelegramUser(user: rhs)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ public final class ChannelBoostStatus: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_getChannelBoostStatus(account: Account, peerId: PeerId) -> Signal<ChannelBoostStatus?, NoError> {
|
func _internal_getChannelBoostStatus(account: Account, peerId: PeerId) -> Signal<ChannelBoostStatus?, NoError> {
|
||||||
return .single(nil)
|
return .single(ChannelBoostStatus(level: 0, boosts: 0, currentLevelBoosts: 0, nextLevelBoosts: nil, premiumAudience: nil, url: "", prepaidGiveaways: []))
|
||||||
// return account.postbox.transaction { transaction -> Api.InputPeer? in
|
// return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||||
// return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
// return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||||
// }
|
// }
|
||||||
|
|||||||
@ -318,6 +318,34 @@ func managedGroupPhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void,
|
|||||||
return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func managedBackgroundIconEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||||
|
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
|
||||||
|
return network.request(Api.functions.account.getDefaultBackgroundEmojis(hash: hash))
|
||||||
|
|> retryRequest
|
||||||
|
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
|
||||||
|
switch result {
|
||||||
|
case .emojiListNotModified:
|
||||||
|
return .single(nil)
|
||||||
|
case let .emojiList(_, documentIds):
|
||||||
|
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: documentIds)
|
||||||
|
|> map { files -> [OrderedItemListEntry] in
|
||||||
|
var items: [OrderedItemListEntry] = []
|
||||||
|
for fileId in documentIds {
|
||||||
|
guard let file = files[fileId] else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if let entry = CodableEntry(RecentMediaItem(file)) {
|
||||||
|
items.append(OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||||
|
}
|
||||||
|
|
||||||
func managedRecentReactions(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
func managedRecentReactions(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||||
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentReactions, extractItemId: { rawId in
|
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentReactions, extractItemId: { rawId in
|
||||||
switch RecentReactionItemId(rawId).id {
|
switch RecentReactionItemId(rawId).id {
|
||||||
|
|||||||
@ -24,6 +24,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
public let maxStoriesSuggestedReactions: Int32
|
public let maxStoriesSuggestedReactions: Int32
|
||||||
public let maxGiveawayChannelsCount: Int32
|
public let maxGiveawayChannelsCount: Int32
|
||||||
public let maxGiveawayCountriesCount: Int32
|
public let maxGiveawayCountriesCount: Int32
|
||||||
|
public let maxGiveawayPeriodSeconds: Int32
|
||||||
public let minChannelNameColorLevel: Int32
|
public let minChannelNameColorLevel: Int32
|
||||||
|
|
||||||
public static var defaultValue: UserLimitsConfiguration {
|
public static var defaultValue: UserLimitsConfiguration {
|
||||||
@ -50,6 +51,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
maxStoriesSuggestedReactions: 1,
|
maxStoriesSuggestedReactions: 1,
|
||||||
maxGiveawayChannelsCount: 10,
|
maxGiveawayChannelsCount: 10,
|
||||||
maxGiveawayCountriesCount: 10,
|
maxGiveawayCountriesCount: 10,
|
||||||
|
maxGiveawayPeriodSeconds: 86400 * 7,
|
||||||
minChannelNameColorLevel: 10
|
minChannelNameColorLevel: 10
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -77,6 +79,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
maxStoriesSuggestedReactions: Int32,
|
maxStoriesSuggestedReactions: Int32,
|
||||||
maxGiveawayChannelsCount: Int32,
|
maxGiveawayChannelsCount: Int32,
|
||||||
maxGiveawayCountriesCount: Int32,
|
maxGiveawayCountriesCount: Int32,
|
||||||
|
maxGiveawayPeriodSeconds: Int32,
|
||||||
minChannelNameColorLevel: Int32
|
minChannelNameColorLevel: Int32
|
||||||
) {
|
) {
|
||||||
self.maxPinnedChatCount = maxPinnedChatCount
|
self.maxPinnedChatCount = maxPinnedChatCount
|
||||||
@ -101,6 +104,7 @@ public struct UserLimitsConfiguration: Equatable {
|
|||||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||||
|
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,6 +152,7 @@ extension UserLimitsConfiguration {
|
|||||||
self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount)
|
self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount)
|
||||||
self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount)
|
self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount)
|
||||||
self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount)
|
self.maxGiveawayCountriesCount = getGeneralValue("giveaway_countries_max", orElse: defaultValue.maxGiveawayCountriesCount)
|
||||||
|
self.maxGiveawayPeriodSeconds = getGeneralValue("giveaway_period_max", orElse: defaultValue.maxGiveawayPeriodSeconds)
|
||||||
self.minChannelNameColorLevel = getGeneralValue("channel_color_level_min", orElse: defaultValue.minChannelNameColorLevel)
|
self.minChannelNameColorLevel = getGeneralValue("channel_color_level_min", orElse: defaultValue.minChannelNameColorLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,9 +128,6 @@ public enum PeerNameColor: Int32, CaseIterable {
|
|||||||
case greenDash
|
case greenDash
|
||||||
case cyanDash
|
case cyanDash
|
||||||
case blueDash
|
case blueDash
|
||||||
case other13
|
|
||||||
case other14
|
|
||||||
case other15
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PeerEmojiStatus: Equatable, Codable {
|
public struct PeerEmojiStatus: Equatable, Codable {
|
||||||
|
|||||||
@ -80,6 +80,7 @@ public struct Namespaces {
|
|||||||
public static let CloudFeaturedProfilePhotoEmoji: Int32 = 23
|
public static let CloudFeaturedProfilePhotoEmoji: Int32 = 23
|
||||||
public static let CloudFeaturedGroupPhotoEmoji: Int32 = 24
|
public static let CloudFeaturedGroupPhotoEmoji: Int32 = 24
|
||||||
public static let NewSessionReviews: Int32 = 25
|
public static let NewSessionReviews: Int32 = 25
|
||||||
|
public static let CloudFeaturedBackgroundIconEmoji: Int32 = 26
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CachedItemCollection {
|
public struct CachedItemCollection {
|
||||||
|
|||||||
@ -58,6 +58,7 @@ public enum EngineConfiguration {
|
|||||||
public let maxStoriesSuggestedReactions: Int32
|
public let maxStoriesSuggestedReactions: Int32
|
||||||
public let maxGiveawayChannelsCount: Int32
|
public let maxGiveawayChannelsCount: Int32
|
||||||
public let maxGiveawayCountriesCount: Int32
|
public let maxGiveawayCountriesCount: Int32
|
||||||
|
public let maxGiveawayPeriodSeconds: Int32
|
||||||
public let minChannelNameColorLevel: Int32
|
public let minChannelNameColorLevel: Int32
|
||||||
|
|
||||||
public static var defaultValue: UserLimits {
|
public static var defaultValue: UserLimits {
|
||||||
@ -87,6 +88,7 @@ public enum EngineConfiguration {
|
|||||||
maxStoriesSuggestedReactions: Int32,
|
maxStoriesSuggestedReactions: Int32,
|
||||||
maxGiveawayChannelsCount: Int32,
|
maxGiveawayChannelsCount: Int32,
|
||||||
maxGiveawayCountriesCount: Int32,
|
maxGiveawayCountriesCount: Int32,
|
||||||
|
maxGiveawayPeriodSeconds: Int32,
|
||||||
minChannelNameColorLevel: Int32
|
minChannelNameColorLevel: Int32
|
||||||
) {
|
) {
|
||||||
self.maxPinnedChatCount = maxPinnedChatCount
|
self.maxPinnedChatCount = maxPinnedChatCount
|
||||||
@ -111,6 +113,7 @@ public enum EngineConfiguration {
|
|||||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||||
|
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,6 +174,7 @@ public extension EngineConfiguration.UserLimits {
|
|||||||
maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions,
|
maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions,
|
||||||
maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount,
|
maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount,
|
||||||
maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount,
|
maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount,
|
||||||
|
maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds,
|
||||||
minChannelNameColorLevel: userLimitsConfiguration.minChannelNameColorLevel
|
minChannelNameColorLevel: userLimitsConfiguration.minChannelNameColorLevel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -514,7 +514,7 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
}
|
}
|
||||||
} else if let chatInvite = chatInvite, let chatInviteHash = chatInviteHash {
|
} else if let chatInvite = chatInvite, let chatInviteHash = chatInviteHash {
|
||||||
switch chatInvite {
|
switch chatInvite {
|
||||||
case let .chatInvite(flags, title, _, photo, participantsCount, participants):
|
case let .chatInvite(flags, title, _, photo, participantsCount, participants, nameColor):
|
||||||
let photo = telegramMediaImageFromApiPhoto(photo).flatMap({ smallestImageRepresentation($0.representations) })
|
let photo = telegramMediaImageFromApiPhoto(photo).flatMap({ smallestImageRepresentation($0.representations) })
|
||||||
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0)
|
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0)
|
||||||
|
|
||||||
@ -522,6 +522,7 @@ private class AdMessagesHistoryContextImpl {
|
|||||||
let _ = flags
|
let _ = flags
|
||||||
let _ = participantsCount
|
let _ = participantsCount
|
||||||
let _ = participants
|
let _ = participants
|
||||||
|
let _ = nameColor
|
||||||
|
|
||||||
target = .invite(CachedMessage.Target.Invite(
|
target = .invite(CachedMessage.Target.Invite(
|
||||||
title: title,
|
title: title,
|
||||||
|
|||||||
@ -47,6 +47,7 @@ public enum ExternalJoiningChatState {
|
|||||||
public let photoRepresentation: TelegramMediaImageRepresentation?
|
public let photoRepresentation: TelegramMediaImageRepresentation?
|
||||||
public let participantsCount: Int32
|
public let participantsCount: Int32
|
||||||
public let participants: [EnginePeer]?
|
public let participants: [EnginePeer]?
|
||||||
|
public let nameColor: PeerNameColor?
|
||||||
}
|
}
|
||||||
|
|
||||||
case invite(Invite)
|
case invite(Invite)
|
||||||
@ -105,10 +106,10 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|
|||||||
|> mapToSignal { result -> Signal<ExternalJoiningChatState, JoinLinkInfoError> in
|
|> mapToSignal { result -> Signal<ExternalJoiningChatState, JoinLinkInfoError> in
|
||||||
if let result = result {
|
if let result = result {
|
||||||
switch result {
|
switch result {
|
||||||
case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants):
|
case let .chatInvite(flags, title, about, invitePhoto, participantsCount, participants, nameColor):
|
||||||
let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) })
|
let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) })
|
||||||
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0)
|
let flags: ExternalJoiningChatState.Invite.Flags = .init(isChannel: (flags & (1 << 0)) != 0, isBroadcast: (flags & (1 << 1)) != 0, isPublic: (flags & (1 << 2)) != 0, isMegagroup: (flags & (1 << 3)) != 0, requestNeeded: (flags & (1 << 6)) != 0, isVerified: (flags & (1 << 7)) != 0, isScam: (flags & (1 << 8)) != 0, isFake: (flags & (1 << 9)) != 0)
|
||||||
return .single(.invite(ExternalJoiningChatState.Invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({ EnginePeer(TelegramUser(user: $0)) }))))
|
return .single(.invite(ExternalJoiningChatState.Invite(flags: flags, title: title, about: about, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({ EnginePeer(TelegramUser(user: $0)) }), nameColor: nameColor.flatMap({ PeerNameColor(rawValue: $0) }))))
|
||||||
case let .chatInviteAlready(chat):
|
case let .chatInviteAlready(chat):
|
||||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||||
return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in
|
return account.postbox.transaction({ (transaction) -> ExternalJoiningChatState in
|
||||||
|
|||||||
@ -509,6 +509,10 @@ public extension EnginePeer {
|
|||||||
var nameColor: PeerNameColor? {
|
var nameColor: PeerNameColor? {
|
||||||
return self._asPeer().nameColor
|
return self._asPeer().nameColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var backgroundEmojiId: Int64? {
|
||||||
|
return self._asPeer().backgroundEmojiId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension EnginePeer {
|
public extension EnginePeer {
|
||||||
|
|||||||
@ -243,6 +243,17 @@ public extension Peer {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var backgroundEmojiId: Int64? {
|
||||||
|
switch self {
|
||||||
|
case let user as TelegramUser:
|
||||||
|
return user.backgroundEmojiId
|
||||||
|
case let channel as TelegramChannel:
|
||||||
|
return channel.backgroundEmojiId
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension TelegramPeerUsername {
|
public extension TelegramPeerUsername {
|
||||||
|
|||||||
@ -151,7 +151,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou
|
|||||||
if let image = generateImage(size, rotatedContext: { size, context in
|
if let image = generateImage(size, rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), font: avatarFont, letters: peer.displayLetters, peerId: peer.id)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width, height: size.height), font: avatarFont, letters: peer.displayLetters, peerId: peer.id, nameColor: peer.nameColor)
|
||||||
})?.withRenderingMode(.alwaysOriginal) {
|
})?.withRenderingMode(.alwaysOriginal) {
|
||||||
avatarImage = image
|
avatarImage = image
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,12 +35,6 @@ public extension PeerNameColor {
|
|||||||
return (UIColor(rgb: 0x27ACCE), UIColor(rgb: 0x82E8D6))
|
return (UIColor(rgb: 0x27ACCE), UIColor(rgb: 0x82E8D6))
|
||||||
case .blueDash:
|
case .blueDash:
|
||||||
return (UIColor(rgb: 0x3391D4), UIColor(rgb: 0x7DD3F0))
|
return (UIColor(rgb: 0x3391D4), UIColor(rgb: 0x7DD3F0))
|
||||||
case .other13:
|
|
||||||
return (.black, nil)
|
|
||||||
case .other14:
|
|
||||||
return (.black, nil)
|
|
||||||
case .other15:
|
|
||||||
return (.black, nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1323,11 +1323,11 @@ public struct PresentationResourcesChat {
|
|||||||
let radius: CGFloat = 3.0
|
let radius: CGFloat = 3.0
|
||||||
let offset: CGFloat = 5.0
|
let offset: CGFloat = 5.0
|
||||||
|
|
||||||
return generateImage(CGSize(width: radius, height: radius * 6.0), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 8.0, height: radius * 6.0), rotatedContext: { size, context in
|
||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
context.move(to: CGPoint(x: size.width, y: offset))
|
context.move(to: CGPoint(x: radius, y: offset))
|
||||||
context.addLine(to: CGPoint(x: size.width, y: offset + radius * 3.0))
|
context.addLine(to: CGPoint(x: radius, y: offset + radius * 3.0))
|
||||||
context.addLine(to: CGPoint(x: 0.0, y: offset + radius * 4.0))
|
context.addLine(to: CGPoint(x: 0.0, y: offset + radius * 4.0))
|
||||||
context.addLine(to: CGPoint(x: 0.0, y: offset + radius))
|
context.addLine(to: CGPoint(x: 0.0, y: offset + radius))
|
||||||
context.closePath()
|
context.closePath()
|
||||||
|
|||||||
@ -1509,12 +1509,8 @@ public final class AvatarEditorScreen: ViewControllerComponentContainer {
|
|||||||
animationCache: context.animationCache,
|
animationCache: context.animationCache,
|
||||||
animationRenderer: context.animationRenderer,
|
animationRenderer: context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: isGroup ? .groupPhoto : .profilePhoto,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
isProfilePhotoEmojiSelection: !isGroup,
|
|
||||||
isGroupPhotoEmojiSelection: isGroup,
|
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
areCustomEmojiEnabled: true,
|
areCustomEmojiEnabled: true,
|
||||||
|
|||||||
@ -771,6 +771,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
context.add(frontFlash
|
context.add(frontFlash
|
||||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||||
|
.scale(1.5 - component.cameraState.flashTintSize * 0.5)
|
||||||
.appear(.default(alpha: true))
|
.appear(.default(alpha: true))
|
||||||
.disappear(.default(alpha: true))
|
.disappear(.default(alpha: true))
|
||||||
)
|
)
|
||||||
|
|||||||
@ -53,6 +53,7 @@ public struct ChatMessageAttachedContentNodeMediaFlags: OptionSet {
|
|||||||
|
|
||||||
public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||||
private var backgroundView: UIImageView?
|
private var backgroundView: UIImageView?
|
||||||
|
private var lineDashView: UIImageView?
|
||||||
|
|
||||||
private var title: TextNodeWithEntities?
|
private var title: TextNodeWithEntities?
|
||||||
private var subtitle: TextNodeWithEntities?
|
private var subtitle: TextNodeWithEntities?
|
||||||
@ -159,29 +160,32 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
|
let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing
|
||||||
|
|
||||||
let mainColor: UIColor
|
let mainColor: UIColor
|
||||||
|
var secondaryColor: UIColor?
|
||||||
if !incoming {
|
if !incoming {
|
||||||
mainColor = messageTheme.accentTextColor
|
mainColor = messageTheme.accentTextColor
|
||||||
} else {
|
} else {
|
||||||
var authorNameColor: UIColor?
|
var authorNameColor: UIColor?
|
||||||
let author = message.author
|
let author = message.author
|
||||||
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), author?.id.namespace == Namespaces.Peer.CloudUser {
|
if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), author?.id.namespace == Namespaces.Peer.CloudUser {
|
||||||
authorNameColor = author.flatMap { chatMessagePeerIdColors[Int(clamping: $0.id.id._internalGetInt64Value() % 7)] }
|
authorNameColor = author?.nameColor?.color
|
||||||
if let rawAuthorNameColor = authorNameColor {
|
secondaryColor = author?.nameColor?.dashColors.1
|
||||||
var dimColors = false
|
|
||||||
switch presentationData.theme.theme.name {
|
// if let rawAuthorNameColor = authorNameColor {
|
||||||
case .builtin(.nightAccent), .builtin(.night):
|
// var dimColors = false
|
||||||
dimColors = true
|
// switch presentationData.theme.theme.name {
|
||||||
default:
|
// case .builtin(.nightAccent), .builtin(.night):
|
||||||
break
|
// dimColors = true
|
||||||
}
|
// default:
|
||||||
if dimColors {
|
// break
|
||||||
var hue: CGFloat = 0.0
|
// }
|
||||||
var saturation: CGFloat = 0.0
|
// if dimColors {
|
||||||
var brightness: CGFloat = 0.0
|
// var hue: CGFloat = 0.0
|
||||||
rawAuthorNameColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil)
|
// var saturation: CGFloat = 0.0
|
||||||
authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0)
|
// var brightness: CGFloat = 0.0
|
||||||
}
|
// rawAuthorNameColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil)
|
||||||
}
|
// authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let authorNameColor {
|
if let authorNameColor {
|
||||||
@ -399,7 +403,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
cutout = TextNodeCutout(topRight: CGSize(width: cutoutWidth, height: remainingCutoutHeight))
|
cutout = TextNodeCutout(topRight: CGSize(width: cutoutWidth, height: remainingCutoutHeight))
|
||||||
}
|
}
|
||||||
|
|
||||||
let titleString = NSAttributedString(string: title, font: titleFont, textColor: messageTheme.accentTextColor)
|
let titleString = NSAttributedString(string: title, font: titleFont, textColor: mainColor)
|
||||||
let titleLayoutAndApplyValue = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: maxContentsWidth, height: 10000.0), alignment: .natural, lineSpacing: textLineSpacing, cutout: cutout, insets: UIEdgeInsets()))
|
let titleLayoutAndApplyValue = makeTitleLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: maxContentsWidth, height: 10000.0), alignment: .natural, lineSpacing: textLineSpacing, cutout: cutout, insets: UIEdgeInsets()))
|
||||||
titleLayoutAndApply = titleLayoutAndApplyValue
|
titleLayoutAndApply = titleLayoutAndApplyValue
|
||||||
|
|
||||||
@ -764,6 +768,26 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
backgroundView.tintColor = mainColor
|
backgroundView.tintColor = mainColor
|
||||||
|
|
||||||
|
if let secondaryColor {
|
||||||
|
let lineDashView: UIImageView
|
||||||
|
if let current = self.lineDashView {
|
||||||
|
lineDashView = current
|
||||||
|
} else {
|
||||||
|
lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(presentationData.theme.theme))
|
||||||
|
lineDashView.clipsToBounds = true
|
||||||
|
self.lineDashView = lineDashView
|
||||||
|
self.view.insertSubview(lineDashView, aboveSubview: backgroundView)
|
||||||
|
}
|
||||||
|
lineDashView.tintColor = secondaryColor
|
||||||
|
lineDashView.frame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 8.0, height: backgroundFrame.height))
|
||||||
|
lineDashView.layer.cornerRadius = 4.0
|
||||||
|
} else {
|
||||||
|
if let lineDashView = self.lineDashView {
|
||||||
|
self.lineDashView = nil
|
||||||
|
lineDashView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if let backgroundView = self.backgroundView {
|
if let backgroundView = self.backgroundView {
|
||||||
self.backgroundView = nil
|
self.backgroundView = nil
|
||||||
|
|||||||
@ -154,7 +154,7 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode
|
|||||||
|
|
||||||
let presentationData = item.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = item.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can't participate in this giveaway.", timeout: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false })
|
let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: nil, text: "You can't participate in this giveaway.", timeout: nil), elevatedLayout: false, position: .bottom, animateInAsReplacement: false, action: { _ in return false })
|
||||||
item.controllerInteraction.presentController(controller, nil)
|
item.controllerInteraction.presentControllerInCurrent(controller, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removePlaceholder(animated: Bool) {
|
private func removePlaceholder(animated: Bool) {
|
||||||
|
|||||||
@ -1708,12 +1708,12 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum PeerAvatarReference: Equatable {
|
private enum PeerAvatarReference: Equatable {
|
||||||
case letters(PeerId, [String])
|
case letters(PeerId, PeerNameColor?, [String])
|
||||||
case image(PeerReference, TelegramMediaImageRepresentation)
|
case image(PeerReference, TelegramMediaImageRepresentation)
|
||||||
|
|
||||||
var peerId: PeerId {
|
var peerId: PeerId {
|
||||||
switch self {
|
switch self {
|
||||||
case let .letters(value, _):
|
case let .letters(value, _, _):
|
||||||
return value
|
return value
|
||||||
case let .image(value, _):
|
case let .image(value, _):
|
||||||
return value.id
|
return value.id
|
||||||
@ -1726,7 +1726,7 @@ private extension PeerAvatarReference {
|
|||||||
if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) {
|
if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) {
|
||||||
self = .image(peerReference, photo)
|
self = .image(peerReference, photo)
|
||||||
} else {
|
} else {
|
||||||
self = .letters(peer.id, peer.displayLetters)
|
self = .letters(peer.id, peer.nameColor, peer.displayLetters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1885,9 +1885,9 @@ public final class MergedAvatarsNode: ASDisplayNode {
|
|||||||
|
|
||||||
context.saveGState()
|
context.saveGState()
|
||||||
switch parameters.peers[i] {
|
switch parameters.peers[i] {
|
||||||
case let .letters(peerId, letters):
|
case let .letters(peerId, nameColor, letters):
|
||||||
context.translateBy(x: currentX, y: 0.0)
|
context.translateBy(x: currentX, y: 0.0)
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: mergedImageSize, height: mergedImageSize), font: avatarFont, letters: letters, peerId: peerId)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: mergedImageSize, height: mergedImageSize), font: avatarFont, letters: letters, peerId: peerId, nameColor: nameColor)
|
||||||
context.translateBy(x: -currentX, y: 0.0)
|
context.translateBy(x: -currentX, y: 0.0)
|
||||||
case .image:
|
case .image:
|
||||||
if let image = parameters.images[parameters.peers[i].peerId] {
|
if let image = parameters.images[parameters.peers[i].peerId] {
|
||||||
|
|||||||
@ -654,11 +654,13 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
lineDashView = current
|
lineDashView = current
|
||||||
} else {
|
} else {
|
||||||
lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(arguments.presentationData.theme.theme))
|
lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(arguments.presentationData.theme.theme))
|
||||||
|
lineDashView.clipsToBounds = true
|
||||||
node.lineDashView = lineDashView
|
node.lineDashView = lineDashView
|
||||||
node.contentNode.view.addSubview(lineDashView)
|
node.contentNode.view.addSubview(lineDashView)
|
||||||
}
|
}
|
||||||
lineDashView.tintColor = secondaryColor
|
lineDashView.tintColor = secondaryColor
|
||||||
lineDashView.frame = CGRect(origin: .zero, size: CGSize(width: 3.0, height: backgroundFrame.height))
|
lineDashView.frame = CGRect(origin: .zero, size: CGSize(width: 8.0, height: backgroundFrame.height))
|
||||||
|
lineDashView.layer.cornerRadius = 4.0
|
||||||
} else {
|
} else {
|
||||||
if let lineDashView = node.lineDashView {
|
if let lineDashView = node.lineDashView {
|
||||||
node.lineDashView = nil
|
node.lineDashView = nil
|
||||||
|
|||||||
@ -160,7 +160,20 @@ public final class ChatEntityKeyboardInputNode: ChatInputNode {
|
|||||||
let animationCache = context.animationCache
|
let animationCache = context.animationCache
|
||||||
let animationRenderer = context.animationRenderer
|
let animationRenderer = context.animationRenderer
|
||||||
|
|
||||||
let emojiItems = EmojiPagerContentComponent.emojiInputData(context: context, animationCache: animationCache, animationRenderer: animationRenderer, isStandalone: false, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: true, hasTrending: hasTrending, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: chatPeerId, hasSearch: hasSearch, hideBackground: hideBackground)
|
let emojiItems = EmojiPagerContentComponent.emojiInputData(
|
||||||
|
context: context,
|
||||||
|
animationCache: animationCache,
|
||||||
|
animationRenderer: animationRenderer,
|
||||||
|
isStandalone: false,
|
||||||
|
subject: .emoji,
|
||||||
|
hasTrending: hasTrending,
|
||||||
|
topReactionItems: [],
|
||||||
|
areUnicodeEmojiEnabled: true,
|
||||||
|
areCustomEmojiEnabled: areCustomEmojiEnabled,
|
||||||
|
chatPeerId: chatPeerId,
|
||||||
|
hasSearch: hasSearch,
|
||||||
|
hideBackground: hideBackground
|
||||||
|
)
|
||||||
|
|
||||||
let stickerNamespaces: [ItemCollectionId.Namespace] = [Namespaces.ItemCollection.CloudStickerPacks]
|
let stickerNamespaces: [ItemCollectionId.Namespace] = [Namespaces.ItemCollection.CloudStickerPacks]
|
||||||
let stickerOrderedItemListCollectionIds: [Int32] = [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers, Namespaces.OrderedItemList.CloudAllPremiumStickers]
|
let stickerOrderedItemListCollectionIds: [Int32] = [Namespaces.OrderedItemList.CloudSavedStickers, Namespaces.OrderedItemList.CloudRecentStickers, Namespaces.OrderedItemList.CloudAllPremiumStickers]
|
||||||
@ -2256,7 +2269,19 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi
|
|||||||
|
|
||||||
let semaphore = DispatchSemaphore(value: 0)
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
var emojiComponent: EmojiPagerContentComponent?
|
var emojiComponent: EmojiPagerContentComponent?
|
||||||
let _ = EmojiPagerContentComponent.emojiInputData(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, isStandalone: true, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: false, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: nil, forceHasPremium: forceHasPremium).start(next: { value in
|
let _ = EmojiPagerContentComponent.emojiInputData(
|
||||||
|
context: context,
|
||||||
|
animationCache: self.animationCache,
|
||||||
|
animationRenderer: self.animationRenderer,
|
||||||
|
isStandalone: true,
|
||||||
|
subject: .generic,
|
||||||
|
hasTrending: false,
|
||||||
|
topReactionItems: [],
|
||||||
|
areUnicodeEmojiEnabled: true,
|
||||||
|
areCustomEmojiEnabled: areCustomEmojiEnabled,
|
||||||
|
chatPeerId: nil,
|
||||||
|
forceHasPremium: forceHasPremium
|
||||||
|
).start(next: { value in
|
||||||
emojiComponent = value
|
emojiComponent = value
|
||||||
semaphore.signal()
|
semaphore.signal()
|
||||||
})
|
})
|
||||||
@ -2271,7 +2296,20 @@ public final class EntityInputView: UIInputView, AttachmentTextInputPanelInputVi
|
|||||||
gifs: nil,
|
gifs: nil,
|
||||||
availableGifSearchEmojies: []
|
availableGifSearchEmojies: []
|
||||||
),
|
),
|
||||||
updatedInputData: EmojiPagerContentComponent.emojiInputData(context: context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, isStandalone: true, isStatusSelection: false, isReactionSelection: false, isEmojiSelection: false, hasTrending: false, topReactionItems: [], areUnicodeEmojiEnabled: true, areCustomEmojiEnabled: areCustomEmojiEnabled, chatPeerId: nil, forceHasPremium: forceHasPremium, hideBackground: hideBackground) |> map { emojiComponent -> ChatEntityKeyboardInputNode.InputData in
|
updatedInputData: EmojiPagerContentComponent.emojiInputData(
|
||||||
|
context: context,
|
||||||
|
animationCache: self.animationCache,
|
||||||
|
animationRenderer: self.animationRenderer,
|
||||||
|
isStandalone: true,
|
||||||
|
subject: .generic,
|
||||||
|
hasTrending: false,
|
||||||
|
topReactionItems: [],
|
||||||
|
areUnicodeEmojiEnabled: true,
|
||||||
|
areCustomEmojiEnabled: areCustomEmojiEnabled,
|
||||||
|
chatPeerId: nil,
|
||||||
|
forceHasPremium: forceHasPremium,
|
||||||
|
hideBackground: hideBackground
|
||||||
|
) |> map { emojiComponent -> ChatEntityKeyboardInputNode.InputData in
|
||||||
return ChatEntityKeyboardInputNode.InputData(
|
return ChatEntityKeyboardInputNode.InputData(
|
||||||
emoji: emojiComponent,
|
emoji: emojiComponent,
|
||||||
stickers: nil,
|
stickers: nil,
|
||||||
|
|||||||
@ -822,6 +822,8 @@ public final class EmojiStatusSelectionController: ViewController {
|
|||||||
dynamicColor: self.presentationData.theme.list.itemAccentColor
|
dynamicColor: self.presentationData.theme.list.itemAccentColor
|
||||||
)
|
)
|
||||||
switch item.tintMode {
|
switch item.tintMode {
|
||||||
|
case let .custom(color):
|
||||||
|
baseItemLayer.contentTintColor = color
|
||||||
case .accent:
|
case .accent:
|
||||||
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
baseItemLayer.contentTintColor = self.presentationData.theme.list.itemAccentColor
|
||||||
case .primary:
|
case .primary:
|
||||||
|
|||||||
@ -1328,6 +1328,8 @@ private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, Pager
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch item.tintMode {
|
switch item.tintMode {
|
||||||
|
case let .custom(color):
|
||||||
|
itemLayer.layerTintColor = color.cgColor
|
||||||
case .accent:
|
case .accent:
|
||||||
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
|
itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor
|
||||||
case .primary:
|
case .primary:
|
||||||
@ -2443,6 +2445,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
public enum Icon: Equatable, Hashable {
|
public enum Icon: Equatable, Hashable {
|
||||||
case premiumStar
|
case premiumStar
|
||||||
case topic(String, Int32)
|
case topic(String, Int32)
|
||||||
|
case stop
|
||||||
}
|
}
|
||||||
|
|
||||||
case animation(EntityKeyboardAnimationData)
|
case animation(EntityKeyboardAnimationData)
|
||||||
@ -2468,10 +2471,11 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
case premium
|
case premium
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TintMode {
|
public enum TintMode: Equatable {
|
||||||
case none
|
case none
|
||||||
case accent
|
case accent
|
||||||
case primary
|
case primary
|
||||||
|
case custom(UIColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
public let animationData: EntityKeyboardAnimationData?
|
public let animationData: EntityKeyboardAnimationData?
|
||||||
@ -2538,6 +2542,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
public let displayPremiumBadges: Bool
|
public let displayPremiumBadges: Bool
|
||||||
public let headerItem: EntityKeyboardAnimationData?
|
public let headerItem: EntityKeyboardAnimationData?
|
||||||
public let fillWithLoadingPlaceholders: Bool
|
public let fillWithLoadingPlaceholders: Bool
|
||||||
|
public let customTintColor: UIColor?
|
||||||
public let items: [Item]
|
public let items: [Item]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@ -2554,6 +2559,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
displayPremiumBadges: Bool,
|
displayPremiumBadges: Bool,
|
||||||
headerItem: EntityKeyboardAnimationData?,
|
headerItem: EntityKeyboardAnimationData?,
|
||||||
fillWithLoadingPlaceholders: Bool,
|
fillWithLoadingPlaceholders: Bool,
|
||||||
|
customTintColor: UIColor? = nil,
|
||||||
items: [Item]
|
items: [Item]
|
||||||
) {
|
) {
|
||||||
self.supergroupId = supergroupId
|
self.supergroupId = supergroupId
|
||||||
@ -2569,6 +2575,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
self.displayPremiumBadges = displayPremiumBadges
|
self.displayPremiumBadges = displayPremiumBadges
|
||||||
self.headerItem = headerItem
|
self.headerItem = headerItem
|
||||||
self.fillWithLoadingPlaceholders = fillWithLoadingPlaceholders
|
self.fillWithLoadingPlaceholders = fillWithLoadingPlaceholders
|
||||||
|
self.customTintColor = customTintColor
|
||||||
self.items = items
|
self.items = items
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2615,6 +2622,9 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
if lhs.fillWithLoadingPlaceholders != rhs.fillWithLoadingPlaceholders {
|
if lhs.fillWithLoadingPlaceholders != rhs.fillWithLoadingPlaceholders {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.customTintColor != rhs.customTintColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if lhs.items != rhs.items {
|
if lhs.items != rhs.items {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -3400,6 +3410,11 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
let imageSize = image.size//.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0))
|
let imageSize = image.size//.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0))
|
||||||
image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize))
|
image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize))
|
||||||
}
|
}
|
||||||
|
case .stop:
|
||||||
|
if let image = generateTintedImage(image: UIImage(bundleImageName: "Peer Info/ButtonStop"), color: UIColor(rgb: 0xcdcdcd)) {
|
||||||
|
let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0))
|
||||||
|
image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIGraphicsPopContext()
|
UIGraphicsPopContext()
|
||||||
@ -5838,6 +5853,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor)
|
itemLayer.update(transition: transition, size: itemFrame.size, badge: badge, blurredBadgeColor: UIColor(white: 0.0, alpha: 0.1), blurredBadgeBackgroundColor: keyboardChildEnvironment.theme.list.plainBackgroundColor)
|
||||||
|
|
||||||
switch item.tintMode {
|
switch item.tintMode {
|
||||||
|
case let .custom(color):
|
||||||
|
itemLayer.layerTintColor = color.cgColor
|
||||||
case .accent:
|
case .accent:
|
||||||
itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemAccentColor.cgColor
|
itemLayer.layerTintColor = keyboardChildEnvironment.theme.list.itemAccentColor.cgColor
|
||||||
case .primary:
|
case .primary:
|
||||||
@ -7088,19 +7105,25 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
return hasPremium
|
return hasPremium
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Subject {
|
||||||
|
case generic
|
||||||
|
case status
|
||||||
|
case reaction
|
||||||
|
case emoji
|
||||||
|
case topicIcon
|
||||||
|
case quickReaction
|
||||||
|
case profilePhoto
|
||||||
|
case groupPhoto
|
||||||
|
case backgroundIcon
|
||||||
|
}
|
||||||
|
|
||||||
public static func emojiInputData(
|
public static func emojiInputData(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
animationCache: AnimationCache,
|
animationCache: AnimationCache,
|
||||||
animationRenderer: MultiAnimationRenderer,
|
animationRenderer: MultiAnimationRenderer,
|
||||||
isStandalone: Bool,
|
isStandalone: Bool,
|
||||||
isStatusSelection: Bool,
|
subject: Subject,
|
||||||
isReactionSelection: Bool,
|
|
||||||
isEmojiSelection: Bool,
|
|
||||||
hasTrending: Bool,
|
hasTrending: Bool,
|
||||||
isTopicIconSelection: Bool = false,
|
|
||||||
isQuickReactionSelection: Bool = false,
|
|
||||||
isProfilePhotoEmojiSelection: Bool = false,
|
|
||||||
isGroupPhotoEmojiSelection: Bool = false,
|
|
||||||
topReactionItems: [EmojiComponentReactionItem],
|
topReactionItems: [EmojiComponentReactionItem],
|
||||||
areUnicodeEmojiEnabled: Bool,
|
areUnicodeEmojiEnabled: Bool,
|
||||||
areCustomEmojiEnabled: Bool,
|
areCustomEmojiEnabled: Bool,
|
||||||
@ -7109,6 +7132,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
topStatusTitle: String? = nil,
|
topStatusTitle: String? = nil,
|
||||||
topicTitle: String? = nil,
|
topicTitle: String? = nil,
|
||||||
topicColor: Int32? = nil,
|
topicColor: Int32? = nil,
|
||||||
|
backgroundIconColor: UIColor? = nil,
|
||||||
hasSearch: Bool = true,
|
hasSearch: Bool = true,
|
||||||
forceHasPremium: Bool = false,
|
forceHasPremium: Bool = false,
|
||||||
premiumIfSavedMessages: Bool = true,
|
premiumIfSavedMessages: Bool = true,
|
||||||
@ -7125,7 +7149,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
|
|
||||||
var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([])
|
var iconStatusEmoji: Signal<[TelegramMediaFile], NoError> = .single([])
|
||||||
|
|
||||||
if isStatusSelection {
|
if case .status = subject {
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji)
|
||||||
|
|
||||||
@ -7139,10 +7163,10 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
} else if isReactionSelection {
|
} else if [.reaction, .quickReaction].contains(subject) {
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudTopReactions)
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentReactions)
|
||||||
} else if isTopicIconSelection {
|
} else if case .topicIcon = subject {
|
||||||
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false)
|
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconTopicEmoji, forceActualized: false)
|
||||||
|> map { result -> [TelegramMediaFile] in
|
|> map { result -> [TelegramMediaFile] in
|
||||||
switch result {
|
switch result {
|
||||||
@ -7153,25 +7177,27 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> take(1)
|
|> take(1)
|
||||||
} else if isProfilePhotoEmojiSelection {
|
} else if case .profilePhoto = subject {
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji)
|
||||||
} else if isGroupPhotoEmojiSelection {
|
} else if case .groupPhoto = subject {
|
||||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji)
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji)
|
||||||
|
} else if case .backgroundIcon = subject {
|
||||||
|
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji)
|
||||||
}
|
}
|
||||||
|
|
||||||
let availableReactions: Signal<AvailableReactions?, NoError>
|
let availableReactions: Signal<AvailableReactions?, NoError>
|
||||||
if isReactionSelection {
|
if [.reaction, .quickReaction].contains(subject) {
|
||||||
availableReactions = context.engine.stickers.availableReactions()
|
availableReactions = context.engine.stickers.availableReactions()
|
||||||
} else {
|
} else {
|
||||||
availableReactions = .single(nil)
|
availableReactions = .single(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
let searchCategories: Signal<EmojiSearchCategories?, NoError>
|
||||||
if isEmojiSelection || isReactionSelection {
|
if [.emoji, .reaction].contains(subject) {
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .emoji)
|
||||||
} else if isStatusSelection {
|
} else if case .status = subject {
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .status)
|
||||||
} else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection {
|
} else if [.profilePhoto, .groupPhoto].contains(subject) {
|
||||||
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
searchCategories = context.engine.stickers.emojiSearchCategories(kind: .avatar)
|
||||||
} else {
|
} else {
|
||||||
searchCategories = .single(nil)
|
searchCategories = .single(nil)
|
||||||
@ -7312,6 +7338,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
var topReactions: OrderedItemListView?
|
var topReactions: OrderedItemListView?
|
||||||
var recentReactions: OrderedItemListView?
|
var recentReactions: OrderedItemListView?
|
||||||
var featuredAvatarEmoji: OrderedItemListView?
|
var featuredAvatarEmoji: OrderedItemListView?
|
||||||
|
var featuredBackgroundIconEmoji: OrderedItemListView?
|
||||||
for orderedView in view.orderedItemListsViews {
|
for orderedView in view.orderedItemListsViews {
|
||||||
if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji {
|
if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji {
|
||||||
recentEmoji = orderedView
|
recentEmoji = orderedView
|
||||||
@ -7327,10 +7354,12 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
featuredAvatarEmoji = orderedView
|
featuredAvatarEmoji = orderedView
|
||||||
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji {
|
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedGroupPhotoEmoji {
|
||||||
featuredAvatarEmoji = orderedView
|
featuredAvatarEmoji = orderedView
|
||||||
|
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedBackgroundIconEmoji {
|
||||||
|
featuredBackgroundIconEmoji = orderedView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isTopicIconSelection {
|
if case .topicIcon = subject {
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
animationData: nil,
|
animationData: nil,
|
||||||
content: .icon(.topic(String((topicTitle ?? "").prefix(1)), topicColor ?? 0)),
|
content: .icon(.topic(String((topicTitle ?? "").prefix(1)), topicColor ?? 0)),
|
||||||
@ -7389,7 +7418,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if isStatusSelection {
|
} else if case .status = subject {
|
||||||
let resultItem = EmojiPagerContentComponent.Item(
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
animationData: nil,
|
animationData: nil,
|
||||||
content: .icon(.premiumStar),
|
content: .icon(.premiumStar),
|
||||||
@ -7549,7 +7578,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if isReactionSelection {
|
} else if [.reaction, .quickReaction].contains(subject) {
|
||||||
var existingIds = Set<MessageReaction.Reaction>()
|
var existingIds = Set<MessageReaction.Reaction>()
|
||||||
|
|
||||||
var topReactionItems = topReactionItems
|
var topReactionItems = topReactionItems
|
||||||
@ -7676,7 +7705,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemGroups[groupIndex].items.append(resultItem)
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
} else {
|
} else {
|
||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && !isQuickReactionSelection, headerItem: nil, items: [resultItem]))
|
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let groupId = "recent"
|
let groupId = "recent"
|
||||||
@ -7760,11 +7789,11 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
popularInsertIndex += 1
|
popularInsertIndex += 1
|
||||||
} else {
|
} else {
|
||||||
itemGroupIndexById[groupId] = itemGroups.count
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && !isQuickReactionSelection, headerItem: nil, items: [resultItem]))
|
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: popularTitle, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: nil, isClearable: hasRecent && subject != .quickReaction, headerItem: nil, items: [resultItem]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection {
|
} else if [.profilePhoto, .groupPhoto].contains(subject) {
|
||||||
var existingIds = Set<MediaId>()
|
var existingIds = Set<MediaId>()
|
||||||
|
|
||||||
let groupId = "recent"
|
let groupId = "recent"
|
||||||
@ -7812,6 +7841,80 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
tintMode: tintMode
|
tintMode: tintMode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
|
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if case .backgroundIcon = subject {
|
||||||
|
var existingIds = Set<MediaId>()
|
||||||
|
|
||||||
|
let resultItem = EmojiPagerContentComponent.Item(
|
||||||
|
animationData: nil,
|
||||||
|
content: .icon(.stop),
|
||||||
|
itemFile: nil,
|
||||||
|
subgroupId: nil,
|
||||||
|
icon: .none,
|
||||||
|
tintMode: .none
|
||||||
|
)
|
||||||
|
|
||||||
|
let groupId = "recent"
|
||||||
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
|
itemGroups[groupIndex].items.append(resultItem)
|
||||||
|
} else {
|
||||||
|
itemGroupIndexById[groupId] = itemGroups.count
|
||||||
|
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: nil, subtitle: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 5, isClearable: false, headerItem: nil, items: [resultItem]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let featuredBackgroundIconEmoji {
|
||||||
|
for item in featuredBackgroundIconEmoji.items {
|
||||||
|
guard let item = item.contents.get(RecentMediaItem.self) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = item.media
|
||||||
|
if existingIds.contains(file.fileId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
existingIds.insert(file.fileId)
|
||||||
|
|
||||||
|
let resultItem: EmojiPagerContentComponent.Item
|
||||||
|
|
||||||
|
var tintMode: Item.TintMode = .none
|
||||||
|
if file.isCustomTemplateEmoji {
|
||||||
|
if let backgroundIconColor {
|
||||||
|
tintMode = .custom(backgroundIconColor)
|
||||||
|
} else {
|
||||||
|
tintMode = .accent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for attribute in file.attributes {
|
||||||
|
if case let .CustomEmoji(_, _, _, packReference) = attribute {
|
||||||
|
switch packReference {
|
||||||
|
case let .id(id, _):
|
||||||
|
if id == 773947703670341676 || id == 2964141614563343 {
|
||||||
|
tintMode = .accent
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let animationData = EntityKeyboardAnimationData(file: file)
|
||||||
|
resultItem = EmojiPagerContentComponent.Item(
|
||||||
|
animationData: animationData,
|
||||||
|
content: .animation(animationData),
|
||||||
|
itemFile: file,
|
||||||
|
subgroupId: nil,
|
||||||
|
icon: .none,
|
||||||
|
tintMode: tintMode
|
||||||
|
)
|
||||||
|
|
||||||
if let groupIndex = itemGroupIndexById[groupId] {
|
if let groupIndex = itemGroupIndexById[groupId] {
|
||||||
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
if itemGroups[groupIndex].items.count >= (5 + 8) * 8 {
|
||||||
break
|
break
|
||||||
@ -7823,7 +7926,9 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let recentEmoji = recentEmoji, !isReactionSelection, !isStatusSelection, !isProfilePhotoEmojiSelection, !isGroupPhotoEmojiSelection {
|
let hasRecentEmoji = ![.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .topicIcon, .backgroundIcon].contains(subject)
|
||||||
|
|
||||||
|
if let recentEmoji = recentEmoji, hasRecentEmoji {
|
||||||
for item in recentEmoji.items {
|
for item in recentEmoji.items {
|
||||||
guard let item = item.contents.get(RecentEmojiItem.self) else {
|
guard let item = item.contents.get(RecentEmojiItem.self) else {
|
||||||
continue
|
continue
|
||||||
@ -7878,7 +7983,8 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
if !hasPremium {
|
if !hasPremium {
|
||||||
maybeAppendUnicodeEmoji()
|
maybeAppendUnicodeEmoji()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var skippedCollectionIds = Set<AnyHashable>()
|
||||||
if areCustomEmojiEnabled {
|
if areCustomEmojiEnabled {
|
||||||
for entry in view.entries {
|
for entry in view.entries {
|
||||||
guard let item = entry.item as? StickerPackItem else {
|
guard let item = entry.item as? StickerPackItem else {
|
||||||
@ -7886,17 +7992,35 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var icon: EmojiPagerContentComponent.Item.Icon = .none
|
var icon: EmojiPagerContentComponent.Item.Icon = .none
|
||||||
if isReactionSelection, !hasPremium {
|
if [.reaction, .quickReaction].contains(subject), !hasPremium {
|
||||||
icon = .locked
|
icon = .locked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let supergroupId = entry.index.collectionId
|
||||||
|
let groupId: AnyHashable = supergroupId
|
||||||
|
|
||||||
|
if skippedCollectionIds.contains(groupId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var isTemplate = false
|
||||||
var tintMode: Item.TintMode = .none
|
var tintMode: Item.TintMode = .none
|
||||||
if item.file.isCustomTemplateEmoji {
|
if item.file.isCustomTemplateEmoji {
|
||||||
if isStatusSelection {
|
if [.status, .backgroundIcon].contains(subject) {
|
||||||
tintMode = .accent
|
if let backgroundIconColor {
|
||||||
|
tintMode = .custom(backgroundIconColor)
|
||||||
|
} else {
|
||||||
|
tintMode = .accent
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tintMode = .primary
|
tintMode = .primary
|
||||||
}
|
}
|
||||||
|
if case .backgroundIcon = subject {
|
||||||
|
isTemplate = true
|
||||||
|
}
|
||||||
|
} else if case .backgroundIcon = subject {
|
||||||
|
skippedCollectionIds.insert(groupId)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationData = EntityKeyboardAnimationData(file: item.file)
|
let animationData = EntityKeyboardAnimationData(file: item.file)
|
||||||
@ -7909,8 +8033,6 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
tintMode: tintMode
|
tintMode: tintMode
|
||||||
)
|
)
|
||||||
|
|
||||||
let supergroupId = entry.index.collectionId
|
|
||||||
let groupId: AnyHashable = supergroupId
|
|
||||||
let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium
|
let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium
|
||||||
if isPremiumLocked && isPremiumDisabled {
|
if isPremiumLocked && isPremiumDisabled {
|
||||||
continue
|
continue
|
||||||
@ -7943,7 +8065,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
dimensions: thumbnail.dimensions.cgSize,
|
dimensions: thumbnail.dimensions.cgSize,
|
||||||
immediateThumbnailData: info.immediateThumbnailData,
|
immediateThumbnailData: info.immediateThumbnailData,
|
||||||
isReaction: false,
|
isReaction: false,
|
||||||
isTemplate: false
|
isTemplate: isTemplate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7959,15 +8081,29 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
if installedCollectionIds.contains(featuredEmojiPack.info.id) {
|
if installedCollectionIds.contains(featuredEmojiPack.info.id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let supergroupId = featuredEmojiPack.info.id
|
||||||
|
let groupId: AnyHashable = supergroupId
|
||||||
|
|
||||||
|
if skippedCollectionIds.contains(groupId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for item in featuredEmojiPack.topItems {
|
for item in featuredEmojiPack.topItems {
|
||||||
var tintMode: Item.TintMode = .none
|
var tintMode: Item.TintMode = .none
|
||||||
if item.file.isCustomTemplateEmoji {
|
if item.file.isCustomTemplateEmoji {
|
||||||
if isStatusSelection {
|
if [.status, .backgroundIcon].contains(subject) {
|
||||||
tintMode = .accent
|
if let backgroundIconColor {
|
||||||
|
tintMode = .custom(backgroundIconColor)
|
||||||
|
} else {
|
||||||
|
tintMode = .accent
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tintMode = .primary
|
tintMode = .primary
|
||||||
}
|
}
|
||||||
|
} else if case .backgroundIcon = subject {
|
||||||
|
skippedCollectionIds.insert(groupId)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let animationData = EntityKeyboardAnimationData(file: item.file)
|
let animationData = EntityKeyboardAnimationData(file: item.file)
|
||||||
@ -7980,8 +8116,6 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
tintMode: tintMode
|
tintMode: tintMode
|
||||||
)
|
)
|
||||||
|
|
||||||
let supergroupId = featuredEmojiPack.info.id
|
|
||||||
let groupId: AnyHashable = supergroupId
|
|
||||||
let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium
|
let isPremiumLocked: Bool = item.file.isPremiumEmoji && !hasPremium
|
||||||
if isPremiumLocked && isPremiumDisabled {
|
if isPremiumLocked && isPremiumDisabled {
|
||||||
continue
|
continue
|
||||||
@ -8030,13 +8164,13 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
var displaySearchWithPlaceholder: String?
|
var displaySearchWithPlaceholder: String?
|
||||||
let searchInitiallyHidden = true
|
let searchInitiallyHidden = true
|
||||||
if hasSearch {
|
if hasSearch {
|
||||||
if isReactionSelection {
|
if [.reaction, .quickReaction].contains(subject) {
|
||||||
displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder
|
displaySearchWithPlaceholder = strings.EmojiSearch_SearchReactionsPlaceholder
|
||||||
} else if isStatusSelection {
|
} else if case .status = subject {
|
||||||
displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder
|
displaySearchWithPlaceholder = strings.EmojiSearch_SearchStatusesPlaceholder
|
||||||
} else if isEmojiSelection {
|
} else if case .emoji = subject {
|
||||||
displaySearchWithPlaceholder = strings.EmojiSearch_SearchEmojiPlaceholder
|
displaySearchWithPlaceholder = strings.EmojiSearch_SearchEmojiPlaceholder
|
||||||
} else if isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection {
|
} else if [.profilePhoto, .groupPhoto].contains(subject) {
|
||||||
displaySearchWithPlaceholder = strings.Common_Search
|
displaySearchWithPlaceholder = strings.Common_Search
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8080,10 +8214,14 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
displayPremiumBadges: false,
|
displayPremiumBadges: false,
|
||||||
headerItem: headerItem,
|
headerItem: headerItem,
|
||||||
fillWithLoadingPlaceholders: false,
|
fillWithLoadingPlaceholders: false,
|
||||||
|
customTintColor: backgroundIconColor,
|
||||||
items: group.items
|
items: group.items
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let warpContentsOnEdges = [.reaction, .quickReaction, .status, .profilePhoto, .groupPhoto, .backgroundIcon].contains(subject)
|
||||||
|
let enableLongPress = [.reaction, .status].contains(subject)
|
||||||
|
|
||||||
return EmojiPagerContentComponent(
|
return EmojiPagerContentComponent(
|
||||||
id: "emoji",
|
id: "emoji",
|
||||||
context: context,
|
context: context,
|
||||||
@ -8096,7 +8234,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
itemLayoutType: .compact,
|
itemLayoutType: .compact,
|
||||||
itemContentUniqueId: nil,
|
itemContentUniqueId: nil,
|
||||||
searchState: .empty(hasResults: false),
|
searchState: .empty(hasResults: false),
|
||||||
warpContentsOnEdges: isReactionSelection || isStatusSelection || isProfilePhotoEmojiSelection || isGroupPhotoEmojiSelection,
|
warpContentsOnEdges: warpContentsOnEdges,
|
||||||
hideBackground: hideBackground,
|
hideBackground: hideBackground,
|
||||||
displaySearchWithPlaceholder: displaySearchWithPlaceholder,
|
displaySearchWithPlaceholder: displaySearchWithPlaceholder,
|
||||||
searchCategories: searchCategories,
|
searchCategories: searchCategories,
|
||||||
@ -8104,7 +8242,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
searchAlwaysActive: false,
|
searchAlwaysActive: false,
|
||||||
searchIsPlaceholderOnly: false,
|
searchIsPlaceholderOnly: false,
|
||||||
emptySearchResults: nil,
|
emptySearchResults: nil,
|
||||||
enableLongPress: (isReactionSelection && !isQuickReactionSelection) || isStatusSelection,
|
enableLongPress: enableLongPress,
|
||||||
selectedItems: selectedItems
|
selectedItems: selectedItems
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -8287,7 +8425,20 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
let trendingIsPremium = featuredStickersConfiguration?.isPremium ?? false
|
let trendingIsPremium = featuredStickersConfiguration?.isPremium ?? false
|
||||||
let title = trendingIsPremium ? strings.Stickers_TrendingPremiumStickers : strings.StickerPacksSettings_FeaturedPacks
|
let title = trendingIsPremium ? strings.Stickers_TrendingPremiumStickers : strings.StickerPacksSettings_FeaturedPacks
|
||||||
|
|
||||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: title, subtitle: nil, actionButtonTitle: nil, isPremiumLocked: false, isFeatured: false, displayPremiumBadges: false, headerItem: nil, items: [resultItem]))
|
itemGroups.append(
|
||||||
|
ItemGroup(
|
||||||
|
supergroupId: groupId,
|
||||||
|
id: groupId,
|
||||||
|
title: title,
|
||||||
|
subtitle: nil,
|
||||||
|
actionButtonTitle: nil,
|
||||||
|
isPremiumLocked: false,
|
||||||
|
isFeatured: false,
|
||||||
|
displayPremiumBadges: false,
|
||||||
|
headerItem: nil,
|
||||||
|
items: [resultItem]
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -610,6 +610,7 @@ public final class EntityKeyboardComponent: Component {
|
|||||||
animationRenderer: emojiContent.animationRenderer,
|
animationRenderer: emojiContent.animationRenderer,
|
||||||
theme: component.theme,
|
theme: component.theme,
|
||||||
title: itemGroup.title ?? "",
|
title: itemGroup.title ?? "",
|
||||||
|
customTintColor: itemGroup.customTintColor,
|
||||||
pressed: { [weak self] in
|
pressed: { [weak self] in
|
||||||
self?.scrollToItemGroup(contentId: "emoji", groupId: itemGroup.supergroupId, subgroupId: nil)
|
self?.scrollToItemGroup(contentId: "emoji", groupId: itemGroup.supergroupId, subgroupId: nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
let animationCache: AnimationCache
|
let animationCache: AnimationCache
|
||||||
let animationRenderer: MultiAnimationRenderer
|
let animationRenderer: MultiAnimationRenderer
|
||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
|
let customTintColor: UIColor?
|
||||||
let title: String
|
let title: String
|
||||||
let pressed: () -> Void
|
let pressed: () -> Void
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
animationRenderer: MultiAnimationRenderer,
|
animationRenderer: MultiAnimationRenderer,
|
||||||
theme: PresentationTheme,
|
theme: PresentationTheme,
|
||||||
title: String,
|
title: String,
|
||||||
|
customTintColor: UIColor? = nil,
|
||||||
pressed: @escaping () -> Void
|
pressed: @escaping () -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
@ -46,6 +48,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
self.animationRenderer = animationRenderer
|
self.animationRenderer = animationRenderer
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.customTintColor = customTintColor
|
||||||
self.pressed = pressed
|
self.pressed = pressed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +77,9 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.customTintColor != rhs.customTintColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -109,6 +115,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
let displaySize = dimensions.aspectFitted(CGSize(width: 44.0, height: 44.0))
|
let displaySize = dimensions.aspectFitted(CGSize(width: 44.0, height: 44.0))
|
||||||
|
|
||||||
if self.itemLayer == nil {
|
if self.itemLayer == nil {
|
||||||
|
let tintColor: EmojiPagerContentComponent.Item.TintMode = component.customTintColor.flatMap { .custom($0) } ?? .primary
|
||||||
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
let itemLayer = EmojiPagerContentComponent.View.ItemLayer(
|
||||||
item: EmojiPagerContentComponent.Item(
|
item: EmojiPagerContentComponent.Item(
|
||||||
animationData: component.item,
|
animationData: component.item,
|
||||||
@ -116,7 +123,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
itemFile: nil,
|
itemFile: nil,
|
||||||
subgroupId: nil,
|
subgroupId: nil,
|
||||||
icon: .none,
|
icon: .none,
|
||||||
tintMode: component.item.isTemplate ? .primary : .none
|
tintMode: component.item.isTemplate ? tintColor : .none
|
||||||
),
|
),
|
||||||
context: component.context,
|
context: component.context,
|
||||||
attemptSynchronousLoad: false,
|
attemptSynchronousLoad: false,
|
||||||
@ -165,6 +172,8 @@ final class EntityKeyboardAnimationTopPanelComponent: Component {
|
|||||||
itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor
|
itemLayer.layerTintColor = component.theme.list.itemPrimaryTextColor.cgColor
|
||||||
case .accent:
|
case .accent:
|
||||||
itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor
|
itemLayer.layerTintColor = component.theme.list.itemAccentColor.cgColor
|
||||||
|
case let .custom(color):
|
||||||
|
itemLayer.layerTintColor = component.customTintColor?.cgColor ?? color.cgColor
|
||||||
}
|
}
|
||||||
|
|
||||||
itemLayer.isVisibleForAnimations = itemEnvironment.isContentInFocus && component.context.sharedContext.energyUsageSettings.loopEmoji
|
itemLayer.isVisibleForAnimations = itemEnvironment.isContentInFocus && component.context.sharedContext.energyUsageSettings.loopEmoji
|
||||||
|
|||||||
@ -513,11 +513,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
|||||||
animationCache: self.context.animationCache,
|
animationCache: self.context.animationCache,
|
||||||
animationRenderer: self.context.animationRenderer,
|
animationRenderer: self.context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .topicIcon,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
isTopicIconSelection: true,
|
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
areCustomEmojiEnabled: true,
|
areCustomEmojiEnabled: true,
|
||||||
@ -582,11 +579,8 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
|||||||
animationCache: self.context.animationCache,
|
animationCache: self.context.animationCache,
|
||||||
animationRenderer: self.context.animationRenderer,
|
animationRenderer: self.context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .topicIcon,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
isTopicIconSelection: true,
|
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
areCustomEmojiEnabled: true,
|
areCustomEmojiEnabled: true,
|
||||||
@ -916,9 +910,6 @@ private final class ForumCreateTopicScreenComponent: CombinedComponent {
|
|||||||
}
|
}
|
||||||
for featuredEmojiPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) {
|
for featuredEmojiPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) {
|
||||||
if featuredEmojiPack.info.id == collectionId {
|
if featuredEmojiPack.info.id == collectionId {
|
||||||
// if let strongSelf = self {
|
|
||||||
// strongSelf.scheduledEmojiContentAnimationHint = EmojiPagerContentComponent.ContentAnimation(type: .groupInstalled(id: collectionId))
|
|
||||||
// }
|
|
||||||
let _ = accountContext.engine.stickers.addStickerPackInteractively(info: featuredEmojiPack.info, items: featuredEmojiPack.topItems).start()
|
let _ = accountContext.engine.stickers.addStickerPackInteractively(info: featuredEmojiPack.info, items: featuredEmojiPack.topItems).start()
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|||||||
@ -11,6 +11,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
let presentationData: ItemListPresentationData
|
let presentationData: ItemListPresentationData
|
||||||
let dateTimeFormat: PresentationDateTimeFormat
|
let dateTimeFormat: PresentationDateTimeFormat
|
||||||
let date: Int32?
|
let date: Int32?
|
||||||
|
let minDate: Int32?
|
||||||
|
let maxDate: Int32?
|
||||||
public let sectionId: ItemListSectionId
|
public let sectionId: ItemListSectionId
|
||||||
let style: ItemListStyle
|
let style: ItemListStyle
|
||||||
let updated: ((Int32) -> Void)?
|
let updated: ((Int32) -> Void)?
|
||||||
@ -20,6 +22,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
presentationData: ItemListPresentationData,
|
presentationData: ItemListPresentationData,
|
||||||
dateTimeFormat: PresentationDateTimeFormat,
|
dateTimeFormat: PresentationDateTimeFormat,
|
||||||
date: Int32?,
|
date: Int32?,
|
||||||
|
minDate: Int32? = nil,
|
||||||
|
maxDate: Int32? = nil,
|
||||||
sectionId: ItemListSectionId,
|
sectionId: ItemListSectionId,
|
||||||
style: ItemListStyle,
|
style: ItemListStyle,
|
||||||
updated: ((Int32) -> Void)?,
|
updated: ((Int32) -> Void)?,
|
||||||
@ -28,6 +32,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
|||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.dateTimeFormat = dateTimeFormat
|
self.dateTimeFormat = dateTimeFormat
|
||||||
self.date = date
|
self.date = date
|
||||||
|
self.minDate = minDate
|
||||||
|
self.maxDate = maxDate
|
||||||
self.sectionId = sectionId
|
self.sectionId = sectionId
|
||||||
self.style = style
|
self.style = style
|
||||||
self.updated = updated
|
self.updated = updated
|
||||||
@ -223,7 +229,15 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
|||||||
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
strongSelf.item?.updated?(Int32(date.timeIntervalSince1970))
|
||||||
}
|
}
|
||||||
|
|
||||||
datePickerNode.minimumDate = Date()
|
if let minDate = item.minDate {
|
||||||
|
datePickerNode.minimumDate = Date(timeIntervalSince1970: TimeInterval(minDate))
|
||||||
|
} else {
|
||||||
|
datePickerNode.minimumDate = Date()
|
||||||
|
}
|
||||||
|
if let maxDate = item.maxDate {
|
||||||
|
datePickerNode.maximumDate = Date(timeIntervalSince1970: TimeInterval(maxDate))
|
||||||
|
}
|
||||||
|
|
||||||
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
|
datePickerNode.date = item.date.flatMap { Date(timeIntervalSince1970: TimeInterval($0)) }
|
||||||
|
|
||||||
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
||||||
|
|||||||
@ -1960,9 +1960,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
|||||||
animationCache: controller.context.animationCache,
|
animationCache: controller.context.animationCache,
|
||||||
animationRenderer: controller.context.animationRenderer,
|
animationRenderer: controller.context.animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .emoji,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: true,
|
|
||||||
hasTrending: true,
|
hasTrending: true,
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: true,
|
areUnicodeEmojiEnabled: true,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ swift_library(
|
|||||||
"//submodules/UndoUI",
|
"//submodules/UndoUI",
|
||||||
"//submodules/WallpaperBackgroundNode",
|
"//submodules/WallpaperBackgroundNode",
|
||||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||||
|
"//submodules/TelegramUI/Components/EntityKeyboard",
|
||||||
"//submodules/SolidRoundedButtonNode",
|
"//submodules/SolidRoundedButtonNode",
|
||||||
"//submodules/AppBundle",
|
"//submodules/AppBundle",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,429 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
import Display
|
|
||||||
import AsyncDisplayKit
|
|
||||||
import SwiftSignalKit
|
|
||||||
import TelegramPresentationData
|
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
|
||||||
import ItemListUI
|
|
||||||
import EmojiStatusComponent
|
|
||||||
import ComponentFlow
|
|
||||||
import AccountContext
|
|
||||||
|
|
||||||
enum ItemListReactionArrowStyle {
|
|
||||||
case arrow
|
|
||||||
case none
|
|
||||||
}
|
|
||||||
|
|
||||||
final class BackgroundEmojiItem: ListViewItem, ItemListItem {
|
|
||||||
let context: AccountContext
|
|
||||||
let presentationData: ItemListPresentationData
|
|
||||||
let icon: UIImage?
|
|
||||||
let title: String
|
|
||||||
let arrowStyle: ItemListReactionArrowStyle
|
|
||||||
let reaction: MessageReaction.Reaction
|
|
||||||
let availableReactions: AvailableReactions?
|
|
||||||
public let sectionId: ItemListSectionId
|
|
||||||
let style: ItemListStyle
|
|
||||||
let action: (() -> Void)?
|
|
||||||
public let tag: ItemListItemTag?
|
|
||||||
|
|
||||||
public init(context: AccountContext, presentationData: ItemListPresentationData, icon: UIImage? = nil, title: String, arrowStyle: ItemListReactionArrowStyle = .none, reaction: MessageReaction.Reaction, availableReactions: AvailableReactions?, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)?, tag: ItemListItemTag? = nil) {
|
|
||||||
self.context = context
|
|
||||||
self.presentationData = presentationData
|
|
||||||
self.icon = icon
|
|
||||||
self.title = title
|
|
||||||
self.arrowStyle = arrowStyle
|
|
||||||
self.reaction = reaction
|
|
||||||
self.availableReactions = availableReactions
|
|
||||||
self.sectionId = sectionId
|
|
||||||
self.style = style
|
|
||||||
self.action = action
|
|
||||||
self.tag = tag
|
|
||||||
}
|
|
||||||
|
|
||||||
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
|
||||||
async {
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
let node = BackgroundEmojiItemNode()
|
|
||||||
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
|
||||||
|
|
||||||
node.contentSize = layout.contentSize
|
|
||||||
node.insets = layout.insets
|
|
||||||
|
|
||||||
completion(node, {
|
|
||||||
return (nil, { _ in apply() })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
if let nodeValue = node() as? BackgroundEmojiItemNode {
|
|
||||||
let makeLayout = nodeValue.asyncLayout()
|
|
||||||
|
|
||||||
async {
|
|
||||||
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
completion(layout, { _ in
|
|
||||||
apply()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public var selectable: Bool = true
|
|
||||||
|
|
||||||
public func selected(listView: ListView){
|
|
||||||
listView.clearHighlightAnimated(true)
|
|
||||||
self.action?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let badgeFont = Font.regular(15.0)
|
|
||||||
|
|
||||||
final class BackgroundEmojiItemNode: ListViewItemNode, ItemListItemNode {
|
|
||||||
private let backgroundNode: ASDisplayNode
|
|
||||||
private let topStripeNode: ASDisplayNode
|
|
||||||
private let bottomStripeNode: ASDisplayNode
|
|
||||||
private let highlightedBackgroundNode: ASDisplayNode
|
|
||||||
private let maskNode: ASImageNode
|
|
||||||
|
|
||||||
let iconNode: ASImageNode
|
|
||||||
let titleNode: TextNode
|
|
||||||
let arrowNode: ASImageNode
|
|
||||||
let iconView: ComponentHostView<Empty>
|
|
||||||
|
|
||||||
private let activateArea: AccessibilityAreaNode
|
|
||||||
|
|
||||||
private var item: BackgroundEmojiItem?
|
|
||||||
private var fileDisposable: Disposable?
|
|
||||||
private var file: TelegramMediaFile?
|
|
||||||
|
|
||||||
override var canBeSelected: Bool {
|
|
||||||
if let item = self.item, let _ = item.action {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tag: ItemListItemTag? {
|
|
||||||
return self.item?.tag
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
self.backgroundNode = ASDisplayNode()
|
|
||||||
self.backgroundNode.isLayerBacked = true
|
|
||||||
self.backgroundNode.backgroundColor = .white
|
|
||||||
|
|
||||||
self.maskNode = ASImageNode()
|
|
||||||
self.maskNode.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.topStripeNode = ASDisplayNode()
|
|
||||||
self.topStripeNode.isLayerBacked = true
|
|
||||||
|
|
||||||
self.bottomStripeNode = ASDisplayNode()
|
|
||||||
self.bottomStripeNode.isLayerBacked = true
|
|
||||||
|
|
||||||
self.iconNode = ASImageNode()
|
|
||||||
self.iconNode.isLayerBacked = true
|
|
||||||
self.iconNode.displaysAsynchronously = false
|
|
||||||
|
|
||||||
self.titleNode = TextNode()
|
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.iconView = ComponentHostView<Empty>()
|
|
||||||
|
|
||||||
self.arrowNode = ASImageNode()
|
|
||||||
self.arrowNode.displayWithoutProcessing = true
|
|
||||||
self.arrowNode.displaysAsynchronously = false
|
|
||||||
self.arrowNode.isLayerBacked = true
|
|
||||||
|
|
||||||
self.highlightedBackgroundNode = ASDisplayNode()
|
|
||||||
self.highlightedBackgroundNode.isLayerBacked = true
|
|
||||||
|
|
||||||
self.activateArea = AccessibilityAreaNode()
|
|
||||||
|
|
||||||
super.init(layerBacked: false, dynamicBounce: false)
|
|
||||||
|
|
||||||
self.addSubnode(self.titleNode)
|
|
||||||
self.view.addSubview(self.iconView)
|
|
||||||
self.addSubnode(self.arrowNode)
|
|
||||||
|
|
||||||
self.addSubnode(self.activateArea)
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
self.fileDisposable?.dispose()
|
|
||||||
}
|
|
||||||
|
|
||||||
func asyncLayout() -> (_ item: BackgroundEmojiItem, _ params: ListViewItemLayoutParams, _ insets: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
|
||||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
|
||||||
|
|
||||||
let currentItem = self.item
|
|
||||||
|
|
||||||
return { item, params, neighbors in
|
|
||||||
var rightInset: CGFloat
|
|
||||||
rightInset = 34.0 + params.rightInset
|
|
||||||
let _ = rightInset
|
|
||||||
|
|
||||||
let contentSize: CGSize
|
|
||||||
let insets: UIEdgeInsets
|
|
||||||
let separatorHeight = UIScreenPixel
|
|
||||||
let itemBackgroundColor: UIColor
|
|
||||||
let itemSeparatorColor: UIColor
|
|
||||||
|
|
||||||
var updatedTheme: PresentationTheme?
|
|
||||||
var updateArrowImage: UIImage?
|
|
||||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
|
||||||
updatedTheme = item.presentationData.theme
|
|
||||||
updateArrowImage = PresentationResourcesItemList.disclosureArrowImage(item.presentationData.theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateIcon = false
|
|
||||||
if currentItem?.icon != item.icon {
|
|
||||||
updateIcon = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var leftInset = 16.0 + params.leftInset
|
|
||||||
if item.icon != nil {
|
|
||||||
leftInset += 43.0
|
|
||||||
}
|
|
||||||
|
|
||||||
var additionalTextRightInset: CGFloat = 0.0
|
|
||||||
additionalTextRightInset += 44.0
|
|
||||||
if item.arrowStyle == .arrow {
|
|
||||||
additionalTextRightInset += 24.0
|
|
||||||
}
|
|
||||||
|
|
||||||
let titleColor: UIColor = item.presentationData.theme.list.itemPrimaryTextColor
|
|
||||||
|
|
||||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
|
|
||||||
|
|
||||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - 20.0 - leftInset - additionalTextRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
|
||||||
|
|
||||||
let verticalInset: CGFloat = 11.0
|
|
||||||
|
|
||||||
let height: CGFloat
|
|
||||||
height = verticalInset * 2.0 + titleLayout.size.height
|
|
||||||
|
|
||||||
switch item.style {
|
|
||||||
case .plain:
|
|
||||||
itemBackgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
|
||||||
itemSeparatorColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
|
||||||
contentSize = CGSize(width: params.width, height: height)
|
|
||||||
insets = itemListNeighborsPlainInsets(neighbors)
|
|
||||||
case .blocks:
|
|
||||||
itemBackgroundColor = item.presentationData.theme.list.itemBlocksBackgroundColor
|
|
||||||
itemSeparatorColor = item.presentationData.theme.list.itemBlocksSeparatorColor
|
|
||||||
contentSize = CGSize(width: params.width, height: height)
|
|
||||||
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
|
||||||
|
|
||||||
return (ListViewItemNodeLayout(contentSize: contentSize, insets: insets), { [weak self] in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.item = item
|
|
||||||
|
|
||||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: params.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
|
||||||
strongSelf.activateArea.accessibilityLabel = item.title
|
|
||||||
|
|
||||||
strongSelf.activateArea.accessibilityTraits = []
|
|
||||||
|
|
||||||
if let icon = item.icon {
|
|
||||||
if strongSelf.iconNode.supernode == nil {
|
|
||||||
strongSelf.addSubnode(strongSelf.iconNode)
|
|
||||||
}
|
|
||||||
if updateIcon {
|
|
||||||
strongSelf.iconNode.image = icon
|
|
||||||
}
|
|
||||||
let iconY = floor((layout.contentSize.height - icon.size.height) / 2.0)
|
|
||||||
strongSelf.iconNode.frame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - icon.size.width) / 2.0), y: iconY), size: icon.size)
|
|
||||||
} else if strongSelf.iconNode.supernode != nil {
|
|
||||||
strongSelf.iconNode.image = nil
|
|
||||||
strongSelf.iconNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let _ = updatedTheme {
|
|
||||||
strongSelf.topStripeNode.backgroundColor = itemSeparatorColor
|
|
||||||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
|
||||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
|
||||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if let updateArrowImage = updateArrowImage {
|
|
||||||
strongSelf.arrowNode.image = updateArrowImage
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = titleApply()
|
|
||||||
|
|
||||||
switch item.style {
|
|
||||||
case .plain:
|
|
||||||
if strongSelf.backgroundNode.supernode != nil {
|
|
||||||
strongSelf.backgroundNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
if strongSelf.topStripeNode.supernode != nil {
|
|
||||||
strongSelf.topStripeNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
if strongSelf.bottomStripeNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 0)
|
|
||||||
}
|
|
||||||
if strongSelf.maskNode.supernode != nil {
|
|
||||||
strongSelf.maskNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: leftInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - leftInset, height: separatorHeight))
|
|
||||||
case .blocks:
|
|
||||||
if strongSelf.backgroundNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
|
||||||
}
|
|
||||||
if strongSelf.topStripeNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
|
||||||
}
|
|
||||||
if strongSelf.bottomStripeNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
|
||||||
}
|
|
||||||
if strongSelf.maskNode.supernode == nil {
|
|
||||||
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasCorners = itemListHasRoundedBlockLayout(params)
|
|
||||||
var hasTopCorners = false
|
|
||||||
var hasBottomCorners = false
|
|
||||||
switch neighbors.top {
|
|
||||||
case .sameSection(false):
|
|
||||||
strongSelf.topStripeNode.isHidden = true
|
|
||||||
default:
|
|
||||||
hasTopCorners = true
|
|
||||||
strongSelf.topStripeNode.isHidden = hasCorners
|
|
||||||
}
|
|
||||||
let bottomStripeInset: CGFloat
|
|
||||||
switch neighbors.bottom {
|
|
||||||
case .sameSection(false):
|
|
||||||
bottomStripeInset = leftInset
|
|
||||||
strongSelf.bottomStripeNode.isHidden = false
|
|
||||||
default:
|
|
||||||
bottomStripeInset = 0.0
|
|
||||||
hasBottomCorners = true
|
|
||||||
strongSelf.bottomStripeNode.isHidden = hasCorners
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
|
||||||
|
|
||||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
|
||||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
|
||||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
|
||||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height - separatorHeight), size: CGSize(width: params.width - bottomStripeInset, height: separatorHeight))
|
|
||||||
}
|
|
||||||
|
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: leftInset, y: 11.0), size: titleLayout.size)
|
|
||||||
strongSelf.titleNode.frame = titleFrame
|
|
||||||
|
|
||||||
var animationContent: EmojiStatusComponent.AnimationContent?
|
|
||||||
switch item.reaction {
|
|
||||||
case .builtin:
|
|
||||||
if let availableReactions = item.availableReactions {
|
|
||||||
for reaction in availableReactions.reactions {
|
|
||||||
if reaction.value == item.reaction {
|
|
||||||
animationContent = .file(file: reaction.selectAnimation)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .custom(fileId):
|
|
||||||
animationContent = .customEmoji(fileId: fileId)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var rightInset: CGFloat = 0.0
|
|
||||||
if let arrowImage = strongSelf.arrowNode.image, item.arrowStyle == .arrow {
|
|
||||||
let arrowRightOffset: CGFloat = 7.0
|
|
||||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - arrowRightOffset - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
|
||||||
rightInset += arrowRightOffset + arrowImage.size.width
|
|
||||||
strongSelf.arrowNode.isHidden = false
|
|
||||||
} else {
|
|
||||||
strongSelf.arrowNode.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if let animationContent = animationContent {
|
|
||||||
let iconBoundingSize = CGSize(width: 28.0, height: 28.0)
|
|
||||||
let iconOffsetX: CGFloat = 0.0
|
|
||||||
let iconSize = strongSelf.iconView.update(
|
|
||||||
transition: .immediate,
|
|
||||||
component: AnyComponent(EmojiStatusComponent(
|
|
||||||
context: item.context,
|
|
||||||
animationCache: item.context.animationCache,
|
|
||||||
animationRenderer: item.context.animationRenderer,
|
|
||||||
content: .animation(content: animationContent, size: iconBoundingSize, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .forever),
|
|
||||||
isVisibleForAnimations: true,
|
|
||||||
action: nil
|
|
||||||
)),
|
|
||||||
environment: {},
|
|
||||||
containerSize: iconBoundingSize
|
|
||||||
)
|
|
||||||
strongSelf.iconView.isUserInteractionEnabled = false
|
|
||||||
strongSelf.iconView.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - iconSize.width + iconOffsetX - rightInset, y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: params.width, height: height + UIScreenPixel))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
|
|
||||||
super.setHighlighted(highlighted, at: point, animated: animated)
|
|
||||||
|
|
||||||
if highlighted {
|
|
||||||
self.highlightedBackgroundNode.alpha = 1.0
|
|
||||||
if self.highlightedBackgroundNode.supernode == nil {
|
|
||||||
var anchorNode: ASDisplayNode?
|
|
||||||
if self.bottomStripeNode.supernode != nil {
|
|
||||||
anchorNode = self.bottomStripeNode
|
|
||||||
} else if self.topStripeNode.supernode != nil {
|
|
||||||
anchorNode = self.topStripeNode
|
|
||||||
} else if self.backgroundNode.supernode != nil {
|
|
||||||
anchorNode = self.backgroundNode
|
|
||||||
}
|
|
||||||
if let anchorNode = anchorNode {
|
|
||||||
self.insertSubnode(self.highlightedBackgroundNode, aboveSubnode: anchorNode)
|
|
||||||
} else {
|
|
||||||
self.addSubnode(self.highlightedBackgroundNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if self.highlightedBackgroundNode.supernode != nil {
|
|
||||||
if animated {
|
|
||||||
self.highlightedBackgroundNode.layer.animateAlpha(from: self.highlightedBackgroundNode.alpha, to: 0.0, duration: 0.4, completion: { [weak self] completed in
|
|
||||||
if let strongSelf = self {
|
|
||||||
if completed {
|
|
||||||
strongSelf.highlightedBackgroundNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
self.highlightedBackgroundNode.alpha = 0.0
|
|
||||||
} else {
|
|
||||||
self.highlightedBackgroundNode.removeFromSupernode()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
|
||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func animateAdded(_ currentTimestamp: Double, duration: Double) {
|
|
||||||
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
|
||||||
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,380 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
import ComponentFlow
|
||||||
|
import ItemListUI
|
||||||
|
import TelegramPresentationData
|
||||||
|
import EntityKeyboard
|
||||||
|
import PagerComponent
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
|
final class EmojiPickerItem: ListViewItem, ItemListItem {
|
||||||
|
let context: AccountContext
|
||||||
|
let theme: PresentationTheme
|
||||||
|
let strings: PresentationStrings
|
||||||
|
let emojiContent: EmojiPagerContentComponent
|
||||||
|
let backgroundIconColor: UIColor
|
||||||
|
let sectionId: ItemListSectionId
|
||||||
|
|
||||||
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
theme: PresentationTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
|
emojiContent: EmojiPagerContentComponent,
|
||||||
|
backgroundIconColor: UIColor,
|
||||||
|
sectionId: ItemListSectionId
|
||||||
|
) {
|
||||||
|
self.context = context
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.emojiContent = emojiContent
|
||||||
|
self.backgroundIconColor = backgroundIconColor
|
||||||
|
self.sectionId = sectionId
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
|
async {
|
||||||
|
let node = EmojiPickerItemNode()
|
||||||
|
let (layout, apply) = node.asyncLayout()(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
|
||||||
|
node.contentSize = layout.contentSize
|
||||||
|
node.insets = layout.insets
|
||||||
|
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(node, {
|
||||||
|
return (nil, { _ in apply() })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
if let nodeValue = node() as? EmojiPickerItemNode {
|
||||||
|
let makeLayout = nodeValue.asyncLayout()
|
||||||
|
|
||||||
|
async {
|
||||||
|
let (layout, apply) = makeLayout(self, params, itemListNeighbors(item: self, topItem: previousItem as? ItemListItem, bottomItem: nextItem as? ItemListItem))
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
completion(layout, { _ in
|
||||||
|
apply()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class EmojiPickerItemNode: ListViewItemNode {
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
private let topStripeNode: ASDisplayNode
|
||||||
|
private let bottomStripeNode: ASDisplayNode
|
||||||
|
private let maskNode: ASImageNode
|
||||||
|
|
||||||
|
private let picker = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private var item: EmojiPickerItem?
|
||||||
|
|
||||||
|
private let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.topStripeNode = ASDisplayNode()
|
||||||
|
self.topStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.bottomStripeNode = ASDisplayNode()
|
||||||
|
self.bottomStripeNode.isLayerBacked = true
|
||||||
|
|
||||||
|
self.maskNode = ASImageNode()
|
||||||
|
self.maskNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
super.init(layerBacked: false, dynamicBounce: false)
|
||||||
|
|
||||||
|
self.clipsToBounds = true
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncLayout() -> (_ item: EmojiPickerItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
|
return { item, params, neighbors in
|
||||||
|
let insets: UIEdgeInsets
|
||||||
|
let separatorHeight = UIScreenPixel
|
||||||
|
|
||||||
|
let contentSize = CGSize(width: params.width, height: 190.0)
|
||||||
|
insets = itemListNeighborsGroupedInsets(neighbors, params)
|
||||||
|
|
||||||
|
let layout = ListViewItemNodeLayout(contentSize: contentSize, insets: insets)
|
||||||
|
let layoutSize = layout.size
|
||||||
|
|
||||||
|
return (layout, { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.item = item
|
||||||
|
|
||||||
|
if let currentItem, currentItem.backgroundIconColor != item.backgroundIconColor {
|
||||||
|
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size)
|
||||||
|
strongSelf.view.addSubview(snapshot)
|
||||||
|
snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
|
snapshot.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||||
|
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||||
|
|
||||||
|
if strongSelf.backgroundNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||||
|
}
|
||||||
|
if strongSelf.topStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.topStripeNode, at: 1)
|
||||||
|
}
|
||||||
|
if strongSelf.bottomStripeNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.bottomStripeNode, at: 2)
|
||||||
|
}
|
||||||
|
if strongSelf.maskNode.supernode == nil {
|
||||||
|
strongSelf.insertSubnode(strongSelf.maskNode, at: 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasCorners = itemListHasRoundedBlockLayout(params)
|
||||||
|
var hasTopCorners = false
|
||||||
|
var hasBottomCorners = false
|
||||||
|
switch neighbors.top {
|
||||||
|
case .sameSection(false):
|
||||||
|
strongSelf.topStripeNode.isHidden = true
|
||||||
|
default:
|
||||||
|
hasTopCorners = true
|
||||||
|
strongSelf.topStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
let bottomStripeInset: CGFloat
|
||||||
|
let bottomStripeOffset: CGFloat
|
||||||
|
switch neighbors.bottom {
|
||||||
|
case .sameSection(false):
|
||||||
|
bottomStripeInset = 0.0
|
||||||
|
bottomStripeOffset = -separatorHeight
|
||||||
|
strongSelf.bottomStripeNode.isHidden = false
|
||||||
|
default:
|
||||||
|
bottomStripeInset = 0.0
|
||||||
|
bottomStripeOffset = 0.0
|
||||||
|
hasBottomCorners = true
|
||||||
|
strongSelf.bottomStripeNode.isHidden = hasCorners
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
|
||||||
|
strongSelf.backgroundNode.frame = backgroundFrame
|
||||||
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||||
|
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||||
|
|
||||||
|
let pickerSize = strongSelf.picker.update(
|
||||||
|
transition: .immediate,
|
||||||
|
component: AnyComponent(
|
||||||
|
EmojiSelectionComponent(
|
||||||
|
theme: item.theme,
|
||||||
|
strings: item.strings,
|
||||||
|
deviceMetrics: .iPhone14ProMax,
|
||||||
|
emojiContent: item.emojiContent,
|
||||||
|
backgroundColor: item.theme.list.itemBlocksBackgroundColor,
|
||||||
|
separatorColor: item.theme.list.itemBlocksSeparatorColor
|
||||||
|
)
|
||||||
|
),
|
||||||
|
environment: {},
|
||||||
|
containerSize: CGSize(width: params.width - params.leftInset - params.rightInset, height: contentSize.height)
|
||||||
|
)
|
||||||
|
if let view = strongSelf.picker.view {
|
||||||
|
if view.superview == nil {
|
||||||
|
view.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
|
strongSelf.view.insertSubview(view, at: 1)
|
||||||
|
}
|
||||||
|
view.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: pickerSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
|
||||||
|
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func animateRemoved(_ currentTimestamp: Double, duration: Double) {
|
||||||
|
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class EmojiSelectionComponent: Component {
|
||||||
|
public typealias EnvironmentType = Empty
|
||||||
|
|
||||||
|
public let theme: PresentationTheme
|
||||||
|
public let strings: PresentationStrings
|
||||||
|
public let deviceMetrics: DeviceMetrics
|
||||||
|
public let emojiContent: EmojiPagerContentComponent
|
||||||
|
public let backgroundColor: UIColor
|
||||||
|
public let separatorColor: UIColor
|
||||||
|
|
||||||
|
public init(
|
||||||
|
theme: PresentationTheme,
|
||||||
|
strings: PresentationStrings,
|
||||||
|
deviceMetrics: DeviceMetrics,
|
||||||
|
emojiContent: EmojiPagerContentComponent,
|
||||||
|
backgroundColor: UIColor,
|
||||||
|
separatorColor: UIColor
|
||||||
|
) {
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.deviceMetrics = deviceMetrics
|
||||||
|
self.emojiContent = emojiContent
|
||||||
|
self.backgroundColor = backgroundColor
|
||||||
|
self.separatorColor = separatorColor
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: EmojiSelectionComponent, rhs: EmojiSelectionComponent) -> Bool {
|
||||||
|
if lhs.theme !== rhs.theme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.strings != rhs.strings {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.deviceMetrics != rhs.deviceMetrics {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.emojiContent != rhs.emojiContent {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.backgroundColor != rhs.backgroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.separatorColor != rhs.separatorColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIView {
|
||||||
|
private let keyboardView: ComponentView<Empty>
|
||||||
|
private let keyboardClippingView: UIView
|
||||||
|
private let panelHostView: PagerExternalTopPanelContainer
|
||||||
|
private let panelBackgroundView: BlurredBackgroundView
|
||||||
|
private let panelSeparatorView: UIView
|
||||||
|
|
||||||
|
private var component: EmojiSelectionComponent?
|
||||||
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
self.keyboardView = ComponentView<Empty>()
|
||||||
|
self.keyboardClippingView = UIView()
|
||||||
|
self.panelHostView = PagerExternalTopPanelContainer()
|
||||||
|
self.panelBackgroundView = BlurredBackgroundView(color: .clear, enableBlur: true)
|
||||||
|
self.panelSeparatorView = UIView()
|
||||||
|
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.addSubview(self.keyboardClippingView)
|
||||||
|
self.addSubview(self.panelBackgroundView)
|
||||||
|
self.addSubview(self.panelSeparatorView)
|
||||||
|
self.addSubview(self.panelHostView)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: EmojiSelectionComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||||
|
self.backgroundColor = component.backgroundColor
|
||||||
|
let panelBackgroundColor = component.backgroundColor.withMultipliedAlpha(0.85)
|
||||||
|
self.panelBackgroundView.updateColor(color: panelBackgroundColor, transition: .immediate)
|
||||||
|
self.panelSeparatorView.backgroundColor = component.separatorColor
|
||||||
|
|
||||||
|
self.component = component
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
let topPanelHeight: CGFloat = 42.0
|
||||||
|
|
||||||
|
let keyboardSize = self.keyboardView.update(
|
||||||
|
transition: transition.withUserData(EmojiPagerContentComponent.SynchronousLoadBehavior(isDisabled: true)),
|
||||||
|
component: AnyComponent(EntityKeyboardComponent(
|
||||||
|
theme: component.theme,
|
||||||
|
strings: component.strings,
|
||||||
|
isContentInFocus: false,
|
||||||
|
containerInsets: UIEdgeInsets(top: topPanelHeight - 34.0, left: 0.0, bottom: 0.0, right: 0.0),
|
||||||
|
topPanelInsets: UIEdgeInsets(top: 0.0, left: 4.0, bottom: 0.0, right: 4.0),
|
||||||
|
emojiContent: component.emojiContent,
|
||||||
|
stickerContent: nil,
|
||||||
|
maskContent: nil,
|
||||||
|
gifContent: nil,
|
||||||
|
hasRecentGifs: false,
|
||||||
|
availableGifSearchEmojies: [],
|
||||||
|
defaultToEmojiTab: true,
|
||||||
|
externalTopPanelContainer: self.panelHostView,
|
||||||
|
externalBottomPanelContainer: nil,
|
||||||
|
displayTopPanelBackground: .blur,
|
||||||
|
topPanelExtensionUpdated: { _, _ in },
|
||||||
|
topPanelScrollingOffset: { _, _ in },
|
||||||
|
hideInputUpdated: { _, _, _ in },
|
||||||
|
hideTopPanelUpdated: { _, _ in },
|
||||||
|
switchToTextInput: {},
|
||||||
|
switchToGifSubject: { _ in },
|
||||||
|
reorderItems: { _, _ in },
|
||||||
|
makeSearchContainerNode: { _ in return nil },
|
||||||
|
contentIdUpdated: { _ in },
|
||||||
|
deviceMetrics: component.deviceMetrics,
|
||||||
|
hiddenInputHeight: 0.0,
|
||||||
|
inputHeight: 0.0,
|
||||||
|
displayBottomPanel: false,
|
||||||
|
isExpanded: true,
|
||||||
|
clipContentToTopPanel: false,
|
||||||
|
useExternalSearchContainer: false
|
||||||
|
)),
|
||||||
|
environment: {},
|
||||||
|
containerSize: availableSize
|
||||||
|
)
|
||||||
|
if let keyboardComponentView = self.keyboardView.view {
|
||||||
|
if keyboardComponentView.superview == nil {
|
||||||
|
self.keyboardClippingView.addSubview(keyboardComponentView)
|
||||||
|
}
|
||||||
|
|
||||||
|
if panelBackgroundColor.alpha < 0.01 {
|
||||||
|
self.keyboardClippingView.clipsToBounds = true
|
||||||
|
} else {
|
||||||
|
self.keyboardClippingView.clipsToBounds = false
|
||||||
|
}
|
||||||
|
|
||||||
|
transition.setFrame(view: self.keyboardClippingView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: availableSize.width, height: availableSize.height - topPanelHeight)))
|
||||||
|
|
||||||
|
transition.setFrame(view: keyboardComponentView, frame: CGRect(origin: CGPoint(x: 0.0, y: -topPanelHeight), size: keyboardSize))
|
||||||
|
transition.setFrame(view: self.panelHostView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight - 34.0), size: CGSize(width: keyboardSize.width, height: 0.0)))
|
||||||
|
|
||||||
|
transition.setFrame(view: self.panelBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: keyboardSize.width, height: topPanelHeight)))
|
||||||
|
self.panelBackgroundView.update(size: self.panelBackgroundView.bounds.size, transition: transition.containedViewLayoutTransition)
|
||||||
|
|
||||||
|
transition.setFrame(view: self.panelSeparatorView, frame: CGRect(origin: CGPoint(x: 0.0, y: topPanelHeight), size: CGSize(width: keyboardSize.width, height: UIScreenPixel)))
|
||||||
|
transition.setAlpha(view: self.panelSeparatorView, alpha: 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View(frame: CGRect())
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -12,7 +12,7 @@ import PresentationDataUtils
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import WallpaperBackgroundNode
|
import WallpaperBackgroundNode
|
||||||
|
|
||||||
class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
|
final class PeerNameColorChatPreviewItem: ListViewItem, ItemListItem {
|
||||||
struct MessageItem: Equatable {
|
struct MessageItem: Equatable {
|
||||||
static func == (lhs: MessageItem, rhs: MessageItem) -> Bool {
|
static func == (lhs: MessageItem, rhs: MessageItem) -> Bool {
|
||||||
if lhs.outgoing != rhs.outgoing {
|
if lhs.outgoing != rhs.outgoing {
|
||||||
@ -131,7 +131,6 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
private var itemHeaderNodes: [ListViewItemNode.HeaderId: ListViewItemHeaderNode] = [:]
|
private var itemHeaderNodes: [ListViewItemNode.HeaderId: ListViewItemHeaderNode] = [:]
|
||||||
|
|
||||||
private var item: PeerNameColorChatPreviewItem?
|
private var item: PeerNameColorChatPreviewItem?
|
||||||
private var finalImage = true
|
|
||||||
|
|
||||||
private let disposable = MetaDisposable()
|
private let disposable = MetaDisposable()
|
||||||
|
|
||||||
@ -164,6 +163,8 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
var currentBackgroundNode = self.backgroundNode
|
var currentBackgroundNode = self.backgroundNode
|
||||||
|
|
||||||
|
let currentItem = self.item
|
||||||
|
|
||||||
return { item, params, neighbors in
|
return { item, params, neighbors in
|
||||||
if currentBackgroundNode == nil {
|
if currentBackgroundNode == nil {
|
||||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||||
@ -214,7 +215,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
itemNode.frame = nodeFrame
|
itemNode.frame = nodeFrame
|
||||||
itemNode.isUserInteractionEnabled = false
|
itemNode.isUserInteractionEnabled = false
|
||||||
|
|
||||||
apply(ListViewItemApply(isOnScreen: true))
|
Queue.mainQueue().after(0.01) {
|
||||||
|
apply(ListViewItemApply(isOnScreen: true))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -249,6 +252,16 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)
|
||||||
|
|
||||||
|
if let currentItem, currentItem.messageItems.first?.nameColor != item.messageItems.first?.nameColor {
|
||||||
|
if let snapshot = strongSelf.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
snapshot.frame = CGRect(origin: CGPoint(x: 0.0, y: -insets.top), size: snapshot.frame.size)
|
||||||
|
strongSelf.view.addSubview(snapshot)
|
||||||
|
snapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in
|
||||||
|
snapshot.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
strongSelf.messageNodes = nodes
|
strongSelf.messageNodes = nodes
|
||||||
var topOffset: CGFloat = 4.0
|
var topOffset: CGFloat = 4.0
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
@ -334,7 +347,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
|
|
||||||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.componentTheme, top: hasTopCorners, bottom: hasBottomCorners) : nil
|
||||||
|
|
||||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||||
|
|
||||||
let displayMode: WallpaperDisplayMode
|
let displayMode: WallpaperDisplayMode
|
||||||
if abs(params.availableHeight - params.width) < 100.0, params.availableHeight > 700.0 {
|
if abs(params.availableHeight - params.width) < 100.0, params.availableHeight > 700.0 {
|
||||||
@ -352,7 +365,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let backgroundNode = strongSelf.backgroundNode {
|
if let backgroundNode = strongSelf.backgroundNode {
|
||||||
backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0)
|
backgroundNode.frame = backgroundFrame
|
||||||
backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate)
|
backgroundNode.updateLayout(size: backgroundNode.bounds.size, displayMode: displayMode, transition: .immediate)
|
||||||
}
|
}
|
||||||
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||||
|
|||||||
@ -184,8 +184,10 @@ private final class PeerNameColorIconItemNode : ListViewItemNode {
|
|||||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
||||||
if selected {
|
if selected {
|
||||||
transition.updateTransformScale(node: self.fillNode, scale: 0.8)
|
transition.updateTransformScale(node: self.fillNode, scale: 0.8)
|
||||||
|
transition.updateTransformScale(node: self.ringNode, scale: 1.0)
|
||||||
} else {
|
} else {
|
||||||
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
||||||
|
transition.updateTransformScale(node: self.ringNode, scale: 0.99)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,20 +11,21 @@ import ItemListUI
|
|||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import UndoUI
|
import UndoUI
|
||||||
|
import EntityKeyboard
|
||||||
|
|
||||||
private final class PeerNameColorScreenArguments {
|
private final class PeerNameColorScreenArguments {
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let updateNameColor: (PeerNameColor) -> Void
|
let updateNameColor: (PeerNameColor) -> Void
|
||||||
let openBackgroundEmoji: () -> Void
|
let updateBackgroundEmojiId: (Int64?) -> Void
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
updateNameColor: @escaping (PeerNameColor) -> Void,
|
updateNameColor: @escaping (PeerNameColor) -> Void,
|
||||||
openBackgroundEmoji: @escaping () -> Void
|
updateBackgroundEmojiId: @escaping (Int64?) -> Void
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.updateNameColor = updateNameColor
|
self.updateNameColor = updateNameColor
|
||||||
self.openBackgroundEmoji = openBackgroundEmoji
|
self.updateBackgroundEmojiId = updateBackgroundEmojiId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,22 +40,22 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry {
|
|||||||
case colorMessage
|
case colorMessage
|
||||||
case colorPicker
|
case colorPicker
|
||||||
case colorDescription
|
case colorDescription
|
||||||
|
case backgroundEmojiHeader
|
||||||
case backgroundEmoji
|
case backgroundEmoji
|
||||||
case backgroundEmojiDescription
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case colorHeader(String)
|
case colorHeader(String)
|
||||||
case colorMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, items: [PeerNameColorChatPreviewItem.MessageItem])
|
case colorMessage(wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, bubbleCorners: PresentationChatBubbleCorners, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, items: [PeerNameColorChatPreviewItem.MessageItem])
|
||||||
case colorPicker(colors: [PeerNameColor], currentColor: PeerNameColor)
|
case colorPicker(colors: [PeerNameColor], currentColor: PeerNameColor)
|
||||||
case colorDescription(String)
|
case colorDescription(String)
|
||||||
case backgroundEmoji(String, MessageReaction.Reaction, AvailableReactions)
|
case backgroundEmojiHeader(String)
|
||||||
case backgroundEmojiDescription(String)
|
case backgroundEmoji(EmojiPagerContentComponent, UIColor)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .colorHeader, .colorMessage, .colorPicker, .colorDescription:
|
case .colorHeader, .colorMessage, .colorPicker, .colorDescription:
|
||||||
return PeerNameColorScreenSection.nameColor.rawValue
|
return PeerNameColorScreenSection.nameColor.rawValue
|
||||||
case .backgroundEmoji, .backgroundEmojiDescription:
|
case .backgroundEmojiHeader, .backgroundEmoji:
|
||||||
return PeerNameColorScreenSection.backgroundEmoji.rawValue
|
return PeerNameColorScreenSection.backgroundEmoji.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,10 +70,10 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry {
|
|||||||
return .colorPicker
|
return .colorPicker
|
||||||
case .colorDescription:
|
case .colorDescription:
|
||||||
return .colorDescription
|
return .colorDescription
|
||||||
|
case .backgroundEmojiHeader:
|
||||||
|
return .backgroundEmojiHeader
|
||||||
case .backgroundEmoji:
|
case .backgroundEmoji:
|
||||||
return .backgroundEmoji
|
return .backgroundEmoji
|
||||||
case .backgroundEmojiDescription:
|
|
||||||
return .backgroundEmojiDescription
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,9 +87,9 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry {
|
|||||||
return 2
|
return 2
|
||||||
case .colorDescription:
|
case .colorDescription:
|
||||||
return 3
|
return 3
|
||||||
case .backgroundEmoji:
|
case .backgroundEmojiHeader:
|
||||||
return 4
|
return 4
|
||||||
case .backgroundEmojiDescription:
|
case .backgroundEmoji:
|
||||||
return 5
|
return 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,14 +120,14 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .backgroundEmoji(lhsText, lhsReaction, lhsAvailableReactions):
|
case let .backgroundEmojiHeader(text):
|
||||||
if case let .backgroundEmoji(rhsText, rhsReaction, rhsAvailableReactions) = rhs, lhsText == rhsText, lhsReaction == rhsReaction, lhsAvailableReactions == rhsAvailableReactions {
|
if case .backgroundEmojiHeader(text) = rhs {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .backgroundEmojiDescription(text):
|
case let .backgroundEmoji(lhsEmojiContent, lhsBackgroundIconColor):
|
||||||
if case .backgroundEmojiDescription(text) = rhs {
|
if case let .backgroundEmoji(rhsEmojiContent, rhsBackgroundIconColor) = rhs, lhsEmojiContent == rhsEmojiContent, lhsBackgroundIconColor == rhsBackgroundIconColor {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -168,33 +169,25 @@ private enum PeerNameColorScreenEntry: ItemListNodeEntry {
|
|||||||
)
|
)
|
||||||
case let .colorDescription(text):
|
case let .colorDescription(text):
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .backgroundEmoji(title, reaction, availableReactions):
|
case let .backgroundEmojiHeader(text):
|
||||||
return BackgroundEmojiItem(
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
context: arguments.context,
|
case let .backgroundEmoji(emojiContent, backgroundIconColor):
|
||||||
presentationData: presentationData,
|
return EmojiPickerItem(context: arguments.context, theme: presentationData.theme, strings: presentationData.strings, emojiContent: emojiContent, backgroundIconColor: backgroundIconColor, sectionId: self.section)
|
||||||
title: title,
|
|
||||||
reaction: reaction,
|
|
||||||
availableReactions: availableReactions,
|
|
||||||
sectionId: self.section,
|
|
||||||
style: .blocks,
|
|
||||||
action: {
|
|
||||||
arguments.openBackgroundEmoji()
|
|
||||||
})
|
|
||||||
case let .backgroundEmojiDescription(text):
|
|
||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct PeerNameColorScreenState: Equatable {
|
private struct PeerNameColorScreenState: Equatable {
|
||||||
var updatedNameColor: PeerNameColor?
|
var updatedNameColor: PeerNameColor?
|
||||||
|
var updatedBackgroundEmojiId: Int64?
|
||||||
}
|
}
|
||||||
|
|
||||||
private func peerNameColorScreenEntries(
|
private func peerNameColorScreenEntries(
|
||||||
presentationData: PresentationData,
|
presentationData: PresentationData,
|
||||||
state: PeerNameColorScreenState,
|
state: PeerNameColorScreenState,
|
||||||
peer: EnginePeer?,
|
peer: EnginePeer?,
|
||||||
isPremium: Bool
|
isPremium: Bool,
|
||||||
|
emojiContent: EmojiPagerContentComponent
|
||||||
) -> [PeerNameColorScreenEntry] {
|
) -> [PeerNameColorScreenEntry] {
|
||||||
var entries: [PeerNameColorScreenEntry] = []
|
var entries: [PeerNameColorScreenEntry] = []
|
||||||
|
|
||||||
@ -203,7 +196,6 @@ private func peerNameColorScreenEntries(
|
|||||||
.blue
|
.blue
|
||||||
]
|
]
|
||||||
allColors.append(contentsOf: PeerNameColor.allCases.filter { $0 != .blue})
|
allColors.append(contentsOf: PeerNameColor.allCases.filter { $0 != .blue})
|
||||||
allColors.removeLast(3)
|
|
||||||
|
|
||||||
let nameColor: PeerNameColor
|
let nameColor: PeerNameColor
|
||||||
if let updatedNameColor = state.updatedNameColor {
|
if let updatedNameColor = state.updatedNameColor {
|
||||||
@ -214,6 +206,19 @@ private func peerNameColorScreenEntries(
|
|||||||
nameColor = .blue
|
nameColor = .blue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let backgroundEmojiId: Int64?
|
||||||
|
if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId {
|
||||||
|
if updatedBackgroundEmojiId == 0 {
|
||||||
|
backgroundEmojiId = nil
|
||||||
|
} else {
|
||||||
|
backgroundEmojiId = updatedBackgroundEmojiId
|
||||||
|
}
|
||||||
|
} else if let emojiId = peer.backgroundEmojiId {
|
||||||
|
backgroundEmojiId = emojiId
|
||||||
|
} else {
|
||||||
|
backgroundEmojiId = nil
|
||||||
|
}
|
||||||
|
|
||||||
let replyText: String
|
let replyText: String
|
||||||
let messageText: String
|
let messageText: String
|
||||||
if case .channel = peer {
|
if case .channel = peer {
|
||||||
@ -229,13 +234,11 @@ private func peerNameColorScreenEntries(
|
|||||||
author: peer.compactDisplayTitle,
|
author: peer.compactDisplayTitle,
|
||||||
photo: peer.profileImageRepresentations,
|
photo: peer.profileImageRepresentations,
|
||||||
nameColor: nameColor,
|
nameColor: nameColor,
|
||||||
backgroundEmojiId: nil,
|
backgroundEmojiId: backgroundEmojiId,
|
||||||
reply: (peer.compactDisplayTitle, replyText),
|
reply: (peer.compactDisplayTitle, replyText),
|
||||||
linkPreview: (presentationData.strings.NameColor_ChatPreview_LinkSite, presentationData.strings.NameColor_ChatPreview_LinkTitle, presentationData.strings.NameColor_ChatPreview_LinkText),
|
linkPreview: (presentationData.strings.NameColor_ChatPreview_LinkSite, presentationData.strings.NameColor_ChatPreview_LinkTitle, presentationData.strings.NameColor_ChatPreview_LinkText),
|
||||||
text: messageText
|
text: messageText
|
||||||
)
|
)
|
||||||
|
|
||||||
entries.append(.colorHeader(presentationData.strings.NameColor_ChatPreview_Title))
|
|
||||||
entries.append(.colorMessage(
|
entries.append(.colorMessage(
|
||||||
wallpaper: presentationData.chatWallpaper,
|
wallpaper: presentationData.chatWallpaper,
|
||||||
fontSize: presentationData.chatFontSize,
|
fontSize: presentationData.chatFontSize,
|
||||||
@ -249,11 +252,11 @@ private func peerNameColorScreenEntries(
|
|||||||
currentColor: nameColor
|
currentColor: nameColor
|
||||||
))
|
))
|
||||||
entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account))
|
entries.append(.colorDescription(presentationData.strings.NameColor_ChatPreview_Description_Account))
|
||||||
|
|
||||||
|
entries.append(.backgroundEmojiHeader(presentationData.strings.NameColor_BackgroundEmoji_Title))
|
||||||
|
entries.append(.backgroundEmoji(emojiContent, nameColor.color))
|
||||||
}
|
}
|
||||||
|
|
||||||
// entries.append(.backgroundEmoji(presentationData.strings.Settings_QuickReactionSetup_ChooseQuickReaction, reactionSettings.quickReaction, availableReactions))
|
|
||||||
// entries.append(.backgroundEmojiDescription(presentationData.strings.Settings_QuickReactionSetup_ChooseQuickReactionInfo))
|
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,8 +293,12 @@ public func PeerNameColorScreen(
|
|||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openBackgroundEmoji: {
|
updateBackgroundEmojiId: { emojiId in
|
||||||
|
updateState { state in
|
||||||
|
var updatedState = state
|
||||||
|
updatedState.updatedBackgroundEmojiId = emojiId
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -303,17 +310,52 @@ public func PeerNameColorScreen(
|
|||||||
peerId = channelId
|
peerId = channelId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let emojiContent = combineLatest(
|
||||||
|
statePromise.get(),
|
||||||
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||||
|
)
|
||||||
|
|> mapToSignal { state, peer in
|
||||||
|
var selectedEmojiId: Int64?
|
||||||
|
if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId {
|
||||||
|
selectedEmojiId = updatedBackgroundEmojiId
|
||||||
|
} else {
|
||||||
|
selectedEmojiId = peer?.backgroundEmojiId
|
||||||
|
}
|
||||||
|
let nameColor: UIColor
|
||||||
|
if let updatedNameColor = state.updatedNameColor {
|
||||||
|
nameColor = updatedNameColor.color
|
||||||
|
} else {
|
||||||
|
nameColor = (peer?.nameColor ?? .blue).color
|
||||||
|
}
|
||||||
|
return EmojiPagerContentComponent.emojiInputData(
|
||||||
|
context: context,
|
||||||
|
animationCache: context.animationCache,
|
||||||
|
animationRenderer: context.animationRenderer,
|
||||||
|
isStandalone: false,
|
||||||
|
subject: .backgroundIcon,
|
||||||
|
hasTrending: false,
|
||||||
|
topReactionItems: [],
|
||||||
|
areUnicodeEmojiEnabled: false,
|
||||||
|
areCustomEmojiEnabled: true,
|
||||||
|
chatPeerId: context.account.peerId,
|
||||||
|
selectedItems: Set(selectedEmojiId.flatMap { [EngineMedia.Id(namespace: Namespaces.Media.CloudFile, id: $0)] } ?? []),
|
||||||
|
backgroundIconColor: nameColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
|
||||||
let signal = combineLatest(queue: .mainQueue(),
|
let signal = combineLatest(queue: .mainQueue(),
|
||||||
presentationData,
|
presentationData,
|
||||||
statePromise.get(),
|
statePromise.get(),
|
||||||
context.engine.stickers.availableReactions(),
|
context.engine.stickers.availableReactions(),
|
||||||
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)),
|
||||||
|
emojiContent
|
||||||
)
|
)
|
||||||
|> deliverOnMainQueue
|
|> deliverOnMainQueue
|
||||||
|> map { presentationData, state, availableReactions, peer -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, availableReactions, peer, emojiContent -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
let isPremium = peer?.isPremium ?? false
|
let isPremium = peer?.isPremium ?? false
|
||||||
let title: String
|
let title: String
|
||||||
|
let buttonTitle: String
|
||||||
let isLocked: Bool
|
let isLocked: Bool
|
||||||
switch subject {
|
switch subject {
|
||||||
case .account:
|
case .account:
|
||||||
@ -324,18 +366,36 @@ public func PeerNameColorScreen(
|
|||||||
isLocked = false
|
isLocked = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let backgroundEmojiId: Int64
|
||||||
|
if let updatedBackgroundEmojiId = state.updatedBackgroundEmojiId {
|
||||||
|
backgroundEmojiId = updatedBackgroundEmojiId
|
||||||
|
} else if let emojiId = peer?.backgroundEmojiId {
|
||||||
|
backgroundEmojiId = emojiId
|
||||||
|
} else {
|
||||||
|
backgroundEmojiId = 0
|
||||||
|
}
|
||||||
|
if backgroundEmojiId != 0 {
|
||||||
|
buttonTitle = presentationData.strings.NameColor_ApplyColorAndBackgroundEmoji
|
||||||
|
} else {
|
||||||
|
buttonTitle = presentationData.strings.NameColor_ApplyColor
|
||||||
|
}
|
||||||
|
|
||||||
let footerItem = ApplyColorFooterItem(
|
let footerItem = ApplyColorFooterItem(
|
||||||
theme: presentationData.theme,
|
theme: presentationData.theme,
|
||||||
title: presentationData.strings.NameColor_ApplyColor,
|
title: buttonTitle,
|
||||||
locked: isLocked,
|
locked: isLocked,
|
||||||
action: {
|
action: {
|
||||||
if isPremium {
|
if isPremium {
|
||||||
let state = stateValue.with { $0 }
|
let state = stateValue.with { $0 }
|
||||||
if let nameColor = state.updatedNameColor {
|
|
||||||
let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor, backgroundEmojiId: nil).startStandalone()
|
let nameColor = state.updatedNameColor ?? peer?.nameColor
|
||||||
}
|
let backgroundEmojiId = state.updatedBackgroundEmojiId ?? peer?.backgroundEmojiId
|
||||||
|
|
||||||
|
let _ = context.engine.accountData.updateNameColorAndEmoji(nameColor: nameColor ?? .blue, backgroundEmojiId: backgroundEmojiId ?? 0).startStandalone()
|
||||||
|
|
||||||
dismissImpl?()
|
dismissImpl?()
|
||||||
} else {
|
} else {
|
||||||
|
HapticFeedback().error()
|
||||||
let controller = UndoOverlayController(
|
let controller = UndoOverlayController(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
content: .premiumPaywall(
|
content: .premiumPaywall(
|
||||||
@ -359,11 +419,81 @@ public func PeerNameColorScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
emojiContent.inputInteractionHolder.inputInteraction = EmojiPagerContentComponent.InputInteraction(
|
||||||
|
performItemAction: { _, item, _, _, _, _ in
|
||||||
|
var selectedFileId: Int64?
|
||||||
|
if let fileId = item.itemFile?.fileId.id {
|
||||||
|
selectedFileId = fileId
|
||||||
|
} else {
|
||||||
|
selectedFileId = 0
|
||||||
|
}
|
||||||
|
arguments.updateBackgroundEmojiId(selectedFileId)
|
||||||
|
},
|
||||||
|
deleteBackwards: {
|
||||||
|
},
|
||||||
|
openStickerSettings: {
|
||||||
|
},
|
||||||
|
openFeatured: {
|
||||||
|
},
|
||||||
|
openSearch: {
|
||||||
|
},
|
||||||
|
addGroupAction: { groupId, isPremiumLocked, _ in
|
||||||
|
guard let collectionId = groupId.base as? ItemCollectionId else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewKey = PostboxViewKey.orderedItemList(id: Namespaces.OrderedItemList.CloudFeaturedEmojiPacks)
|
||||||
|
let _ = (context.account.postbox.combinedView(keys: [viewKey])
|
||||||
|
|> take(1)
|
||||||
|
|> deliverOnMainQueue).start(next: { views in
|
||||||
|
guard let view = views.views[viewKey] as? OrderedItemListView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for featuredEmojiPack in view.items.lazy.map({ $0.contents.get(FeaturedStickerPackItem.self)! }) {
|
||||||
|
if featuredEmojiPack.info.id == collectionId {
|
||||||
|
let _ = context.engine.stickers.addStickerPackInteractively(info: featuredEmojiPack.info, items: featuredEmojiPack.topItems).start()
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clearGroup: { _ in
|
||||||
|
},
|
||||||
|
pushController: { c in
|
||||||
|
},
|
||||||
|
presentController: { c in
|
||||||
|
},
|
||||||
|
presentGlobalOverlayController: { c in
|
||||||
|
},
|
||||||
|
navigationController: {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
requestUpdate: { _ in
|
||||||
|
},
|
||||||
|
updateSearchQuery: { _ in
|
||||||
|
},
|
||||||
|
updateScrollingToItemGroup: {
|
||||||
|
},
|
||||||
|
onScroll: {},
|
||||||
|
chatPeerId: nil,
|
||||||
|
peekBehavior: nil,
|
||||||
|
customLayout: nil,
|
||||||
|
externalBackground: nil,
|
||||||
|
externalExpansionView: nil,
|
||||||
|
customContentView: nil,
|
||||||
|
useOpaqueTheme: true,
|
||||||
|
hideBackground: false,
|
||||||
|
stateContext: nil,
|
||||||
|
addImage: nil
|
||||||
|
)
|
||||||
|
|
||||||
let entries = peerNameColorScreenEntries(
|
let entries = peerNameColorScreenEntries(
|
||||||
presentationData: presentationData,
|
presentationData: presentationData,
|
||||||
state: state,
|
state: state,
|
||||||
peer: peer,
|
peer: peer,
|
||||||
isPremium: isPremium
|
isPremium: isPremium,
|
||||||
|
emojiContent: emojiContent
|
||||||
)
|
)
|
||||||
|
|
||||||
let controllerState = ItemListControllerState(
|
let controllerState = ItemListControllerState(
|
||||||
@ -389,77 +519,6 @@ public func PeerNameColorScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let controller = ItemListController(context: context, state: signal)
|
let controller = ItemListController(context: context, state: signal)
|
||||||
// openQuickReactionImpl = { [weak controller] in
|
|
||||||
// let _ = (combineLatest(queue: .mainQueue(),
|
|
||||||
// settings,
|
|
||||||
// context.engine.stickers.availableReactions()
|
|
||||||
// )
|
|
||||||
// |> take(1)
|
|
||||||
// |> deliverOnMainQueue).start(next: { settings, availableReactions in
|
|
||||||
// var currentSelectedFileId: MediaId?
|
|
||||||
// switch settings.quickReaction {
|
|
||||||
// case .builtin:
|
|
||||||
// if let availableReactions = availableReactions {
|
|
||||||
// if let reaction = availableReactions.reactions.first(where: { $0.value == settings.quickReaction }) {
|
|
||||||
// currentSelectedFileId = reaction.selectAnimation.fileId
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// case let .custom(fileId):
|
|
||||||
// currentSelectedFileId = MediaId(namespace: Namespaces.Media.CloudFile, id: fileId)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var selectedItems = Set<MediaId>()
|
|
||||||
// if let currentSelectedFileId = currentSelectedFileId {
|
|
||||||
// selectedItems.insert(currentSelectedFileId)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// guard let controller = controller else {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// var sourceItemNode: ItemListReactionItemNode?
|
|
||||||
// controller.forEachItemNode { itemNode in
|
|
||||||
// if let itemNode = itemNode as? ItemListReactionItemNode {
|
|
||||||
// sourceItemNode = itemNode
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if let sourceItemNode = sourceItemNode {
|
|
||||||
// controller.present(EmojiStatusSelectionController(
|
|
||||||
// context: context,
|
|
||||||
// mode: .quickReactionSelection(completion: {
|
|
||||||
// updateState { state in
|
|
||||||
// var state = state
|
|
||||||
// state.hasReaction = false
|
|
||||||
// return state
|
|
||||||
// }
|
|
||||||
// }),
|
|
||||||
// sourceView: sourceItemNode.iconView,
|
|
||||||
// emojiContent: EmojiPagerContentComponent.emojiInputData(
|
|
||||||
// context: context,
|
|
||||||
// animationCache: context.animationCache,
|
|
||||||
// animationRenderer: context.animationRenderer,
|
|
||||||
// isStandalone: false,
|
|
||||||
// isStatusSelection: false,
|
|
||||||
// isReactionSelection: true,
|
|
||||||
// isEmojiSelection: false,
|
|
||||||
// hasTrending: false,
|
|
||||||
// isQuickReactionSelection: true,
|
|
||||||
// topReactionItems: [],
|
|
||||||
// areUnicodeEmojiEnabled: false,
|
|
||||||
// areCustomEmojiEnabled: true,
|
|
||||||
// chatPeerId: context.account.peerId,
|
|
||||||
// selectedItems: selectedItems
|
|
||||||
// ),
|
|
||||||
// currentSelection: nil,
|
|
||||||
// destinationItemView: { [weak sourceItemNode] in
|
|
||||||
// return sourceItemNode?.iconView
|
|
||||||
// }
|
|
||||||
// ), in: .window(.root))
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
presentImpl = { [weak controller] c in
|
presentImpl = { [weak controller] c in
|
||||||
guard let controller else {
|
guard let controller else {
|
||||||
return
|
return
|
||||||
|
|||||||
@ -4261,9 +4261,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .reaction,
|
||||||
isReactionSelection: true,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: mappedReactionItems,
|
topReactionItems: mappedReactionItems,
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
|
|||||||
@ -1496,9 +1496,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: false,
|
subject: .reaction,
|
||||||
isReactionSelection: true,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: reactionItems,
|
topReactionItems: reactionItems,
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
@ -11002,7 +11000,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.presentAttachmentPremiumGift()
|
strongSelf.presentAttachmentMenu(subject: .gift)
|
||||||
Queue.mainQueue().after(0.5) {
|
Queue.mainQueue().after(0.5) {
|
||||||
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).startStandalone()
|
let _ = ApplicationSpecificNotice.incrementDismissedPremiumGiftSuggestion(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peerId).startStandalone()
|
||||||
}
|
}
|
||||||
@ -13457,10 +13455,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentAttachmentPremiumGift() {
|
|
||||||
self.presentAttachmentMenu(subject: .gift)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AttachMenuSubject {
|
enum AttachMenuSubject {
|
||||||
case `default`
|
case `default`
|
||||||
case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
|
case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
|
||||||
@ -13788,7 +13782,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
let _ = currentLocationController.swap(controller)
|
let _ = currentLocationController.swap(controller)
|
||||||
})
|
})
|
||||||
case .contact:
|
case .contact:
|
||||||
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true))
|
let contactsController = ContactSelectionControllerImpl(ContactSelectionControllerParams(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: { $0.Contacts_Title }, displayDeviceContacts: true, multipleSelection: true, requirePhoneNumbers: true))
|
||||||
contactsController.presentScheduleTimePicker = { [weak self] completion in
|
contactsController.presentScheduleTimePicker = { [weak self] completion in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.presentScheduleTimePicker(completion: completion)
|
strongSelf.presentScheduleTimePicker(completion: completion)
|
||||||
|
|||||||
@ -1188,17 +1188,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for media in message.media {
|
if !isCopyProtected {
|
||||||
if let file = media as? TelegramMediaFile {
|
for media in message.media {
|
||||||
if file.isMusic {
|
if let file = media as? TelegramMediaFile {
|
||||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
if file.isMusic {
|
||||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
||||||
}, action: { _, f in
|
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
||||||
controllerInteraction.saveMediaToFiles(message.id)
|
}, action: { _, f in
|
||||||
f(.default)
|
controllerInteraction.saveMediaToFiles(message.id)
|
||||||
})))
|
f(.default)
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,6 +37,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
private let displayDeviceContacts: Bool
|
private let displayDeviceContacts: Bool
|
||||||
private let displayCallIcons: Bool
|
private let displayCallIcons: Bool
|
||||||
private let multipleSelection: Bool
|
private let multipleSelection: Bool
|
||||||
|
private let requirePhoneNumbers: Bool
|
||||||
|
|
||||||
private var _ready = Promise<Bool>()
|
private var _ready = Promise<Bool>()
|
||||||
override var ready: Promise<Bool> {
|
override var ready: Promise<Bool> {
|
||||||
@ -91,6 +92,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
self.displayCallIcons = params.displayCallIcons
|
self.displayCallIcons = params.displayCallIcons
|
||||||
self.confirmation = params.confirmation
|
self.confirmation = params.confirmation
|
||||||
self.multipleSelection = params.multipleSelection
|
self.multipleSelection = params.multipleSelection
|
||||||
|
self.requirePhoneNumbers = params.requirePhoneNumbers
|
||||||
|
|
||||||
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
@ -178,7 +180,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func loadDisplayNode() {
|
override func loadDisplayNode() {
|
||||||
self.displayNode = ContactSelectionControllerNode(context: self.context, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection)
|
self.displayNode = ContactSelectionControllerNode(context: self.context, presentationData: self.presentationData, options: self.options, displayDeviceContacts: self.displayDeviceContacts, displayCallIcons: self.displayCallIcons, multipleSelection: self.multipleSelection, requirePhoneNumbers: self.requirePhoneNumbers)
|
||||||
self._ready.set(self.contactsNode.contactListNode.ready)
|
self._ready.set(self.contactsNode.contactListNode.ready)
|
||||||
|
|
||||||
self.contactsNode.navigationBar = self.navigationBar
|
self.contactsNode.navigationBar = self.navigationBar
|
||||||
|
|||||||
@ -23,6 +23,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
private let displayDeviceContacts: Bool
|
private let displayDeviceContacts: Bool
|
||||||
private let displayCallIcons: Bool
|
private let displayCallIcons: Bool
|
||||||
|
private let filters: [ContactListFilter]
|
||||||
|
|
||||||
let contactListNode: ContactListNode
|
let contactListNode: ContactListNode
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
@ -53,14 +54,20 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var searchContainerNode: ContactsSearchContainerNode?
|
var searchContainerNode: ContactsSearchContainerNode?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, options: [ContactListAdditionalOption], displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool) {
|
init(context: AccountContext, presentationData: PresentationData, options: [ContactListAdditionalOption], displayDeviceContacts: Bool, displayCallIcons: Bool, multipleSelection: Bool, requirePhoneNumbers: Bool) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
self.displayDeviceContacts = displayDeviceContacts
|
self.displayDeviceContacts = displayDeviceContacts
|
||||||
self.displayCallIcons = displayCallIcons
|
self.displayCallIcons = displayCallIcons
|
||||||
|
|
||||||
|
var filters: [ContactListFilter] = [.excludeSelf]
|
||||||
|
if requirePhoneNumbers {
|
||||||
|
filters.append(.excludeWithoutPhoneNumbers)
|
||||||
|
}
|
||||||
|
self.filters = filters
|
||||||
|
|
||||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), filters: filters, displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _, _ in
|
||||||
contextActionImpl?(peer, node, gesture, nil)
|
contextActionImpl?(peer, node, gesture, nil)
|
||||||
} : nil, multipleSelection: multipleSelection)
|
} : nil, multipleSelection: multipleSelection)
|
||||||
|
|
||||||
@ -186,7 +193,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
|||||||
categories.insert(.global)
|
categories.insert(.global)
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, addContact: nil, openPeer: { [weak self] peer in
|
let searchContainerNode = ContactsSearchContainerNode(context: self.context, updatedPresentationData: (self.presentationData, self.presentationDataPromise.get()), onlyWriteable: false, categories: categories, filters: self.filters, addContact: nil, openPeer: { [weak self] peer in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
var updated = false
|
var updated = false
|
||||||
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
||||||
|
|||||||
@ -3754,9 +3754,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||||||
animationCache: animationCache,
|
animationCache: animationCache,
|
||||||
animationRenderer: animationRenderer,
|
animationRenderer: animationRenderer,
|
||||||
isStandalone: false,
|
isStandalone: false,
|
||||||
isStatusSelection: true,
|
subject: .status,
|
||||||
isReactionSelection: false,
|
|
||||||
isEmojiSelection: false,
|
|
||||||
hasTrending: false,
|
hasTrending: false,
|
||||||
topReactionItems: [],
|
topReactionItems: [],
|
||||||
areUnicodeEmojiEnabled: false,
|
areUnicodeEmojiEnabled: false,
|
||||||
@ -10304,7 +10302,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
context.translateBy(x: inset, y: inset)
|
context.translateBy(x: inset, y: inset)
|
||||||
|
|
||||||
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id)
|
drawPeerAvatarLetters(context: context, size: CGSize(width: size.width - inset * 2.0, height: size.height - inset * 2.0), font: avatarFont, letters: displayLetters, peerId: primary.1.id, nameColor: primary.1.nameColor)
|
||||||
})?.withRenderingMode(.alwaysOriginal)
|
})?.withRenderingMode(.alwaysOriginal)
|
||||||
if let image = image {
|
if let image = image {
|
||||||
subscriber.putNext((image, image))
|
subscriber.putNext((image, image))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user