mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-26 01:52:25 +00:00
Various Fixes
This commit is contained in:
parent
857122fe72
commit
d43415d4fd
@ -7224,3 +7224,7 @@ Sorry for the inconvenience.";
|
|||||||
"Chat.OutgoingContextReactionCount_1" = "1 reacted";
|
"Chat.OutgoingContextReactionCount_1" = "1 reacted";
|
||||||
"Chat.OutgoingContextReactionCount_any" = "%@ reacted";
|
"Chat.OutgoingContextReactionCount_any" = "%@ reacted";
|
||||||
"Chat.OutgoingContextMixedReactionCount" = "%1$@/%2$@ reacted";
|
"Chat.OutgoingContextMixedReactionCount" = "%1$@/%2$@ reacted";
|
||||||
|
|
||||||
|
"Contacts.Sort" = "Sort";
|
||||||
|
"Contacts.Sort.ByName" = "by Name";
|
||||||
|
"Contacts.Sort.ByLastSeen" = "by Last Seen";
|
||||||
|
|||||||
@ -1757,7 +1757,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
strongSelf.dustNode = dustNode
|
strongSelf.dustNode = dustNode
|
||||||
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
||||||
}
|
}
|
||||||
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, textColor: theme.messageTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||||
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
|
|
||||||
} else if let dustNode = strongSelf.dustNode {
|
} else if let dustNode = strongSelf.dustNode {
|
||||||
|
|||||||
@ -96,7 +96,6 @@ private enum ContactListNodeEntryId: Hashable {
|
|||||||
|
|
||||||
private final class ContactListNodeInteraction {
|
private final class ContactListNodeInteraction {
|
||||||
fileprivate let activateSearch: () -> Void
|
fileprivate let activateSearch: () -> Void
|
||||||
fileprivate let openSortMenu: () -> Void
|
|
||||||
fileprivate let authorize: () -> Void
|
fileprivate let authorize: () -> Void
|
||||||
fileprivate let suppressWarning: () -> Void
|
fileprivate let suppressWarning: () -> Void
|
||||||
fileprivate let openPeer: (ContactListPeer, ContactListAction) -> Void
|
fileprivate let openPeer: (ContactListPeer, ContactListAction) -> Void
|
||||||
@ -104,9 +103,8 @@ private final class ContactListNodeInteraction {
|
|||||||
|
|
||||||
let itemHighlighting = ContactItemHighlighting()
|
let itemHighlighting = ContactItemHighlighting()
|
||||||
|
|
||||||
init(activateSearch: @escaping () -> Void, openSortMenu: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) {
|
init(activateSearch: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||||
self.activateSearch = activateSearch
|
self.activateSearch = activateSearch
|
||||||
self.openSortMenu = openSortMenu
|
|
||||||
self.authorize = authorize
|
self.authorize = authorize
|
||||||
self.suppressWarning = suppressWarning
|
self.suppressWarning = suppressWarning
|
||||||
self.openPeer = openPeer
|
self.openPeer = openPeer
|
||||||
@ -162,7 +160,6 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
|||||||
text = strings.Contacts_SortedByPresence
|
text = strings.Contacts_SortedByPresence
|
||||||
}
|
}
|
||||||
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, accessible: false, header: nil, action: {
|
return ContactListActionItem(presentationData: ItemListPresentationData(presentationData), title: text, icon: .inline(dropDownIcon, .right), highlight: .alpha, accessible: false, header: nil, action: {
|
||||||
interaction.openSortMenu()
|
|
||||||
})
|
})
|
||||||
case let .permissionInfo(_, title, text, suppressed):
|
case let .permissionInfo(_, title, text, suppressed):
|
||||||
return InfoListItem(presentationData: ItemListPresentationData(presentationData), title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
return InfoListItem(presentationData: ItemListPresentationData(presentationData), title: title, text: .plain(text), style: .plain, closeAction: suppressed ? nil : {
|
||||||
@ -443,10 +440,6 @@ private func contactListNodeEntries(accountPeer: EnginePeer?, peers: [ContactLis
|
|||||||
var orderedPeers: [ContactListPeer]
|
var orderedPeers: [ContactListPeer]
|
||||||
var headers: [ContactListPeerId: ContactListNameIndexHeader] = [:]
|
var headers: [ContactListPeerId: ContactListNameIndexHeader] = [:]
|
||||||
|
|
||||||
if displaySortOptions, let sortOrder = presentation.sortOrder {
|
|
||||||
entries.append(.sort(theme, strings, sortOrder))
|
|
||||||
}
|
|
||||||
|
|
||||||
var addHeader = false
|
var addHeader = false
|
||||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||||
let (suppressed, syncDisabled) = warningSuppressed
|
let (suppressed, syncDisabled) = warningSuppressed
|
||||||
@ -867,7 +860,6 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
public var contentScrollingEnded: ((ListView) -> Bool)?
|
public var contentScrollingEnded: ((ListView) -> Bool)?
|
||||||
|
|
||||||
public var activateSearch: (() -> Void)?
|
public var activateSearch: (() -> Void)?
|
||||||
public var openSortMenu: (() -> Void)?
|
|
||||||
public var openPeer: ((ContactListPeer, ContactListAction) -> Void)?
|
public var openPeer: ((ContactListPeer, ContactListAction) -> Void)?
|
||||||
public var openPrivacyPolicy: (() -> Void)?
|
public var openPrivacyPolicy: (() -> Void)?
|
||||||
public var suppressPermissionWarning: (() -> Void)?
|
public var suppressPermissionWarning: (() -> Void)?
|
||||||
@ -955,8 +947,6 @@ public final class ContactListNode: ASDisplayNode {
|
|||||||
|
|
||||||
let interaction = ContactListNodeInteraction(activateSearch: { [weak self] in
|
let interaction = ContactListNodeInteraction(activateSearch: { [weak self] in
|
||||||
self?.activateSearch?()
|
self?.activateSearch?()
|
||||||
}, openSortMenu: { [weak self] in
|
|
||||||
self?.openSortMenu?()
|
|
||||||
}, authorize: {
|
}, authorize: {
|
||||||
authorizeImpl?()
|
authorizeImpl?()
|
||||||
}, suppressWarning: { [weak self] in
|
}, suppressWarning: { [weak self] in
|
||||||
|
|||||||
@ -20,20 +20,90 @@ import StickerResources
|
|||||||
import ContextUI
|
import ContextUI
|
||||||
import QrCodeUI
|
import QrCodeUI
|
||||||
|
|
||||||
private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool {
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 {
|
private let controller: ViewController
|
||||||
let scrollToItem: ListViewScrollToItem
|
private let sourceNode: ContextReferenceContentNode
|
||||||
let targetProgress: CGFloat
|
|
||||||
if searchNode.expansionProgress < 0.6 {
|
|
||||||
scrollToItem = ListViewScrollToItem(index: 1, position: .top(-navigationBarSearchContentHeight), animated: true, curve: .Default(duration: nil), directionHint: .Up)
|
|
||||||
targetProgress = 0.0
|
|
||||||
} else {
|
|
||||||
scrollToItem = ListViewScrollToItem(index: 1, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up)
|
|
||||||
targetProgress = 1.0
|
|
||||||
}
|
|
||||||
searchNode.updateExpansionProgress(targetProgress, animated: true)
|
|
||||||
|
|
||||||
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
init(controller: ViewController, sourceNode: ContextReferenceContentNode) {
|
||||||
|
self.controller = controller
|
||||||
|
self.sourceNode = sourceNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||||
|
return ContextControllerReferenceViewInfo(referenceNode: self.sourceNode, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class SortHeaderButton: HighlightableButtonNode {
|
||||||
|
let referenceNode: ContextReferenceContentNode
|
||||||
|
let containerNode: ContextControllerSourceNode
|
||||||
|
private let textNode: ImmediateTextNode
|
||||||
|
|
||||||
|
var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||||
|
|
||||||
|
init(presentationData: PresentationData) {
|
||||||
|
self.referenceNode = ContextReferenceContentNode()
|
||||||
|
self.containerNode = ContextControllerSourceNode()
|
||||||
|
self.containerNode.animateScale = false
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.containerNode.addSubnode(self.referenceNode)
|
||||||
|
self.referenceNode.addSubnode(self.textNode)
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
|
|
||||||
|
self.containerNode.shouldBegin = { [weak self] location in
|
||||||
|
guard let strongSelf = self, let _ = strongSelf.contextAction else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
self.containerNode.activated = { [weak self] gesture, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.contextAction?(strongSelf.containerNode, gesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.containerNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 26.0, height: 44.0))
|
||||||
|
self.referenceNode.frame = self.containerNode.bounds
|
||||||
|
|
||||||
|
self.update(theme: presentationData.theme, strings: presentationData.strings)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
self.view.isOpaque = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(theme: PresentationTheme, strings: PresentationStrings) {
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: strings.Contacts_Sort, font: Font.regular(17.0), textColor: theme.rootController.navigationBar.accentTextColor)
|
||||||
|
let size = self.textNode.updateLayout(CGSize(width: 100.0, height: 44.0))
|
||||||
|
self.textNode.frame = CGRect(origin: CGPoint(x: 0.0, y: floorToScreenPixels((44.0 - size.height) / 2.0)), size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
|
return CGSize(width: 44.0, height: 44.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func onLayout() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool {
|
||||||
|
if listNode.scroller.isDragging {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 {
|
||||||
|
let offset: CGFloat
|
||||||
|
if searchNode.expansionProgress < 0.6 {
|
||||||
|
offset = navigationBarSearchContentHeight
|
||||||
|
} else {
|
||||||
|
offset = 0.0
|
||||||
|
}
|
||||||
|
let _ = listNode.scrollToOffsetFromTop(offset)
|
||||||
return true
|
return true
|
||||||
} else if searchNode.expansionProgress == 1.0 {
|
} else if searchNode.expansionProgress == 1.0 {
|
||||||
var sortItemNode: ListViewItemNode?
|
var sortItemNode: ListViewItemNode?
|
||||||
@ -47,14 +117,14 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if let sortItemNode = sortItemNode {
|
if false, let sortItemNode = sortItemNode {
|
||||||
let itemFrame = sortItemNode.apparentFrame
|
let itemFrame = sortItemNode.apparentFrame
|
||||||
if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) {
|
if itemFrame.contains(CGPoint(x: 0.0, y: listNode.insets.top)) {
|
||||||
var scrollToItem: ListViewScrollToItem?
|
var scrollToItem: ListViewScrollToItem?
|
||||||
if itemFrame.minY + itemFrame.height * 0.6 < listNode.insets.top {
|
if itemFrame.minY + itemFrame.height * 0.6 < listNode.insets.top {
|
||||||
scrollToItem = ListViewScrollToItem(index: 0, position: .top(-50), animated: true, curve: .Default(duration: nil), directionHint: .Up)
|
scrollToItem = ListViewScrollToItem(index: 0, position: .top(-76.0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
|
||||||
} else {
|
} else {
|
||||||
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0), animated: true, curve: .Default(duration: nil), directionHint: .Up)
|
scrollToItem = ListViewScrollToItem(index: 0, position: .top(0), animated: true, curve: .Default(duration: 0.3), directionHint: .Up)
|
||||||
}
|
}
|
||||||
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: ListViewDeleteAndInsertOptions(), scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||||
return true
|
return true
|
||||||
@ -95,11 +165,15 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let sortButton: SortHeaderButton
|
||||||
|
|
||||||
public init(context: AccountContext) {
|
public init(context: AccountContext) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
|
self.sortButton = SortHeaderButton(presentationData: self.presentationData)
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
|
|
||||||
self.tabBarItemContextActionType = .always
|
self.tabBarItemContextActionType = .always
|
||||||
@ -120,6 +194,8 @@ public class ContactsController: ViewController {
|
|||||||
self.tabBarItem.selectedImage = icon
|
self.tabBarItem.selectedImage = icon
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customDisplayNode: self.sortButton)
|
||||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationAddIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.addPressed))
|
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationAddIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.addPressed))
|
||||||
self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Contacts_VoiceOver_AddContact
|
self.navigationItem.rightBarButtonItem?.accessibilityLabel = self.presentationData.strings.Contacts_VoiceOver_AddContact
|
||||||
|
|
||||||
@ -184,6 +260,8 @@ public class ContactsController: ViewController {
|
|||||||
self?.activateSearch()
|
self?.activateSearch()
|
||||||
})
|
})
|
||||||
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
self.navigationBar?.setContentNode(self.searchContentNode, animated: false)
|
||||||
|
|
||||||
|
self.sortButton.addTarget(self, action: #selector(self.sortPressed), forControlEvents: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(coder aDecoder: NSCoder) {
|
required public init(coder aDecoder: NSCoder) {
|
||||||
@ -196,6 +274,7 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateThemeAndStrings() {
|
private func updateThemeAndStrings() {
|
||||||
|
self.sortButton.update(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||||
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData))
|
||||||
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search)
|
self.searchContentNode?.updateThemeAndPlaceholder(theme: self.presentationData.theme, placeholder: self.presentationData.strings.Common_Search)
|
||||||
@ -413,22 +492,13 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contactsNode.contactListNode.openSortMenu = { [weak self] in
|
|
||||||
self?.presentSortMenu()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.contactsNode.contactListNode.contentOffsetChanged = { [weak self] offset in
|
self.contactsNode.contactListNode.contentOffsetChanged = { [weak self] offset in
|
||||||
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let validLayout = strongSelf.validLayout {
|
||||||
var progress: CGFloat = 0.0
|
var offset = offset
|
||||||
switch offset {
|
if validLayout.inVoiceOver {
|
||||||
case let .known(offset):
|
offset = .known(0.0)
|
||||||
progress = max(0.0, (searchContentNode.nominalHeight - max(0.0, offset - 50.0))) / searchContentNode.nominalHeight
|
|
||||||
case .none:
|
|
||||||
progress = 1.0
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
searchContentNode.updateExpansionProgress(progress)
|
searchContentNode.updateListVisibleContentOffset(offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,6 +510,10 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.sortButton.contextAction = { [weak self] sourceNode, gesture in
|
||||||
|
self?.presentSortMenu(sourceNode: sourceNode, gesture: gesture)
|
||||||
|
}
|
||||||
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,6 +539,10 @@ public class ContactsController: ViewController {
|
|||||||
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.contactsNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, actualNavigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func sortPressed() {
|
||||||
|
self.sortButton.contextAction?(self.sortButton.containerNode, nil)
|
||||||
|
}
|
||||||
|
|
||||||
private func activateSearch() {
|
private func activateSearch() {
|
||||||
if self.displayNavigationBar {
|
if self.displayNavigationBar {
|
||||||
if let searchContentNode = self.searchContentNode {
|
if let searchContentNode = self.searchContentNode {
|
||||||
@ -483,7 +561,7 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func presentSortMenu() {
|
private func presentSortMenu(sourceNode: ASDisplayNode, gesture: ContextGesture?) {
|
||||||
let updateSortOrder: (ContactsSortOrder) -> Void = { [weak self] sortOrder in
|
let updateSortOrder: (ContactsSortOrder) -> Void = { [weak self] sortOrder in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.sortOrderPromise.set(.single(sortOrder))
|
strongSelf.sortOrderPromise.set(.single(sortOrder))
|
||||||
@ -495,23 +573,31 @@ public class ContactsController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let actionSheet = ActionSheetController(presentationData: self.presentationData)
|
let presentationData = self.presentationData
|
||||||
var items: [ActionSheetItem] = []
|
let items: Signal<[ContextMenuItem], NoError> = self.context.sharedContext.accountManager.transaction { transaction in
|
||||||
items.append(ActionSheetTextItem(title: self.presentationData.strings.Contacts_SortBy))
|
return transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)
|
||||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Contacts_SortByName, color: .accent, action: { [weak actionSheet] in
|
}
|
||||||
actionSheet?.dismissAnimated()
|
|> map { entry -> [ContextMenuItem] in
|
||||||
updateSortOrder(.natural)
|
let currentSettings: ContactSynchronizationSettings
|
||||||
}))
|
if let entry = entry?.get(ContactSynchronizationSettings.self) {
|
||||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.Contacts_SortByPresence, color: .accent, action: { [weak actionSheet] in
|
currentSettings = entry
|
||||||
actionSheet?.dismissAnimated()
|
} else {
|
||||||
|
currentSettings = .defaultSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
var items: [ContextMenuItem] = []
|
||||||
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByLastSeen, icon: { theme in return currentSettings.sortOrder == .presence ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in
|
||||||
|
f(.default)
|
||||||
updateSortOrder(.presence)
|
updateSortOrder(.presence)
|
||||||
}))
|
})))
|
||||||
actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [
|
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Contacts_Sort_ByName, icon: { theme in return currentSettings.sortOrder == .natural ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in
|
||||||
ActionSheetButtonItem(title: self.presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
f(.default)
|
||||||
actionSheet?.dismissAnimated()
|
updateSortOrder(.natural)
|
||||||
})
|
})))
|
||||||
])])
|
return items
|
||||||
self.present(actionSheet, in: .window(.root))
|
}
|
||||||
|
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(HeaderContextReferenceContentSource(controller: self, sourceNode: self.sortButton.referenceNode)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||||
|
self.presentInGlobalOverlay(contextController)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func addPressed() {
|
@objc func addPressed() {
|
||||||
|
|||||||
@ -741,7 +741,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll
|
|||||||
|
|
||||||
}
|
}
|
||||||
if let dustNode = self.dustNode {
|
if let dustNode = self.dustNode {
|
||||||
dustNode.update(size: textFrame.size, color: .white, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
dustNode.update(size: textFrame.size, color: .white, textColor: .white, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -43,7 +43,7 @@ private func generateMaskImage(size originalSize: CGSize, position: CGPoint, inv
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class InvisibleInkDustNode: ASDisplayNode {
|
public class InvisibleInkDustNode: ASDisplayNode {
|
||||||
private var currentParams: (size: CGSize, color: UIColor, rects: [CGRect], wordRects: [CGRect])?
|
private var currentParams: (size: CGSize, color: UIColor, textColor: UIColor, rects: [CGRect], wordRects: [CGRect])?
|
||||||
private var animColor: CGColor?
|
private var animColor: CGColor?
|
||||||
|
|
||||||
private weak var textNode: TextNode?
|
private weak var textNode: TextNode?
|
||||||
@ -155,7 +155,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) {
|
@objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||||
guard let (_, _, _, _) = self.currentParams, let textNode = self.textNode, !self.isRevealed else {
|
guard let (_, _, textColor, _, _) = self.currentParams, let textNode = self.textNode, !self.isRevealed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
self?.alpha = 0.0
|
self?.alpha = 0.0
|
||||||
self?.emitterNode.view.mask = nil
|
self?.emitterNode.view.mask = nil
|
||||||
|
|
||||||
self?.emitter?.color = UIColor(rgb: 0x000000).cgColor
|
self?.emitter?.color = textColor.cgColor
|
||||||
})
|
})
|
||||||
self.emitterMaskFillNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
self.emitterMaskFillNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
|
|
||||||
let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04)))
|
let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04)))
|
||||||
Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) {
|
Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) {
|
||||||
if let (_, color, _, _) = self.currentParams {
|
if let (_, color, _, _, _) = self.currentParams {
|
||||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||||
let animation = POPBasicAnimation()
|
let animation = POPBasicAnimation()
|
||||||
animation.property = (POPAnimatableProperty.property(withName: "color", initializer: { property in
|
animation.property = (POPAnimatableProperty.property(withName: "color", initializer: { property in
|
||||||
@ -277,7 +277,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateEmitter() {
|
private func updateEmitter() {
|
||||||
guard let (size, color, _, wordRects) = self.currentParams else {
|
guard let (size, color, _, _, wordRects) = self.currentParams else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,8 +299,8 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(size: CGSize, color: UIColor, rects: [CGRect], wordRects: [CGRect]) {
|
public func update(size: CGSize, color: UIColor, textColor: UIColor, rects: [CGRect], wordRects: [CGRect]) {
|
||||||
self.currentParams = (size, color, rects, wordRects)
|
self.currentParams = (size, color, textColor, rects, wordRects)
|
||||||
|
|
||||||
self.emitterNode.frame = CGRect(origin: CGPoint(), size: size)
|
self.emitterNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||||
self.emitterMaskNode.frame = self.emitterNode.bounds
|
self.emitterMaskNode.frame = self.emitterNode.bounds
|
||||||
@ -313,7 +313,7 @@ public class InvisibleInkDustNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||||
if let (_, _, rects, _) = self.currentParams, !self.isRevealed {
|
if let (_, _, _, rects, _) = self.currentParams, !self.isRevealed {
|
||||||
for rect in rects {
|
for rect in rects {
|
||||||
if rect.contains(point) {
|
if rect.contains(point) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -414,7 +414,7 @@ final class ChatMessageNotificationItemNode: NotificationItemNode {
|
|||||||
self.insertSubnode(dustNode, aboveSubnode: self.textNode)
|
self.insertSubnode(dustNode, aboveSubnode: self.textNode)
|
||||||
}
|
}
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
dustNode.update(size: dustNode.frame.size, color: presentationData.theme.inAppNotification.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
dustNode.update(size: dustNode.frame.size, color: presentationData.theme.inAppNotification.primaryTextColor, textColor: presentationData.theme.inAppNotification.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
} else if let dustNode = self.dustNode {
|
} else if let dustNode = self.dustNode {
|
||||||
dustNode.removeFromSupernode()
|
dustNode.removeFromSupernode()
|
||||||
self.dustNode = nil
|
self.dustNode = nil
|
||||||
|
|||||||
@ -258,7 +258,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode {
|
|||||||
node.contentNode.insertSubnode(dustNode, aboveSubnode: textNode)
|
node.contentNode.insertSubnode(dustNode, aboveSubnode: textNode)
|
||||||
}
|
}
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
dustNode.update(size: dustNode.frame.size, color: dustColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
dustNode.update(size: dustNode.frame.size, color: dustColor, textColor: dustColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
} else if let dustNode = node.dustNode {
|
} else if let dustNode = node.dustNode {
|
||||||
dustNode.removeFromSupernode()
|
dustNode.removeFromSupernode()
|
||||||
node.dustNode = nil
|
node.dustNode = nil
|
||||||
|
|||||||
@ -412,7 +412,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
strongSelf.insertSubnode(dustNode, aboveSubnode: spoilerTextNode)
|
strongSelf.insertSubnode(dustNode, aboveSubnode: spoilerTextNode)
|
||||||
}
|
}
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
dustNode.update(size: dustNode.frame.size, color: messageTheme.secondaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
dustNode.update(size: dustNode.frame.size, color: messageTheme.secondaryTextColor, textColor: messageTheme.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
} else if let spoilerTextNode = strongSelf.spoilerTextNode {
|
||||||
strongSelf.spoilerTextNode = nil
|
strongSelf.spoilerTextNode = nil
|
||||||
spoilerTextNode.removeFromSupernode()
|
spoilerTextNode.removeFromSupernode()
|
||||||
|
|||||||
@ -503,7 +503,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
strongSelf.contentTextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode)
|
||||||
}
|
}
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
dustNode.update(size: dustNode.frame.size, color: theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
dustNode.update(size: dustNode.frame.size, color: theme.chat.inputPanel.primaryTextColor, textColor: theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
} else if let dustNode = strongSelf.dustNode {
|
} else if let dustNode = strongSelf.dustNode {
|
||||||
dustNode.removeFromSupernode()
|
dustNode.removeFromSupernode()
|
||||||
strongSelf.dustNode = nil
|
strongSelf.dustNode = nil
|
||||||
|
|||||||
@ -1860,7 +1860,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
self.dustNode = dustNode
|
self.dustNode = dustNode
|
||||||
}
|
}
|
||||||
dustNode.frame = CGRect(origin: CGPoint(), size: textInputNode.textView.contentSize)
|
dustNode.frame = CGRect(origin: CGPoint(), size: textInputNode.textView.contentSize)
|
||||||
dustNode.update(size: textInputNode.textView.contentSize, color: textColor, rects: rects, wordRects: rects)
|
dustNode.update(size: textInputNode.textView.contentSize, color: textColor, textColor: textColor, rects: rects, wordRects: rects)
|
||||||
} else if let dustNode = self.dustNode {
|
} else if let dustNode = self.dustNode {
|
||||||
dustNode.removeFromSupernode()
|
dustNode.removeFromSupernode()
|
||||||
self.dustNode = nil
|
self.dustNode = nil
|
||||||
|
|||||||
@ -804,7 +804,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||||||
self.dustNode = dustNode
|
self.dustNode = dustNode
|
||||||
}
|
}
|
||||||
dustNode.frame = CGRect(origin: CGPoint(), size: textInputNode.textView.contentSize)
|
dustNode.frame = CGRect(origin: CGPoint(), size: textInputNode.textView.contentSize)
|
||||||
dustNode.update(size: textInputNode.textView.contentSize, color: textColor, rects: rects, wordRects: rects)
|
dustNode.update(size: textInputNode.textView.contentSize, color: textColor, textColor: textColor, rects: rects, wordRects: rects)
|
||||||
} else if let dustNode = self.dustNode {
|
} else if let dustNode = self.dustNode {
|
||||||
dustNode.removeFromSupernode()
|
dustNode.removeFromSupernode()
|
||||||
self.dustNode = nil
|
self.dustNode = nil
|
||||||
@ -977,7 +977,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A
|
|||||||
if let oneLineDustNode = self.oneLineDustNode {
|
if let oneLineDustNode = self.oneLineDustNode {
|
||||||
let textFrame = self.oneLineNode.frame.insetBy(dx: 0.0, dy: -3.0)
|
let textFrame = self.oneLineNode.frame.insetBy(dx: 0.0, dy: -3.0)
|
||||||
|
|
||||||
oneLineDustNode.update(size: textFrame.size, color: .white, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) })
|
oneLineDustNode.update(size: textFrame.size, color: .white, textColor: .white, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) })
|
||||||
oneLineDustNode.frame = textFrame
|
oneLineDustNode.frame = textFrame
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -330,7 +330,7 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode {
|
|||||||
|
|
||||||
}
|
}
|
||||||
if let dustNode = self.dustNode {
|
if let dustNode = self.dustNode {
|
||||||
dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.primaryTextColor, textColor: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) })
|
||||||
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||||
}
|
}
|
||||||
} else if let dustNode = self.dustNode {
|
} else if let dustNode = self.dustNode {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user