Merge commit 'ee0c0ec3e5979ede0ee153aa60801e33ce7d98b6'

This commit is contained in:
Ali 2023-10-19 00:11:12 +04:00
commit 9e94faf08b
61 changed files with 1291 additions and 870 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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