mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-04 13:38:21 +00:00
Grouping updates
This commit is contained in:
parent
fe3d243272
commit
ea401a3ab5
@ -1151,6 +1151,7 @@
|
||||
D0F0AAE61EC21B68005EE2A5 /* CallControllerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F0AAE51EC21B68005EE2A5 /* CallControllerButton.swift */; };
|
||||
D0F19F6220E5694D00EEC860 /* GroupStickerPackCurrentItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F19F6120E5694D00EEC860 /* GroupStickerPackCurrentItem.swift */; };
|
||||
D0F19F6420E5A15B00EEC860 /* ChatMediaInputPeerSpecificItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F19F6320E5A15B00EEC860 /* ChatMediaInputPeerSpecificItem.swift */; };
|
||||
D0F339372269D83500CFF53F /* ChatArchiveSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F339362269D83500CFF53F /* ChatArchiveSettings.swift */; };
|
||||
D0F4B01A211073C500912B92 /* DeviceContactInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F4B019211073C500912B92 /* DeviceContactInfoController.swift */; };
|
||||
D0F4B0222110972300912B92 /* ContactInfoStrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F4B0212110972300912B92 /* ContactInfoStrings.swift */; };
|
||||
D0F67FF01EE6B8A8000E5906 /* ChannelMembersSearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F67FEF1EE6B8A8000E5906 /* ChannelMembersSearchController.swift */; };
|
||||
@ -2301,6 +2302,7 @@
|
||||
D0F0AAE51EC21B68005EE2A5 /* CallControllerButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallControllerButton.swift; sourceTree = "<group>"; };
|
||||
D0F19F6120E5694D00EEC860 /* GroupStickerPackCurrentItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStickerPackCurrentItem.swift; sourceTree = "<group>"; };
|
||||
D0F19F6320E5A15B00EEC860 /* ChatMediaInputPeerSpecificItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatMediaInputPeerSpecificItem.swift; sourceTree = "<group>"; };
|
||||
D0F339362269D83500CFF53F /* ChatArchiveSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveSettings.swift; sourceTree = "<group>"; };
|
||||
D0F3A8AA1E82D83E00B4C64C /* TelegramAccountAuxiliaryMethods.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelegramAccountAuxiliaryMethods.swift; sourceTree = "<group>"; };
|
||||
D0F3A8B51E83120A00B4C64C /* FetchResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchResource.swift; sourceTree = "<group>"; };
|
||||
D0F3A8B71E83125C00B4C64C /* MediaResources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResources.swift; sourceTree = "<group>"; };
|
||||
@ -3598,6 +3600,7 @@
|
||||
0952D1762177FB5400194860 /* WatchPresetSettings.swift */,
|
||||
0962E67C21BA048D00245FD9 /* WebSearchSettings.swift */,
|
||||
D005808A21CAB8F000CB7CD3 /* VoipDerivedState.swift */,
|
||||
D0F339362269D83500CFF53F /* ChatArchiveSettings.swift */,
|
||||
);
|
||||
name = Settings;
|
||||
sourceTree = "<group>";
|
||||
@ -6262,6 +6265,7 @@
|
||||
D005808B21CAB8F000CB7CD3 /* VoipDerivedState.swift in Sources */,
|
||||
D0471B621EFEB5B70074D609 /* BotPaymentSwitchItemNode.swift in Sources */,
|
||||
D09250041FE5363D003F693F /* ExperimentalSettings.swift in Sources */,
|
||||
D0F339372269D83500CFF53F /* ChatArchiveSettings.swift in Sources */,
|
||||
D0E8175B201254FA00B82BBB /* ChatRecentActionsEmptyNode.swift in Sources */,
|
||||
D0C44B641FC64D0500227BE0 /* SwipeToDismissGestureRecognizer.swift in Sources */,
|
||||
D0EC6E7D1EB9F58900EBF1C3 /* ChangePhoneNumberIntroController.swift in Sources */,
|
||||
|
||||
43
TelegramUI/ChatArchiveSettings.swift
Normal file
43
TelegramUI/ChatArchiveSettings.swift
Normal file
@ -0,0 +1,43 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
|
||||
struct ChatArchiveSettings: Equatable, PreferencesEntry {
|
||||
var isHiddenByDefault: Bool
|
||||
|
||||
static var `default`: ChatArchiveSettings {
|
||||
return ChatArchiveSettings(isHiddenByDefault: true)
|
||||
}
|
||||
|
||||
init(isHiddenByDefault: Bool) {
|
||||
self.isHiddenByDefault = isHiddenByDefault
|
||||
}
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
self.isHiddenByDefault = decoder.decodeInt32ForKey("isHiddenByDefault", orElse: 1) != 0
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.isHiddenByDefault ? 1 : 0, forKey: "isHiddenByDefault")
|
||||
}
|
||||
|
||||
func isEqual(to: PreferencesEntry) -> Bool {
|
||||
if let to = to as? ChatArchiveSettings {
|
||||
return self == to
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateChatArchiveSettings(transaction: Transaction, _ f: @escaping (ChatArchiveSettings) -> ChatArchiveSettings) {
|
||||
transaction.updatePreferencesEntry(key: ApplicationSpecificPreferencesKeys.chatArchiveSettings, { entry in
|
||||
let currentSettings: ChatArchiveSettings
|
||||
if let entry = entry as? ChatArchiveSettings {
|
||||
currentSettings = entry
|
||||
} else {
|
||||
currentSettings = .default
|
||||
}
|
||||
return f(currentSettings)
|
||||
})
|
||||
}
|
||||
@ -202,14 +202,18 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
self.titleDisposable = combineLatest(queue: .mainQueue(), context.account.networkState, hasProxy, passcode, self.chatListDisplayNode.chatListNode.state).start(next: { [weak self] networkState, proxy, passcode, state in
|
||||
if let strongSelf = self {
|
||||
if state.editing {
|
||||
strongSelf.navigationItem.rightBarButtonItem = nil
|
||||
if strongSelf.groupId == nil {
|
||||
strongSelf.navigationItem.rightBarButtonItem = nil
|
||||
}
|
||||
|
||||
let title = !state.selectedPeerIds.isEmpty ? strongSelf.presentationData.strings.ChatList_SelectedChats(Int32(state.selectedPeerIds.count)) : strongSelf.presentationData.strings.DialogList_Title
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false)
|
||||
} else {
|
||||
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(strongSelf.presentationData.theme), style: .plain, target: strongSelf, action: #selector(strongSelf.composePressed))
|
||||
rightBarButtonItem.accessibilityLabel = "Compose"
|
||||
strongSelf.navigationItem.rightBarButtonItem = rightBarButtonItem
|
||||
if strongSelf.groupId == nil {
|
||||
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(strongSelf.presentationData.theme), style: .plain, target: strongSelf, action: #selector(strongSelf.composePressed))
|
||||
rightBarButtonItem.accessibilityLabel = "Compose"
|
||||
strongSelf.navigationItem.rightBarButtonItem = rightBarButtonItem
|
||||
}
|
||||
|
||||
let (hasProxy, connectsViaProxy) = proxy
|
||||
let (isPasscodeSet, isManuallyLocked) = passcode
|
||||
@ -738,9 +742,9 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
}
|
||||
searchContentNode.updateExpansionProgress(progress)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
self.chatListDisplayNode.chatListNode.contentScrollingEnded = { [weak self] listView in
|
||||
/*self.chatListDisplayNode.chatListNode.contentScrollingEnded = { [weak self] listView in
|
||||
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
||||
return fixListNodeScrolling(listView, searchNode: searchContentNode)
|
||||
} else {
|
||||
@ -748,6 +752,10 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
}
|
||||
}*/
|
||||
|
||||
self.chatListDisplayNode.toolbarActionSelected = { [weak self] left in
|
||||
self?.toolbarActionSelected(left: left)
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
let peerIdsAndOptions: Signal<(ChatListSelectionOptions, Set<PeerId>)?, NoError> = self.chatListDisplayNode.chatListNode.state
|
||||
|> map { state -> Set<PeerId>? in
|
||||
@ -769,18 +777,29 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
}
|
||||
|
||||
self.stateDisposable.set(combineLatest(queue: .mainQueue(), self.presentationDataValue.get(), peerIdsAndOptions).start(next: { [weak self] presentationData, peerIdsAndOptions in
|
||||
var toolbar: Toolbar?
|
||||
if let (options, _) = peerIdsAndOptions {
|
||||
let leftAction: ToolbarAction
|
||||
switch options.read {
|
||||
case let .all(enabled):
|
||||
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
|
||||
case let .selective(enabled):
|
||||
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
|
||||
}
|
||||
toolbar = Toolbar(leftAction: leftAction, rightAction: ToolbarAction(title: presentationData.strings.Common_Delete, isEnabled: options.delete))
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
self?.setToolbar(toolbar, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
var toolbar: Toolbar?
|
||||
if strongSelf.groupId == nil {
|
||||
if let (options, _) = peerIdsAndOptions {
|
||||
let leftAction: ToolbarAction
|
||||
switch options.read {
|
||||
case let .all(enabled):
|
||||
leftAction = ToolbarAction(title: presentationData.strings.ChatList_ReadAll, isEnabled: enabled)
|
||||
case let .selective(enabled):
|
||||
leftAction = ToolbarAction(title: presentationData.strings.ChatList_Read, isEnabled: enabled)
|
||||
}
|
||||
toolbar = Toolbar(leftAction: leftAction, rightAction: ToolbarAction(title: presentationData.strings.Common_Delete, isEnabled: options.delete))
|
||||
}
|
||||
} else {
|
||||
if let (options, peerIds) = peerIdsAndOptions {
|
||||
let leftAction: ToolbarAction
|
||||
leftAction = ToolbarAction(title: presentationData.strings.ChatList_UnarchiveAction, isEnabled: !peerIds.isEmpty)
|
||||
toolbar = Toolbar(leftAction: leftAction, rightAction: ToolbarAction(title: presentationData.strings.Common_Delete, isEnabled: options.delete))
|
||||
}
|
||||
}
|
||||
strongSelf.setToolbar(toolbar, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}))
|
||||
|
||||
/*self.badgeIconDisposable = (self.chatListDisplayNode.chatListNode.scrollToTopOption
|
||||
@ -939,7 +958,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
self.chatListDisplayNode.chatListNode.scrollToPosition(.top)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, transition: transition)
|
||||
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationInsetHeight, visualNavigationHeight: self.visualNavigationInsetHeight, transition: transition)
|
||||
}
|
||||
|
||||
override public func navigationStackConfigurationUpdated(next: [ViewController]) {
|
||||
@ -1089,7 +1108,7 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .groupReference(groupId, _, _):
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
let chatListController = ChatListController(context: self.context, groupId: groupId, controlsHistoryPreload: false)
|
||||
chatListController.containerLayoutUpdated(ContainerViewLayout(size: contentSize, metrics: LayoutMetrics(), intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: nil, inputHeight: nil, standardInputHeight: 216.0, inputHeightIsInteractivellyChanging: false, inVoiceOver: false), transition: .immediate)
|
||||
return (chatListController, sourceRect)
|
||||
@ -1186,24 +1205,40 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
override public func toolbarActionSelected(left: Bool) {
|
||||
let peerIds = self.chatListDisplayNode.chatListNode.currentState.selectedPeerIds
|
||||
if left {
|
||||
let signal: Signal<Void, NoError>
|
||||
let context = self.context
|
||||
if !peerIds.isEmpty {
|
||||
signal = self.context.account.postbox.transaction { transaction -> Void in
|
||||
for peerId in peerIds {
|
||||
togglePeerUnreadMarkInteractively(transaction: transaction, viewTracker: context.account.viewTracker, peerId: peerId, setToValue: false)
|
||||
if self.groupId == nil {
|
||||
let signal: Signal<Void, NoError>
|
||||
let context = self.context
|
||||
if !peerIds.isEmpty {
|
||||
signal = self.context.account.postbox.transaction { transaction -> Void in
|
||||
for peerId in peerIds {
|
||||
togglePeerUnreadMarkInteractively(transaction: transaction, viewTracker: context.account.viewTracker, peerId: peerId, setToValue: false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
signal = self.context.account.postbox.transaction { transaction -> Void in
|
||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
signal = self.context.account.postbox.transaction { transaction -> Void in
|
||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker)
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.donePressed()
|
||||
})
|
||||
} else if !peerIds.isEmpty {
|
||||
self.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(peerIds.first!)
|
||||
let _ = (self.context.account.postbox.transaction { transaction -> Void in
|
||||
for peerId in peerIds {
|
||||
updatePeerGroupIdInteractively(transaction: transaction, peerId: peerId, groupId: nil)
|
||||
}
|
||||
}
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatListDisplayNode.chatListNode.setCurrentRemovingPeerId(nil)
|
||||
strongSelf.donePressed()
|
||||
})
|
||||
}
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.donePressed()
|
||||
})
|
||||
} else if !peerIds.isEmpty {
|
||||
} else if !left && !peerIds.isEmpty {
|
||||
let actionSheet = ActionSheetController(presentationTheme: self.presentationData.theme)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: self.presentationData.strings.ChatList_DeleteConfirmation(Int32(peerIds.count)), color: .destructive, action: { [weak self, weak actionSheet] in
|
||||
@ -1414,4 +1449,13 @@ public class ChatListController: TelegramController, KeyShortcutResponder, UIVie
|
||||
}
|
||||
}), in: .window(.root))
|
||||
}
|
||||
|
||||
override public func setToolbar(_ toolbar: Toolbar?, transition: ContainedViewLayoutTransition) {
|
||||
if self.groupId == nil {
|
||||
super.setToolbar(toolbar, transition: transition)
|
||||
} else {
|
||||
self.chatListDisplayNode.toolbar = toolbar
|
||||
self.requestLayout(transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,9 +28,13 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
var navigationBar: NavigationBar?
|
||||
weak var controller: ChatListController?
|
||||
|
||||
var toolbar: Toolbar?
|
||||
private var toolbarNode: ToolbarNode?
|
||||
var toolbarActionSelected: ((Bool) -> Void)?
|
||||
|
||||
private(set) var searchDisplayController: SearchDisplayController?
|
||||
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat)?
|
||||
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((Peer, Bool) -> Void)?
|
||||
@ -69,8 +73,8 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
let chatListEmptyNode = ChatListEmptyNode(theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings)
|
||||
strongSelf.chatListEmptyNode = chatListEmptyNode
|
||||
strongSelf.insertSubnode(chatListEmptyNode, belowSubnode: strongSelf.chatListNode)
|
||||
if let (layout, navigationHeight) = strongSelf.containerLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate)
|
||||
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate)
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -87,8 +91,8 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
let chatListEmptyIndicator = ActivityIndicator(type: .custom(strongSelf.presentationData.theme.list.itemAccentColor, 22.0, 1.0, false))
|
||||
strongSelf.chatListEmptyIndicator = chatListEmptyIndicator
|
||||
strongSelf.insertSubnode(chatListEmptyIndicator, belowSubnode: strongSelf.chatListNode)
|
||||
if let (layout, navigationHeight) = strongSelf.containerLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate)
|
||||
if let (layout, navigationHeight, visualNavigationHeight) = strongSelf.containerLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, visualNavigationHeight: visualNavigationHeight, transition: .immediate)
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -114,10 +118,14 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
self.chatListNode.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: self.presentationData.disableAnimations)
|
||||
self.searchDisplayController?.updatePresentationData(presentationData)
|
||||
self.chatListEmptyNode?.updateThemeAndStrings(theme: self.presentationData.theme, strings: self.presentationData.strings)
|
||||
|
||||
if let toolbarNode = self.toolbarNode {
|
||||
toolbarNode.updateTheme(TabBarControllerTheme(rootControllerTheme: self.presentationData.theme))
|
||||
}
|
||||
}
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.containerLayout = (layout, navigationBarHeight)
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.containerLayout = (layout, navigationBarHeight, visualNavigationHeight)
|
||||
|
||||
var insets = layout.insets(options: [.input])
|
||||
insets.top += navigationBarHeight
|
||||
@ -152,6 +160,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, duration: duration, curve: listViewCurve)
|
||||
|
||||
self.chatListNode.visualInsets = UIEdgeInsets(top: visualNavigationHeight, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
|
||||
if let chatListEmptyNode = self.chatListEmptyNode {
|
||||
@ -168,10 +177,49 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
if let searchDisplayController = self.searchDisplayController {
|
||||
searchDisplayController.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: transition)
|
||||
}
|
||||
|
||||
if let toolbar = self.toolbar {
|
||||
var tabBarHeight: CGFloat
|
||||
var options: ContainerViewLayoutInsetOptions = []
|
||||
if layout.metrics.widthClass == .regular {
|
||||
options.insert(.input)
|
||||
}
|
||||
let bottomInset: CGFloat = layout.insets(options: options).bottom
|
||||
if !layout.safeInsets.left.isZero {
|
||||
tabBarHeight = 34.0 + bottomInset
|
||||
} else {
|
||||
tabBarHeight = 49.0 + bottomInset
|
||||
}
|
||||
|
||||
let tabBarFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
|
||||
|
||||
if let toolbarNode = self.toolbarNode {
|
||||
transition.updateFrame(node: toolbarNode, frame: tabBarFrame)
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: transition)
|
||||
} else {
|
||||
let toolbarNode = ToolbarNode(theme: TabBarControllerTheme(rootControllerTheme: self.presentationData.theme), displaySeparator: true, left: { [weak self] in
|
||||
self?.toolbarActionSelected?(true)
|
||||
}, right: { [weak self] in
|
||||
self?.toolbarActionSelected?(false)
|
||||
})
|
||||
toolbarNode.frame = tabBarFrame
|
||||
toolbarNode.updateLayout(size: tabBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, toolbar: toolbar, transition: .immediate)
|
||||
self.addSubnode(toolbarNode)
|
||||
self.toolbarNode = toolbarNode
|
||||
if transition.isAnimated {
|
||||
toolbarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
} else if let toolbarNode = self.toolbarNode {
|
||||
self.toolbarNode = nil
|
||||
transition.updateAlpha(node: toolbarNode, alpha: 0.0, completion: { [weak toolbarNode] _ in
|
||||
toolbarNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func activateSearch(placeholderNode: SearchBarPlaceholderNode) {
|
||||
guard let (containerLayout, navigationBarHeight) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
|
||||
guard let (containerLayout, navigationBarHeight, _) = self.containerLayout, let navigationBar = self.navigationBar, self.searchDisplayController == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import TelegramCore
|
||||
|
||||
enum ChatListItemContent {
|
||||
case peer(message: Message?, peer: RenderedPeer, combinedReadState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, embeddedState: PeerChatListEmbeddedInterfaceState?, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool, ignoreUnreadBadge: Bool)
|
||||
case groupReference(groupId: PeerGroupId, message: Message?, unreadState: ChatListTotalUnreadState)
|
||||
case groupReference(groupId: PeerGroupId, peer: RenderedPeer, message: Message?, unreadState: ChatListTotalUnreadState, hiddenByDefault: Bool)
|
||||
|
||||
var chatLocation: ChatLocation? {
|
||||
switch self {
|
||||
@ -30,13 +30,14 @@ class ChatListItem: ListViewItem {
|
||||
let hasActiveRevealControls: Bool
|
||||
let selected: Bool
|
||||
let enableContextActions: Bool
|
||||
let hiddenOffset: Bool
|
||||
let interaction: ChatListNodeInteraction
|
||||
|
||||
let selectable: Bool = true
|
||||
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
init(presentationData: ChatListPresentationData, account: Account, peerGroupId: PeerGroupId?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, interaction: ChatListNodeInteraction) {
|
||||
init(presentationData: ChatListPresentationData, account: Account, peerGroupId: PeerGroupId?, index: ChatListIndex, content: ChatListItemContent, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, header: ListViewItemHeader?, enableContextActions: Bool, hiddenOffset: Bool, interaction: ChatListNodeInteraction) {
|
||||
self.presentationData = presentationData
|
||||
self.peerGroupId = peerGroupId
|
||||
self.account = account
|
||||
@ -47,6 +48,7 @@ class ChatListItem: ListViewItem {
|
||||
self.selected = selected
|
||||
self.header = header
|
||||
self.enableContextActions = enableContextActions
|
||||
self.hiddenOffset = hiddenOffset
|
||||
self.interaction = interaction
|
||||
}
|
||||
|
||||
@ -107,7 +109,7 @@ class ChatListItem: ListViewItem {
|
||||
} else if let peer = peer.peers[peer.peerId] {
|
||||
self.interaction.peerSelected(peer)
|
||||
}
|
||||
case let .groupReference(groupId, _, _):
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
self.interaction.groupSelected(groupId)
|
||||
}
|
||||
}
|
||||
@ -200,10 +202,10 @@ private func revealOptions(strings: PresentationStrings, theme: PresentationThem
|
||||
return options
|
||||
}
|
||||
|
||||
private func groupReferenceRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isEditing: Bool) -> [ItemListRevealOption] {
|
||||
private func groupReferenceRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isEditing: Bool, hiddenByDefault: Bool) -> [ItemListRevealOption] {
|
||||
var options: [ItemListRevealOption] = []
|
||||
if !isEditing {
|
||||
if false {
|
||||
if hiddenByDefault {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.unhide.rawValue, title: strings.ChatList_UnhideAction, icon: unhideIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
} else {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.hide.rawValue, title: strings.ChatList_HideAction, icon: hideIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
@ -212,20 +214,20 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr
|
||||
return options
|
||||
}
|
||||
|
||||
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool?) -> [ItemListRevealOption] {
|
||||
private func leftRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isUnread: Bool, isEditing: Bool, isPinned: Bool, isSavedMessages: Bool) -> [ItemListRevealOption] {
|
||||
var options: [ItemListRevealOption] = []
|
||||
if isUnread {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.toggleMarkedUnread.rawValue, title: strings.DialogList_Read, icon: readIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
} else {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.toggleMarkedUnread.rawValue, title: strings.DialogList_Unread, icon: unreadIcon, color: theme.list.itemDisclosureActions.accent.fillColor, textColor: theme.list.itemDisclosureActions.accent.foregroundColor))
|
||||
if !isSavedMessages {
|
||||
if isUnread {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.toggleMarkedUnread.rawValue, title: strings.DialogList_Read, icon: readIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
} else {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.toggleMarkedUnread.rawValue, title: strings.DialogList_Unread, icon: unreadIcon, color: theme.list.itemDisclosureActions.accent.fillColor, textColor: theme.list.itemDisclosureActions.accent.foregroundColor))
|
||||
}
|
||||
}
|
||||
if !isEditing {
|
||||
if let isPinned = isPinned {
|
||||
if isPinned {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
} else {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
}
|
||||
if isPinned {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
} else {
|
||||
options.append(ItemListRevealOption(key: RevealOptionKey.pin.rawValue, title: strings.DialogList_Pin, icon: pinIcon, color: theme.list.itemDisclosureActions.neutral1.fillColor, textColor: theme.list.itemDisclosureActions.neutral1.foregroundColor))
|
||||
}
|
||||
}
|
||||
return options
|
||||
@ -526,8 +528,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let currentItem = self.layoutParams?.0
|
||||
|
||||
let multipleAvatarsLayout = MultipleAvatarsNode.asyncLayout(self.multipleAvatarsNode)
|
||||
|
||||
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
||||
let account = item.account
|
||||
var message: Message?
|
||||
@ -542,6 +542,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let isPeerGroup: Bool
|
||||
let isAd: Bool
|
||||
|
||||
var groupHiddenByDefault = false
|
||||
|
||||
switch item.content {
|
||||
case let .peer(messageValue, peerValue, combinedReadStateValue, notificationSettingsValue, peerPresenceValue, summaryInfoValue, embeddedStateValue, inputActivitiesValue, isAdValue, ignoreUnreadBadge):
|
||||
message = messageValue
|
||||
@ -563,12 +565,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
inputActivities = inputActivitiesValue
|
||||
isPeerGroup = false
|
||||
isAd = isAdValue
|
||||
case let .groupReference(_, messageValue, unreadState):
|
||||
if let messageValue = messageValue {
|
||||
itemPeer = RenderedPeer(message: messageValue)
|
||||
} else {
|
||||
itemPeer = RenderedPeer(peerId: item.index.messageIndex.id.peerId, peers: SimpleDictionary())
|
||||
}
|
||||
case let .groupReference(_, peer, messageValue, unreadState, hiddenByDefault):
|
||||
itemPeer = peer
|
||||
message = messageValue
|
||||
combinedReadState = nil
|
||||
notificationSettings = nil
|
||||
@ -576,6 +574,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
summaryInfo = ChatListMessageTagSummaryInfo()
|
||||
inputActivities = nil
|
||||
isPeerGroup = true
|
||||
groupHiddenByDefault = hiddenByDefault
|
||||
let unmutedCount = unreadState.count(for: .filtered, in: .chats, with: [.regularChatsAndPrivateGroups, .publicGroups, .channels])
|
||||
let mutedCount = unreadState.count(for: .raw, in: .chats, with: [.regularChatsAndPrivateGroups, .publicGroups, .channels])
|
||||
unreadCount = (unmutedCount, unmutedCount != 0 || mutedCount != 0, false, max(0, mutedCount - unmutedCount))
|
||||
@ -688,7 +687,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
|
||||
}
|
||||
} else {
|
||||
attributedText = NSAttributedString(string: messageText as String, font: textFont, textColor: theme.messageTextColor)
|
||||
attributedText = NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
|
||||
|
||||
var peerText: String?
|
||||
if case .groupReference = item.content {
|
||||
if let messagePeer = itemPeer.chatMainPeer {
|
||||
peerText = messagePeer.displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder)
|
||||
}
|
||||
}
|
||||
|
||||
if let peerText = peerText {
|
||||
authorAttributedString = NSAttributedString(string: peerText, font: textFont, textColor: theme.authorNameColor)
|
||||
}
|
||||
}
|
||||
|
||||
switch item.content {
|
||||
@ -793,7 +803,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
var isVerified = false
|
||||
let isSecret = item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat
|
||||
let isSecret = !isPeerGroup && item.index.messageIndex.id.peerId.namespace == Namespaces.Peer.SecretChat
|
||||
|
||||
if case .peer = item.content {
|
||||
if let peer = itemPeer.chatMainPeer {
|
||||
@ -823,7 +833,12 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
titleIconsWidth += currentVerificationIconImage.size.width
|
||||
}
|
||||
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0))
|
||||
var layoutOffset: CGFloat = 0.0
|
||||
if item.hiddenOffset {
|
||||
layoutOffset = -itemHeight
|
||||
}
|
||||
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0))
|
||||
|
||||
let (dateLayout, dateApply) = dateLayout(TextNodeLayoutArguments(attributedString: dateAttributedString, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: rawContentRect.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
@ -880,24 +895,17 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
var isPinned: Bool?
|
||||
if item.peerGroupId == nil {
|
||||
isPinned = item.index.pinningIndex != nil
|
||||
}
|
||||
let isPinned = item.index.pinningIndex != nil
|
||||
|
||||
if item.enableContextActions && !isAd {
|
||||
peerRevealOptions = revealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isPinned: isPinned, isMuted: item.account.peerId != item.index.messageIndex.id.peerId ? (currentMutedIconImage != nil) : nil, groupId: item.peerGroupId, canDelete: true, isEditing: item.editing)
|
||||
if itemPeer.peerId != item.account.peerId && item.peerGroupId == nil {
|
||||
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread, isEditing: item.editing, isPinned: isPinned)
|
||||
} else {
|
||||
peerLeftRevealOptions = []
|
||||
}
|
||||
peerLeftRevealOptions = leftRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isUnread: unreadCount.unread, isEditing: item.editing, isPinned: isPinned, isSavedMessages: itemPeer.peerId != item.account.peerId)
|
||||
} else {
|
||||
peerRevealOptions = []
|
||||
peerLeftRevealOptions = []
|
||||
}
|
||||
case .groupReference:
|
||||
peerRevealOptions = groupReferenceRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isEditing: item.editing)
|
||||
peerRevealOptions = groupReferenceRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isEditing: item.editing, hiddenByDefault: groupHiddenByDefault)
|
||||
peerLeftRevealOptions = []
|
||||
}
|
||||
|
||||
@ -908,7 +916,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: firstWithHeader)
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: itemHeight), insets: insets)
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: max(0.0, itemHeight + layoutOffset)), insets: insets)
|
||||
|
||||
return (layout, { [weak self] synchronousLoads, animated in
|
||||
if let strongSelf = self {
|
||||
@ -931,14 +939,14 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var crossfadeContent = false
|
||||
if let selectableControlSizeAndApply = selectableControlSizeAndApply {
|
||||
let selectableControlSize = CGSize(width: selectableControlSizeAndApply.0, height: layout.contentSize.height)
|
||||
let selectableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: 0.0), size: selectableControlSize)
|
||||
let selectableControlFrame = CGRect(origin: CGPoint(x: params.leftInset + revealOffset, y: layoutOffset), size: selectableControlSize)
|
||||
if strongSelf.selectableControlNode == nil {
|
||||
crossfadeContent = true
|
||||
let selectableControlNode = selectableControlSizeAndApply.1(selectableControlSize, false)
|
||||
strongSelf.selectableControlNode = selectableControlNode
|
||||
strongSelf.addSubnode(selectableControlNode)
|
||||
selectableControlNode.frame = selectableControlFrame
|
||||
transition.animatePosition(node: selectableControlNode, from: CGPoint(x: -selectableControlFrame.size.width / 2.0, y: selectableControlFrame.midY))
|
||||
transition.animatePosition(node: selectableControlNode, from: CGPoint(x: -selectableControlFrame.size.width / 2.0, y: layoutOffset + selectableControlFrame.midY))
|
||||
selectableControlNode.alpha = 0.0
|
||||
transition.updateAlpha(node: selectableControlNode, alpha: 1.0)
|
||||
} else if let selectableControlNode = strongSelf.selectableControlNode {
|
||||
@ -957,7 +965,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
if let reorderControlSizeAndApply = reorderControlSizeAndApply {
|
||||
let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0.width, y: 0.0), size: reorderControlSizeAndApply.0)
|
||||
let reorderControlFrame = CGRect(origin: CGPoint(x: params.width + revealOffset - params.rightInset - reorderControlSizeAndApply.0.width, y: layoutOffset), size: reorderControlSizeAndApply.0)
|
||||
if strongSelf.reorderControlNode == nil {
|
||||
let reorderControlNode = reorderControlSizeAndApply.1(false)
|
||||
strongSelf.reorderControlNode = reorderControlNode
|
||||
@ -987,7 +995,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
transition.updateAlpha(node: strongSelf.statusNode, alpha: 1.0)
|
||||
}
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: leftInset - 78.0 + editingOffset + 10.0 + revealOffset, y: 7.0), size: CGSize(width: 60.0, height: 60.0))
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: leftInset - 78.0 + editingOffset + 10.0 + revealOffset, y: layoutOffset + 7.0), size: CGSize(width: 60.0, height: 60.0))
|
||||
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
|
||||
|
||||
let onlineFrame = CGRect(origin: CGPoint(x: avatarFrame.maxX - onlineLayout.width - 2.0, y: avatarFrame.maxY - onlineLayout.height - 2.0), size: onlineLayout)
|
||||
@ -1029,7 +1037,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0)
|
||||
|
||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size)
|
||||
transition.updateFrame(node: strongSelf.dateNode, frame: CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width, y: contentRect.origin.y + 2.0), size: dateLayout.size))
|
||||
|
||||
let statusSize = CGSize(width: 24.0, height: 24.0)
|
||||
strongSelf.statusNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + contentRect.size.width - dateLayout.size.width - statusSize.width, y: contentRect.origin.y + 2.0 - UIScreenPixel + floor((dateLayout.size.height - statusSize.height) / 2.0)), size: statusSize)
|
||||
@ -1131,7 +1139,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.mutedIconNode.isHidden = true
|
||||
}
|
||||
|
||||
let contentDeltaX = contentRect.origin.x - (strongSelf.titleNode.frame.minX - titleOffset)
|
||||
let contentDelta = CGPoint(x: contentRect.origin.x - (strongSelf.titleNode.frame.minX - titleOffset), y: contentRect.origin.y - (strongSelf.titleNode.frame.minY - UIScreenPixel))
|
||||
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: contentRect.origin.y + UIScreenPixel), size: titleLayout.size)
|
||||
let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.minY + titleLayout.size.height), size: authorLayout.size)
|
||||
strongSelf.authorNode.frame = authorNodeFrame
|
||||
@ -1185,15 +1193,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
inputActivitiesApply?()
|
||||
|
||||
if !contentDeltaX.isZero {
|
||||
if !contentDelta.x.isZero || !contentDelta.y.isZero {
|
||||
let titlePosition = strongSelf.titleNode.position
|
||||
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDeltaX, y: titlePosition.y))
|
||||
transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y))
|
||||
|
||||
let textPosition = strongSelf.textNode.position
|
||||
transition.animatePosition(node: strongSelf.textNode, from: CGPoint(x: textPosition.x - contentDeltaX, y: textPosition.y))
|
||||
transition.animatePosition(node: strongSelf.textNode, from: CGPoint(x: textPosition.x - contentDelta.x, y: textPosition.y - contentDelta.y))
|
||||
|
||||
let authorPosition = strongSelf.authorNode.position
|
||||
transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDeltaX, y: authorPosition.y))
|
||||
transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDelta.x, y: authorPosition.y - contentDelta.y))
|
||||
}
|
||||
|
||||
if crossfadeContent {
|
||||
@ -1203,15 +1211,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
let separatorInset: CGFloat
|
||||
if (!nextIsPinned && item.index.pinningIndex != nil) || last {
|
||||
if (!nextIsPinned && item.index.pinningIndex != nil) || last || groupHiddenByDefault {
|
||||
separatorInset = 0.0
|
||||
} else {
|
||||
separatorInset = editingOffset + leftInset + rawContentRect.origin.x
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: layoutOffset + itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.contentSize))
|
||||
if item.selected {
|
||||
strongSelf.backgroundNode.backgroundColor = theme.itemSelectedBackgroundColor
|
||||
} else if item.index.pinningIndex != nil {
|
||||
@ -1220,7 +1228,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.backgroundNode.backgroundColor = theme.itemBackgroundColor
|
||||
}
|
||||
let topNegativeInset: CGFloat = 0.0
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight - topNegativeInset), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height + separatorHeight + topNegativeInset))
|
||||
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layoutOffset - separatorHeight - topNegativeInset), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height + separatorHeight + topNegativeInset))
|
||||
|
||||
if let peerPresence = peerPresence as? TelegramUserPresence {
|
||||
strongSelf.peerPresenceManager?.reset(presence: peerPresence)
|
||||
@ -1262,7 +1270,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.updateRevealOffset(offset: offset, transition: transition)
|
||||
|
||||
if let _ = self.item, let params = self.layoutParams?.5 {
|
||||
if let item = self.item, let params = self.layoutParams?.5 {
|
||||
let editingOffset: CGFloat
|
||||
if let selectableControlNode = self.selectableControlNode {
|
||||
editingOffset = selectableControlNode.bounds.size.width
|
||||
@ -1273,6 +1281,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
editingOffset = 0.0
|
||||
}
|
||||
|
||||
var layoutOffset: CGFloat = 0.0
|
||||
if item.hiddenOffset {
|
||||
layoutOffset = -itemHeight
|
||||
}
|
||||
|
||||
if let reorderControlNode = self.reorderControlNode {
|
||||
var reorderControlFrame = reorderControlNode.frame
|
||||
reorderControlFrame.origin.x = params.width - params.rightInset - reorderControlFrame.size.width + offset
|
||||
@ -1281,7 +1294,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
let leftInset: CGFloat = params.leftInset + 78.0
|
||||
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0))
|
||||
let rawContentRect = CGRect(origin: CGPoint(x: 2.0, y: layoutOffset + 8.0), size: CGSize(width: params.width - leftInset - params.rightInset - 10.0 - 1.0 - editingOffset, height: itemHeight - 12.0 - 9.0))
|
||||
|
||||
let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + offset, dy: 0.0)
|
||||
|
||||
@ -1408,6 +1421,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
case RevealOptionKey.toggleMarkedUnread.rawValue:
|
||||
item.interaction.togglePeerMarkedUnread(item.index.messageIndex.id.peerId, animated)
|
||||
close = false
|
||||
case RevealOptionKey.hide.rawValue, RevealOptionKey.unhide.rawValue:
|
||||
item.interaction.toggleArchivedFolderHiddenByDefault()
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
@ -69,10 +69,11 @@ final class ChatListNodeInteraction {
|
||||
let deletePeer: (PeerId) -> Void
|
||||
let updatePeerGrouping: (PeerId, Bool) -> Void
|
||||
let togglePeerMarkedUnread: (PeerId, Bool) -> Void
|
||||
let toggleArchivedFolderHiddenByDefault: () -> Void
|
||||
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
|
||||
init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void) {
|
||||
init(activateSearch: @escaping () -> Void, peerSelected: @escaping (Peer) -> Void, togglePeerSelected: @escaping (PeerId) -> Void, messageSelected: @escaping (Peer, Message, Bool) -> Void, groupSelected: @escaping (PeerGroupId) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (PeerId?, PeerId?) -> Void, setItemPinned: @escaping (PinnedItemId, Bool) -> Void, setPeerMuted: @escaping (PeerId, Bool) -> Void, deletePeer: @escaping (PeerId) -> Void, updatePeerGrouping: @escaping (PeerId, Bool) -> Void, togglePeerMarkedUnread: @escaping (PeerId, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
self.togglePeerSelected = togglePeerSelected
|
||||
@ -85,6 +86,7 @@ final class ChatListNodeInteraction {
|
||||
self.deletePeer = deletePeer
|
||||
self.updatePeerGrouping = updatePeerGrouping
|
||||
self.togglePeerMarkedUnread = togglePeerMarkedUnread
|
||||
self.toggleArchivedFolderHiddenByDefault = toggleArchivedFolderHiddenByDefault
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,6 +106,7 @@ struct ChatListNodeState: Equatable {
|
||||
var peerInputActivities: ChatListNodePeerInputActivities?
|
||||
var pendingRemovalPeerIds: Set<PeerId>
|
||||
var pendingClearHistoryPeerIds: Set<PeerId>
|
||||
var archiveShouldBeTemporaryRevealed: Bool
|
||||
|
||||
static func ==(lhs: ChatListNodeState, rhs: ChatListNodeState) -> Bool {
|
||||
if lhs.presentationData !== rhs.presentationData {
|
||||
@ -127,6 +130,9 @@ struct ChatListNodeState: Equatable {
|
||||
if lhs.pendingClearHistoryPeerIds != rhs.pendingClearHistoryPeerIds {
|
||||
return false
|
||||
}
|
||||
if lhs.archiveShouldBeTemporaryRevealed != rhs.archiveShouldBeTemporaryRevealed {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -137,7 +143,7 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
case let .peers(filter):
|
||||
let itemPeer = peer.chatMainPeer
|
||||
var chatPeer: Peer?
|
||||
@ -204,8 +210,8 @@ private func mappedInsertEntries(account: Account, nodeInteraction: ChatListNode
|
||||
}
|
||||
case let .HoleEntry(_, theme):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
||||
case let .GroupReferenceEntry(index, presentationData, groupId, message, editing, unreadState):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, unreadState: unreadState), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
case let .GroupReferenceEntry(index, presentationData, groupId, peer, message, editing, unreadState, revealed, hiddenByDefault):
|
||||
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, peer: peer, message: message, unreadState: unreadState, hiddenByDefault: hiddenByDefault), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, hiddenOffset: hiddenByDefault && !revealed, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,7 +222,7 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode
|
||||
case let .PeerEntry(index, presentationData, message, combinedReadState, notificationSettings, embeddedState, peer, presence, summaryInfo, editing, hasActiveRevealControls, selected, inputActivities, isAd):
|
||||
switch mode {
|
||||
case .chatList:
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .peer(message: message, peer: peer, combinedReadState: combinedReadState, notificationSettings: notificationSettings, presence: presence, summaryInfo: summaryInfo, embeddedState: embeddedState, inputActivities: inputActivities, isAd: isAd, ignoreUnreadBadge: false), editing: editing, hasActiveRevealControls: hasActiveRevealControls, selected: selected, header: nil, enableContextActions: true, hiddenOffset: false, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
case let .peers(filter):
|
||||
let itemPeer = peer.chatMainPeer
|
||||
var chatPeer: Peer?
|
||||
@ -241,8 +247,8 @@ private func mappedUpdateEntries(account: Account, nodeInteraction: ChatListNode
|
||||
}
|
||||
case let .HoleEntry(_, theme):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
|
||||
case let .GroupReferenceEntry(index, presentationData, groupId, message, editing, unreadState):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, message: message, unreadState: unreadState), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
case let .GroupReferenceEntry(index, presentationData, groupId, peer, message, editing, unreadState, revealed, hiddenByDefault):
|
||||
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(presentationData: presentationData, account: account, peerGroupId: peerGroupId, index: index, content: .groupReference(groupId: groupId, peer: peer, message: message, unreadState: unreadState, hiddenByDefault: hiddenByDefault), editing: editing, hasActiveRevealControls: false, selected: false, header: nil, enableContextActions: true, hiddenOffset: hiddenByDefault && !revealed, interaction: nodeInteraction), directionHint: entry.directionHint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -366,7 +372,7 @@ final class ChatListNode: ListView {
|
||||
self.controlsHistoryPreload = controlsHistoryPreload
|
||||
self.mode = mode
|
||||
|
||||
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), peerInputActivities: nil, pendingRemovalPeerIds: Set(), pendingClearHistoryPeerIds: Set())
|
||||
self.currentState = ChatListNodeState(presentationData: ChatListPresentationData(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, nameSortOrder: nameSortOrder, nameDisplayOrder: nameDisplayOrder, disableAnimations: disableAnimations), editing: false, peerIdWithRevealedOptions: nil, selectedPeerIds: Set(), peerInputActivities: nil, pendingRemovalPeerIds: Set(), pendingClearHistoryPeerIds: Set(), archiveShouldBeTemporaryRevealed: false)
|
||||
self.statePromise = ValuePromise(self.currentState, ignoreRepeated: true)
|
||||
|
||||
self.theme = theme
|
||||
@ -415,7 +421,7 @@ final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
}, setItemPinned: { [weak self] itemId, _ in
|
||||
let _ = (toggleItemPinned(postbox: context.account.postbox, itemId: itemId) |> deliverOnMainQueue).start(next: { result in
|
||||
let _ = (toggleItemPinned(postbox: context.account.postbox, groupId: groupId, itemId: itemId) |> deliverOnMainQueue).start(next: { result in
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
case .done:
|
||||
@ -451,6 +457,25 @@ final class ChatListNode: ListView {
|
||||
return state
|
||||
}
|
||||
})
|
||||
}, toggleArchivedFolderHiddenByDefault: { [weak self] in
|
||||
let _ = (context.account.postbox.transaction { transaction -> Bool in
|
||||
var updatedValue = false
|
||||
updateChatArchiveSettings(transaction: transaction, { settings in
|
||||
var settings = settings
|
||||
settings.isHiddenByDefault = !settings.isHiddenByDefault
|
||||
updatedValue = settings.isHiddenByDefault
|
||||
return settings
|
||||
})
|
||||
return updatedValue
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { updatedValue in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !updatedValue {
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
@ -467,16 +492,24 @@ final class ChatListNode: ListView {
|
||||
|
||||
let savedMessagesPeer: Signal<Peer?, NoError>
|
||||
if case let .peers(filter) = mode, filter == [.onlyWriteable] {
|
||||
savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId) |> map(Optional.init)
|
||||
savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|
||||
|> map(Optional.init)
|
||||
} else {
|
||||
savedMessagesPeer = .single(nil)
|
||||
}
|
||||
|
||||
let hideArchivedFolderByDefault = context.account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.chatArchiveSettings])
|
||||
|> map { view -> Bool in
|
||||
let settings: ChatArchiveSettings = view.values[ApplicationSpecificPreferencesKeys.chatArchiveSettings] as? ChatArchiveSettings ?? .default
|
||||
return settings.isHiddenByDefault
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|
||||
let currentPeerId: PeerId = context.account.peerId
|
||||
|
||||
let chatListNodeViewTransition = combineLatest(savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
let chatListNodeViewTransition = combineLatest(hideArchivedFolderByDefault, savedMessagesPeer, chatListViewUpdate, self.statePromise.get()) |> mapToQueue { (hideArchivedFolderByDefault, savedMessagesPeer, update, state) -> Signal<ChatListNodeListViewTransition, NoError> in
|
||||
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, mode: mode)
|
||||
let (rawEntries, isLoading) = chatListNodeEntriesForView(update.view, state: state, savedMessagesPeer: savedMessagesPeer, hideArchivedFolderByDefault: hideArchivedFolderByDefault, mode: mode)
|
||||
let entries = rawEntries.filter { entry in
|
||||
switch entry {
|
||||
case let .PeerEntry(_, _, _, _, _, _, peer, _, _, _, _, _, _, _):
|
||||
@ -571,6 +604,7 @@ final class ChatListNode: ListView {
|
||||
var updatedPinnedChats: [PeerId] = []
|
||||
|
||||
var didIncludeRemovingPeerId = false
|
||||
var didIncludeHiddenByDefaultArchive = false
|
||||
if let previous = previousView {
|
||||
for entry in previous.filteredEntries {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _, _, _, _, _, _) = entry {
|
||||
@ -580,10 +614,14 @@ final class ChatListNode: ListView {
|
||||
if index.messageIndex.id.peerId == removingPeerId {
|
||||
didIncludeRemovingPeerId = true
|
||||
}
|
||||
} else if case let .GroupReferenceEntry(entry) = entry {
|
||||
didIncludeHiddenByDefaultArchive = entry.hiddenByDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
var doesIncludeRemovingPeerId = false
|
||||
var doesIncludeArchive = false
|
||||
var doesIncludeHiddenByDefaultArchive = false
|
||||
for entry in processedView.filteredEntries {
|
||||
if case let .PeerEntry(index, _, _, _, _, _, _, _, _ , _, _, _, _, _) = entry {
|
||||
if index.pinningIndex != nil {
|
||||
@ -592,6 +630,9 @@ final class ChatListNode: ListView {
|
||||
if index.messageIndex.id.peerId == removingPeerId {
|
||||
doesIncludeRemovingPeerId = true
|
||||
}
|
||||
} else if case let .GroupReferenceEntry(entry) = entry {
|
||||
doesIncludeArchive = true
|
||||
doesIncludeHiddenByDefaultArchive = entry.hiddenByDefault
|
||||
}
|
||||
}
|
||||
if previousPinnedChats != updatedPinnedChats {
|
||||
@ -603,6 +644,12 @@ final class ChatListNode: ListView {
|
||||
if doesIncludeRemovingPeerId != didIncludeRemovingPeerId {
|
||||
disableAnimations = false
|
||||
}
|
||||
if previousState.archiveShouldBeTemporaryRevealed != state.archiveShouldBeTemporaryRevealed && doesIncludeArchive {
|
||||
disableAnimations = false
|
||||
}
|
||||
if didIncludeHiddenByDefaultArchive != doesIncludeHiddenByDefaultArchive {
|
||||
disableAnimations = false
|
||||
}
|
||||
}
|
||||
|
||||
var searchMode = false
|
||||
@ -778,17 +825,6 @@ final class ChatListNode: ListView {
|
||||
}
|
||||
})
|
||||
|
||||
self.beganInteractiveDragging = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
state.peerIdWithRevealedOptions = nil
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.reorderItem = { [weak self] fromIndex, toIndex, transactionOpaqueState -> Signal<Bool, NoError> in
|
||||
if let strongSelf = self, let filteredEntries = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListView.filteredEntries {
|
||||
if fromIndex >= 0 && fromIndex < filteredEntries.count && toIndex >= 0 && toIndex < filteredEntries.count {
|
||||
@ -812,7 +848,7 @@ final class ChatListNode: ListView {
|
||||
|
||||
if let _ = fromEntry.index.pinningIndex {
|
||||
return strongSelf.context.account.postbox.transaction { transaction -> Bool in
|
||||
var itemIds = transaction.getPinnedItemIds()
|
||||
var itemIds = transaction.getPinnedItemIds(groupId: groupId)
|
||||
|
||||
var itemId: PinnedItemId?
|
||||
switch fromEntry {
|
||||
@ -847,7 +883,7 @@ final class ChatListNode: ListView {
|
||||
} else {
|
||||
itemIds.append(itemId)
|
||||
}
|
||||
return reorderPinnedItemIds(transaction: transaction, itemIds: itemIds)
|
||||
return reorderPinnedItemIds(transaction: transaction, groupId: groupId, itemIds: itemIds)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
@ -857,9 +893,46 @@ final class ChatListNode: ListView {
|
||||
}
|
||||
return .single(false)
|
||||
}
|
||||
var startedScrollingAtUpperBound = false
|
||||
|
||||
self.beganInteractiveDragging = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch strongSelf.visibleContentOffset() {
|
||||
case .none, .unknown:
|
||||
startedScrollingAtUpperBound = false
|
||||
case let .known(value):
|
||||
startedScrollingAtUpperBound = value <= 0.0
|
||||
}
|
||||
if strongSelf.currentState.peerIdWithRevealedOptions != nil {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
state.peerIdWithRevealedOptions = nil
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.didEndScrolling = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let _ = strongSelf.contentScrollingEnded?(strongSelf)
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
startedScrollingAtUpperBound = false
|
||||
let _ = strongSelf.contentScrollingEnded?(strongSelf)
|
||||
let revealHiddenItems: Bool
|
||||
switch strongSelf.visibleContentOffset() {
|
||||
case .none, .unknown:
|
||||
revealHiddenItems = false
|
||||
case let .known(value):
|
||||
revealHiddenItems = value <= 76.0
|
||||
}
|
||||
if !revealHiddenItems && strongSelf.currentState.archiveShouldBeTemporaryRevealed {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
state.archiveShouldBeTemporaryRevealed = false
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -890,16 +963,28 @@ final class ChatListNode: ListView {
|
||||
})
|
||||
|
||||
self.visibleContentOffsetChanged = { [weak self] offset in
|
||||
if let strongSelf = self {
|
||||
let atTop: Bool
|
||||
switch offset {
|
||||
case .none, .unknown:
|
||||
atTop = false
|
||||
case let .known(value):
|
||||
atTop = value <= 0.0
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let atTop: Bool
|
||||
var revealHiddenItems: Bool = false
|
||||
switch offset {
|
||||
case .none, .unknown:
|
||||
atTop = false
|
||||
case let .known(value):
|
||||
atTop = value <= 0.0
|
||||
if startedScrollingAtUpperBound && strongSelf.isTracking {
|
||||
revealHiddenItems = value <= -32.0
|
||||
}
|
||||
}
|
||||
strongSelf.scrolledAtTopValue = atTop
|
||||
strongSelf.contentOffsetChanged?(offset)
|
||||
if revealHiddenItems && !strongSelf.currentState.archiveShouldBeTemporaryRevealed {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
state.archiveShouldBeTemporaryRevealed = true
|
||||
return state
|
||||
}
|
||||
strongSelf.scrolledAtTopValue = atTop
|
||||
strongSelf.contentOffsetChanged?(offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ enum ChatListNodeEntryId: Hashable {
|
||||
enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
case PeerEntry(index: ChatListIndex, presentationData: ChatListPresentationData, message: Message?, readState: CombinedPeerReadState?, notificationSettings: PeerNotificationSettings?, embeddedInterfaceState: PeerChatListEmbeddedInterfaceState?, peer: RenderedPeer, presence: PeerPresence?, summaryInfo: ChatListMessageTagSummaryInfo, editing: Bool, hasActiveRevealControls: Bool, selected: Bool, inputActivities: [(Peer, PeerInputActivity)]?, isAd: Bool)
|
||||
case HoleEntry(ChatListHole, theme: PresentationTheme)
|
||||
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, message: Message?, editing: Bool, unreadState: ChatListTotalUnreadState)
|
||||
case GroupReferenceEntry(index: ChatListIndex, presentationData: ChatListPresentationData, groupId: PeerGroupId, peer: RenderedPeer, message: Message?, editing: Bool, unreadState: ChatListTotalUnreadState, revealed: Bool, hiddenByDefault: Bool)
|
||||
|
||||
var index: ChatListIndex {
|
||||
switch self {
|
||||
@ -53,7 +53,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return index
|
||||
case let .HoleEntry(hole, _):
|
||||
return ChatListIndex(pinningIndex: nil, messageIndex: hole.index)
|
||||
case let .GroupReferenceEntry(index, _, _, _, _, _):
|
||||
case let .GroupReferenceEntry(index, _, _, _, _, _, _, _, _):
|
||||
return index
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
return .PeerId(index.messageIndex.id.peerId.toInt64())
|
||||
case let .HoleEntry(hole, _):
|
||||
return .Hole(Int64(hole.index.id.id))
|
||||
case let .GroupReferenceEntry(_, _, groupId, _, _, _):
|
||||
case let .GroupReferenceEntry(_, _, groupId, _, _, _, _, _, _):
|
||||
return .GroupId(groupId)
|
||||
}
|
||||
}
|
||||
@ -156,8 +156,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .GroupReferenceEntry(lhsIndex, lhsPresentationData, lhsGroupId, lhsMessage, lhsEditing, lhsUnreadState):
|
||||
if case let .GroupReferenceEntry(rhsIndex, rhsPresentationData, rhsGroupId, rhsMessage, rhsEditing, rhsUnreadState) = rhs {
|
||||
case let .GroupReferenceEntry(lhsIndex, lhsPresentationData, lhsGroupId, lhsPeer, lhsMessage, lhsEditing, lhsUnreadState, lhsRevealed, lhsHiddenByDefault):
|
||||
if case let .GroupReferenceEntry(rhsIndex, rhsPresentationData, rhsGroupId, rhsPeer, rhsMessage, rhsEditing, rhsUnreadState, rhsRevealed, rhsHiddenByDefault) = rhs {
|
||||
if lhsIndex != rhsIndex {
|
||||
return false
|
||||
}
|
||||
@ -167,6 +167,9 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
if lhsGroupId != rhsGroupId {
|
||||
return false
|
||||
}
|
||||
if lhsPeer != rhsPeer {
|
||||
return false
|
||||
}
|
||||
if lhsMessage?.stableVersion != rhsMessage?.stableVersion {
|
||||
return false
|
||||
}
|
||||
@ -179,6 +182,12 @@ enum ChatListNodeEntry: Comparable, Identifiable {
|
||||
if lhsUnreadState != rhsUnreadState {
|
||||
return false
|
||||
}
|
||||
if lhsRevealed != rhsRevealed {
|
||||
return false
|
||||
}
|
||||
if lhsHiddenByDefault != rhsHiddenByDefault {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
@ -195,7 +204,7 @@ private func offsetPinnedIndex(_ index: ChatListIndex, offset: UInt16) -> ChatLi
|
||||
}
|
||||
}
|
||||
|
||||
func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState, savedMessagesPeer: Peer?, hideArchivedFolderByDefault: Bool, mode: ChatListNodeMode) -> (entries: [ChatListNodeEntry], loading: Bool) {
|
||||
var result: [ChatListNodeEntry] = []
|
||||
|
||||
var pinnedIndexOffset: UInt16 = 0
|
||||
@ -259,11 +268,15 @@ func chatListNodeEntriesForView(_ view: ChatListView, state: ChatListNodeState,
|
||||
|
||||
if view.laterIndex == nil, case .chatList = mode {
|
||||
for groupReference in view.groupEntries {
|
||||
let messageIndex: MessageIndex
|
||||
if let message = groupReference.message {
|
||||
result.append(.GroupReferenceEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: message.index), presentationData: state.presentationData, groupId: groupReference.groupId, message: groupReference.message, editing: state.editing, unreadState: groupReference.unreadState))
|
||||
if pinningIndex != 0 {
|
||||
pinningIndex -= 1
|
||||
}
|
||||
messageIndex = message.index
|
||||
} else {
|
||||
messageIndex = MessageIndex(id: MessageId(peerId: groupReference.renderedPeer.peerId, namespace: 0, id: 0), timestamp: 1)
|
||||
}
|
||||
result.append(.GroupReferenceEntry(index: ChatListIndex(pinningIndex: pinningIndex, messageIndex: messageIndex), presentationData: state.presentationData, groupId: groupReference.groupId, peer: groupReference.renderedPeer, message: groupReference.message, editing: state.editing, unreadState: groupReference.unreadState, revealed: state.archiveShouldBeTemporaryRevealed, hiddenByDefault: hideArchivedFolderByDefault))
|
||||
if pinningIndex != 0 {
|
||||
pinningIndex -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,7 +446,7 @@ enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
interaction.peerSelected(peer.peer)
|
||||
})
|
||||
case let .message(message, readState, presentationData):
|
||||
return ChatListItem(presentationData: presentationData, account: context.account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, interaction: interaction)
|
||||
return ChatListItem(presentationData: presentationData, account: context.account, peerGroupId: nil, index: ChatListIndex(pinningIndex: nil, messageIndex: message.index), content: .peer(message: message, peer: RenderedPeer(message: message), combinedReadState: readState, notificationSettings: nil, presence: nil, summaryInfo: ChatListMessageTagSummaryInfo(), embeddedState: nil, inputActivities: nil, isAd: false, ignoreUnreadBadge: true), editing: false, hasActiveRevealControls: false, selected: false, header: enableHeaders ? ChatListSearchItemHeader(type: .messages, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil) : nil, enableContextActions: false, hiddenOffset: false, interaction: interaction)
|
||||
case let .addContact(phoneNumber, theme, strings):
|
||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
interaction.addContact(phoneNumber)
|
||||
@ -641,7 +641,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
let accountPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|
||||
|> take(1)
|
||||
|
||||
let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased(), groupId: groupId)
|
||||
let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased(), groupId: nil)
|
||||
|> mapToSignal { local -> Signal<([PeerView], [RenderedPeer]), NoError> in
|
||||
return combineLatest(local.map { context.account.postbox.peerView(id: $0.peerId) }) |> map { views in
|
||||
return (views, local)
|
||||
@ -671,18 +671,16 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}
|
||||
|
||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
|
||||
if groupId == nil {
|
||||
foundRemotePeers = (.single(([], [], true)) |> then(searchPeers(account: context.account, query: query) |> map { ($0.0, $0.1, false) }
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue())))
|
||||
} else {
|
||||
foundRemotePeers = .single(([], [], false))
|
||||
}
|
||||
foundRemotePeers = (
|
||||
.single(([], [], true))
|
||||
|> then(
|
||||
searchPeers(account: context.account, query: query)
|
||||
|> map { ($0.0, $0.1, false) }
|
||||
|> delay(0.2, queue: Queue.concurrentDefaultQueue())
|
||||
)
|
||||
)
|
||||
let location: SearchMessagesLocation
|
||||
if let groupId = groupId {
|
||||
location = .group(groupId)
|
||||
} else {
|
||||
location = .general
|
||||
}
|
||||
location = .general
|
||||
|
||||
updateSearchContext { _ in
|
||||
return (nil, true)
|
||||
@ -857,6 +855,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
}, deletePeer: { _ in
|
||||
}, updatePeerGrouping: { _, _ in
|
||||
}, togglePeerMarkedUnread: { _, _ in
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
})
|
||||
|
||||
let previousRecentItems = Atomic<[ChatListRecentEntry]?>(value: nil)
|
||||
@ -901,7 +900,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
|> mapToSignal { hasRecentPeers, peers, presentationData, state -> Signal<[ChatListRecentEntry], NoError> in
|
||||
var entries: [ChatListRecentEntry] = []
|
||||
if !filter.contains(.onlyGroups) {
|
||||
if groupId == nil, hasRecentPeers {
|
||||
if hasRecentPeers {
|
||||
entries.append(.topPeers([], presentationData.theme, presentationData.strings))
|
||||
}
|
||||
}
|
||||
@ -1175,7 +1174,7 @@ final class ChatListSearchContainerNode: SearchDisplayControllerContentNode {
|
||||
switch item.content {
|
||||
case let .peer(message, peer, _, _, _, _, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, message?.id ?? peer.peerId)
|
||||
case let .groupReference(groupId, _, _):
|
||||
case let .groupReference(groupId, _, _, _, _):
|
||||
return (selectedItemNode.view, bounds, groupId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ private var telegramUIDeclaredEncodables: Void = {
|
||||
declareEncodable(RecentWallpaperSearchQueryItem.self, f: { RecentWallpaperSearchQueryItem(decoder: $0) })
|
||||
declareEncodable(RecentSettingsSearchQueryItem.self, f: { RecentSettingsSearchQueryItem(decoder: $0) })
|
||||
declareEncodable(VoipDerivedState.self, f: { VoipDerivedState(decoder: $0) })
|
||||
declareEncodable(ChatArchiveSettings.self, f: { ChatArchiveSettings(decoder: $0) })
|
||||
return
|
||||
}()
|
||||
|
||||
|
||||
@ -61,6 +61,7 @@ final class HashtagSearchController: TelegramController {
|
||||
}, deletePeer: { _ in
|
||||
}, updatePeerGrouping: { _, _ in
|
||||
}, togglePeerMarkedUnread: { _, _ in
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
})
|
||||
|
||||
let previousSearchItems = Atomic<[ChatListSearchEntry]?>(value: nil)
|
||||
|
||||
@ -4,10 +4,12 @@ import Postbox
|
||||
|
||||
private enum ApplicationSpecificPreferencesKeyValues: Int32 {
|
||||
case voipDerivedState = 16
|
||||
case chatArchiveSettings = 17
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificPreferencesKeys {
|
||||
public static let voipDerivedState = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.voipDerivedState.rawValue)
|
||||
public static let chatArchiveSettings = applicationSpecificPreferencesKey(ApplicationSpecificPreferencesKeyValues.chatArchiveSettings.rawValue)
|
||||
}
|
||||
|
||||
private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
||||
|
||||
@ -81,6 +81,10 @@ public class TelegramController: ViewController {
|
||||
return super.navigationInsetHeight + self.additionalHeight
|
||||
}
|
||||
|
||||
override public var visualNavigationInsetHeight: CGFloat {
|
||||
return super.visualNavigationInsetHeight + self.additionalHeight
|
||||
}
|
||||
|
||||
private var additionalHeight: CGFloat {
|
||||
var height: CGFloat = 0.0
|
||||
if let _ = self.mediaAccessoryPanel {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user