mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-10 06:41:11 +00:00
Support Dynamic Type
This commit is contained in:
@@ -15,7 +15,7 @@ public enum ContactListActionItemHighlight {
|
||||
}
|
||||
|
||||
class ContactListActionItem: ListViewItem, ListViewItemWithHeader {
|
||||
let theme: PresentationTheme
|
||||
let presentationData: ItemListPresentationData
|
||||
let title: String
|
||||
let icon: ContactListActionItemIcon
|
||||
let highlight: ContactListActionItemHighlight
|
||||
@@ -23,8 +23,8 @@ class ContactListActionItem: ListViewItem, ListViewItemWithHeader {
|
||||
let action: () -> Void
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
init(theme: PresentationTheme, title: String, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
||||
self.theme = theme
|
||||
init(presentationData: ItemListPresentationData, title: String, icon: ContactListActionItemIcon, highlight: ContactListActionItemHighlight = .cell, clearHighlightAutomatically: Bool = true, header: ListViewItemHeader?, action: @escaping () -> Void) {
|
||||
self.presentationData = presentationData
|
||||
self.title = title
|
||||
self.icon = icon
|
||||
self.highlight = highlight
|
||||
@@ -110,8 +110,6 @@ class ContactListActionItem: ListViewItem, ListViewItemWithHeader {
|
||||
}
|
||||
}
|
||||
|
||||
private let titleFont = Font.regular(17.0)
|
||||
|
||||
class ContactListActionItemNode: ListViewItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
@@ -123,8 +121,6 @@ class ContactListActionItemNode: ListViewItemNode {
|
||||
|
||||
private let activateArea: AccessibilityAreaNode
|
||||
|
||||
private var theme: PresentationTheme?
|
||||
|
||||
private var item: ContactListActionItem?
|
||||
|
||||
init() {
|
||||
@@ -167,23 +163,25 @@ class ContactListActionItemNode: ListViewItemNode {
|
||||
|
||||
func asyncLayout() -> (_ item: ContactListActionItem, _ params: ListViewItemLayoutParams, _ firstWithHeader: Bool, _ last: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let currentTheme = self.theme
|
||||
let currentItem = self.item
|
||||
|
||||
return { item, params, firstWithHeader, last in
|
||||
var updatedTheme: PresentationTheme?
|
||||
|
||||
if currentTheme !== item.theme {
|
||||
updatedTheme = item.theme
|
||||
if currentItem?.presentationData.theme !== item.presentationData.theme {
|
||||
updatedTheme = item.presentationData.theme
|
||||
}
|
||||
|
||||
let titleFont = Font.regular(item.presentationData.fontSize.itemListBaseFontSize)
|
||||
|
||||
var leftInset: CGFloat = 16.0 + params.leftInset
|
||||
if case .generic = item.icon {
|
||||
leftInset += 49.0
|
||||
}
|
||||
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.theme.list.itemAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - 10.0 - leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.title, font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - 10.0 - leftInset - params.rightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let contentSize = CGSize(width: params.width, height: 50.0)
|
||||
let contentSize = CGSize(width: params.width, height: 12.0 * 2.0 + titleLayout.size.height)
|
||||
let insets = UIEdgeInsets(top: firstWithHeader ? 29.0 : 0.0, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
let separatorHeight = UIScreenPixel
|
||||
|
||||
@@ -192,18 +190,17 @@ class ContactListActionItemNode: ListViewItemNode {
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.item = item
|
||||
strongSelf.theme = item.theme
|
||||
|
||||
strongSelf.activateArea.accessibilityLabel = item.title
|
||||
strongSelf.activateArea.frame = CGRect(origin: CGPoint(x: params.leftInset, y: 0.0), size: CGSize(width: layout.contentSize.width - params.leftInset - params.rightInset, height: layout.contentSize.height))
|
||||
|
||||
if let _ = updatedTheme {
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = item.theme.list.plainBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.theme.list.itemHighlightedBackgroundColor
|
||||
strongSelf.topStripeNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.presentationData.theme.list.itemPlainSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = item.presentationData.theme.list.plainBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
|
||||
strongSelf.iconNode.image = generateTintedImage(image: item.icon.image, color: item.theme.list.itemAccentColor)
|
||||
strongSelf.iconNode.image = generateTintedImage(image: item.icon.image, color: item.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
|
||||
let _ = titleApply()
|
||||
@@ -218,12 +215,12 @@ class ContactListActionItemNode: ListViewItemNode {
|
||||
let iconSpacing: CGFloat = 4.0
|
||||
let totalWidth: CGFloat = titleLayout.size.width + image.size.width + iconSpacing
|
||||
switch position {
|
||||
case .left:
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((contentSize.width - params.leftInset - params.rightInset - totalWidth) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
titleOffset = iconFrame.minX + iconSpacing
|
||||
case .right:
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((contentSize.width - params.leftInset - params.rightInset - totalWidth) / 2.0) + totalWidth - image.size.width, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
titleOffset = iconFrame.maxX - totalWidth
|
||||
case .left:
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((contentSize.width - params.leftInset - params.rightInset - totalWidth) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
titleOffset = iconFrame.minX + iconSpacing
|
||||
case .right:
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((contentSize.width - params.leftInset - params.rightInset - totalWidth) / 2.0) + totalWidth - image.size.width, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
titleOffset = iconFrame.maxX - totalWidth
|
||||
}
|
||||
default:
|
||||
iconFrame = CGRect(origin: CGPoint(x: params.leftInset + floor((leftInset - params.leftInset - image.size.width) / 2.0) + 3.0, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size)
|
||||
|
||||
@@ -152,9 +152,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem {
|
||||
func item(account: Account, presentationData: PresentationData, interaction: ContactListNodeInteraction) -> ListViewItem {
|
||||
switch self {
|
||||
case let .search(theme, strings):
|
||||
return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: {
|
||||
@@ -165,19 +163,19 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
||||
if case .presence = sortOrder {
|
||||
text = strings.Contacts_SortedByPresence
|
||||
}
|
||||
return ContactListActionItem(theme: theme, title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, header: nil, action: {
|
||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, header: nil, action: {
|
||||
interaction.openSortMenu()
|
||||
})
|
||||
case let .permissionInfo(theme, title, text, suppressed):
|
||||
return InfoListItem(theme: theme, title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
||||
return InfoListItem(presentationData: ItemListPresentationData(presentationData), title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
||||
interaction.suppressWarning()
|
||||
})
|
||||
case let .permissionEnable(theme, text):
|
||||
return ContactListActionItem(theme: theme, title: text, icon: .none, header: nil, action: {
|
||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .none, header: nil, action: {
|
||||
interaction.authorize()
|
||||
})
|
||||
case let .option(_, option, header, theme, _):
|
||||
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
|
||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, clearHighlightAutomatically: false, header: header, action: option.action)
|
||||
case let .peer(_, peer, presence, header, selection, theme, strings, dateTimeFormat, nameSortOrder, nameDisplayOrder, enabled):
|
||||
let status: ContactsPeerItemStatus
|
||||
let itemPeer: ContactsPeerItemPeer
|
||||
@@ -221,7 +219,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
}
|
||||
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: itemPeer, status: status, enabled: enabled, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
interaction.openPeer(peer)
|
||||
}, itemHighlighting: interaction.itemHighlighting, contextAction: itemContextAction)
|
||||
}
|
||||
@@ -602,12 +600,12 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
|
||||
return entries
|
||||
}
|
||||
|
||||
private func preparedContactListNodeTransition(account: Account, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animation: ContactListAnimation) -> ContactsListNodeTransition {
|
||||
private func preparedContactListNodeTransition(account: Account, presentationData: PresentationData, from fromEntries: [ContactListNodeEntry], to toEntries: [ContactListNodeEntry], interaction: ContactListNodeInteraction, firstTime: Bool, isEmpty: Bool, generateIndexSections: Bool, animation: ContactListAnimation) -> ContactsListNodeTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
|
||||
var shouldFixScroll = false
|
||||
var indexSections: [String] = []
|
||||
@@ -772,7 +770,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool)>
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
|
||||
private var authorizationNode: PermissionContentNode
|
||||
private let displayPermissionPlaceholder: Bool
|
||||
@@ -790,7 +788,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
|
||||
self.indexNode = CollectionIndexNode()
|
||||
|
||||
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.dateTimeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder, self.presentationData.disableAnimations))
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
let contactsAuthorization = Promise<AccessType>()
|
||||
contactsAuthorization.set(.single(.allowed)
|
||||
@@ -900,7 +898,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
var firstTime: Int32 = 1
|
||||
let selectionStateSignal = self.selectionStatePromise.get()
|
||||
let transition: Signal<ContactsListNodeTransition, NoError>
|
||||
let themeAndStringsPromise = self.themeAndStringsPromise
|
||||
let presentationDataPromise = self.presentationDataPromise
|
||||
|
||||
transition = presentation
|
||||
|> mapToSignal { presentation in
|
||||
@@ -980,8 +978,8 @@ public final class ContactListNode: ASDisplayNode {
|
||||
foundDeviceContacts = .single([:])
|
||||
}
|
||||
|
||||
return combineLatest(foundLocalContacts, foundRemoteContacts, foundDeviceContacts, selectionStateSignal, themeAndStringsPromise.get())
|
||||
|> mapToQueue { localPeersAndStatuses, remotePeers, deviceContacts, selectionState, themeAndStrings -> Signal<ContactsListNodeTransition, NoError> in
|
||||
return combineLatest(foundLocalContacts, foundRemoteContacts, foundDeviceContacts, selectionStateSignal, presentationDataPromise.get())
|
||||
|> mapToQueue { localPeersAndStatuses, remotePeers, deviceContacts, selectionState, presentationData -> Signal<ContactsListNodeTransition, NoError> in
|
||||
let signal = deferred { () -> Signal<ContactsListNodeTransition, NoError> in
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
var disabledPeerIds = Set<PeerId>()
|
||||
@@ -1084,9 +1082,9 @@ public final class ContactListNode: ASDisplayNode {
|
||||
peers.append(.deviceContact(stableId, contact.0))
|
||||
}
|
||||
|
||||
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false)
|
||||
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: localPeersAndStatuses.1, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: .allowed, warningSuppressed: (true, true), displaySortOptions: false)
|
||||
let previous = previousEntries.swap(entries)
|
||||
return .single(preparedContactListNodeTransition(account: context.account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none))
|
||||
return .single(preparedContactListNodeTransition(account: context.account, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: false, generateIndexSections: generateSections, animation: .none))
|
||||
}
|
||||
|
||||
if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
@@ -1129,8 +1127,8 @@ public final class ContactListNode: ASDisplayNode {
|
||||
chatListSignal = .single([])
|
||||
}
|
||||
|
||||
return (combineLatest(self.contactPeersViewPromise.get(), chatListSignal, selectionStateSignal, themeAndStringsPromise.get(), contactsAuthorization.get(), contactsWarningSuppressed.get())
|
||||
|> mapToQueue { view, chatListPeers, selectionState, themeAndStrings, authorizationStatus, warningSuppressed -> Signal<ContactsListNodeTransition, NoError> in
|
||||
return (combineLatest(self.contactPeersViewPromise.get(), chatListSignal, selectionStateSignal, presentationDataPromise.get(), contactsAuthorization.get(), contactsWarningSuppressed.get())
|
||||
|> mapToQueue { view, chatListPeers, selectionState, presentationData, authorizationStatus, warningSuppressed -> Signal<ContactsListNodeTransition, NoError> in
|
||||
let signal = deferred { () -> Signal<ContactsListNodeTransition, NoError> in
|
||||
var peers = view.peers.map({ ContactListPeer.peer(peer: $0, isGlobal: false, participantCount: nil) })
|
||||
for (peer, memberCount) in chatListPeers {
|
||||
@@ -1162,7 +1160,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
if (authorizationStatus == .notDetermined || authorizationStatus == .denied) && peers.isEmpty {
|
||||
isEmpty = true
|
||||
}
|
||||
let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, dateTimeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions)
|
||||
let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, disabledPeerIds: disabledPeerIds, authorizationStatus: authorizationStatus, warningSuppressed: warningSuppressed, displaySortOptions: displaySortOptions)
|
||||
let previous = previousEntries.swap(entries)
|
||||
|
||||
var hadPermissionInfo = false
|
||||
@@ -1186,13 +1184,13 @@ public final class ContactListNode: ASDisplayNode {
|
||||
if hadPermissionInfo != hasPermissionInfo {
|
||||
animation = .insertion
|
||||
}
|
||||
else if let previous = previous, !themeAndStrings.5, (entries.count - previous.count) < 20 {
|
||||
else if let previous = previous, !presentationData.disableAnimations, (entries.count - previous.count) < 20 {
|
||||
animation = .default
|
||||
} else {
|
||||
animation = .none
|
||||
}
|
||||
|
||||
return .single(preparedContactListNodeTransition(account: context.account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animation: animation))
|
||||
return .single(preparedContactListNodeTransition(account: context.account, presentationData: presentationData, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, isEmpty: isEmpty, generateIndexSections: generateSections, animation: animation))
|
||||
}
|
||||
|
||||
if OSAtomicCompareAndSwap32(1, 0, &firstTime) {
|
||||
@@ -1221,7 +1219,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings || previousDisableAnimations != presentationData.disableAnimations {
|
||||
strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor
|
||||
strongSelf.listNode.verticalScrollIndicatorColor = presentationData.theme.list.scrollIndicatorColor
|
||||
strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, presentationData.disableAnimations)))
|
||||
strongSelf.presentationDataPromise.set(.single(presentationData))
|
||||
|
||||
let authorizationPreviousHidden = strongSelf.authorizationNode.isHidden
|
||||
strongSelf.authorizationNode.removeFromSupernode()
|
||||
|
||||
@@ -15,6 +15,7 @@ import ChatListSearchItemHeader
|
||||
import ContactsPeerItem
|
||||
import ContextUI
|
||||
import PhoneNumberFormat
|
||||
import ItemListUI
|
||||
|
||||
private enum ContactListSearchGroup {
|
||||
case contacts
|
||||
@@ -68,7 +69,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(account: Account, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||
func item(account: Account, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||
let header: ListViewItemHeader
|
||||
let status: ContactsPeerItemStatus
|
||||
switch self.group {
|
||||
@@ -100,7 +101,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
|
||||
case let .deviceContact(stableId, contact):
|
||||
peerItem = .deviceContact(stableId: stableId, contact: contact)
|
||||
}
|
||||
return ContactsPeerItem(theme: self.theme, strings: self.strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: peerItem, status: status, enabled: self.enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: peerItem, status: status, enabled: self.enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
openPeer(peer)
|
||||
}, contextAction: contextAction.flatMap { contextAction in
|
||||
return nativePeer.flatMap { nativePeer in
|
||||
@@ -119,12 +120,12 @@ struct ContactListSearchContainerTransition {
|
||||
let isSearching: Bool
|
||||
}
|
||||
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, account: Account, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, account: Account, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((Peer, ASDisplayNode, ContextGesture?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, openPeer: openPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, openPeer: openPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, openPeer: openPeer, contextAction: contextAction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, timeFormat: timeFormat, openPeer: openPeer, contextAction: contextAction), directionHint: nil) }
|
||||
|
||||
return ContactListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
|
||||
}
|
||||
@@ -323,7 +324,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
if let strongSelf = self {
|
||||
let previousItems = previousSearchItems.swap(items ?? [])
|
||||
|
||||
let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, account: context.account, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, timeFormat: strongSelf.presentationData.dateTimeFormat, openPeer: { peer in self?.listNode.clearHighlightAnimated(true)
|
||||
let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, account: context.account, presentationData: strongSelf.presentationData, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, timeFormat: strongSelf.presentationData.dateTimeFormat, openPeer: { peer in self?.listNode.clearHighlightAnimated(true)
|
||||
self?.openPeer(peer)
|
||||
}, contextAction: strongSelf.contextAction)
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import ContactsPeerItem
|
||||
import ChatListSearchItemHeader
|
||||
import AppBundle
|
||||
import PhoneNumberFormat
|
||||
import ItemListUI
|
||||
|
||||
private enum InviteContactsEntryId: Hashable {
|
||||
case option(index: Int)
|
||||
@@ -46,10 +47,10 @@ private enum InviteContactsEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(account: Account, interaction: InviteContactsInteraction) -> ListViewItem {
|
||||
func item(account: Account, presentationData: PresentationData, interaction: InviteContactsInteraction) -> ListViewItem {
|
||||
switch self {
|
||||
case let .option(_, option, theme, _):
|
||||
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, header: nil, action: option.action)
|
||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: option.title, icon: option.icon, header: nil, action: option.action)
|
||||
case let .peer(_, id, contact, count, selection, theme, strings, nameSortOrder, nameDisplayOrder):
|
||||
let status: ContactsPeerItemStatus
|
||||
if count != 0 {
|
||||
@@ -58,7 +59,7 @@ private enum InviteContactsEntry: Comparable, Identifiable {
|
||||
status = .none
|
||||
}
|
||||
let peer = TelegramUser(id: PeerId(namespace: -1, id: 0), accessHash: nil, firstName: contact.firstName, lastName: contact.lastName, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: status, enabled: true, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .contacts, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: status, enabled: true, selection: selection, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .contacts, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in
|
||||
interaction.toggleContact(id)
|
||||
})
|
||||
}
|
||||
@@ -201,12 +202,12 @@ private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceC
|
||||
return entries
|
||||
}
|
||||
|
||||
private func preparedInviteContactsTransition(account: Account, from fromEntries: [InviteContactsEntry], to toEntries: [InviteContactsEntry], sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)]?, interaction: InviteContactsInteraction, isLoading: Bool, firstTime: Bool, crossfade: Bool) -> InviteContactsTransition {
|
||||
private func preparedInviteContactsTransition(account: Account, presentationData: PresentationData, from fromEntries: [InviteContactsEntry], to toEntries: [InviteContactsEntry], sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)]?, interaction: InviteContactsInteraction, isLoading: Bool, firstTime: Bool, crossfade: Bool) -> InviteContactsTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, interaction: interaction), directionHint: nil) }
|
||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, presentationData: presentationData, interaction: interaction), directionHint: nil) }
|
||||
|
||||
return InviteContactsTransition(deletions: deletions, insertions: insertions, updates: updates, sortedContacts: sortedContacts, isLoading: isLoading, firstTime: firstTime, crossfade: crossfade)
|
||||
}
|
||||
@@ -263,7 +264,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)>
|
||||
private let presentationDataPromise: Promise<PresentationData>
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
private var readyValue = false {
|
||||
@@ -288,7 +289,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder))
|
||||
self.presentationDataPromise = Promise(self.presentationData)
|
||||
|
||||
self.listNode = ListView()
|
||||
|
||||
@@ -316,7 +317,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
let previousStrings = strongSelf.presentationData.strings
|
||||
|
||||
strongSelf.presentationData = presentationData
|
||||
strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder)))
|
||||
strongSelf.presentationDataPromise.set(.single(presentationData))
|
||||
|
||||
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
|
||||
strongSelf.updateThemeAndStrings()
|
||||
@@ -327,7 +328,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
let account = self.context.account
|
||||
let selectionStateSignal = self.selectionStatePromise.get()
|
||||
let transition: Signal<InviteContactsTransition, NoError>
|
||||
let themeAndStringsPromise = self.themeAndStringsPromise
|
||||
let presentationDataPromise = self.presentationDataPromise
|
||||
let previousEntries = Atomic<[InviteContactsEntry]?>(value: nil)
|
||||
|
||||
let interaction = InviteContactsInteraction(toggleContact: { [weak self] id in
|
||||
@@ -403,19 +404,19 @@ final class InviteContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
let processingQueue = Queue()
|
||||
transition = (combineLatest(.single(nil) |> then(sortedContacts), selectionStateSignal, themeAndStringsPromise.get(), .single(true) |> delay(0.2, queue: Queue.mainQueue()))
|
||||
|> mapToQueue { sortedContacts, selectionState, themeAndStrings, ready -> Signal<InviteContactsTransition, NoError> in
|
||||
transition = (combineLatest(.single(nil) |> then(sortedContacts), selectionStateSignal, presentationDataPromise.get(), .single(true) |> delay(0.2, queue: Queue.mainQueue()))
|
||||
|> mapToQueue { sortedContacts, selectionState, presentationData, ready -> Signal<InviteContactsTransition, NoError> in
|
||||
guard sortedContacts != nil || ready else {
|
||||
return .never()
|
||||
}
|
||||
|
||||
let signal = deferred { () -> Signal<InviteContactsTransition, NoError> in
|
||||
let entries = inviteContactsEntries(accountPeer: nil, sortedContacts: sortedContacts, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, interaction: interaction)
|
||||
let entries = inviteContactsEntries(accountPeer: nil, sortedContacts: sortedContacts, selectionState: selectionState, theme: presentationData.theme, strings: presentationData.strings, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, interaction: interaction)
|
||||
let previous = previousEntries.swap(entries)
|
||||
let previousContacts = currentSortedContacts.with { $0 }
|
||||
let crossfade = previous != nil && previousContacts == nil
|
||||
|
||||
return .single(preparedInviteContactsTransition(account: context.account, from: previous ?? [], to: entries, sortedContacts: sortedContacts, interaction: interaction, isLoading: sortedContacts == nil, firstTime: previous == nil, crossfade: crossfade))
|
||||
return .single(preparedInviteContactsTransition(account: context.account, presentationData: presentationData, from: previous ?? [], to: entries, sortedContacts: sortedContacts, interaction: interaction, isLoading: sortedContacts == nil, firstTime: previous == nil, crossfade: crossfade))
|
||||
}
|
||||
return signal
|
||||
|> runOn(processingQueue)
|
||||
|
||||
Reference in New Issue
Block a user