no message

This commit is contained in:
Peter 2018-09-10 18:00:01 +03:00
parent acd863f780
commit c6cf803dc5
36 changed files with 403 additions and 247 deletions

View File

@ -189,6 +189,7 @@
D0642EFC1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */; }; D0642EFC1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */; };
D064EF871F69A06F00AC0398 /* MessageContentKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D064EF861F69A06F00AC0398 /* MessageContentKind.swift */; }; D064EF871F69A06F00AC0398 /* MessageContentKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = D064EF861F69A06F00AC0398 /* MessageContentKind.swift */; };
D0671F232143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0671F222143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift */; }; D0671F232143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0671F222143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift */; };
D0671F2D2145AB28000A8AE7 /* LegacyAvatarPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */; };
D067B4A5211C911C00796039 /* LegacyChannelIntroController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */; }; D067B4A5211C911C00796039 /* LegacyChannelIntroController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */; };
D067B4AA211C916300796039 /* TGChannelIntroController.h in Headers */ = {isa = PBXBuildFile; fileRef = D067B4A6211C916200796039 /* TGChannelIntroController.h */; }; D067B4AA211C916300796039 /* TGChannelIntroController.h in Headers */ = {isa = PBXBuildFile; fileRef = D067B4A6211C916200796039 /* TGChannelIntroController.h */; };
D067B4AD211C916300796039 /* TGChannelIntroController.m in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A9211C916200796039 /* TGChannelIntroController.m */; }; D067B4AD211C916300796039 /* TGChannelIntroController.m in Sources */ = {isa = PBXBuildFile; fileRef = D067B4A9211C916200796039 /* TGChannelIntroController.m */; };
@ -1441,6 +1442,7 @@
D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationButtons.swift; sourceTree = "<group>"; }; D0642EFB1F3E1E7B00792790 /* ChatHistoryNavigationButtons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatHistoryNavigationButtons.swift; sourceTree = "<group>"; };
D064EF861F69A06F00AC0398 /* MessageContentKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContentKind.swift; sourceTree = "<group>"; }; D064EF861F69A06F00AC0398 /* MessageContentKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageContentKind.swift; sourceTree = "<group>"; };
D0671F222143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoStepVerificationEmptyItem.swift; sourceTree = "<group>"; }; D0671F222143BDA6000A8AE7 /* TwoStepVerificationEmptyItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoStepVerificationEmptyItem.swift; sourceTree = "<group>"; };
D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAvatarPicker.swift; sourceTree = "<group>"; };
D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyChannelIntroController.swift; sourceTree = "<group>"; }; D067B4A4211C911C00796039 /* LegacyChannelIntroController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyChannelIntroController.swift; sourceTree = "<group>"; };
D067B4A6211C916200796039 /* TGChannelIntroController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGChannelIntroController.h; sourceTree = "<group>"; }; D067B4A6211C916200796039 /* TGChannelIntroController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGChannelIntroController.h; sourceTree = "<group>"; };
D067B4A9211C916200796039 /* TGChannelIntroController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGChannelIntroController.m; sourceTree = "<group>"; }; D067B4A9211C916200796039 /* TGChannelIntroController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGChannelIntroController.m; sourceTree = "<group>"; };
@ -2869,6 +2871,7 @@
D023ED2D1DDB5BEC00BD496D /* LegacyAttachmentMenu.swift */, D023ED2D1DDB5BEC00BD496D /* LegacyAttachmentMenu.swift */,
D0119CCF20CAE75F00895300 /* LegacySecureIdAttachmentMenu.swift */, D0119CCF20CAE75F00895300 /* LegacySecureIdAttachmentMenu.swift */,
D023EBB11DDA800700BD496D /* LegacyMediaPickers.swift */, D023EBB11DDA800700BD496D /* LegacyMediaPickers.swift */,
D0671F2C2145AB28000A8AE7 /* LegacyAvatarPicker.swift */,
D00E15251DDBD4E700ACF65C /* LegacyCamera.swift */, D00E15251DDBD4E700ACF65C /* LegacyCamera.swift */,
D023ED2F1DDB605D00BD496D /* LegacyEmptyController.swift */, D023ED2F1DDB605D00BD496D /* LegacyEmptyController.swift */,
D023ED311DDB60CF00BD496D /* LegacyNavigationController.swift */, D023ED311DDB60CF00BD496D /* LegacyNavigationController.swift */,
@ -4973,6 +4976,7 @@
D0DE66061F9A51E200EF4AE9 /* GalleryHiddenMediaManager.swift in Sources */, D0DE66061F9A51E200EF4AE9 /* GalleryHiddenMediaManager.swift in Sources */,
D0EC6D5C1EB9F58800EBF1C3 /* ListMessageFileItemNode.swift in Sources */, D0EC6D5C1EB9F58800EBF1C3 /* ListMessageFileItemNode.swift in Sources */,
D0471B561EFDB40F0074D609 /* BotCheckoutActionButton.swift in Sources */, D0471B561EFDB40F0074D609 /* BotCheckoutActionButton.swift in Sources */,
D0671F2D2145AB28000A8AE7 /* LegacyAvatarPicker.swift in Sources */,
D0EC6D5D1EB9F58800EBF1C3 /* ListMessageSnippetItemNode.swift in Sources */, D0EC6D5D1EB9F58800EBF1C3 /* ListMessageSnippetItemNode.swift in Sources */,
D0EC6D5E1EB9F58800EBF1C3 /* ListMessageHoleItem.swift in Sources */, D0EC6D5E1EB9F58800EBF1C3 /* ListMessageHoleItem.swift in Sources */,
D0EC6D5F1EB9F58800EBF1C3 /* GridMessageItem.swift in Sources */, D0EC6D5F1EB9F58800EBF1C3 /* GridMessageItem.swift in Sources */,

View File

@ -48,45 +48,15 @@ final class AuthorizationSequenceSignUpController: ViewController {
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil) let currentAvatarMixin = Atomic<NSObject?>(value: nil)
self.displayNode = AuthorizationSequenceSignUpControllerNode(theme: self.theme, strings: self.strings, addPhoto: { [weak self] in self.displayNode = AuthorizationSequenceSignUpControllerNode(theme: self.theme, strings: self.strings, addPhoto: { [weak self] in
guard let strongSelf = self else { presentLegacyAvatarPicker(holder: currentAvatarMixin, signup: true, theme: defaultPresentationTheme, present: { c, a in
return self?.view.endEditing(true)
} self?.present(c, in: .window(.root), with: a)
let legacyController = LegacyController(presentation: .custom, theme: defaultPresentationTheme) }, completion: { image in
legacyController.statusBar.statusBarStyle = .Ignore self?.controllerNode.currentPhoto = image
})
let emptyController = LegacyEmptyController(context: legacyController.context)!
let navigationController = makeLegacyNavigationController(rootController: emptyController)
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
legacyController.bind(controller: navigationController)
strongSelf.view.endEditing(true)
strongSelf.present(legacyController, in: .window(.root))
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: false, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false)!
let _ = currentAvatarMixin.swap(mixin)
mixin.didFinishWithImage = { image in
guard let strongSelf = self, let image = image else {
return
}
strongSelf.controllerNode.currentPhoto = image
}
/*mixin.didFinishWithDelete = {
}*/
mixin.didDismiss = { [weak legacyController] in
let _ = currentAvatarMixin.swap(nil)
legacyController?.dismiss()
}
let menuController = mixin.present()
if let menuController = menuController {
menuController.customRemoveFromParentViewController = { [weak legacyController] in
legacyController?.dismiss()
}
}
}) })
self.displayNodeDidLoad() self.displayNodeDidLoad()

View File

@ -75,7 +75,7 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
self.currentOptionNode = ASTextNode() self.currentOptionNode = ASTextNode()
self.currentOptionNode.isLayerBacked = true self.currentOptionNode.isLayerBacked = true
self.currentOptionNode.displaysAsynchronously = false self.currentOptionNode.displaysAsynchronously = false
self.currentOptionNode.attributedText = NSAttributedString(string: self.strings.Login_InfoHelp, font: Font.regular(16.0), textColor: theme.primaryColor, paragraphAlignment: .center) self.currentOptionNode.attributedText = NSAttributedString(string: self.strings.Login_InfoHelp, font: Font.regular(16.0), textColor: theme.textPlaceholderColor, paragraphAlignment: .center)
self.firstSeparatorNode = ASDisplayNode() self.firstSeparatorNode = ASDisplayNode()
self.firstSeparatorNode.isLayerBacked = true self.firstSeparatorNode.isLayerBacked = true
@ -91,6 +91,11 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
self.firstNameField.textField.textAlignment = .natural self.firstNameField.textField.textAlignment = .natural
self.firstNameField.textField.returnKeyType = .next self.firstNameField.textField.returnKeyType = .next
self.firstNameField.textField.attributedPlaceholder = NSAttributedString(string: self.strings.UserInfo_FirstNamePlaceholder, font: self.firstNameField.textField.font, textColor: self.theme.textPlaceholderColor) self.firstNameField.textField.attributedPlaceholder = NSAttributedString(string: self.strings.UserInfo_FirstNamePlaceholder, font: self.firstNameField.textField.font, textColor: self.theme.textPlaceholderColor)
self.firstNameField.textField.autocapitalizationType = .words
self.firstNameField.textField.autocorrectionType = .no
if #available(iOSApplicationExtension 10.0, *) {
self.firstNameField.textField.textContentType = .givenName
}
self.lastNameField = TextFieldNode() self.lastNameField = TextFieldNode()
self.lastNameField.textField.font = Font.regular(20.0) self.lastNameField.textField.font = Font.regular(20.0)
@ -98,6 +103,11 @@ final class AuthorizationSequenceSignUpControllerNode: ASDisplayNode, UITextFiel
self.lastNameField.textField.textAlignment = .natural self.lastNameField.textField.textAlignment = .natural
self.lastNameField.textField.returnKeyType = .done self.lastNameField.textField.returnKeyType = .done
self.lastNameField.textField.attributedPlaceholder = NSAttributedString(string: strings.UserInfo_LastNamePlaceholder, font: self.lastNameField.textField.font, textColor: self.theme.textPlaceholderColor) self.lastNameField.textField.attributedPlaceholder = NSAttributedString(string: strings.UserInfo_LastNamePlaceholder, font: self.lastNameField.textField.font, textColor: self.theme.textPlaceholderColor)
self.lastNameField.textField.autocapitalizationType = .words
self.lastNameField.textField.autocorrectionType = .no
if #available(iOSApplicationExtension 10.0, *) {
self.lastNameField.textField.textContentType = .familyName
}
self.currentPhotoNode = ASImageNode() self.currentPhotoNode = ASImageNode()
self.currentPhotoNode.isUserInteractionEnabled = false self.currentPhotoNode.isUserInteractionEnabled = false

View File

@ -87,10 +87,10 @@ private final class ChannelMembersSearchEntry: Comparable, Identifiable {
return lhs.index < rhs.index return lhs.index < rhs.index
} }
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ListViewItem { func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ListViewItem {
switch self.content { switch self.content {
case let .peer(peer): case let .peer(peer):
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: peer, chatPeer: peer), status: .none, enabled: true, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in
peerSelected(peer, nil) peerSelected(peer, nil)
}) })
case let .participant(participant, label, enabled): case let .participant(participant, label, enabled):
@ -100,7 +100,7 @@ private final class ChannelMembersSearchEntry: Comparable, Identifiable {
} else { } else {
status = .none status = .none
} }
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: status, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: participant.peer), status: status, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: self.section.chatListHeaderType.flatMap({ ChatListSearchItemHeader(type: $0, theme: theme, strings: strings, actionTitle: nil, action: nil) }), action: { _ in
peerSelected(participant.peer, participant) peerSelected(participant.peer, participant)
}) })
} }
@ -113,12 +113,12 @@ struct ChannelMembersSearchContainerTransition {
let isSearching: Bool let isSearching: Bool
} }
private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ChannelMembersSearchContainerTransition { private func channelMembersSearchContainerPreparedRecentTransition(from fromEntries: [ChannelMembersSearchEntry], to toEntries: [ChannelMembersSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerSelected: @escaping (Peer, RenderedChannelParticipant?) -> Void) -> ChannelMembersSearchContainerTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } 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, theme: theme, strings: strings, peerSelected: peerSelected), directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, peerSelected: peerSelected), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, peerSelected: peerSelected), directionHint: nil) }
return ChannelMembersSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching) return ChannelMembersSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
} }
@ -140,7 +140,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)> private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)>
init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) { init(account: Account, peerId: PeerId, mode: ChannelMembersSearchMode, filters: [ChannelMembersSearchFilter], openPeer: @escaping (Peer, RenderedChannelParticipant?) -> Void) {
self.account = account self.account = account
@ -148,7 +148,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
self.mode = mode self.mode = mode
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder))
self.dimNode = ASDisplayNode() self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5) self.dimNode.backgroundColor = UIColor.black.withAlphaComponent(0.5)
@ -359,7 +359,7 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
let previousEntries = previousSearchItems.swap(entries) let previousEntries = previousSearchItems.swap(entries)
let firstTime = previousEntries == nil let firstTime = previousEntries == nil
let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, peerSelected: openPeer) let transition = channelMembersSearchContainerPreparedRecentTransition(from: previousEntries ?? [], to: entries ?? [], isSearching: entries != nil, account: account, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, peerSelected: openPeer)
strongSelf.enqueueTransition(transition, firstTime: firstTime) strongSelf.enqueueTransition(transition, firstTime: firstTime)
} }
})) }))

View File

@ -58,7 +58,7 @@ final class ChannelMembersSearchController: ViewController {
} }
override func loadDisplayNode() { override func loadDisplayNode() {
self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, peerId: self.peerId, mode: self.mode, filters: self.filters) self.displayNode = ChannelMembersSearchControllerNode(account: self.account, theme: self.presentationData.theme, strings: self.presentationData.strings, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, peerId: self.peerId, mode: self.mode, filters: self.filters)
self.controllerNode.navigationBar = self.navigationBar self.controllerNode.navigationBar = self.navigationBar
self.controllerNode.requestActivateSearch = { [weak self] in self.controllerNode.requestActivateSearch = { [weak self] in
self?.activateSearch() self?.activateSearch()

View File

@ -67,7 +67,7 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
} }
} }
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: ChannelMembersSearchInteraction) -> ListViewItem { func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchInteraction) -> ListViewItem {
switch self { switch self {
case .search: case .search:
return ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
@ -80,7 +80,7 @@ private enum ChannelMembersSearchEntry: Comparable, Identifiable {
} else { } else {
status = .none status = .none
} }
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: nil, action: { _ in return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .peer, peer: .peer(peer: participant.peer, chatPeer: nil), status: status, enabled: enabled, selection: .none, editing: editing, index: nil, header: nil, action: { _ in
interaction.openPeer(participant.peer, participant) interaction.openPeer(participant.peer, participant)
}) })
} }
@ -94,12 +94,12 @@ private struct ChannelMembersSearchTransition {
let initial: Bool let initial: Bool
} }
private func preparedTransition(from fromEntries: [ChannelMembersSearchEntry]?, to toEntries: [ChannelMembersSearchEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, interaction: ChannelMembersSearchInteraction) -> ChannelMembersSearchTransition { private func preparedTransition(from fromEntries: [ChannelMembersSearchEntry]?, to toEntries: [ChannelMembersSearchEntry], account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: ChannelMembersSearchInteraction) -> ChannelMembersSearchTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries ?? [], rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries ?? [], rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } 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, theme: theme, strings: strings, interaction: interaction), directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, interaction: interaction), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction), directionHint: nil) }
return ChannelMembersSearchTransition(deletions: deletions, insertions: insertions, updates: updates, initial: fromEntries == nil) return ChannelMembersSearchTransition(deletions: deletions, insertions: insertions, updates: updates, initial: fromEntries == nil)
} }
@ -127,7 +127,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
private var disposable: Disposable? private var disposable: Disposable?
private var listControl: PeerChannelMemberCategoryControl? private var listControl: PeerChannelMemberCategoryControl?
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter]) { init(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, peerId: PeerId, mode: ChannelMembersSearchControllerMode, filters: [ChannelMembersSearchFilter]) {
self.account = account self.account = account
self.listNode = ListView() self.listNode = ListView()
self.peerId = peerId self.peerId = peerId
@ -203,7 +203,7 @@ class ChannelMembersSearchControllerNode: ASDisplayNode {
let previous = previousEntries.swap(entries) let previous = previousEntries.swap(entries)
strongSelf.enqueueTransition(preparedTransition(from: previous, to: entries, account: account, theme: theme, strings: strings, interaction: interaction)) strongSelf.enqueueTransition(preparedTransition(from: previous, to: entries, account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, interaction: interaction))
}) })
self.disposable = disposable self.disposable = disposable
self.listControl = loadMoreControl self.listControl = loadMoreControl

View File

@ -253,12 +253,12 @@ public class ChatListController: TelegramController, UIViewControllerPreviewingD
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
if self.isNodeLoaded { if self.isNodeLoaded {
self.chatListDisplayNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat) self.chatListDisplayNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder)
} }
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = ChatListControllerNode(account: self.account, groupId: self.groupId, controlsHistoryPreload: self.controlsHistoryPreload, theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat) self.displayNode = ChatListControllerNode(account: self.account, groupId: self.groupId, controlsHistoryPreload: self.controlsHistoryPreload, theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder)
self.chatListDisplayNode.navigationBar = self.navigationBar self.chatListDisplayNode.navigationBar = self.navigationBar

View File

@ -23,10 +23,10 @@ class ChatListControllerNode: ASDisplayNode {
var themeAndStrings: (PresentationTheme, PresentationStrings, timeFormat: PresentationTimeFormat) var themeAndStrings: (PresentationTheme, PresentationStrings, timeFormat: PresentationTimeFormat)
init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) { init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) {
self.account = account self.account = account
self.groupId = groupId self.groupId = groupId
self.chatListNode = ChatListNode(account: account, groupId: groupId, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: theme, strings: strings, timeFormat: timeFormat) self.chatListNode = ChatListNode(account: account, groupId: groupId, controlsHistoryPreload: controlsHistoryPreload, mode: .chatList, theme: theme, strings: strings, timeFormat: timeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder)
self.themeAndStrings = (theme, strings, timeFormat) self.themeAndStrings = (theme, strings, timeFormat)
@ -61,11 +61,11 @@ class ChatListControllerNode: ASDisplayNode {
} }
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) {
self.themeAndStrings = (theme, strings, timeFormat) self.themeAndStrings = (theme, strings, timeFormat)
self.backgroundColor = theme.chatList.backgroundColor self.backgroundColor = theme.chatList.backgroundColor
self.chatListNode.updateThemeAndStrings(theme: theme, strings: strings, timeFormat: timeFormat) self.chatListNode.updateThemeAndStrings(theme: theme, strings: strings, timeFormat: timeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder)
self.searchDisplayController?.updateThemeAndStrings(theme: theme, strings: strings) self.searchDisplayController?.updateThemeAndStrings(theme: theme, strings: strings)
self.chatListEmptyNode?.updateThemeAndStrings(theme: theme, strings: strings) self.chatListEmptyNode?.updateThemeAndStrings(theme: theme, strings: strings)
} }

View File

@ -36,7 +36,7 @@ final class ChatListEmptyNode: ASDisplayNode {
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size self.validLayout = size
let textSize = self.textNode.updateLayout(size) let textSize = self.textNode.updateLayout(CGSize(width: size.width - 40.0, height: size.height))
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize)) transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize))
} }
} }

View File

@ -168,7 +168,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
enabled = false enabled = false
} }
} }
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
nodeInteraction.peerSelected(chatPeer) nodeInteraction.peerSelected(chatPeer)
} }
@ -229,7 +229,7 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode
enabled = false enabled = false
} }
} }
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(theme: presentationData.theme, strings: presentationData.strings, sortOrder: presentationData.nameSortOrder, displayOrder: presentationData.nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: itemPeer, chatPeer: chatPeer), status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { _ in
if let chatPeer = chatPeer { if let chatPeer = chatPeer {
nodeInteraction.peerSelected(chatPeer) nodeInteraction.peerSelected(chatPeer)
} }
@ -333,12 +333,12 @@ final class ChatListNode: ListView {
var isEmptyUpdated: ((Bool) -> Void)? var isEmptyUpdated: ((Bool) -> Void)?
private var wasEmpty: Bool? private var wasEmpty: Bool?
init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) { init(account: Account, groupId: PeerGroupId?, controlsHistoryPreload: Bool, mode: ChatListNodeMode, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) {
self.account = account self.account = account
self.controlsHistoryPreload = controlsHistoryPreload self.controlsHistoryPreload = controlsHistoryPreload
self.mode = mode self.mode = mode
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, timeFormat: timeFormat), editing: false, peerIdWithRevealedOptions: nil, peerInputActivities: nil) self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, timeFormat: timeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder), editing: false, peerIdWithRevealedOptions: nil, peerInputActivities: nil)
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true) self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
self.theme = theme self.theme = theme
@ -766,7 +766,7 @@ final class ChatListNode: ListView {
self.activityStatusesDisposable?.dispose() self.activityStatusesDisposable?.dispose()
} }
func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) { func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) {
if theme !== self.currentState.presentationData.theme || strings !== self.currentState.presentationData.strings || timeFormat != self.currentState.presentationData.timeFormat { if theme !== self.currentState.presentationData.theme || strings !== self.currentState.presentationData.strings || timeFormat != self.currentState.presentationData.timeFormat {
self.theme = theme self.theme = theme
@ -775,7 +775,7 @@ final class ChatListNode: ListView {
} }
self.updateState { self.updateState {
return $0.withUpdatedPresentationData(ChatListPresentationData(theme: theme, strings: strings, timeFormat: timeFormat)) return $0.withUpdatedPresentationData(ChatListPresentationData(theme: theme, strings: strings, timeFormat: timeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder))
} }
} }
} }

View File

@ -4,10 +4,14 @@ final class ChatListPresentationData {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let timeFormat: PresentationTimeFormat let timeFormat: PresentationTimeFormat
let nameSortOrder: PresentationPersonNameOrder
let nameDisplayOrder: PresentationPersonNameOrder
init(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat) { init(theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.timeFormat = timeFormat self.timeFormat = timeFormat
self.nameSortOrder = nameSortOrder
self.nameDisplayOrder = nameDisplayOrder
} }
} }

View File

@ -38,13 +38,13 @@ private enum ChatListRecentEntryStableId: Hashable {
private enum ChatListRecentEntry: Comparable, Identifiable { private enum ChatListRecentEntry: Comparable, Identifiable {
case topPeers([Peer], PresentationTheme, PresentationStrings) case topPeers([Peer], PresentationTheme, PresentationStrings)
case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationTimeFormat, Bool) case peer(index: Int, peer: RecentlySearchedPeer, PresentationTheme, PresentationStrings, PresentationTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool)
var stableId: ChatListRecentEntryStableId { var stableId: ChatListRecentEntryStableId {
switch self { switch self {
case .topPeers: case .topPeers:
return .topPeers return .topPeers
case let .peer(_, peer, _, _, _, _): case let .peer(_, peer, _, _, _, _, _, _):
return .peerId(peer.peer.peerId) return .peerId(peer.peer.peerId)
} }
} }
@ -71,8 +71,8 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsHasRevealControls): case let .peer(lhsIndex, lhsPeer, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsHasRevealControls):
if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsHasRevealControls) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsHasRevealControls == rhsHasRevealControls { if case let .peer(rhsIndex, rhsPeer, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsHasRevealControls) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings && lhsTimeFormat == rhsTimeFormat && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsHasRevealControls == rhsHasRevealControls {
return true return true
} else { } else {
return false return false
@ -84,11 +84,11 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
switch lhs { switch lhs {
case .topPeers: case .topPeers:
return true return true
case let .peer(lhsIndex, _, _, _, _, _): case let .peer(lhsIndex, _, _, _, _, _, _, _):
switch rhs { switch rhs {
case .topPeers: case .topPeers:
return false return false
case let .peer(rhsIndex, _, _, _, _, _): case let .peer(rhsIndex, _, _, _, _, _, _, _):
return lhsIndex <= rhsIndex return lhsIndex <= rhsIndex
} }
} }
@ -102,7 +102,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
}, peerLongTapped: { peer in }, peerLongTapped: { peer in
peerLongTapped(peer) peerLongTapped(peer)
}) })
case let .peer(_, peer, theme, strings, timeFormat, hasRevealControls): case let .peer(_, peer, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder, hasRevealControls):
let primaryPeer: Peer let primaryPeer: Peer
var chatPeer: Peer? var chatPeer: Peer?
let maybeChatPeer = peer.peer.peers[peer.peer.peerId]! let maybeChatPeer = peer.peer.peers[peer.peer.peerId]!
@ -180,7 +180,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active) badge = ContactsPeerItemBadge(count: peer.unreadCount, type: isMuted ? .inactive : .active)
} }
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: status, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: true, editing: false, revealed: hasRevealControls), index: nil, header: ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear.uppercased(), action: { return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: status, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: true, editing: false, revealed: hasRevealControls), index: nil, header: ChatListSearchItemHeader(type: .recentPeers, theme: theme, strings: strings, actionTitle: strings.WebSearch_RecentSectionClear.uppercased(), action: {
clearRecentlySearchedPeers() clearRecentlySearchedPeers()
}), action: { _ in }), action: { _ in
if let chatPeer = peer.peer.peers[peer.peer.peerId] { if let chatPeer = peer.peer.peers[peer.peer.peerId] {
@ -233,15 +233,15 @@ enum ChatListSearchEntryStableId: Hashable {
enum ChatListSearchEntry: Comparable, Identifiable { enum ChatListSearchEntry: Comparable, Identifiable {
case localPeer(Peer, Peer?, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings) case localPeer(Peer, Peer?, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)
case globalPeer(FoundPeer, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings) case globalPeer(FoundPeer, UnreadSearchBadge?, Int, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)
case message(Message, CombinedPeerReadState?, ChatListPresentationData) case message(Message, CombinedPeerReadState?, ChatListPresentationData)
var stableId: ChatListSearchEntryStableId { var stableId: ChatListSearchEntryStableId {
switch self { switch self {
case let .localPeer(peer, _, _, _, _, _): case let .localPeer(peer, _, _, _, _, _, _, _):
return .localPeerId(peer.id) return .localPeerId(peer.id)
case let .globalPeer(peer, _, _, _, _): case let .globalPeer(peer, _, _, _, _, _, _):
return .globalPeerId(peer.peer.id) return .globalPeerId(peer.peer.id)
case let .message(message, _, _): case let .message(message, _, _):
return .messageId(message.id) return .messageId(message.id)
@ -250,14 +250,14 @@ enum ChatListSearchEntry: Comparable, Identifiable {
static func ==(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool { static func ==(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
switch lhs { switch lhs {
case let .localPeer(lhsPeer, lhsAssociatedPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings): case let .localPeer(lhsPeer, lhsAssociatedPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder):
if case let .localPeer(rhsPeer, rhsAssociatedPeer, rhsUnreadBasge, rhsIndex, rhsTheme, rhsStrings) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsUnreadBadge == rhsUnreadBasge { if case let .localPeer(rhsPeer, rhsAssociatedPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer.isEqual(rhsPeer) && arePeersEqual(lhsAssociatedPeer, rhsAssociatedPeer) && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge == rhsUnreadBadge {
return true return true
} else { } else {
return false return false
} }
case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings): case let .globalPeer(lhsPeer, lhsUnreadBadge, lhsIndex, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder):
if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsUnreadBadge == rhsUnreadBadge { if case let .globalPeer(rhsPeer, rhsUnreadBadge, rhsIndex, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder) = rhs, lhsPeer == rhsPeer && lhsIndex == rhsIndex && lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsSortOrder == rhsSortOrder && lhsDisplayOrder == rhsDisplayOrder && lhsUnreadBadge == rhsUnreadBadge {
return true return true
} else { } else {
return false return false
@ -285,17 +285,17 @@ enum ChatListSearchEntry: Comparable, Identifiable {
static func <(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool { static func <(lhs: ChatListSearchEntry, rhs: ChatListSearchEntry) -> Bool {
switch lhs { switch lhs {
case let .localPeer(_, _, _, lhsIndex, _, _): case let .localPeer(_, _, _, lhsIndex, _, _, _, _):
if case let .localPeer(_, _, _, rhsIndex, _, _) = rhs { if case let .localPeer(_, _, _, rhsIndex, _, _, _, _) = rhs {
return lhsIndex <= rhsIndex return lhsIndex <= rhsIndex
} else { } else {
return true return true
} }
case let .globalPeer(_, _, lhsIndex, _, _): case let .globalPeer(_, _, lhsIndex, _, _, _, _):
switch rhs { switch rhs {
case .localPeer: case .localPeer:
return false return false
case let .globalPeer(_, _, rhsIndex, _, _): case let .globalPeer(_, _, rhsIndex, _, _, _, _):
return lhsIndex <= rhsIndex return lhsIndex <= rhsIndex
case .message: case .message:
return true return true
@ -311,7 +311,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
func item(account: Account, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction) -> ListViewItem { func item(account: Account, enableHeaders: Bool, filter: ChatListNodePeersFilter, interaction: ChatListNodeInteraction) -> ListViewItem {
switch self { switch self {
case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings): case let .localPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
let primaryPeer: Peer let primaryPeer: Peer
var chatPeer: Peer? var chatPeer: Peer?
if let associatedPeer = associatedPeer { if let associatedPeer = associatedPeer {
@ -356,10 +356,10 @@ enum ChatListSearchEntry: Comparable, Identifiable {
badge = ContactsPeerItemBadge(count: unreadBadge.count, type: unreadBadge.isMuted ? .inactive : .active) badge = ContactsPeerItemBadge(count: unreadBadge.count, type: unreadBadge.isMuted ? .inactive : .active)
} }
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .localPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: primaryPeer, chatPeer: chatPeer), status: .none, badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .localPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in
interaction.peerSelected(peer) interaction.peerSelected(peer)
}) })
case let .globalPeer(peer, unreadBadge, _, theme, strings): case let .globalPeer(peer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
var enabled = true var enabled = true
if filter.contains(.onlyWriteable) { if filter.contains(.onlyWriteable) {
enabled = canSendMessagesToPeer(peer.peer) enabled = canSendMessagesToPeer(peer.peer)
@ -393,7 +393,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
badge = ContactsPeerItemBadge(count: unreadBadge.count, type: unreadBadge.isMuted ? .inactive : .active) badge = ContactsPeerItemBadge(count: unreadBadge.count, type: unreadBadge.isMuted ? .inactive : .active)
} }
return ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in return ContactsPeerItem(theme: theme, strings: strings, sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, account: account, peerMode: .generalSearch, peer: .peer(peer: peer.peer, chatPeer: peer.peer), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil), action: { _ in
interaction.peerSelected(peer.peer) interaction.peerSelected(peer.peer)
}) })
case let .message(message, readState, presentationData): case let .message(message, readState, presentationData):
@ -506,7 +506,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
self.openMessage = openMessage self.openMessage = openMessage
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat)) self.presentationDataPromise = Promise(ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder))
self.recentListNode = ListView() self.recentListNode = ListView()
self.listNode = ListView() self.listNode = ListView()
@ -526,13 +526,15 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
let foundItems = searchQuery.get() let foundItems = searchQuery.get()
|> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in |> mapToSignal { query -> Signal<([ChatListSearchEntry], Bool)?, NoError> in
if let query = query, !query.isEmpty { if let query = query, !query.isEmpty {
let accountPeer = account.postbox.loadedPeerWithId(account.peerId) |> take(1) let accountPeer = account.postbox.loadedPeerWithId(account.peerId)
|> take(1)
let foundLocalPeers = account.postbox.searchPeers(query: query.lowercased(), groupId: groupId) |> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in let foundLocalPeers = account.postbox.searchPeers(query: query.lowercased(), groupId: groupId) |> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in
return combineLatest(local.map {account.postbox.peerView(id: $0.peerId)}) |> map { views in return combineLatest(local.map {account.postbox.peerView(id: $0.peerId)}) |> map { views in
return (views, local) return (views, local)
} }
} |> mapToSignal{ viewsAndPeers -> Signal<(peers: [RenderedPeer], unread: [PeerId : UnreadSearchBadge]), NoError> in }
|> mapToSignal{ viewsAndPeers -> Signal<(peers: [RenderedPeer], unread: [PeerId : UnreadSearchBadge]), NoError> in
return account.postbox.unreadMessageCountsView(items: viewsAndPeers.0.map {.peer($0.peerId)}) |> map { values in return account.postbox.unreadMessageCountsView(items: viewsAndPeers.0.map {.peer($0.peerId)}) |> map { values in
var unread:[PeerId: UnreadSearchBadge] = [:] var unread:[PeerId: UnreadSearchBadge] = [:]
for peerView in viewsAndPeers.0 { for peerView in viewsAndPeers.0 {
@ -554,8 +556,6 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
} }
} }
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError> let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
if groupId == nil { if groupId == nil {
foundRemotePeers = (.single(([], [], true)) |> then(searchPeers(account: account, query: query) |> map { ($0.0, $0.1, false) } foundRemotePeers = (.single(([], [], true)) |> then(searchPeers(account: account, query: query) |> map { ($0.0, $0.1, false) }
@ -570,8 +570,8 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
location = .general location = .general
} }
let foundRemoteMessages: Signal<(([Message], [PeerId : CombinedPeerReadState]), Bool), NoError> = .single((([], [:]), true)) |> then(searchMessages(account: account, location: location, query: query) let foundRemoteMessages: Signal<(([Message], [PeerId : CombinedPeerReadState]), Bool), NoError> = .single((([], [:]), true)) |> then(searchMessages(account: account, location: location, query: query)
|> map { ($0, false) } |> map { ($0, false) }
|> delay(0.2, queue: Queue.concurrentDefaultQueue())) |> delay(0.2, queue: Queue.concurrentDefaultQueue()))
return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get()) return combineLatest(accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationDataPromise.get())
|> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData -> ([ChatListSearchEntry], Bool)? in |> map { accountPeer, foundLocalPeers, foundRemotePeers, foundRemoteMessages, presentationData -> ([ChatListSearchEntry], Bool)? in
@ -584,7 +584,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(query.lowercased()) { if presentationData.strings.DialogList_SavedMessages.lowercased().hasPrefix(query.lowercased()) {
if !existingPeerIds.contains(accountPeer.id) { if !existingPeerIds.contains(accountPeer.id) {
existingPeerIds.insert(accountPeer.id) existingPeerIds.insert(accountPeer.id)
entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings)) entries.append(.localPeer(accountPeer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
index += 1 index += 1
} }
} }
@ -597,7 +597,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
if let associatedPeerId = peer.associatedPeerId { if let associatedPeerId = peer.associatedPeerId {
associatedPeer = renderedPeer.peers[associatedPeerId] associatedPeer = renderedPeer.peers[associatedPeerId]
} }
entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings)) entries.append(.localPeer(peer, associatedPeer, foundLocalPeers.unread[peer.id], index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
index += 1 index += 1
} }
} }
@ -606,7 +606,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
for peer in foundRemotePeers.0 { for peer in foundRemotePeers.0 {
if !existingPeerIds.contains(peer.peer.id) { if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id) existingPeerIds.insert(peer.peer.id)
entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings)) entries.append(.localPeer(peer.peer, nil, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
index += 1 index += 1
} }
} }
@ -615,7 +615,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
for peer in foundRemotePeers.1 { for peer in foundRemotePeers.1 {
if !existingPeerIds.contains(peer.peer.id) { if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id) existingPeerIds.insert(peer.peer.id)
entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings)) entries.append(.globalPeer(peer, nil, index, presentationData.theme, presentationData.strings, presentationData.nameSortOrder, presentationData.nameDisplayOrder))
index += 1 index += 1
} }
} }
@ -696,7 +696,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
} }
peerIds.insert(peer.id) peerIds.insert(peer.id)
entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.timeFormat, state.peerIdWithRevealedOptions == peer.id)) entries.append(.peer(index: index, peer: searchedPeer, presentationData.theme, presentationData.strings, presentationData.timeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder, state.peerIdWithRevealedOptions == peer.id))
index += 1 index += 1
} }
} }

View File

@ -34,7 +34,7 @@ final class ComposeControllerNode: ASDisplayNode {
var openCreateNewSecretChatImpl: (() -> Void)? var openCreateNewSecretChatImpl: (() -> Void)?
var openCreateNewChannelImpl: (() -> Void)? var openCreateNewChannelImpl: (() -> Void)?
self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, ordering: .lastFirst, options: [ self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, options: [
ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: presentationData.theme.list.itemAccentColor), action: { ContactListAdditionalOption(title: self.presentationData.strings.Compose_NewGroup, icon: generateTintedImage(image: UIImage(bundleImageName: "Contact List/CreateGroupActionIcon"), color: presentationData.theme.list.itemAccentColor), action: {
openCreateNewGroupImpl?() openCreateNewGroupImpl?()
}), }),

View File

@ -118,7 +118,7 @@ enum ContactListPeer: Equatable {
private enum ContactListNodeEntry: Comparable, Identifiable { private enum ContactListNodeEntry: Comparable, Identifiable {
case search(PresentationTheme, PresentationStrings) case search(PresentationTheme, PresentationStrings)
case option(Int, ContactListAdditionalOption, PresentationTheme, PresentationStrings) case option(Int, ContactListAdditionalOption, PresentationTheme, PresentationStrings)
case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationTimeFormat, Bool) case peer(Int, ContactListPeer, PeerPresence?, ListViewItemHeader?, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder, Bool)
var stableId: ContactListNodeEntryId { var stableId: ContactListNodeEntryId {
switch self { switch self {
@ -126,7 +126,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
return .search return .search
case let .option(index, _, _, _): case let .option(index, _, _, _):
return .option(index: index) return .option(index: index)
case let .peer(_, peer, _, _, _, _, _, _, _): case let .peer(_, peer, _, _, _, _, _, _, _, _, _):
switch peer { switch peer {
case let .peer(peer, _): case let .peer(peer, _):
return .peerId(peer.id.toInt64()) return .peerId(peer.id.toInt64())
@ -139,12 +139,12 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem { func item(account: Account, interaction: ContactListNodeInteraction) -> ListViewItem {
switch self { switch self {
case let .search(theme, strings): case let .search(theme, strings):
return ChatListSearchItem(theme: theme, placeholder: strings.Common_Search, activate: { return ChatListSearchItem(theme: theme, placeholder: strings.Contacts_SearchLabel, activate: {
interaction.activateSearch() interaction.activateSearch()
}) })
case let .option(_, option, theme, _): case let .option(_, option, theme, _):
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, action: option.action) return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, action: option.action)
case let .peer(_, peer, presence, header, selection, theme, strings, timeFormat, enabled): case let .peer(_, peer, presence, header, selection, theme, strings, timeFormat, nameSortOrder, nameDisplayOrder, enabled):
let status: ContactsPeerItemStatus let status: ContactsPeerItemStatus
let itemPeer: ContactsPeerItemPeer let itemPeer: ContactsPeerItemPeer
switch peer { switch peer {
@ -161,7 +161,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
status = .none status = .none
itemPeer = .deviceContact(stableId: id, contact: contact) itemPeer = .deviceContact(stableId: id, contact: contact)
} }
return ContactsPeerItem(theme: theme, strings: strings, 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(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
interaction.openPeer(peer) interaction.openPeer(peer)
}) })
} }
@ -181,9 +181,9 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsEnabled): case let .peer(lhsIndex, lhsPeer, lhsPresence, lhsHeader, lhsSelection, lhsTheme, lhsStrings, lhsTimeFormat, lhsSortOrder, lhsDisplayOrder, lhsEnabled):
switch rhs { switch rhs {
case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsEnabled): case let .peer(rhsIndex, rhsPeer, rhsPresence, rhsHeader, rhsSelection, rhsTheme, rhsStrings, rhsTimeFormat, rhsSortOrder, rhsDisplayOrder, rhsEnabled):
if lhsIndex != rhsIndex { if lhsIndex != rhsIndex {
return false return false
} }
@ -212,6 +212,12 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
if lhsTimeFormat != rhsTimeFormat { if lhsTimeFormat != rhsTimeFormat {
return false return false
} }
if lhsSortOrder != rhsSortOrder {
return false
}
if lhsDisplayOrder != rhsDisplayOrder {
return false
}
if lhsEnabled != rhsEnabled { if lhsEnabled != rhsEnabled {
return false return false
} }
@ -235,11 +241,11 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
case .peer: case .peer:
return true return true
} }
case let .peer(lhsIndex, _, _, _, _, _, _, _, _): case let .peer(lhsIndex, _, _, _, _, _, _, _, _, _, _):
switch rhs { switch rhs {
case .search, .option: case .search, .option:
return false return false
case let .peer(rhsIndex, _, _, _, _, _, _, _, _): case let .peer(rhsIndex, _, _, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex return lhsIndex < rhsIndex
} }
} }
@ -247,7 +253,7 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
} }
private extension PeerIndexNameRepresentation { private extension PeerIndexNameRepresentation {
func isLessThan(other: PeerIndexNameRepresentation, ordering: ContactNameOrdering) -> ComparisonResult { func isLessThan(other: PeerIndexNameRepresentation, ordering: PresentationPersonNameOrder) -> ComparisonResult {
switch self { switch self {
case let .title(lhsTitle, _): case let .title(lhsTitle, _):
switch other { switch other {
@ -292,7 +298,7 @@ private extension PeerIndexNameRepresentation {
} }
} }
private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, disabledPeerIds:Set<PeerId>) -> [ContactListNodeEntry] { private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer], presences: [PeerId: PeerPresence], presentation: ContactListPresentation, selectionState: ContactListNodeGroupSelectionState?, theme: PresentationTheme, strings: PresentationStrings, timeFormat: PresentationTimeFormat, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, disabledPeerIds:Set<PeerId>) -> [ContactListNodeEntry] {
var entries: [ContactListNodeEntry] = [] var entries: [ContactListNodeEntry] = []
var orderedPeers: [ContactListPeer] var orderedPeers: [ContactListPeer]
@ -326,9 +332,9 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
for i in 0 ..< options.count { for i in 0 ..< options.count {
entries.append(.option(i, options[i], theme, strings)) entries.append(.option(i, options[i], theme, strings))
} }
case let .natural(displaySearch, ordering, options): case let .natural(displaySearch, options):
orderedPeers = peers.sorted(by: { lhs, rhs in orderedPeers = peers.sorted(by: { lhs, rhs in
let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: ordering) let result = lhs.indexName.isLessThan(other: rhs.indexName, ordering: sortOrder)
if result == .orderedSame { if result == .orderedSame {
if case let .peer(lhsPeer, _) = lhs, case let .peer(rhsPeer, _) = rhs { if case let .peer(lhsPeer, _) = lhs, case let .peer(rhsPeer, _) = rhs {
return lhsPeer.id < rhsPeer.id return lhsPeer.id < rhsPeer.id
@ -352,10 +358,19 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
indexHeader = c indexHeader = c
} }
case let .personName(first, last, _, _): case let .personName(first, last, _, _):
if let c = last.utf16.first { switch sortOrder {
indexHeader = c case .firstLast:
} else if let c = first.utf16.first { if let c = first.utf16.first {
indexHeader = c indexHeader = c
} else if let c = last.utf16.first {
indexHeader = c
}
case .lastFirst:
if let c = last.utf16.first {
indexHeader = c
} else if let c = first.utf16.first {
indexHeader = c
}
} }
} }
let header: ContactListNameIndexHeader let header: ContactListNameIndexHeader
@ -424,12 +439,12 @@ private func contactListNodeEntries(accountPeer: Peer?, peers: [ContactListPeer]
} }
let enabled: Bool let enabled: Bool
switch orderedPeers[i] { switch orderedPeers[i] {
case let .peer(peer, _): case let .peer(peer, _):
enabled = !disabledPeerIds.contains(peer.id) enabled = !disabledPeerIds.contains(peer.id)
default: default:
enabled = true enabled = true
} }
entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, timeFormat, enabled)) entries.append(.peer(i, orderedPeers[i], presence, header, selection, theme, strings, timeFormat, sortOrder, displayOrder, enabled))
} }
return entries return entries
} }
@ -462,14 +477,9 @@ public struct ContactListAdditionalOption: Equatable {
} }
} }
enum ContactNameOrdering {
case firstLast
case lastFirst
}
enum ContactListPresentation { enum ContactListPresentation {
case orderedByPresence(options: [ContactListAdditionalOption]) case orderedByPresence(options: [ContactListAdditionalOption])
case natural(displaySearch: Bool, ordering: ContactNameOrdering, options: [ContactListAdditionalOption]) case natural(displaySearch: Bool, options: [ContactListAdditionalOption])
case search(signal: Signal<String, NoError>, searchDeviceContacts: Bool) case search(signal: Signal<String, NoError>, searchDeviceContacts: Bool)
} }
@ -556,7 +566,7 @@ final class ContactListNode: ASDisplayNode {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationTimeFormat)> private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationTimeFormat, PresentationPersonNameOrder, PresentationPersonNameOrder)>
init(account: Account, presentation: ContactListPresentation, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) { init(account: Account, presentation: ContactListPresentation, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil) {
self.account = account self.account = account
@ -567,7 +577,7 @@ final class ContactListNode: ASDisplayNode {
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.timeFormat)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.timeFormat, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder))
super.init() super.init()
@ -670,7 +680,7 @@ final class ContactListNode: ASDisplayNode {
peers.append(.deviceContact(stableId, contact)) peers.append(.deviceContact(stableId, contact))
} }
let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: [:], presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, disabledPeerIds: disabledPeerIds) let entries = contactListNodeEntries(accountPeer: nil, peers: peers, presences: [:], presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds)
let previous = previousEntries.swap(entries) let previous = previousEntries.swap(entries)
return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, animated: false)) return .single(preparedContactListNodeTransition(account: account, from: previous ?? [], to: entries, interaction: interaction, firstTime: previous == nil, animated: false))
} }
@ -710,7 +720,7 @@ final class ContactListNode: ASDisplayNode {
} }
} }
let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, disabledPeerIds: disabledPeerIds) let entries = contactListNodeEntries(accountPeer: view.accountPeer, peers: peers, presences: view.peerPresences, presentation: presentation, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, timeFormat: themeAndStrings.2, sortOrder: themeAndStrings.3, displayOrder: themeAndStrings.4, disabledPeerIds: disabledPeerIds)
let previous = previousEntries.swap(entries) let previous = previousEntries.swap(entries)
let animated: Bool let animated: Bool
if let previous = previous { if let previous = previous {
@ -743,7 +753,7 @@ final class ContactListNode: ASDisplayNode {
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor strongSelf.backgroundColor = presentationData.theme.chatList.backgroundColor
strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.timeFormat))) strongSelf.themeAndStringsPromise.set(.single((presentationData.theme, presentationData.strings, presentationData.timeFormat, presentationData.nameSortOrder, presentationData.nameDisplayOrder)))
strongSelf.listNode.forEachAccessoryItemNode({ accessoryItemNode in strongSelf.listNode.forEachAccessoryItemNode({ accessoryItemNode in
if let accessoryItemNode = accessoryItemNode as? ContactsSectionHeaderAccessoryItemNode { if let accessoryItemNode = accessoryItemNode as? ContactsSectionHeaderAccessoryItemNode {

View File

@ -48,7 +48,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
self.account = account self.account = account
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, ordering: .lastFirst, options: options), filters: filters, selectionState: ContactListNodeGroupSelectionState()) self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: false, options: options), filters: filters, selectionState: ContactListNodeGroupSelectionState())
self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: self.presentationData.strings.Compose_TokenListPlaceholder) self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: self.presentationData.theme.rootController.navigationBar.backgroundColor, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.chatList.searchBarKeyboardColor), placeholder: self.presentationData.strings.Compose_TokenListPlaceholder)
super.init() super.init()

View File

@ -36,14 +36,13 @@ final class ContactSelectionControllerNode: ASDisplayNode {
init(account: Account, options: [ContactListAdditionalOption], displayDeviceContacts: Bool) { init(account: Account, options: [ContactListAdditionalOption], displayDeviceContacts: Bool) {
self.account = account self.account = account
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.displayDeviceContacts = displayDeviceContacts self.displayDeviceContacts = displayDeviceContacts
self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, ordering: .lastFirst, options: options)) self.contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, options: options))
self.dimNode = ASDisplayNode() self.dimNode = ASDisplayNode()
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
super.init() super.init()
self.setViewBlock({ self.setViewBlock({

View File

@ -108,6 +108,8 @@ enum ContactsPeerItemPeer: Equatable {
class ContactsPeerItem: ListViewItem { class ContactsPeerItem: ListViewItem {
let theme: PresentationTheme let theme: PresentationTheme
let strings: PresentationStrings let strings: PresentationStrings
let sortOrder: PresentationPersonNameOrder
let displayOrder: PresentationPersonNameOrder
let account: Account let account: Account
let peerMode: ContactsPeerItemPeerMode let peerMode: ContactsPeerItemPeerMode
let peer: ContactsPeerItemPeer let peer: ContactsPeerItemPeer
@ -126,9 +128,11 @@ class ContactsPeerItem: ListViewItem {
let header: ListViewItemHeader? let header: ListViewItemHeader?
init(theme: PresentationTheme, strings: PresentationStrings, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) { init(theme: PresentationTheme, strings: PresentationStrings, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, account: Account, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, editing: ContactsPeerItemEditing, index: PeerNameIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, setPeerIdWithRevealedOptions: ((PeerId?, PeerId?) -> Void)? = nil, deletePeer: ((PeerId) -> Void)? = nil) {
self.theme = theme self.theme = theme
self.strings = strings self.strings = strings
self.sortOrder = sortOrder
self.displayOrder = displayOrder
self.account = account self.account = account
self.peerMode = peerMode self.peerMode = peerMode
self.peer = peer self.peer = peer
@ -444,9 +448,9 @@ class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
titleAttributedString = NSAttributedString(string: item.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor) titleAttributedString = NSAttributedString(string: item.strings.DialogList_SavedMessages, font: titleBoldFont, textColor: textColor)
} else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty { } else if let firstName = user.firstName, let lastName = user.lastName, !firstName.isEmpty, !lastName.isEmpty {
let string = NSMutableAttributedString() let string = NSMutableAttributedString()
string.append(NSAttributedString(string: firstName, font: titleFont, textColor: textColor)) string.append(NSAttributedString(string: firstName, font: item.sortOrder == .firstLast ? titleBoldFont : titleFont, textColor: textColor))
string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor)) string.append(NSAttributedString(string: " ", font: titleFont, textColor: textColor))
string.append(NSAttributedString(string: lastName, font: titleBoldFont, textColor: textColor)) string.append(NSAttributedString(string: lastName, font: item.sortOrder == .firstLast ? titleFont : titleBoldFont, textColor: textColor))
titleAttributedString = string titleAttributedString = string
} else if let firstName = user.firstName, !firstName.isEmpty { } else if let firstName = user.firstName, !firstName.isEmpty {
titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: textColor) titleAttributedString = NSAttributedString(string: firstName, font: titleBoldFont, textColor: textColor)

View File

@ -41,7 +41,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
return lhs.index < rhs.index return lhs.index < rhs.index
} }
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, openPeer: @escaping (ContactListPeer) -> Void) -> ListViewItem { func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, openPeer: @escaping (ContactListPeer) -> Void) -> ListViewItem {
let header: ListViewItemHeader let header: ListViewItemHeader
let status: ContactsPeerItemStatus let status: ContactsPeerItemStatus
switch self.group { switch self.group {
@ -50,7 +50,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
status = .none status = .none
case .global: case .global:
header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil) header = ChatListSearchItemHeader(type: .globalPeers, theme: theme, strings: strings, actionTitle: nil, action: nil)
if case let .peer(peer, _) = self.peer, let addressName = peer.addressName { if case let .peer(peer, _) = self.peer, let _ = peer.addressName {
status = .addressName("") status = .addressName("")
} else { } else {
status = .none status = .none
@ -67,7 +67,7 @@ private struct ContactListSearchEntry: Identifiable, Comparable {
case let .deviceContact(stableId, contact): case let .deviceContact(stableId, contact):
peerItem = .deviceContact(stableId: stableId, contact: contact) peerItem = .deviceContact(stableId: stableId, contact: contact)
} }
return ContactsPeerItem(theme: theme, strings: strings, 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(theme: theme, strings: 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
openPeer(peer) openPeer(peer)
}) })
} }
@ -80,12 +80,12 @@ struct ContactListSearchContainerTransition {
let isSearching: Bool let isSearching: Bool
} }
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, openPeer: @escaping (ContactListPeer) -> Void) -> ContactListSearchContainerTransition { private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, account: Account, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, openPeer: @escaping (ContactListPeer) -> Void) -> ContactListSearchContainerTransition {
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } 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, theme: theme, strings: strings, openPeer: openPeer), directionHint: nil) } let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, openPeer: openPeer), directionHint: nil) }
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, openPeer: openPeer), directionHint: nil) } let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: theme, strings: strings, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, openPeer: openPeer), directionHint: nil) }
return ContactListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching) return ContactListSearchContainerTransition(deletions: deletions, insertions: insertions, updates: updates, isSearching: isSearching)
} }
@ -176,12 +176,12 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
var disabledPeerIds = Set<PeerId>() var disabledPeerIds = Set<PeerId>()
for filter in filters { for filter in filters {
switch filter { switch filter {
case .excludeSelf: case .excludeSelf:
existingPeerIds.insert(account.peerId) existingPeerIds.insert(account.peerId)
case let .exclude(peerIds): case let .exclude(peerIds):
existingPeerIds = existingPeerIds.union(peerIds) existingPeerIds = existingPeerIds.union(peerIds)
case let .disable(peerIds): case let .disable(peerIds):
disabledPeerIds = disabledPeerIds.union(peerIds) disabledPeerIds = disabledPeerIds.union(peerIds)
} }
} }
var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>() var existingNormalizedPhoneNumbers = Set<DeviceContactNormalizedPhoneNumber>()
@ -222,6 +222,9 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
} }
} }
for peer in remotePeers.1 { for peer in remotePeers.1 {
if !(peer.peer is TelegramUser) {
continue
}
if !existingPeerIds.contains(peer.peer.id) { if !existingPeerIds.contains(peer.peer.id) {
existingPeerIds.insert(peer.peer.id) existingPeerIds.insert(peer.peer.id)
@ -264,25 +267,10 @@ final class ContactsSearchContainerNode: SearchDisplayControllerContentNode {
if let strongSelf = self { if let strongSelf = self {
let previousItems = previousSearchItems.swap(items ?? []) let previousItems = previousSearchItems.swap(items ?? [])
let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, account: account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, openPeer: { peer in self?.listNode.clearHighlightAnimated(true) let transition = contactListSearchContainerPreparedRecentTransition(from: previousItems, to: items ?? [], isSearching: items != nil, account: account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, nameSortOrder: strongSelf.presentationData.nameSortOrder, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder, openPeer: { peer in self?.listNode.clearHighlightAnimated(true)
self?.openPeer(peer) self?.openPeer(peer)
}) })
/*var listItems: [ListViewItem] = []
for item in items {
switch item {
case let .peer(peer, theme, strings):
listItems.append(ContactsPeerItem(theme: theme, strings: strings, account: account, peerMode: .peer, peer: peer, chatPeer: peer, status: .none, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: nil, action: { [weak self] _ in
if let openPeer = self?.openPeer {
self?.listNode.clearHighlightAnimated(true)
openPeer(peer.id)
}
}))
}
}*/
strongSelf.enqueueTransition(transition) strongSelf.enqueueTransition(transition)
} }
})) }))

View File

@ -402,7 +402,10 @@ func createPasswordController(account: Account, state: CreatePasswordState, comp
resultItemNode.focus() resultItemNode.focus()
} }
} }
controller.didAppear = { controller.didAppear = { firstTime in
if !firstTime {
return
}
initialFocusImpl?() initialFocusImpl?()
} }

View File

@ -719,6 +719,8 @@ private func groupInfoEntries(account: Account, presentationData: PresentationDa
} }
if channel.hasAdminRights(.canInviteUsers) { if channel.hasAdminRights(.canInviteUsers) {
canAddMembers = true canAddMembers = true
} else if case let .group(info) = channel.info, info.flags.contains(.everyMemberCanInviteMembers) {
canAddMembers = true
} }
} }

View File

@ -33,7 +33,7 @@ final class HashtagSearchController: TelegramController {
self.title = query self.title = query
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil)
let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat) let chatListPresentationData = ChatListPresentationData(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder)
let location: SearchMessagesLocation = .general let location: SearchMessagesLocation = .general
let search = searchMessages(account: account, location: location, query: query) let search = searchMessages(account: account, location: location, query: query)

View File

@ -39,7 +39,7 @@ public class InviteContactsController: ViewController, MFMessageComposeViewContr
self.scrollToTop = { [weak self] in self.scrollToTop = { [weak self] in
if let strongSelf = self { if let strongSelf = self {
//strongSelf.contactsNode.listNode.scrollToTop() strongSelf.contactsNode.scrollToTop()
} }
} }

View File

@ -66,7 +66,7 @@ private final class InviteContactsInteraction {
private enum InviteContactsEntry: Comparable, Identifiable { private enum InviteContactsEntry: Comparable, Identifiable {
case search(PresentationTheme, PresentationStrings) case search(PresentationTheme, PresentationStrings)
case option(Int, ContactListAdditionalOption, PresentationTheme, PresentationStrings) case option(Int, ContactListAdditionalOption, PresentationTheme, PresentationStrings)
case peer(Int, DeviceContactStableId, DeviceContactBasicData, Int32, ContactsPeerItemSelection, PresentationTheme, PresentationStrings) case peer(Int, DeviceContactStableId, DeviceContactBasicData, Int32, ContactsPeerItemSelection, PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)
var stableId: InviteContactsEntryId { var stableId: InviteContactsEntryId {
switch self { switch self {
@ -74,7 +74,7 @@ private enum InviteContactsEntry: Comparable, Identifiable {
return .search return .search
case let .option(index, _, _, _): case let .option(index, _, _, _):
return .option(index: index) return .option(index: index)
case let .peer(_, id, _, _, _, _, _): case let .peer(_, id, _, _, _, _, _, _, _):
return .contactId(id) return .contactId(id)
} }
} }
@ -87,7 +87,7 @@ private enum InviteContactsEntry: Comparable, Identifiable {
}) })
case let .option(_, option, theme, _): case let .option(_, option, theme, _):
return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, action: option.action) return ContactListActionItem(theme: theme, title: option.title, icon: option.icon, action: option.action)
case let .peer(_, id, contact, count, selection, theme, strings): case let .peer(_, id, contact, count, selection, theme, strings, nameSortOrder, nameDisplayOrder):
let status: ContactsPeerItemStatus let status: ContactsPeerItemStatus
if count != 0 { if count != 0 {
status = .custom(strings.Contacts_ImportersCount(count)) status = .custom(strings.Contacts_ImportersCount(count))
@ -95,7 +95,7 @@ private enum InviteContactsEntry: Comparable, Identifiable {
status = .none 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: []) 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, 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(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
interaction.toggleContact(id) interaction.toggleContact(id)
}) })
} }
@ -115,9 +115,9 @@ private enum InviteContactsEntry: Comparable, Identifiable {
} else { } else {
return false return false
} }
case let .peer(lhsIndex, lhsId, lhsContact, lhsCount, lhsSelection, lhsTheme, lhsStrings): case let .peer(lhsIndex, lhsId, lhsContact, lhsCount, lhsSelection, lhsTheme, lhsStrings, lhsSortOrder, lhsDisplayOrder):
switch rhs { switch rhs {
case let .peer(rhsIndex, rhsId, rhsContact, rhsCount, rhsSelection, rhsTheme, rhsStrings): case let .peer(rhsIndex, rhsId, rhsContact, rhsCount, rhsSelection, rhsTheme, rhsStrings, rhsSortOrder, rhsDisplayOrder):
if lhsIndex != rhsIndex { if lhsIndex != rhsIndex {
return false return false
} }
@ -139,6 +139,12 @@ private enum InviteContactsEntry: Comparable, Identifiable {
if lhsStrings !== rhsStrings { if lhsStrings !== rhsStrings {
return false return false
} }
if lhsSortOrder != rhsSortOrder {
return false
}
if lhsDisplayOrder != rhsDisplayOrder {
return false
}
return true return true
default: default:
return false return false
@ -159,11 +165,11 @@ private enum InviteContactsEntry: Comparable, Identifiable {
case .peer: case .peer:
return true return true
} }
case let .peer(lhsIndex, _, _, _, _, _, _): case let .peer(lhsIndex, _, _, _, _, _, _, _, _):
switch rhs { switch rhs {
case .search, .option: case .search, .option:
return false return false
case let .peer(rhsIndex, _, _, _, _, _, _): case let .peer(rhsIndex, _, _, _, _, _, _, _, _):
return lhsIndex < rhsIndex return lhsIndex < rhsIndex
} }
} }
@ -214,7 +220,7 @@ struct InviteContactsGroupSelectionState: Equatable {
} }
} }
private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)], selectionState: InviteContactsGroupSelectionState, theme: PresentationTheme, strings: PresentationStrings, interaction: InviteContactsInteraction) -> [InviteContactsEntry] { private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceContactStableId, DeviceContactBasicData, Int32)], selectionState: InviteContactsGroupSelectionState, theme: PresentationTheme, strings: PresentationStrings, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, interaction: InviteContactsInteraction) -> [InviteContactsEntry] {
var entries: [InviteContactsEntry] = [] var entries: [InviteContactsEntry] = []
entries.append(.search(theme, strings)) entries.append(.search(theme, strings))
@ -225,7 +231,7 @@ private func inviteContactsEntries(accountPeer: Peer?, sortedContacts: [(DeviceC
var index = 0 var index = 0
for (id, contact, count) in sortedContacts { for (id, contact, count) in sortedContacts {
entries.append(.peer(index, id, contact, count, .selectable(selected: selectionState.selectedContactIndices[id] != nil), theme, strings)) entries.append(.peer(index, id, contact, count, .selectable(selected: selectionState.selectedContactIndices[id] != nil), theme, strings, nameSortOrder, nameDisplayOrder))
index += 1 index += 1
} }
@ -290,7 +296,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
private var presentationData: PresentationData private var presentationData: PresentationData
private var presentationDataDisposable: Disposable? private var presentationDataDisposable: Disposable?
private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings)> private let themeAndStringsPromise: Promise<(PresentationTheme, PresentationStrings, PresentationPersonNameOrder, PresentationPersonNameOrder)>
private let _ready = Promise<Bool>() private let _ready = Promise<Bool>()
private var readyValue = false { private var readyValue = false {
@ -313,7 +319,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } self.presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings)) self.themeAndStringsPromise = Promise((self.presentationData.theme, self.presentationData.strings, self.presentationData.nameSortOrder, self.presentationData.nameDisplayOrder))
self.listNode = ListView() self.listNode = ListView()
@ -427,7 +433,7 @@ final class InviteContactsControllerNode: ASDisplayNode {
transition = (combineLatest(sortedContacts, selectionStateSignal, themeAndStringsPromise.get()) transition = (combineLatest(sortedContacts, selectionStateSignal, themeAndStringsPromise.get())
|> mapToQueue { sortedContacts, selectionState, themeAndStrings -> Signal<InviteContactsTransition, NoError> in |> mapToQueue { sortedContacts, selectionState, themeAndStrings -> Signal<InviteContactsTransition, NoError> in
let signal = deferred { () -> Signal<InviteContactsTransition, NoError> in let signal = deferred { () -> Signal<InviteContactsTransition, NoError> in
let entries = inviteContactsEntries(accountPeer: nil, sortedContacts: sortedContacts, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, interaction: interaction) let entries = inviteContactsEntries(accountPeer: nil, sortedContacts: sortedContacts, selectionState: selectionState, theme: themeAndStrings.0, strings: themeAndStrings.1, nameSortOrder: themeAndStrings.2, nameDisplayOrder: themeAndStrings.3, interaction: interaction)
let previous = previousEntries.swap(entries) let previous = previousEntries.swap(entries)
let animated: Bool let animated: Bool
if let previous = previous { if let previous = previous {
@ -485,6 +491,10 @@ final class InviteContactsControllerNode: ASDisplayNode {
self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
} }
func scrollToTop() {
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
let hadValidLayout = self.validLayout != nil let hadValidLayout = self.validLayout != nil
self.validLayout = (layout, navigationBarHeight) self.validLayout = (layout, navigationBarHeight)

View File

@ -141,7 +141,7 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController {
private var didPlayPresentationAnimation = false private var didPlayPresentationAnimation = false
private(set) var didAppearOnce = false private(set) var didAppearOnce = false
var didAppear: (() -> Void)? var didAppear: ((Bool) -> Void)?
var titleControlValueChanged: ((Int) -> Void)? var titleControlValueChanged: ((Int) -> Void)?
@ -419,10 +419,8 @@ class ItemListController<Entry: ItemListNodeEntry>: ViewController {
} }
} }
if !self.didAppearOnce { self.didAppear?(!self.didAppearOnce)
self.didAppearOnce = true self.didAppearOnce = true
self.didAppear?()
}
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {

View File

@ -0,0 +1,37 @@
import Foundation
import Display
import SwiftSignalKit
import LegacyComponents
func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, completion: @escaping (UIImage) -> Void) {
let legacyController = LegacyController(presentation: .custom, theme: theme)
legacyController.statusBar.statusBarStyle = .Ignore
let emptyController = LegacyEmptyController(context: legacyController.context)!
let navigationController = makeLegacyNavigationController(rootController: emptyController)
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0)
legacyController.bind(controller: navigationController)
present(legacyController, nil)
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasDeleteButton: false, hasViewButton: false, personalPhoto: true, saveEditedPhotos: false, saveCapturedMedia: false, signup: signup)!
let _ = holder.swap(mixin)
mixin.didFinishWithImage = { image in
guard let image = image else {
return
}
completion(image)
}
mixin.didDismiss = { [weak legacyController] in
let _ = holder.swap(nil)
legacyController?.dismiss()
}
let menuController = mixin.present()
if let menuController = menuController {
menuController.customRemoveFromParentViewController = { [weak legacyController] in
legacyController?.dismiss()
}
}
}

View File

@ -257,14 +257,15 @@ static int callControllerNetworkTypeForType(OngoingCallNetworkType type) {
config.logFilePath = ""; config.logFilePath = "";
config.statsDumpFilePath = ""; config.statsDumpFilePath = "";
_controller->SetConfig(config); if (_controller != nil) {
_controller->SetConfig(config);
_controller->SetEncryptionKey((char *)key.bytes, isOutgoing);
/*releasable*/ _controller->SetEncryptionKey((char *)key.bytes, isOutgoing);
_controller->SetRemoteEndpoints(endpoints, _allowP2P, maxLayer); _controller->SetRemoteEndpoints(endpoints, _allowP2P, maxLayer);
_controller->Start(); _controller->Start();
_controller->Connect(); _controller->Connect();
}
} }
- (void)stop { - (void)stop {

View File

@ -62,7 +62,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.segmentedControl.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor self.segmentedControl.tintColor = self.presentationData.theme.rootController.navigationBar.accentTextColor
self.segmentedControl.selectedSegmentIndex = 0 self.segmentedControl.selectedSegmentIndex = 0
self.chatListNode = ChatListNode(account: account, groupId: nil, controlsHistoryPreload: false, mode: .peers(filter: filter), theme: presentationData.theme, strings: presentationData.strings, timeFormat: presentationData.timeFormat) self.chatListNode = ChatListNode(account: account, groupId: nil, controlsHistoryPreload: false, mode: .peers(filter: filter), theme: presentationData.theme, strings: presentationData.strings, timeFormat: presentationData.timeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder)
super.init() super.init()
@ -107,7 +107,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private func updateThemeAndStrings() { private func updateThemeAndStrings() {
self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings) self.searchDisplayController?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat) self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, timeFormat: self.presentationData.timeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder)
} }
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -315,7 +315,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.recursivelyEnsureDisplaySynchronously(true) self.recursivelyEnsureDisplaySynchronously(true)
contactListNode.enableUpdates = true contactListNode.enableUpdates = true
} else { } else {
let contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, ordering: .lastFirst, options: [])) let contactListNode = ContactListNode(account: account, presentation: .natural(displaySearch: true, options: []))
self.contactListNode = contactListNode self.contactListNode = contactListNode
contactListNode.enableUpdates = true contactListNode.enableUpdates = true
contactListNode.activateSearch = { [weak self] in contactListNode.activateSearch = { [weak self] in

View File

@ -2,25 +2,38 @@ import Foundation
import SwiftSignalKit import SwiftSignalKit
import Postbox import Postbox
import TelegramCore import TelegramCore
import Contacts
import AddressBook
public enum PresentationTimeFormat { public enum PresentationTimeFormat {
case regular case regular
case military case military
} }
public enum PresentationPersonNameOrder {
case firstLast
case lastFirst
}
public final class PresentationData: Equatable { public final class PresentationData: Equatable {
public let strings: PresentationStrings public let strings: PresentationStrings
public let theme: PresentationTheme public let theme: PresentationTheme
public let chatWallpaper: TelegramWallpaper public let chatWallpaper: TelegramWallpaper
public let fontSize: PresentationFontSize public let fontSize: PresentationFontSize
public let timeFormat: PresentationTimeFormat public let timeFormat: PresentationTimeFormat
public let nameDisplayOrder: PresentationPersonNameOrder
public let nameSortOrder: PresentationPersonNameOrder
public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, fontSize: PresentationFontSize, timeFormat: PresentationTimeFormat) { public init(strings: PresentationStrings, theme: PresentationTheme, chatWallpaper: TelegramWallpaper, fontSize: PresentationFontSize, timeFormat: PresentationTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, nameSortOrder: PresentationPersonNameOrder) {
self.strings = strings self.strings = strings
self.theme = theme self.theme = theme
self.chatWallpaper = chatWallpaper self.chatWallpaper = chatWallpaper
self.fontSize = fontSize self.fontSize = fontSize
self.timeFormat = timeFormat self.timeFormat = timeFormat
self.nameDisplayOrder = nameDisplayOrder
self.nameSortOrder = nameSortOrder
} }
public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool { public static func ==(lhs: PresentationData, rhs: PresentationData) -> Bool {
@ -71,6 +84,40 @@ private func currentTimeFormat() -> PresentationTimeFormat {
} }
} }
private func currentPersonNameSortOrder() -> PresentationPersonNameOrder {
if #available(iOSApplicationExtension 9.0, *) {
switch CNContactsUserDefaults.shared().sortOrder {
case .givenName:
return .firstLast
default:
return .lastFirst
}
} else {
if ABPersonGetSortOrdering() == kABPersonSortByFirstName {
return .firstLast
} else {
return .lastFirst
}
}
}
private func currentPersonNameDisplayOrder() -> PresentationPersonNameOrder {
if #available(iOSApplicationExtension 9.0, *) {
switch CNContactFormatter.nameOrder(for: CNContact()) {
case .givenNameFirst:
return .firstLast
default:
return .lastFirst
}
} else {
if ABPersonGetCompositeNameFormat() == kABPersonCompositeNameFormatFirstNameFirst {
return .firstLast
} else {
return .lastFirst
}
}
}
public final class InitialPresentationDataAndSettings { public final class InitialPresentationDataAndSettings {
public let presentationData: PresentationData public let presentationData: PresentationData
public let automaticMediaDownloadSettings: AutomaticMediaDownloadSettings public let automaticMediaDownloadSettings: AutomaticMediaDownloadSettings
@ -186,7 +233,9 @@ public func currentPresentationDataAndSettings(postbox: Postbox) -> Signal<Initi
stringsValue = defaultPresentationStrings stringsValue = defaultPresentationStrings
} }
let timeFormat = currentTimeFormat() let timeFormat = currentTimeFormat()
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, timeFormat: timeFormat), automaticMediaDownloadSettings: automaticMediaDownloadSettings, loggingSettings: loggingSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings) let nameDisplayOrder = currentPersonNameDisplayOrder()
let nameSortOrder = currentPersonNameSortOrder()
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, timeFormat: timeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder), automaticMediaDownloadSettings: automaticMediaDownloadSettings, loggingSettings: loggingSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, experimentalUISettings: experimentalUISettings)
} }
} }
@ -314,8 +363,10 @@ public func updatedPresentationData(postbox: Postbox) -> Signal<PresentationData
} }
let timeFormat = currentTimeFormat() let timeFormat = currentTimeFormat()
let nameDisplayOrder = currentPersonNameDisplayOrder()
let nameSortOrder = currentPersonNameSortOrder()
return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, timeFormat: timeFormat) return PresentationData(strings: stringsValue, theme: themeValue, chatWallpaper: effectiveChatWallpaper, fontSize: themeSettings.fontSize, timeFormat: timeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder)
} }
} }
} }

View File

@ -219,7 +219,10 @@ func resetPasswordController(account: Account, emailPattern: String, completion:
resultItemNode.focus() resultItemNode.focus()
} }
} }
controller.didAppear = { controller.didAppear = { firstTime in
if !firstTime {
return
}
initialFocusImpl?() initialFocusImpl?()
} }

View File

@ -122,7 +122,9 @@ final class SecureIdAuthPasswordOptionContentNode: ASDisplayNode, SecureIdAuthCo
transition.updateFrame(node: self.inputContainer, frame: inputContainerFrame) transition.updateFrame(node: self.inputContainer, frame: inputContainerFrame)
transition.updateFrame(node: self.titleNode, frame: titleFrame) transition.updateFrame(node: self.titleNode, frame: titleFrame)
transition.updateFrame(node: self.inputBackground, frame: inputFrame) transition.updateFrame(node: self.inputBackground, frame: inputFrame)
transition.updateFrame(node: self.inputField, frame: inputFrame.insetBy(dx: 6.0, dy: 0.0)) var inputFieldFrame = inputFrame.insetBy(dx: 6.0, dy: 0.0)
inputFieldFrame.size.width -= 16.0
transition.updateFrame(node: self.inputField, frame: inputFieldFrame)
transition.updateFrame(node: self.inputButtonNode, frame: CGRect(origin: CGPoint(x: inputFrame.maxX - self.inputButtonNode.bounds.size.width - 6.0, y: inputFrame.minY + floor((inputFrame.height - self.inputButtonNode.bounds.size.height) / 2.0)), size: self.inputButtonNode.bounds.size)) transition.updateFrame(node: self.inputButtonNode, frame: CGRect(origin: CGPoint(x: inputFrame.maxX - self.inputButtonNode.bounds.size.width - 6.0, y: inputFrame.minY + floor((inputFrame.height - self.inputButtonNode.bounds.size.height) / 2.0)), size: self.inputButtonNode.bounds.size))

View File

@ -345,7 +345,7 @@ private struct SettingsState: Equatable {
} }
} }
private func settingsEntries(presentationData: PresentationData, state: SettingsState, view: PeerView, proxySettings: ProxySettings, unreadTrendingStickerPacks: Int, archivedPacks: [ArchivedStickerPackItem]?) -> [SettingsEntry] { private func settingsEntries(presentationData: PresentationData, state: SettingsState, view: PeerView, proxySettings: ProxySettings, unreadTrendingStickerPacks: Int, archivedPacks: [ArchivedStickerPackItem]?, hasPassport: Bool) -> [SettingsEntry] {
var entries: [SettingsEntry] = [] var entries: [SettingsEntry] = []
if let peer = peerViewMainPeer(view) as? TelegramUser { if let peer = peerViewMainPeer(view) as? TelegramUser {
@ -383,8 +383,8 @@ private func settingsEntries(presentationData: PresentationData, state: Settings
entries.append(.themes(presentationData.theme, SettingsItemIcons.appearance, presentationData.strings.ChatSettings_Appearance.lowercased().capitalized)) entries.append(.themes(presentationData.theme, SettingsItemIcons.appearance, presentationData.strings.ChatSettings_Appearance.lowercased().capitalized))
entries.append(.language(presentationData.theme, SettingsItemIcons.language, presentationData.strings.Settings_AppLanguage, presentationData.strings.Localization_LanguageName)) entries.append(.language(presentationData.theme, SettingsItemIcons.language, presentationData.strings.Settings_AppLanguage, presentationData.strings.Localization_LanguageName))
if GlobalExperimentalSettings.enablePassport { if hasPassport {
entries.append(.passport(presentationData.theme, SettingsItemIcons.secureId, "Telegram Passport", "")) entries.append(.passport(presentationData.theme, SettingsItemIcons.secureId, presentationData.strings.Settings_Passport, ""))
} }
entries.append(.askAQuestion(presentationData.theme, SettingsItemIcons.support, presentationData.strings.Settings_Support)) entries.append(.askAQuestion(presentationData.theme, SettingsItemIcons.support, presentationData.strings.Settings_Support))
@ -422,6 +422,9 @@ public func settingsController(account: Account, accountManager: AccountManager)
let hiddenAvatarRepresentationDisposable = MetaDisposable() let hiddenAvatarRepresentationDisposable = MetaDisposable()
actionsDisposable.add(hiddenAvatarRepresentationDisposable) actionsDisposable.add(hiddenAvatarRepresentationDisposable)
let updatePassportDisposable = MetaDisposable()
actionsDisposable.add(updatePassportDisposable)
let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil) let currentAvatarMixin = Atomic<TGMediaAvatarMenuMixin?>(value: nil)
var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)? var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)?
@ -624,8 +627,17 @@ public func settingsController(account: Account, accountManager: AccountManager)
archivedPacks.set(.single(nil) |> then(archivedStickerPacks(account: account) |> map(Optional.init))) archivedPacks.set(.single(nil) |> then(archivedStickerPacks(account: account) |> map(Optional.init)))
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get(), peerView, account.postbox.preferencesView(keys: [PreferencesKeys.proxySettings]), combineLatest(account.viewTracker.featuredStickerPacks(), archivedPacks.get())) let hasPassport = ValuePromise<Bool>(false)
|> map { presentationData, state, view, preferences, featuredAndArchived -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in let updatePassport: () -> Void = {
updatePassportDisposable.set((twoStepAuthData(account.network)
|> deliverOnMainQueue).start(next: { value in
hasPassport.set(value.hasSecretValues)
}))
}
updatePassport()
let signal = combineLatest(account.telegramApplicationContext.presentationData, statePromise.get(), peerView, account.postbox.preferencesView(keys: [PreferencesKeys.proxySettings]), combineLatest(account.viewTracker.featuredStickerPacks(), archivedPacks.get()), hasPassport.get())
|> map { presentationData, state, view, preferences, featuredAndArchived, hasPassport -> (ItemListControllerState, (ItemListNodeState<SettingsEntry>, SettingsEntry.ItemGenerationArguments)) in
let proxySettings: ProxySettings let proxySettings: ProxySettings
if let value = preferences.values[PreferencesKeys.proxySettings] as? ProxySettings { if let value = preferences.values[PreferencesKeys.proxySettings] as? ProxySettings {
proxySettings = value proxySettings = value
@ -649,7 +661,7 @@ public func settingsController(account: Account, accountManager: AccountManager)
} }
} }
let listState = ItemListNodeState(entries: settingsEntries(presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1), style: .blocks) let listState = ItemListNodeState(entries: settingsEntries(presentationData: presentationData, state: state, view: view, proxySettings: proxySettings, unreadTrendingStickerPacks: unreadTrendingStickerPacks, archivedPacks: featuredAndArchived.1, hasPassport: hasPassport), style: .blocks)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} |> afterDisposed { } |> afterDisposed {
@ -700,6 +712,9 @@ public func settingsController(account: Account, accountManager: AccountManager)
controller.tabBarItemDebugTapAction = { controller.tabBarItemDebugTapAction = {
pushControllerImpl?(debugController(account: account, accountManager: accountManager)) pushControllerImpl?(debugController(account: account, accountManager: accountManager))
} }
controller.didAppear = { _ in
updatePassport()
}
return controller return controller
} }

View File

@ -350,44 +350,56 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone
} }
func navigationBarPallete() -> TGNavigationBarPallete! { func navigationBarPallete() -> TGNavigationBarPallete! {
let theme: PresentationTheme
if let account = legacyComponentsAccount { if let account = legacyComponentsAccount {
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let theme = presentationData.theme.rootController.navigationBar theme = presentationData.theme
return TGNavigationBarPallete(backgroundColor: theme.backgroundColor, separatorColor: theme.separatorColor, titleColor: theme.primaryTextColor, tintColor: theme.accentTextColor)
} else { } else {
return nil theme = defaultPresentationTheme
} }
let barTheme = theme.rootController.navigationBar
return TGNavigationBarPallete(backgroundColor: barTheme.backgroundColor, separatorColor: barTheme.separatorColor, titleColor: barTheme.primaryTextColor, tintColor: barTheme.accentTextColor)
} }
func menuSheetPallete() -> TGMenuSheetPallete! { func menuSheetPallete() -> TGMenuSheetPallete! {
let theme: PresentationTheme
if let account = legacyComponentsAccount { if let account = legacyComponentsAccount {
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let theme = presentationData.theme.actionSheet theme = presentationData.theme
return TGMenuSheetPallete(dark: presentationData.theme.overallDarkAppearance, backgroundColor: theme.opaqueItemBackgroundColor, selectionColor: theme.opaqueItemHighlightedBackgroundColor, separatorColor: theme.opaqueItemSeparatorColor, accentColor: theme.controlAccentColor, destructiveColor: theme.destructiveActionTextColor, textColor: theme.primaryTextColor, secondaryTextColor: theme.secondaryTextColor, spinnerColor: theme.secondaryTextColor, badgeTextColor: theme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: theme.opaqueItemBackgroundColor))
} else { } else {
return nil theme = defaultPresentationTheme
} }
let sheetTheme = theme.actionSheet
return TGMenuSheetPallete(dark: theme.overallDarkAppearance, backgroundColor: sheetTheme.opaqueItemBackgroundColor, selectionColor: sheetTheme.opaqueItemHighlightedBackgroundColor, separatorColor: sheetTheme.opaqueItemSeparatorColor, accentColor: sheetTheme.controlAccentColor, destructiveColor: sheetTheme.destructiveActionTextColor, textColor: sheetTheme.primaryTextColor, secondaryTextColor: sheetTheme.secondaryTextColor, spinnerColor: sheetTheme.secondaryTextColor, badgeTextColor: sheetTheme.controlAccentColor, badgeImage: nil, cornersImage: generateStretchableFilledCircleImage(diameter: 11.0, color: nil, strokeColor: nil, strokeWidth: nil, backgroundColor: sheetTheme.opaqueItemBackgroundColor))
} }
func mediaAssetsPallete() -> TGMediaAssetsPallete! { func mediaAssetsPallete() -> TGMediaAssetsPallete! {
let presentationTheme: PresentationTheme
if let account = legacyComponentsAccount { if let account = legacyComponentsAccount {
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let theme = presentationData.theme.list presentationTheme = presentationData.theme
let navigationBar = presentationData.theme.rootController.navigationBar
return TGMediaAssetsPallete(dark: presentationData.theme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: navigationBar.backgroundColor, barSeparatorColor: navigationBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationData.theme), maybeAccentColor: navigationBar.accentTextColor)
} else { } else {
return nil presentationTheme = defaultPresentationTheme
} }
let theme = presentationTheme.list
let navigationBar = presentationTheme.rootController.navigationBar
return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: navigationBar.backgroundColor, barSeparatorColor: navigationBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor)
} }
func checkButtonPallete() -> TGCheckButtonPallete! { func checkButtonPallete() -> TGCheckButtonPallete! {
let presentationTheme: PresentationTheme
if let account = legacyComponentsAccount { if let account = legacyComponentsAccount {
let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 } let presentationData = account.telegramApplicationContext.currentPresentationData.with { $0 }
let theme = presentationData.theme presentationTheme = presentationData.theme
return TGCheckButtonPallete(defaultBackgroundColor: theme.chat.bubble.selectionControlFillColor, accentBackgroundColor: theme.chat.bubble.selectionControlFillColor, defaultBorderColor: theme.chat.bubble.selectionControlBorderColor, mediaBorderColor: theme.chat.bubble.selectionControlBorderColor, chatBorderColor: theme.chat.bubble.selectionControlBorderColor, check: theme.chat.bubble.selectionControlForegroundColor, blueColor: theme.chat.bubble.selectionControlFillColor, barBackgroundColor: theme.chat.bubble.selectionControlFillColor)
} else { } else {
return nil presentationTheme = defaultPresentationTheme
} }
let theme = presentationTheme
return TGCheckButtonPallete(defaultBackgroundColor: theme.chat.bubble.selectionControlFillColor, accentBackgroundColor: theme.chat.bubble.selectionControlFillColor, defaultBorderColor: theme.chat.bubble.selectionControlBorderColor, mediaBorderColor: theme.chat.bubble.selectionControlBorderColor, chatBorderColor: theme.chat.bubble.selectionControlBorderColor, check: theme.chat.bubble.selectionControlForegroundColor, blueColor: theme.chat.bubble.selectionControlFillColor, barBackgroundColor: theme.chat.bubble.selectionControlFillColor)
} }
} }

View File

@ -287,7 +287,7 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
case let .automatic(_, _, sunset, sunrise, localizedName): case let .automatic(_, _, sunset, sunrise, localizedName):
entries.append(.timeBasedAutomaticLocationValue(theme, strings.AutoNightTheme_UpdateLocation, localizedName)) entries.append(.timeBasedAutomaticLocationValue(theme, strings.AutoNightTheme_UpdateLocation, localizedName))
if sunset != 0 || sunrise != 0 { if sunset != 0 || sunrise != 0 {
entries.append(.settingInfo(theme, strings.AutoNightTheme_LocationHelp(stringForMessageTimestamp(timestamp: sunrise, timeFormat: timeFormat, local: false), stringForMessageTimestamp(timestamp: sunset, timeFormat: timeFormat, local: false)).0)) entries.append(.settingInfo(theme, strings.AutoNightTheme_LocationHelp(stringForMessageTimestamp(timestamp: sunset, timeFormat: timeFormat, local: false), stringForMessageTimestamp(timestamp: sunrise, timeFormat: timeFormat, local: false)).0))
} }
case let .manual(fromSeconds, toSeconds): case let .manual(fromSeconds, toSeconds):
entries.append(.timeBasedManualFrom(theme, strings.AutoNightTheme_ScheduledFrom, stringForMessageTimestamp(timestamp: fromSeconds, timeFormat: timeFormat, local: false))) entries.append(.timeBasedManualFrom(theme, strings.AutoNightTheme_ScheduledFrom, stringForMessageTimestamp(timestamp: fromSeconds, timeFormat: timeFormat, local: false)))
@ -313,7 +313,7 @@ private func themeAutoNightSettingsControllerEntries(theme: PresentationTheme, s
private func roundTimeToDay(_ timestamp: Int32) -> Int32 { private func roundTimeToDay(_ timestamp: Int32) -> Int32 {
let calendar = Calendar.current let calendar = Calendar.current
let offset = TimeZone.current.secondsFromGMT(for: Date()) let offset = 0//TimeZone.current.secondsFromGMT(for: Date())
let components = calendar.dateComponents([.hour, .minute, .second], from: Date(timeIntervalSince1970: Double(timestamp + Int32(offset)))) let components = calendar.dateComponents([.hour, .minute, .second], from: Date(timeIntervalSince1970: Double(timestamp + Int32(offset))))
return Int32(components.hour! * 60 * 60 + components.minute! * 60 + components.second!) return Int32(components.hour! * 60 * 60 + components.minute! * 60 + components.second!)
} }
@ -324,7 +324,7 @@ private func areSettingsValid(_ settings: AutomaticThemeSwitchSetting) -> Bool {
return true return true
case let .timeBased(setting): case let .timeBased(setting):
switch setting { switch setting {
case let .automatic(latitude, longitude, sunset, sunrise, _): case let .automatic(latitude, longitude, _, _, _):
if !latitude.isZero || !longitude.isZero { if !latitude.isZero || !longitude.isZero {
return true return true
} else { } else {
@ -391,7 +391,7 @@ public func themeAutoNightSettingsController(account: Account) -> ViewController
updateSettings { settings in updateSettings { settings in
var settings = settings var settings = settings
if case let .timeBased(setting) = settings.trigger, case .automatic = setting { if case let .timeBased(setting) = settings.trigger, case .automatic = setting {
let calculator = EDSunriseSet(date: Date(), timezone: TimeZone(secondsFromGMT: 0), latitude: location.0, longitude: location.1)! let calculator = EDSunriseSet(date: Date(), timezone: TimeZone.current, latitude: location.0, longitude: location.1)!
let sunset = roundTimeToDay(Int32(calculator.sunset.timeIntervalSince1970)) let sunset = roundTimeToDay(Int32(calculator.sunset.timeIntervalSince1970))
let sunrise = roundTimeToDay(Int32(calculator.sunrise.timeIntervalSince1970)) let sunrise = roundTimeToDay(Int32(calculator.sunrise.timeIntervalSince1970))

View File

@ -628,7 +628,10 @@ func twoStepVerificationUnlockSettingsController(account: Account, mode: TwoStep
resultItemNode.focus() resultItemNode.focus()
} }
} }
controller.didAppear = { controller.didAppear = { firstTime in
if !firstTime {
return
}
initialFocusImpl?() initialFocusImpl?()
} }

View File

@ -1270,8 +1270,8 @@ public func userInfoController(account: Account, peerId: PeerId) -> ViewControll
} }
} }
controller.didAppear = { [weak controller] in controller.didAppear = { [weak controller] firstTime in
guard let controller = controller else { guard let controller = controller, firstTime else {
return return
} }

View File

@ -8,10 +8,12 @@ private final class UsernameSetupControllerArguments {
let account: Account let account: Account
let updatePublicLinkText: (String?, String) -> Void let updatePublicLinkText: (String?, String) -> Void
let shareLink: () -> Void
init(account: Account, updatePublicLinkText: @escaping (String?, String) -> Void) { init(account: Account, updatePublicLinkText: @escaping (String?, String) -> Void, shareLink: @escaping () -> Void) {
self.account = account self.account = account
self.updatePublicLinkText = updatePublicLinkText self.updatePublicLinkText = updatePublicLinkText
self.shareLink = shareLink
} }
} }
@ -20,7 +22,7 @@ private enum UsernameSetupSection: Int32 {
} }
private enum UsernameSetupEntry: ItemListNodeEntry { private enum UsernameSetupEntry: ItemListNodeEntry {
case editablePublicLink(PresentationTheme, String?, String) case editablePublicLink(PresentationTheme, String, String?, String)
case publicLinkStatus(PresentationTheme, String, AddressNameValidationStatus, String) case publicLinkStatus(PresentationTheme, String, AddressNameValidationStatus, String)
case publicLinkInfo(PresentationTheme, String) case publicLinkInfo(PresentationTheme, String)
@ -44,8 +46,8 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
static func ==(lhs: UsernameSetupEntry, rhs: UsernameSetupEntry) -> Bool { static func ==(lhs: UsernameSetupEntry, rhs: UsernameSetupEntry) -> Bool {
switch lhs { switch lhs {
case let .editablePublicLink(lhsTheme, lhsCurrentText, lhsText): case let .editablePublicLink(lhsTheme, lhsPrefix, lhsCurrentText, lhsText):
if case let .editablePublicLink(rhsTheme, rhsCurrentText, rhsText) = rhs, lhsTheme === rhsTheme, lhsCurrentText == rhsCurrentText, lhsText == rhsText { if case let .editablePublicLink(rhsTheme, rhsPrefix, rhsCurrentText, rhsText) = rhs, lhsTheme === rhsTheme, lhsPrefix == rhsPrefix, lhsCurrentText == rhsCurrentText, lhsText == rhsText {
return true return true
} else { } else {
return false return false
@ -71,14 +73,18 @@ private enum UsernameSetupEntry: ItemListNodeEntry {
func item(_ arguments: UsernameSetupControllerArguments) -> ListViewItem { func item(_ arguments: UsernameSetupControllerArguments) -> ListViewItem {
switch self { switch self {
case let .editablePublicLink(theme, currentText, text): case let .editablePublicLink(theme, prefix, currentText, text):
return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: "t.me/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, sectionId: self.section, textUpdated: { updatedText in return ItemListSingleLineInputItem(theme: theme, title: NSAttributedString(string: prefix, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .username, spacing: 10.0, sectionId: self.section, textUpdated: { updatedText in
arguments.updatePublicLinkText(currentText, updatedText) arguments.updatePublicLinkText(currentText, updatedText)
}, action: { }, action: {
}) })
case let .publicLinkInfo(theme, text): case let .publicLinkInfo(theme, text):
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section) return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section, linkAction: { action in
if case .tap = action {
arguments.shareLink()
}
})
case let .publicLinkStatus(theme, _, status, text): case let .publicLinkStatus(theme, _, status, text):
var displayActivity = false var displayActivity = false
let string: NSAttributedString let string: NSAttributedString
@ -160,7 +166,7 @@ private func usernameSetupControllerEntries(presentationData: PresentationData,
} }
} }
entries.append(.editablePublicLink(presentationData.theme, peer.addressName, currentAddressName)) entries.append(.editablePublicLink(presentationData.theme, presentationData.strings.Username_Title, peer.addressName, currentAddressName))
if let status = state.addressNameValidationStatus { if let status = state.addressNameValidationStatus {
let statusText: String let statusText: String
switch status { switch status {
@ -187,7 +193,12 @@ private func usernameSetupControllerEntries(presentationData: PresentationData,
} }
entries.append(.publicLinkStatus(presentationData.theme, currentAddressName, status, statusText)) entries.append(.publicLinkStatus(presentationData.theme, currentAddressName, status, statusText))
} }
entries.append(.publicLinkInfo(presentationData.theme, presentationData.strings.Username_Help))
var infoText = presentationData.strings.Username_Help
infoText += "\n\n"
let hintText = presentationData.strings.Username_LinkHint(currentAddressName.replacingOccurrences(of: "[", with: "").replacingOccurrences(of: "]", with: "")).0.replacingOccurrences(of: "]", with: "]()")
infoText += hintText
entries.append(.publicLinkInfo(presentationData.theme, infoText))
} }
return entries return entries
@ -201,6 +212,7 @@ public func usernameSetupController(account: Account) -> ViewController {
} }
var dismissImpl: (() -> Void)? var dismissImpl: (() -> Void)?
var presentControllerImpl: ((ViewController, Any?) -> Void)?
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
@ -233,10 +245,25 @@ public func usernameSetupController(account: Account) -> ViewController {
} }
})) }))
} }
}, shareLink: {
let _ = (account.postbox.loadedPeerWithId(account.peerId)
|> take(1)
|> deliverOnMainQueue).start(next: { peer in
var currentAddressName: String = peer.addressName ?? ""
updateState { state in
if let current = state.editingPublicLinkText {
currentAddressName = current
}
return state
}
if !currentAddressName.isEmpty {
presentControllerImpl?(ShareController(account: account, subject: .url("https://t.me/\(currentAddressName)")), nil)
}
})
}) })
let peerView = account.viewTracker.peerView(account.peerId) let peerView = account.viewTracker.peerView(account.peerId)
|> deliverOnMainQueue |> deliverOnMainQueue
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get() |> deliverOnMainQueue, peerView) let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, statePromise.get() |> deliverOnMainQueue, peerView)
|> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState<UsernameSetupEntry>, UsernameSetupEntry.ItemGenerationArguments)) in |> map { presentationData, state, view -> (ItemListControllerState, (ItemListNodeState<UsernameSetupEntry>, UsernameSetupEntry.ItemGenerationArguments)) in
@ -306,6 +333,9 @@ public func usernameSetupController(account: Account) -> ViewController {
controller?.view.endEditing(true) controller?.view.endEditing(true)
controller?.dismiss() controller?.dismiss()
} }
presentControllerImpl = { [weak controller] c, a in
controller?.present(c, in: .window(.root), with: a)
}
return controller return controller
} }