Various improvements

This commit is contained in:
Ilya Laktyushin 2023-10-18 03:34:47 +04:00
parent bcbcc94b58
commit ec0f8028aa
23 changed files with 199 additions and 79 deletions

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

@ -1085,6 +1085,7 @@ 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:
@ -1094,6 +1095,8 @@ public final class ContactListNode: ASDisplayNode {
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 {
let phone = user.phone ?? ""
if requirePhoneNumbers && phone.isEmpty {
matches = false
} else {
matches = true 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 {
let phone = user.phone ?? ""
if requirePhoneNumbers && phone.isEmpty {
matches = false
} else {
matches = true 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,6 +1283,7 @@ 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:
@ -1278,12 +1292,20 @@ public final class ContactListNode: ASDisplayNode {
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, _, _):
if requirePhoneNumbers, let user = peer as? TelegramUser {
let phone = user.phone ?? ""
if phone.isEmpty {
return false
}
}
return !existingPeerIds.contains(peer.id) return !existingPeerIds.contains(peer.id)
default: default:
return true 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

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

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

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

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

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

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

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

@ -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))
} }
if let minDate = item.minDate {
datePickerNode.minimumDate = Date(timeIntervalSince1970: TimeInterval(minDate))
} else {
datePickerNode.minimumDate = Date() 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

@ -164,6 +164,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 +216,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
itemNode.frame = nodeFrame itemNode.frame = nodeFrame
itemNode.isUserInteractionEnabled = false itemNode.isUserInteractionEnabled = false
Queue.mainQueue().after(0.01) {
apply(ListViewItemApply(isOnScreen: true)) apply(ListViewItemApply(isOnScreen: true))
}
}) })
} }
} else { } else {
@ -249,6 +253,15 @@ 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) {
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 {

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

@ -336,6 +336,7 @@ public func PeerNameColorScreen(
} }
dismissImpl?() dismissImpl?()
} else { } else {
HapticFeedback().error()
let controller = UndoOverlayController( let controller = UndoOverlayController(
presentationData: presentationData, presentationData: presentationData,
content: .premiumPaywall( content: .premiumPaywall(

View File

@ -10958,7 +10958,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()
} }
@ -13389,10 +13389,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)
@ -13720,7 +13716,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

@ -1177,6 +1177,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
} }
} }
if !isCopyProtected {
for media in message.media { for media in message.media {
if let file = media as? TelegramMediaFile { if let file = media as? TelegramMediaFile {
if file.isMusic { if file.isMusic {
@ -1190,6 +1191,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
break break
} }
} }
}
if (loggingSettings.logToFile || loggingSettings.logToConsole) && !downloadableMediaResourceInfos.isEmpty { if (loggingSettings.logToFile || loggingSettings.logToConsole) && !downloadableMediaResourceInfos.isEmpty {
actions.append(.action(ContextMenuActionItem(text: "Send Logs", icon: { theme in actions.append(.action(ContextMenuActionItem(text: "Send Logs", icon: { theme in

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

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