mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Various improvements
This commit is contained in:
parent
bcbcc94b58
commit
ec0f8028aa
@ -663,9 +663,10 @@ public final class ContactSelectionControllerParams {
|
||||
public let displayDeviceContacts: Bool
|
||||
public let displayCallIcons: Bool
|
||||
public let multipleSelection: Bool
|
||||
public let requirePhoneNumbers: Bool
|
||||
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.updatedPresentationData = updatedPresentationData
|
||||
self.autoDismiss = autoDismiss
|
||||
@ -674,6 +675,7 @@ public final class ContactSelectionControllerParams {
|
||||
self.displayDeviceContacts = displayDeviceContacts
|
||||
self.displayCallIcons = displayCallIcons
|
||||
self.multipleSelection = multipleSelection
|
||||
self.requirePhoneNumbers = requirePhoneNumbers
|
||||
self.confirmation = confirmation
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ public enum ContactMultiselectionControllerMode {
|
||||
}
|
||||
|
||||
public enum ContactListFilter {
|
||||
case excludeWithoutPhoneNumbers
|
||||
case excludeSelf
|
||||
case exclude([EnginePeer.Id])
|
||||
case disable([EnginePeer.Id])
|
||||
|
@ -113,7 +113,7 @@ private final class ContentNode: ASDisplayNode {
|
||||
} else {
|
||||
let image = generateImage(size, rotatedContext: { size, context in
|
||||
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)
|
||||
}
|
||||
@ -346,7 +346,7 @@ public final class AnimatedAvatarSetView: UIView {
|
||||
} else {
|
||||
let image = generateImage(size, rotatedContext: { size, context in
|
||||
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)
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ public func peerAvatarCompleteImage(postbox: Postbox, network: Network, peer: En
|
||||
iconSignal = Signal { subscriber in
|
||||
let image = generateImage(size, rotatedContext: { size, context in
|
||||
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 {
|
||||
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
|
||||
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 {
|
||||
context.beginPath()
|
||||
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 {
|
||||
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
||||
} 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]
|
||||
|
@ -1085,15 +1085,18 @@ public final class ContactListNode: ASDisplayNode {
|
||||
|
||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||
var excludeSelf = false
|
||||
var requirePhoneNumbers = false
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case .excludeSelf:
|
||||
excludeSelf = true
|
||||
existingPeerIds.insert(context.account.peerId)
|
||||
case let .exclude(peerIds):
|
||||
existingPeerIds = existingPeerIds.union(peerIds)
|
||||
case let .disable(peerIds):
|
||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||
case .excludeSelf:
|
||||
excludeSelf = true
|
||||
existingPeerIds.insert(context.account.peerId)
|
||||
case let .exclude(peerIds):
|
||||
existingPeerIds = existingPeerIds.union(peerIds)
|
||||
case let .disable(peerIds):
|
||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||
case .excludeWithoutPhoneNumbers:
|
||||
requirePhoneNumbers = true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1127,8 +1130,13 @@ public final class ContactListNode: ASDisplayNode {
|
||||
}
|
||||
for peer in remotePeers.0 {
|
||||
let matches: Bool
|
||||
if peer.peer is TelegramUser {
|
||||
matches = true
|
||||
if let user = peer.peer as? TelegramUser {
|
||||
let phone = user.phone ?? ""
|
||||
if requirePhoneNumbers && phone.isEmpty {
|
||||
matches = false
|
||||
} else {
|
||||
matches = true
|
||||
}
|
||||
} else if searchGroups || searchChannels {
|
||||
if peer.peer is TelegramGroup && searchGroups {
|
||||
matches = true
|
||||
@ -1157,8 +1165,13 @@ public final class ContactListNode: ASDisplayNode {
|
||||
}
|
||||
for peer in remotePeers.1 {
|
||||
let matches: Bool
|
||||
if peer.peer is TelegramUser {
|
||||
matches = true
|
||||
if let user = peer.peer as? TelegramUser {
|
||||
let phone = user.phone ?? ""
|
||||
if requirePhoneNumbers && phone.isEmpty {
|
||||
matches = false
|
||||
} else {
|
||||
matches = true
|
||||
}
|
||||
} else if searchGroups || searchChannels {
|
||||
if peer.peer is TelegramGroup {
|
||||
matches = searchGroups
|
||||
@ -1270,23 +1283,32 @@ public final class ContactListNode: ASDisplayNode {
|
||||
}
|
||||
var existingPeerIds = Set<EnginePeer.Id>()
|
||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||
var requirePhoneNumbers = false
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case .excludeSelf:
|
||||
existingPeerIds.insert(context.account.peerId)
|
||||
case let .exclude(peerIds):
|
||||
existingPeerIds = existingPeerIds.union(peerIds)
|
||||
case let .disable(peerIds):
|
||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||
case .excludeSelf:
|
||||
existingPeerIds.insert(context.account.peerId)
|
||||
case let .exclude(peerIds):
|
||||
existingPeerIds = existingPeerIds.union(peerIds)
|
||||
case let .disable(peerIds):
|
||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||
case .excludeWithoutPhoneNumbers:
|
||||
requirePhoneNumbers = true
|
||||
}
|
||||
}
|
||||
|
||||
peers = peers.filter { contact in
|
||||
switch contact {
|
||||
case let .peer(peer, _, _):
|
||||
return !existingPeerIds.contains(peer.id)
|
||||
default:
|
||||
return true
|
||||
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)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,6 +322,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
var entries: [ContactListSearchEntry] = []
|
||||
var existingPeerIds = Set<EnginePeer.Id>()
|
||||
var disabledPeerIds = Set<EnginePeer.Id>()
|
||||
var requirePhoneNumbers = false
|
||||
for filter in filters {
|
||||
switch filter {
|
||||
case .excludeSelf:
|
||||
@ -330,6 +331,8 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
existingPeerIds = existingPeerIds.union(peerIds)
|
||||
case let .disable(peerIds):
|
||||
disabledPeerIds = disabledPeerIds.union(peerIds)
|
||||
case .excludeWithoutPhoneNumbers:
|
||||
requirePhoneNumbers = true
|
||||
}
|
||||
}
|
||||
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
|
||||
@ -338,6 +341,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if existingPeerIds.contains(peer.id) {
|
||||
continue
|
||||
}
|
||||
|
||||
if case let .user(user) = peer, requirePhoneNumbers {
|
||||
let phone = user.phone ?? ""
|
||||
if phone.isEmpty {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
existingPeerIds.insert(peer.id)
|
||||
var enabled = true
|
||||
if onlyWriteable {
|
||||
@ -354,6 +365,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if !(peer.peer is TelegramUser) {
|
||||
continue
|
||||
}
|
||||
|
||||
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||
let phone = user.phone ?? ""
|
||||
if phone.isEmpty {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
|
||||
@ -373,6 +392,14 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if !(peer.peer is TelegramUser) {
|
||||
continue
|
||||
}
|
||||
|
||||
if let user = peer.peer as? TelegramUser, requirePhoneNumbers {
|
||||
let phone = user.phone ?? ""
|
||||
if phone.isEmpty {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if !existingPeerIds.contains(peer.peer.id) {
|
||||
existingPeerIds.insert(peer.peer.id)
|
||||
|
||||
|
@ -94,7 +94,7 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
|
||||
case timeHeader(PresentationTheme, String)
|
||||
case timeExpiryDate(PresentationTheme, PresentationDateTimeFormat, Int32?, Bool)
|
||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?)
|
||||
case timeCustomPicker(PresentationTheme, PresentationDateTimeFormat, Int32?, Int32?, Int32?)
|
||||
case timeInfo(PresentationTheme, String)
|
||||
|
||||
case durationHeader(PresentationTheme, String)
|
||||
@ -282,8 +282,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate):
|
||||
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate {
|
||||
case let .timeCustomPicker(lhsTheme, lhsDateTimeFormat, lhsDate, lhsMinDate, lhsMaxDate):
|
||||
if case let .timeCustomPicker(rhsTheme, rhsDateTimeFormat, rhsDate, rhsMinDate, rhsMaxDate) = rhs, lhsTheme === rhsTheme, lhsDateTimeFormat == rhsDateTimeFormat, lhsDate == rhsDate, lhsMinDate == rhsMinDate, lhsMaxDate == rhsMaxDate {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -450,8 +450,8 @@ private enum CreateGiveawayEntry: ItemListNodeEntry {
|
||||
}
|
||||
}
|
||||
})
|
||||
case let .timeCustomPicker(_, dateTimeFormat, date):
|
||||
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, sectionId: self.section, style: .blocks, updated: { date in
|
||||
case let .timeCustomPicker(_, dateTimeFormat, date, minDate, maxDate):
|
||||
return ItemListDatePickerItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, date: date, minDate: minDate, maxDate: maxDate, sectionId: self.section, style: .blocks, updated: { date in
|
||||
arguments.updateState({ state in
|
||||
var updatedState = state
|
||||
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] = []
|
||||
|
||||
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(.timeExpiryDate(presentationData.theme, presentationData.dateTimeFormat, state.time, 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."))
|
||||
}
|
||||
@ -657,7 +668,11 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
|
||||
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 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 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))
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
let context: AccountContext
|
||||
let giftCode: PremiumGiftCodeInfo
|
||||
let action: () -> Void
|
||||
let cancel: () -> Void
|
||||
let cancel: (Bool) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let openMessage: (EngineMessage.Id) -> Void
|
||||
let copyLink: (String) -> Void
|
||||
@ -37,7 +37,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
context: AccountContext,
|
||||
giftCode: PremiumGiftCodeInfo,
|
||||
action: @escaping () -> Void,
|
||||
cancel: @escaping () -> Void,
|
||||
cancel: @escaping (Bool) -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||
copyLink: @escaping (String) -> Void,
|
||||
@ -149,7 +149,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
component: Button(
|
||||
content: AnyComponent(Image(image: closeImage)),
|
||||
action: { [weak component] in
|
||||
component?.cancel()
|
||||
component?.cancel(true)
|
||||
}
|
||||
),
|
||||
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)),
|
||||
action: {
|
||||
if let peer = fromPeer {
|
||||
component.cancel()
|
||||
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)),
|
||||
action: {
|
||||
if let peer = toPeer {
|
||||
component.cancel()
|
||||
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)))),
|
||||
isEnabled: true,
|
||||
action: {
|
||||
component.cancel()
|
||||
if let messageId = giftCode.messageId {
|
||||
component.openMessage(messageId)
|
||||
}
|
||||
Queue.mainQueue().after(1.0) {
|
||||
component.cancel(true)
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
@ -362,7 +368,7 @@ private final class PremiumGiftCodeSheetContent: CombinedComponent {
|
||||
iconPosition: .left,
|
||||
action: {
|
||||
if giftCode.isUsed {
|
||||
component.cancel()
|
||||
component.cancel(true)
|
||||
} else {
|
||||
component.action()
|
||||
}
|
||||
@ -425,7 +431,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
||||
let context: AccountContext
|
||||
let giftCode: PremiumGiftCodeInfo
|
||||
let action: () -> Void
|
||||
let cancel: () -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let openMessage: (EngineMessage.Id) -> Void
|
||||
let copyLink: (String) -> Void
|
||||
@ -435,7 +440,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
||||
context: AccountContext,
|
||||
giftCode: PremiumGiftCodeInfo,
|
||||
action: @escaping () -> Void,
|
||||
cancel: @escaping () -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
openMessage: @escaping (EngineMessage.Id) -> Void,
|
||||
copyLink: @escaping (String) -> Void,
|
||||
@ -444,7 +448,6 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
||||
self.context = context
|
||||
self.giftCode = giftCode
|
||||
self.action = action
|
||||
self.cancel = cancel
|
||||
self.openPeer = openPeer
|
||||
self.openMessage = openMessage
|
||||
self.copyLink = copyLink
|
||||
@ -475,7 +478,7 @@ private final class PremiumGiftCodeSheetComponent: CombinedComponent {
|
||||
context: context.component.context,
|
||||
giftCode: context.component.giftCode,
|
||||
action: context.component.action,
|
||||
cancel: {
|
||||
cancel: { animate in
|
||||
animateOut.invoke(Action { _ in
|
||||
if let controller = controller() {
|
||||
controller.dismiss(completion: nil)
|
||||
@ -535,7 +538,6 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
||||
context: AccountContext,
|
||||
giftCode: PremiumGiftCodeInfo,
|
||||
forceDark: Bool = false,
|
||||
cancel: @escaping () -> Void = {},
|
||||
action: @escaping () -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void = { _ in },
|
||||
openMessage: @escaping (EngineMessage.Id) -> Void = { _ in },
|
||||
@ -544,7 +546,7 @@ public class PremiumGiftCodeScreen: ViewControllerComponentContainer {
|
||||
self.context = context
|
||||
|
||||
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)
|
||||
}, shareLink: shareLink), navigationBarAppearance: .none, statusBarStyle: .ignore, theme: forceDark ? .dark : .default)
|
||||
|
||||
|
@ -49,7 +49,7 @@ public final class ChannelBoostStatus: Equatable {
|
||||
}
|
||||
|
||||
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 transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
// }
|
||||
|
@ -24,6 +24,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
public let maxStoriesSuggestedReactions: Int32
|
||||
public let maxGiveawayChannelsCount: Int32
|
||||
public let maxGiveawayCountriesCount: Int32
|
||||
public let maxGiveawayPeriodSeconds: Int32
|
||||
public let minChannelNameColorLevel: Int32
|
||||
|
||||
public static var defaultValue: UserLimitsConfiguration {
|
||||
@ -50,6 +51,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
maxStoriesSuggestedReactions: 1,
|
||||
maxGiveawayChannelsCount: 10,
|
||||
maxGiveawayCountriesCount: 10,
|
||||
maxGiveawayPeriodSeconds: 86400 * 7,
|
||||
minChannelNameColorLevel: 10
|
||||
)
|
||||
}
|
||||
@ -77,6 +79,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
maxStoriesSuggestedReactions: Int32,
|
||||
maxGiveawayChannelsCount: Int32,
|
||||
maxGiveawayCountriesCount: Int32,
|
||||
maxGiveawayPeriodSeconds: Int32,
|
||||
minChannelNameColorLevel: Int32
|
||||
) {
|
||||
self.maxPinnedChatCount = maxPinnedChatCount
|
||||
@ -101,6 +104,7 @@ public struct UserLimitsConfiguration: Equatable {
|
||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||
}
|
||||
}
|
||||
@ -148,6 +152,7 @@ extension UserLimitsConfiguration {
|
||||
self.maxStoriesSuggestedReactions = getValue("stories_suggested_reactions_limit", orElse: defaultValue.maxStoriesMonthlyCount)
|
||||
self.maxGiveawayChannelsCount = getGeneralValue("giveaway_add_peers_max", orElse: defaultValue.maxGiveawayChannelsCount)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ public enum EngineConfiguration {
|
||||
public let maxStoriesSuggestedReactions: Int32
|
||||
public let maxGiveawayChannelsCount: Int32
|
||||
public let maxGiveawayCountriesCount: Int32
|
||||
public let maxGiveawayPeriodSeconds: Int32
|
||||
public let minChannelNameColorLevel: Int32
|
||||
|
||||
public static var defaultValue: UserLimits {
|
||||
@ -87,6 +88,7 @@ public enum EngineConfiguration {
|
||||
maxStoriesSuggestedReactions: Int32,
|
||||
maxGiveawayChannelsCount: Int32,
|
||||
maxGiveawayCountriesCount: Int32,
|
||||
maxGiveawayPeriodSeconds: Int32,
|
||||
minChannelNameColorLevel: Int32
|
||||
) {
|
||||
self.maxPinnedChatCount = maxPinnedChatCount
|
||||
@ -111,6 +113,7 @@ public enum EngineConfiguration {
|
||||
self.maxStoriesSuggestedReactions = maxStoriesSuggestedReactions
|
||||
self.maxGiveawayChannelsCount = maxGiveawayChannelsCount
|
||||
self.maxGiveawayCountriesCount = maxGiveawayCountriesCount
|
||||
self.maxGiveawayPeriodSeconds = maxGiveawayPeriodSeconds
|
||||
self.minChannelNameColorLevel = minChannelNameColorLevel
|
||||
}
|
||||
}
|
||||
@ -171,6 +174,7 @@ public extension EngineConfiguration.UserLimits {
|
||||
maxStoriesSuggestedReactions: userLimitsConfiguration.maxStoriesSuggestedReactions,
|
||||
maxGiveawayChannelsCount: userLimitsConfiguration.maxGiveawayChannelsCount,
|
||||
maxGiveawayCountriesCount: userLimitsConfiguration.maxGiveawayCountriesCount,
|
||||
maxGiveawayPeriodSeconds: userLimitsConfiguration.maxGiveawayPeriodSeconds,
|
||||
minChannelNameColorLevel: userLimitsConfiguration.minChannelNameColorLevel
|
||||
)
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ public func donateSendMessageIntent(account: Account, sharedContext: SharedAccou
|
||||
if let image = generateImage(size, rotatedContext: { size, context in
|
||||
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) {
|
||||
avatarImage = image
|
||||
}
|
||||
|
@ -771,6 +771,7 @@ private final class CameraScreenComponent: CombinedComponent {
|
||||
)
|
||||
context.add(frontFlash
|
||||
.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))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
|
@ -1708,12 +1708,12 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
|
||||
private enum PeerAvatarReference: Equatable {
|
||||
case letters(PeerId, [String])
|
||||
case letters(PeerId, PeerNameColor?, [String])
|
||||
case image(PeerReference, TelegramMediaImageRepresentation)
|
||||
|
||||
var peerId: PeerId {
|
||||
switch self {
|
||||
case let .letters(value, _):
|
||||
case let .letters(value, _, _):
|
||||
return value
|
||||
case let .image(value, _):
|
||||
return value.id
|
||||
@ -1726,7 +1726,7 @@ private extension PeerAvatarReference {
|
||||
if let photo = peer.smallProfileImage, let peerReference = PeerReference(peer) {
|
||||
self = .image(peerReference, photo)
|
||||
} 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()
|
||||
switch parameters.peers[i] {
|
||||
case let .letters(peerId, letters):
|
||||
case let .letters(peerId, nameColor, letters):
|
||||
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)
|
||||
case .image:
|
||||
if let image = parameters.images[parameters.peers[i].peerId] {
|
||||
|
@ -11,6 +11,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
let presentationData: ItemListPresentationData
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
let date: Int32?
|
||||
let minDate: Int32?
|
||||
let maxDate: Int32?
|
||||
public let sectionId: ItemListSectionId
|
||||
let style: ItemListStyle
|
||||
let updated: ((Int32) -> Void)?
|
||||
@ -20,6 +22,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
presentationData: ItemListPresentationData,
|
||||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
date: Int32?,
|
||||
minDate: Int32? = nil,
|
||||
maxDate: Int32? = nil,
|
||||
sectionId: ItemListSectionId,
|
||||
style: ItemListStyle,
|
||||
updated: ((Int32) -> Void)?,
|
||||
@ -28,6 +32,8 @@ public class ItemListDatePickerItem: ListViewItem, ItemListItem {
|
||||
self.presentationData = presentationData
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
self.date = date
|
||||
self.minDate = minDate
|
||||
self.maxDate = maxDate
|
||||
self.sectionId = sectionId
|
||||
self.style = style
|
||||
self.updated = updated
|
||||
@ -223,7 +229,15 @@ public class ItemListDatePickerItemNode: ListViewItemNode, ItemListItemNode {
|
||||
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)) }
|
||||
|
||||
let datePickerSize = CGSize(width: width, height: contentSize.height)
|
||||
|
@ -164,6 +164,8 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
var currentBackgroundNode = self.backgroundNode
|
||||
|
||||
let currentItem = self.item
|
||||
|
||||
return { item, params, neighbors in
|
||||
if currentBackgroundNode == nil {
|
||||
currentBackgroundNode = createWallpaperBackgroundNode(context: item.context, forChatDisplay: false)
|
||||
@ -214,7 +216,9 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
itemNode.frame = nodeFrame
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
Queue.mainQueue().after(0.01) {
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
@ -249,6 +253,15 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode {
|
||||
|
||||
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
|
||||
var topOffset: CGFloat = 4.0
|
||||
for node in nodes {
|
||||
|
@ -184,8 +184,10 @@ private final class PeerNameColorIconItemNode : ListViewItemNode {
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate
|
||||
if selected {
|
||||
transition.updateTransformScale(node: self.fillNode, scale: 0.8)
|
||||
transition.updateTransformScale(node: self.ringNode, scale: 1.0)
|
||||
} else {
|
||||
transition.updateTransformScale(node: self.fillNode, scale: 1.0)
|
||||
transition.updateTransformScale(node: self.ringNode, scale: 0.99)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,6 +336,7 @@ public func PeerNameColorScreen(
|
||||
}
|
||||
dismissImpl?()
|
||||
} else {
|
||||
HapticFeedback().error()
|
||||
let controller = UndoOverlayController(
|
||||
presentationData: presentationData,
|
||||
content: .premiumPaywall(
|
||||
|
@ -10958,7 +10958,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId else {
|
||||
return
|
||||
}
|
||||
strongSelf.presentAttachmentPremiumGift()
|
||||
strongSelf.presentAttachmentMenu(subject: .gift)
|
||||
Queue.mainQueue().after(0.5) {
|
||||
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 {
|
||||
case `default`
|
||||
case edit(mediaOptions: MessageMediaEditingOptions, mediaReference: AnyMediaReference)
|
||||
@ -13720,7 +13716,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = currentLocationController.swap(controller)
|
||||
})
|
||||
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
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentScheduleTimePicker(completion: completion)
|
||||
|
@ -1177,17 +1177,19 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}
|
||||
}
|
||||
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if file.isMusic {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
controllerInteraction.saveMediaToFiles(message.id)
|
||||
f(.default)
|
||||
})))
|
||||
if !isCopyProtected {
|
||||
for media in message.media {
|
||||
if let file = media as? TelegramMediaFile {
|
||||
if file.isMusic {
|
||||
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_SaveToFiles, icon: { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.actionSheet.primaryTextColor)
|
||||
}, action: { _, f in
|
||||
controllerInteraction.saveMediaToFiles(message.id)
|
||||
f(.default)
|
||||
})))
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
private let displayDeviceContacts: Bool
|
||||
private let displayCallIcons: Bool
|
||||
private let multipleSelection: Bool
|
||||
private let requirePhoneNumbers: Bool
|
||||
|
||||
private var _ready = Promise<Bool>()
|
||||
override var ready: Promise<Bool> {
|
||||
@ -91,6 +92,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
self.displayCallIcons = params.displayCallIcons
|
||||
self.confirmation = params.confirmation
|
||||
self.multipleSelection = params.multipleSelection
|
||||
self.requirePhoneNumbers = params.requirePhoneNumbers
|
||||
|
||||
self.presentationData = params.updatedPresentationData?.initial ?? params.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@ -178,7 +180,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
||||
}
|
||||
|
||||
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.contactsNode.navigationBar = self.navigationBar
|
||||
|
@ -23,6 +23,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
private let displayDeviceContacts: Bool
|
||||
private let displayCallIcons: Bool
|
||||
private let filters: [ContactListFilter]
|
||||
|
||||
let contactListNode: ContactListNode
|
||||
private let dimNode: ASDisplayNode
|
||||
@ -53,14 +54,20 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
|
||||
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.presentationData = presentationData
|
||||
self.displayDeviceContacts = displayDeviceContacts
|
||||
self.displayCallIcons = displayCallIcons
|
||||
|
||||
var filters: [ContactListFilter] = [.excludeSelf]
|
||||
if requirePhoneNumbers {
|
||||
filters.append(.excludeWithoutPhoneNumbers)
|
||||
}
|
||||
self.filters = filters
|
||||
|
||||
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)
|
||||
} : nil, multipleSelection: multipleSelection)
|
||||
|
||||
@ -186,7 +193,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
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 {
|
||||
var updated = false
|
||||
strongSelf.contactListNode.updateSelectionState { state -> ContactListNodeGroupSelectionState? in
|
||||
|
@ -10304,7 +10304,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
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)
|
||||
if let image = image {
|
||||
subscriber.putNext((image, image))
|
||||
|
Loading…
x
Reference in New Issue
Block a user