mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Folder improvements
This commit is contained in:
parent
d6243fb78b
commit
05ffb47c1c
@ -18,7 +18,7 @@ func archiveContextMenuItems(context: AccountContext, groupId: PeerGroupId, chat
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if !transaction.getUnreadChatListPeerIds(groupId: groupId, filterPredicate: nil).isEmpty {
|
||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAllAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { [weak chatListController] _, f in
|
||||
items.append(.action(ContextMenuActionItem(text: strings.ChatList_Context_MarkAllAsRead, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
let _ = (context.account.postbox.transaction { transaction in
|
||||
markAllChatsAsReadInteractively(transaction: transaction, viewTracker: context.account.viewTracker, groupId: groupId, filterPredicate: nil)
|
||||
}
|
||||
@ -40,7 +40,7 @@ func archiveContextMenuItems(context: AccountContext, groupId: PeerGroupId, chat
|
||||
}
|
||||
|
||||
enum ChatContextMenuSource {
|
||||
case chatList
|
||||
case chatList(isFilter: Bool)
|
||||
case search(ChatListSearchContextActionSource)
|
||||
}
|
||||
|
||||
@ -146,20 +146,22 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, source: ChatC
|
||||
})))
|
||||
}
|
||||
|
||||
let isPinned = index.pinningIndex != nil
|
||||
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
let _ = (toggleItemPinned(postbox: context.account.postbox, groupId: group, itemId: .peer(peerId))
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
switch result {
|
||||
case .done:
|
||||
break
|
||||
case let .limitExceeded(maxCount):
|
||||
break
|
||||
//strongSelf.presentAlert?(strongSelf.currentState.presentationData.strings.DialogList_PinLimitError("\(maxCount)").0)
|
||||
}
|
||||
f(.default)
|
||||
})
|
||||
})))
|
||||
if case .chatList(false) = source {
|
||||
let isPinned = index.pinningIndex != nil
|
||||
items.append(.action(ContextMenuActionItem(text: isPinned ? strings.ChatList_Context_Unpin : strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin" : "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
let _ = (toggleItemPinned(postbox: context.account.postbox, groupId: group, itemId: .peer(peerId))
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
switch result {
|
||||
case .done:
|
||||
break
|
||||
case let .limitExceeded(maxCount):
|
||||
break
|
||||
//strongSelf.presentAlert?(strongSelf.currentState.presentationData.strings.DialogList_PinLimitError("\(maxCount)").0)
|
||||
}
|
||||
f(.default)
|
||||
})
|
||||
})))
|
||||
}
|
||||
|
||||
if !isSavedMessages, let notificationSettings = transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings {
|
||||
var isMuted = false
|
||||
|
@ -294,6 +294,28 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
|
||||
let title = !state.selectedPeerIds.isEmpty ? strongSelf.presentationData.strings.ChatList_SelectedChats(Int32(state.selectedPeerIds.count)) : defaultTitle
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: title, activity: false, hasProxy: false, connectsViaProxy: false, isPasscodeSet: false, isManuallyLocked: false)
|
||||
} else if isReorderingTabs {
|
||||
if strongSelf.groupId == .root {
|
||||
strongSelf.navigationItem.rightBarButtonItem = nil
|
||||
}
|
||||
let leftBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.reorderingDonePressed))
|
||||
strongSelf.navigationItem.leftBarButtonItem = leftBarButtonItem
|
||||
|
||||
let (_, connectsViaProxy) = proxy
|
||||
switch networkState {
|
||||
case .waitingForNetwork:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false)
|
||||
case let .connecting(proxy):
|
||||
var text = strongSelf.presentationData.strings.State_Connecting
|
||||
if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {
|
||||
text = strongSelf.presentationData.strings.State_ConnectingToProxy
|
||||
}
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: text, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false)
|
||||
case .updating:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: strongSelf.presentationData.strings.State_Updating, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false)
|
||||
case .online:
|
||||
strongSelf.titleView.title = NetworkStatusTitle(text: defaultTitle, activity: false, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: false, isManuallyLocked: false)
|
||||
}
|
||||
} else {
|
||||
var isRoot = false
|
||||
if case .root = strongSelf.groupId {
|
||||
@ -908,7 +930,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
case let .peer(peer):
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peer.peerId, source: .chatList, chatListController: strongSelf), reactionItems: [], gesture: gesture)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peer.peerId, source: .chatList(isFilter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter != nil), chatListController: strongSelf), reactionItems: [], gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
@ -1148,7 +1170,15 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
} else {
|
||||
text = "Hold to organize your chats with folders."
|
||||
}
|
||||
parentController.present(TooltipScreen(text: text, location: CGPoint(x: absoluteFrame.midX - 14.0, y: absoluteFrame.minY - 8.0)), in: .current)
|
||||
parentController.present(TooltipScreen(text: text, location: CGPoint(x: absoluteFrame.midX - 14.0, y: absoluteFrame.minY - 8.0), shouldDismissOnTouch: { point in
|
||||
guard let strongSelf = self, let parentController = strongSelf.parent as? TabBarController else {
|
||||
return true
|
||||
}
|
||||
if parentController.isPointInsideContentArea(point: point) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}), in: .current)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2450,6 +2480,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
||||
|
||||
private final class ChatListTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
|
||||
private let controller: ChatListController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
@ -2470,6 +2501,7 @@ private final class ChatListTabBarContextExtractedContentSource: ContextExtracte
|
||||
|
||||
private final class ChatListHeaderBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = true
|
||||
|
||||
private let controller: ChatListController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
@ -849,6 +849,44 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
|
||||
var attemptNavigationImpl: (() -> Bool)?
|
||||
var applyImpl: (() -> Void)? = {
|
||||
let state = stateValue.with { $0 }
|
||||
let preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: state.additionallyIncludePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var preset = preset
|
||||
if currentPreset == nil {
|
||||
preset.id = max(2, settings.filters.map({ $0.id + 1 }).max() ?? 2)
|
||||
}
|
||||
var settings = settings
|
||||
if let _ = currentPreset {
|
||||
var found = false
|
||||
for i in 0 ..< settings.filters.count {
|
||||
if settings.filters[i].id == preset.id {
|
||||
settings.filters[i] = preset
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
settings.filters = settings.filters.filter { listFilter in
|
||||
if listFilter.title == preset.title && listFilter.data == preset.data {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
settings.filters.append(preset)
|
||||
}
|
||||
} else {
|
||||
settings.filters.append(preset)
|
||||
}
|
||||
return settings
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updated(settings.filters)
|
||||
dismissImpl?()
|
||||
|
||||
let _ = replaceRemoteChatListFilters(account: context.account).start()
|
||||
})
|
||||
}
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(),
|
||||
context.sharedContext.presentationData,
|
||||
@ -864,42 +902,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
})
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(currentPreset == nil ? presentationData.strings.Common_Create : presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
|
||||
let state = stateValue.with { $0 }
|
||||
let preset = ChatListFilter(id: currentPreset?.id ?? -1, title: state.name, data: ChatListFilterData(categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: state.additionallyIncludePeers, excludePeers: state.additionallyExcludePeers))
|
||||
let _ = (updateChatListFilterSettingsInteractively(postbox: context.account.postbox, { settings in
|
||||
var preset = preset
|
||||
if currentPreset == nil {
|
||||
preset.id = max(2, settings.filters.map({ $0.id + 1 }).max() ?? 2)
|
||||
}
|
||||
var settings = settings
|
||||
if let _ = currentPreset {
|
||||
var found = false
|
||||
for i in 0 ..< settings.filters.count {
|
||||
if settings.filters[i].id == preset.id {
|
||||
settings.filters[i] = preset
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
settings.filters = settings.filters.filter { listFilter in
|
||||
if listFilter.title == preset.title && listFilter.data == preset.data {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
settings.filters.append(preset)
|
||||
}
|
||||
} else {
|
||||
settings.filters.append(preset)
|
||||
}
|
||||
return settings
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { settings in
|
||||
updated(settings.filters)
|
||||
dismissImpl?()
|
||||
|
||||
let _ = replaceRemoteChatListFilters(account: context.account).start()
|
||||
})
|
||||
applyImpl?()
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(currentPreset != nil ? "Folder" : "Create Folder"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
@ -935,8 +938,12 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
|
||||
}
|
||||
let displaySaveAlert: () -> Void = {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: "Are you sure you want to discard this folder?", actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Yes, action: {
|
||||
dismissImpl?()
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: "You have changed the filter. Apply changes?", actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Discard", action: {
|
||||
dismissImpl?()
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: "Apply", action: {
|
||||
applyImpl?()
|
||||
})]), nil)
|
||||
}
|
||||
attemptNavigationImpl = {
|
||||
|
@ -52,6 +52,7 @@ private enum ChatListFilterPresetListEntryStableId: Hashable {
|
||||
case screenHeader
|
||||
case suggestedListHeader
|
||||
case suggestedPreset(ChatListFilterData)
|
||||
case suggestedAddCustom
|
||||
case listHeader
|
||||
case preset(Int32)
|
||||
case addItem
|
||||
@ -70,6 +71,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
case screenHeader(String)
|
||||
case suggestedListHeader(String)
|
||||
case suggestedPreset(index: PresetIndex, title: String, label: String, preset: ChatListFilterData)
|
||||
case suggestedAddCustom(String)
|
||||
case listHeader(String)
|
||||
case preset(index: PresetIndex, title: String, label: String, preset: ChatListFilter, canBeReordered: Bool, canBeDeleted: Bool, isEditing: Bool)
|
||||
case addItem(text: String, isEditing: Bool)
|
||||
@ -79,7 +81,7 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
switch self {
|
||||
case .screenHeader:
|
||||
return ChatListFilterPresetListSection.screenHeader.rawValue
|
||||
case .suggestedListHeader, .suggestedPreset:
|
||||
case .suggestedListHeader, .suggestedPreset, .suggestedAddCustom:
|
||||
return ChatListFilterPresetListSection.suggested.rawValue
|
||||
case .listHeader, .preset, .addItem, .listFooter:
|
||||
return ChatListFilterPresetListSection.list.rawValue
|
||||
@ -102,6 +104,8 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
return 1002
|
||||
case let .suggestedPreset(suggestedPreset):
|
||||
return 1003 + suggestedPreset.index.value
|
||||
case .suggestedAddCustom:
|
||||
return 2000
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,6 +117,8 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
return .suggestedListHeader
|
||||
case let .suggestedPreset(suggestedPreset):
|
||||
return .suggestedPreset(suggestedPreset.preset)
|
||||
case .suggestedAddCustom:
|
||||
return .suggestedAddCustom
|
||||
case .listHeader:
|
||||
return .listHeader
|
||||
case let .preset(preset):
|
||||
@ -139,6 +145,10 @@ private enum ChatListFilterPresetListEntry: ItemListNodeEntry {
|
||||
return ChatListFilterPresetListSuggestedItem(presentationData: presentationData, title: title, label: label, sectionId: self.section, style: .blocks, installAction: {
|
||||
arguments.addSuggestedPresed(title, preset)
|
||||
}, tag: nil)
|
||||
case let .suggestedAddCustom(text):
|
||||
return ItemListPeerActionItem(presentationData: presentationData, icon: nil, title: text, sectionId: self.section, height: .generic, editing: false, action: {
|
||||
arguments.addNew()
|
||||
})
|
||||
case let .listHeader(text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, multiline: true, sectionId: self.section)
|
||||
case let .preset(_, title, label, preset, canBeReordered, canBeDeleted, isEditing):
|
||||
@ -183,6 +193,7 @@ private func filtersWithAppliedOrder(filters: [(ChatListFilter, Int)], order: [I
|
||||
private func chatListFilterPresetListControllerEntries(presentationData: PresentationData, state: ChatListFilterPresetListControllerState, filters: [(ChatListFilter, Int)], updatedFilterOrder: [Int32]?, suggestedFilters: [ChatListFeaturedFilter], settings: ChatListFilterSettings) -> [ChatListFilterPresetListEntry] {
|
||||
var entries: [ChatListFilterPresetListEntry] = []
|
||||
|
||||
|
||||
entries.append(.screenHeader("Create folders for different groups of chats and\nquickly switch between them."))
|
||||
|
||||
let filteredSuggestedFilters = suggestedFilters.filter { suggestedFilter in
|
||||
@ -194,21 +205,26 @@ private func chatListFilterPresetListControllerEntries(presentationData: Present
|
||||
return true
|
||||
}
|
||||
|
||||
entries.append(.listHeader("FOLDERS"))
|
||||
|
||||
for (filter, chatCount) in filtersWithAppliedOrder(filters: filters, order: updatedFilterOrder) {
|
||||
entries.append(.preset(index: PresetIndex(value: entries.count), title: filter.title, label: chatCount == 0 ? "" : "\(chatCount)", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: true, isEditing: state.isEditing))
|
||||
if !filters.isEmpty || suggestedFilters.isEmpty {
|
||||
entries.append(.listHeader("FOLDERS"))
|
||||
|
||||
for (filter, chatCount) in filtersWithAppliedOrder(filters: filters, order: updatedFilterOrder) {
|
||||
entries.append(.preset(index: PresetIndex(value: entries.count), title: filter.title, label: chatCount == 0 ? "" : "\(chatCount)", preset: filter, canBeReordered: filters.count > 1, canBeDeleted: true, isEditing: state.isEditing))
|
||||
}
|
||||
if filters.count < 10 {
|
||||
entries.append(.addItem(text: "Create New Folder", isEditing: state.isEditing))
|
||||
}
|
||||
entries.append(.listFooter("Tap \"Edit\" to change the order or delete folders."))
|
||||
}
|
||||
if filters.count < 10 {
|
||||
entries.append(.addItem(text: "Create New Folder", isEditing: state.isEditing))
|
||||
}
|
||||
entries.append(.listFooter("Tap \"Edit\" to change the order or delete folders."))
|
||||
|
||||
if !filteredSuggestedFilters.isEmpty {
|
||||
entries.append(.suggestedListHeader("RECOMMENDED FOLDERS"))
|
||||
for filter in filteredSuggestedFilters {
|
||||
entries.append(.suggestedPreset(index: PresetIndex(value: entries.count), title: filter.title, label: filter.description, preset: filter.data))
|
||||
}
|
||||
if filters.isEmpty {
|
||||
entries.append(.suggestedAddCustom("Add Custom Folder"))
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
@ -332,7 +348,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
dismissImpl?()
|
||||
})
|
||||
}
|
||||
let rightNavigationButton: ItemListNavigationButton
|
||||
let rightNavigationButton: ItemListNavigationButton?
|
||||
if state.isEditing {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: true, action: {
|
||||
let _ = (updatedFilterOrder.get()
|
||||
@ -381,7 +397,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
} else if !filtersWithCountsValue.isEmpty {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: {
|
||||
updateState { state in
|
||||
var state = state
|
||||
@ -389,6 +405,8 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch
|
||||
return state
|
||||
}
|
||||
})
|
||||
} else {
|
||||
rightNavigationButton = nil
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("Folders"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
|
@ -785,7 +785,9 @@ final class ChatListFilterTabContainerNode: ASDisplayNode {
|
||||
|
||||
if let previousSelectedFrame = self.previousSelectedFrame {
|
||||
let previousContentOffsetX = max(0.0, min(previousContentWidth - previousScrollBounds.width, floor(previousSelectedFrame.midX - previousScrollBounds.width / 2.0)))
|
||||
focusOnSelectedFilter = abs(previousContentOffsetX - previousScrollBounds.minX) < 1.0
|
||||
if abs(previousContentOffsetX - previousScrollBounds.minX) < 1.0 {
|
||||
focusOnSelectedFilter = true
|
||||
}
|
||||
}
|
||||
|
||||
if focusOnSelectedFilter && self.reorderingItem == nil {
|
||||
|
@ -6,8 +6,6 @@ import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
|
||||
private let titleFont = Font.regular(17.0)
|
||||
|
||||
class ChatListHoleItem: ListViewItem {
|
||||
let theme: PresentationTheme
|
||||
|
||||
@ -45,9 +43,6 @@ class ChatListHoleItem: ListViewItem {
|
||||
Queue.mainQueue().async {
|
||||
completion(nodeLayout, { _ in
|
||||
apply()
|
||||
if let nodeValue = node() as? ChatListHoleItemNode {
|
||||
nodeValue.updateBackgroundAndSeparatorsLayout()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -56,25 +51,11 @@ class ChatListHoleItem: ListViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
private let separatorHeight = 1.0 / UIScreen.main.scale
|
||||
|
||||
class ChatListHoleItemNode: ListViewItemNode {
|
||||
let separatorNode: ASDisplayNode
|
||||
let labelNode: TextNode
|
||||
|
||||
var relativePosition: (first: Bool, last: Bool) = (false, false)
|
||||
|
||||
required init() {
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = UIColor(rgb: 0xc8c7cc)
|
||||
self.separatorNode.isLayerBacked = true
|
||||
|
||||
self.labelNode = TextNode()
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.labelNode)
|
||||
}
|
||||
|
||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
@ -84,45 +65,17 @@ class ChatListHoleItemNode: ListViewItemNode {
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ChatListHoleItem, _ params: ListViewItemLayoutParams, _ first: Bool, _ last: Bool) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let labelNodeLayout = TextNode.asyncLayout(self.labelNode)
|
||||
|
||||
return { item, params, first, last in
|
||||
let baseWidth = params.width - params.leftInset - params.rightInset
|
||||
|
||||
let (labelLayout, labelApply) = labelNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "", font: titleFont, textColor: item.theme.chatList.messageTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: baseWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
|
||||
let insets = ChatListItemNode.insets(first: first, last: last, firstWithHeader: false)
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 68.0), insets: insets)
|
||||
|
||||
let separatorInset: CGFloat
|
||||
if last {
|
||||
separatorInset = 0.0
|
||||
} else {
|
||||
separatorInset = 80.0 + params.leftInset
|
||||
}
|
||||
let layout = ListViewItemNodeLayout(contentSize: CGSize(width: params.width, height: 0.0), insets: UIEdgeInsets())
|
||||
|
||||
return (layout, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.relativePosition = (first, last)
|
||||
|
||||
let _ = labelApply()
|
||||
|
||||
strongSelf.separatorNode.backgroundColor = item.theme.chatList.itemSeparatorColor
|
||||
|
||||
strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: floor((params.width - labelLayout.size.width) / 2.0), y: floor((layout.contentSize.height - labelLayout.size.height) / 2.0)), size: labelLayout.size)
|
||||
|
||||
strongSelf.separatorNode.frame = CGRect(origin: CGPoint(x: separatorInset, y: 68.0 - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight))
|
||||
|
||||
strongSelf.contentSize = layout.contentSize
|
||||
strongSelf.insets = layout.insets
|
||||
strongSelf.updateBackgroundAndSeparatorsLayout()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func updateBackgroundAndSeparatorsLayout() {
|
||||
//let size = self.bounds.size
|
||||
//let insets = self.insets
|
||||
}
|
||||
}
|
||||
|
@ -42,25 +42,13 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt
|
||||
|
||||
return combineLatest(queue: context.account.postbox.queue,
|
||||
context.account.postbox.combinedView(keys: keys),
|
||||
context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.inAppNotificationSettings])
|
||||
Signal<Bool, NoError>.single(true)
|
||||
)
|
||||
|> map { view, sharedData -> (Int, [(ChatListFilter, Int)]) in
|
||||
|> map { view, _ -> (Int, [(ChatListFilter, Int)]) in
|
||||
guard let unreadCounts = view.views[unreadKey] as? UnreadMessageCountsView else {
|
||||
return (0, [])
|
||||
}
|
||||
|
||||
let inAppSettings: InAppNotificationSettings
|
||||
if let value = sharedData.entries[ApplicationSpecificSharedDataKeys.inAppNotificationSettings] as? InAppNotificationSettings {
|
||||
inAppSettings = value
|
||||
} else {
|
||||
inAppSettings = .defaultSettings
|
||||
}
|
||||
let type: RenderedTotalUnreadCountType
|
||||
switch inAppSettings.totalUnreadCountDisplayStyle {
|
||||
case .filtered:
|
||||
type = .filtered
|
||||
}
|
||||
|
||||
var result: [(ChatListFilter, Int)] = []
|
||||
|
||||
var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int)] = [:]
|
||||
@ -70,6 +58,8 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt
|
||||
switch entry {
|
||||
case let .total(_, totalStateValue):
|
||||
totalState = totalStateValue
|
||||
case let .totalInGroup(groupId, totalGroupState):
|
||||
break
|
||||
case let .peer(peerId, state):
|
||||
if let state = state, state.isUnread {
|
||||
if let peerView = view.views[.basicPeer(peerId)] as? BasicPeerView, let peer = peerView.peer {
|
||||
@ -88,10 +78,7 @@ func chatListFilterItems(context: AccountContext) -> Signal<(Int, [(ChatListFilt
|
||||
}
|
||||
}
|
||||
|
||||
var totalBadge = 0
|
||||
if let totalState = totalState {
|
||||
totalBadge = Int(totalState.count(for: inAppSettings.totalUnreadCountDisplayStyle.category, in: inAppSettings.totalUnreadCountDisplayCategory.statsType, with: inAppSettings.totalUnreadCountIncludeTags))
|
||||
}
|
||||
let totalBadge = 0
|
||||
|
||||
for filter in filters {
|
||||
var tags: [PeerSummaryCounterTags] = []
|
||||
|
@ -1363,12 +1363,16 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let maybeContentNode = self.contentContainerNode.contentNode {
|
||||
switch maybeContentNode {
|
||||
case let .extracted(contentParentNode, _):
|
||||
let contentPoint = self.view.convert(point, to: contentParentNode.contentNode.view)
|
||||
if let result = contentParentNode.contentNode.hitTest(contentPoint, with: event) {
|
||||
if result is TextSelectionNodeView {
|
||||
return result
|
||||
} else if contentParentNode.contentRect.contains(contentPoint) {
|
||||
return contentParentNode.contentNode.view
|
||||
if case let .extracted(source) = self.source {
|
||||
if !source.ignoreContentTouches {
|
||||
let contentPoint = self.view.convert(point, to: contentParentNode.contentNode.view)
|
||||
if let result = contentParentNode.contentNode.hitTest(contentPoint, with: event) {
|
||||
if result is TextSelectionNodeView {
|
||||
return result
|
||||
} else if contentParentNode.contentRect.contains(contentPoint) {
|
||||
return contentParentNode.contentNode.view
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case let .controller(controller):
|
||||
@ -1429,6 +1433,7 @@ public final class ContextControllerPutBackViewInfo {
|
||||
|
||||
public protocol ContextExtractedContentSource: class {
|
||||
var keepInPlace: Bool { get }
|
||||
var ignoreContentTouches: Bool { get }
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo?
|
||||
func putBack() -> ContextControllerPutBackViewInfo?
|
||||
|
@ -799,8 +799,10 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
|
||||
private func requestLayout() {
|
||||
self.requestedLayout = true
|
||||
self.setNeedsLayout()
|
||||
if self.transitionState == nil {
|
||||
self.requestedLayout = true
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
override open func layout() {
|
||||
|
@ -5,6 +5,7 @@ import AsyncDisplayKit
|
||||
open class ASButtonNode: ASControlNode {
|
||||
public let titleNode: ImmediateTextNode
|
||||
public let highlightedTitleNode: ImmediateTextNode
|
||||
public let disabledTitleNode: ImmediateTextNode
|
||||
public let imageNode: ASImageNode
|
||||
public let disabledImageNode: ASImageNode
|
||||
public let backgroundImageNode: ASImageNode
|
||||
@ -48,6 +49,7 @@ open class ASButtonNode: ASControlNode {
|
||||
|
||||
private var calculatedTitleSize: CGSize = CGSize()
|
||||
private var calculatedHighlightedTitleSize: CGSize = CGSize()
|
||||
private var calculatedDisabledTitleSize: CGSize = CGSize()
|
||||
|
||||
override public init() {
|
||||
self.titleNode = ImmediateTextNode()
|
||||
@ -58,6 +60,10 @@ open class ASButtonNode: ASControlNode {
|
||||
self.highlightedTitleNode.isUserInteractionEnabled = false
|
||||
self.highlightedTitleNode.displaysAsynchronously = false
|
||||
|
||||
self.disabledTitleNode = ImmediateTextNode()
|
||||
self.disabledTitleNode.isUserInteractionEnabled = false
|
||||
self.disabledTitleNode.displaysAsynchronously = false
|
||||
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.isUserInteractionEnabled = false
|
||||
self.imageNode.displaysAsynchronously = false
|
||||
@ -86,6 +92,8 @@ open class ASButtonNode: ASControlNode {
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.highlightedTitleNode)
|
||||
self.highlightedTitleNode.isHidden = true
|
||||
self.addSubnode(self.disabledTitleNode)
|
||||
self.disabledTitleNode.isHidden = true
|
||||
self.addSubnode(self.imageNode)
|
||||
self.addSubnode(self.disabledImageNode)
|
||||
self.disabledImageNode.isHidden = true
|
||||
@ -108,6 +116,7 @@ open class ASButtonNode: ASControlNode {
|
||||
self.calculatedTitleSize = normalTitleSize
|
||||
let highlightedTitleSize = self.highlightedTitleNode.updateLayout(CGSize(width: widthForTitle, height: max(1.0, constrainedSize.height - verticalInsets)))
|
||||
self.calculatedHighlightedTitleSize = highlightedTitleSize
|
||||
self.calculatedDisabledTitleSize = self.disabledTitleNode.updateLayout(CGSize(width: widthForTitle, height: max(1.0, constrainedSize.height - verticalInsets)))
|
||||
|
||||
let titleSize = CGSize(width: max(normalTitleSize.width, highlightedTitleSize.width), height: max(normalTitleSize.height, highlightedTitleSize.height))
|
||||
|
||||
@ -161,6 +170,17 @@ open class ASButtonNode: ASControlNode {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
self.highlightedTitleNode.attributedText = title
|
||||
} else if state == .disabled {
|
||||
if let attributedText = self.disabledTitleNode.attributedText {
|
||||
if !attributedText.isEqual(to: title) {
|
||||
self.invalidateCalculatedLayout()
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
} else {
|
||||
self.invalidateCalculatedLayout()
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
self.disabledTitleNode.attributedText = title
|
||||
} else {
|
||||
if let attributedText = self.titleNode.attributedText {
|
||||
if !attributedText.isEqual(to: title) {
|
||||
@ -178,6 +198,8 @@ open class ASButtonNode: ASControlNode {
|
||||
open func attributedTitle(for state: UIControl.State) -> NSAttributedString? {
|
||||
if state == .highlighted || state == .selected {
|
||||
return self.highlightedTitleNode.attributedText
|
||||
} else if state == .disabled {
|
||||
return self.disabledTitleNode.attributedText
|
||||
} else {
|
||||
return self.titleNode.attributedText
|
||||
}
|
||||
@ -255,6 +277,14 @@ open class ASButtonNode: ASControlNode {
|
||||
override open var isEnabled: Bool {
|
||||
didSet {
|
||||
if self.isEnabled != oldValue {
|
||||
if self.isEnabled || self.disabledTitleNode.attributedText == nil {
|
||||
self.titleNode.isHidden = false
|
||||
self.disabledTitleNode.isHidden = true
|
||||
} else {
|
||||
self.titleNode.isHidden = true
|
||||
self.disabledTitleNode.isHidden = false
|
||||
}
|
||||
|
||||
if self.isEnabled || self.disabledImageNode.image == nil {
|
||||
self.imageNode.isHidden = false
|
||||
self.disabledImageNode.isHidden = true
|
||||
@ -275,6 +305,7 @@ open class ASButtonNode: ASControlNode {
|
||||
|
||||
let titleOrigin: CGPoint
|
||||
let highlightedTitleOrigin: CGPoint
|
||||
let disabledTitleOrigin: CGPoint
|
||||
let imageOrigin: CGPoint
|
||||
|
||||
if self.laysOutHorizontally {
|
||||
@ -282,14 +313,17 @@ open class ASButtonNode: ASControlNode {
|
||||
case .left:
|
||||
titleOrigin = CGPoint(x: contentRect.minX, y: contentRect.minY + floor((contentRect.height - self.calculatedTitleSize.height) / 2.0))
|
||||
highlightedTitleOrigin = CGPoint(x: contentRect.minX, y: contentRect.minY + floor((contentRect.height - self.calculatedHighlightedTitleSize.height) / 2.0))
|
||||
disabledTitleOrigin = CGPoint(x: contentRect.minX, y: contentRect.minY + floor((contentRect.height - self.calculatedDisabledTitleSize.height) / 2.0))
|
||||
imageOrigin = CGPoint(x: titleOrigin.x + self.calculatedTitleSize.width + self.contentSpacing, y: contentRect.minY + floor((contentRect.height - imageSize.height) / 2.0))
|
||||
case .right:
|
||||
titleOrigin = CGPoint(x: contentRect.maxX - self.calculatedTitleSize.width, y: contentRect.minY + floor((contentRect.height - self.calculatedTitleSize.height) / 2.0))
|
||||
highlightedTitleOrigin = CGPoint(x: contentRect.maxX - self.calculatedHighlightedTitleSize.width, y: contentRect.minY + floor((contentRect.height - self.calculatedHighlightedTitleSize.height) / 2.0))
|
||||
disabledTitleOrigin = CGPoint(x: contentRect.maxX - self.calculatedDisabledTitleSize.width, y: contentRect.minY + floor((contentRect.height - self.calculatedDisabledTitleSize.height) / 2.0))
|
||||
imageOrigin = CGPoint(x: titleOrigin.x - self.contentSpacing - imageSize.width, y: contentRect.minY + floor((contentRect.height - imageSize.height) / 2.0))
|
||||
default:
|
||||
titleOrigin = CGPoint(x: contentRect.minY + floor((contentRect.width - self.calculatedTitleSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - self.calculatedTitleSize.height) / 2.0))
|
||||
titleOrigin = CGPoint(x: contentRect.minX + floor((contentRect.width - self.calculatedTitleSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - self.calculatedTitleSize.height) / 2.0))
|
||||
highlightedTitleOrigin = CGPoint(x: floor((contentRect.width - self.calculatedHighlightedTitleSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - self.calculatedHighlightedTitleSize.height) / 2.0))
|
||||
disabledTitleOrigin = CGPoint(x: floor((contentRect.width - self.calculatedDisabledTitleSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - self.calculatedDisabledTitleSize.height) / 2.0))
|
||||
imageOrigin = CGPoint(x: floor((contentRect.width - imageSize.width) / 2.0), y: contentRect.minY + floor((contentRect.height - imageSize.height) / 2.0))
|
||||
}
|
||||
} else {
|
||||
@ -300,11 +334,13 @@ open class ASButtonNode: ASControlNode {
|
||||
let contentY = contentRect.minY + floor((contentRect.height - contentHeight) / 2.0)
|
||||
titleOrigin = CGPoint(x: contentRect.minX + floor((contentRect.width - self.calculatedTitleSize.width) / 2.0), y: contentY + contentHeight - self.calculatedTitleSize.height)
|
||||
highlightedTitleOrigin = CGPoint(x: contentRect.minX + floor((contentRect.width - self.calculatedHighlightedTitleSize.width) / 2.0), y: contentY + contentHeight - self.calculatedHighlightedTitleSize.height)
|
||||
disabledTitleOrigin = CGPoint(x: contentRect.minX + floor((contentRect.width - self.calculatedDisabledTitleSize.width) / 2.0), y: contentY + contentHeight - self.calculatedDisabledTitleSize.height)
|
||||
imageOrigin = CGPoint(x: floor((contentRect.width - imageSize.width) / 2.0), y: contentY)
|
||||
}
|
||||
|
||||
self.titleNode.frame = CGRect(origin: titleOrigin, size: self.calculatedTitleSize)
|
||||
self.highlightedTitleNode.frame = CGRect(origin: highlightedTitleOrigin, size: self.calculatedHighlightedTitleSize)
|
||||
self.disabledTitleNode.frame = CGRect(origin: disabledTitleOrigin, size: self.calculatedDisabledTitleSize)
|
||||
self.imageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
|
||||
self.disabledImageNode.frame = CGRect(origin: imageOrigin, size: imageSize)
|
||||
|
||||
|
@ -174,6 +174,13 @@ open class TabBarController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public func isPointInsideContentArea(point: CGPoint) -> Bool {
|
||||
if point.y < self.tabBarControllerNode.tabBarNode.frame.minY {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override open func loadDisplayNode() {
|
||||
self.displayNode = TabBarControllerNode(theme: self.theme, navigationBar: self.navigationBar, itemSelected: { [weak self] index, longTap, itemNodes in
|
||||
if let strongSelf = self {
|
||||
|
@ -5,7 +5,9 @@ import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
|
||||
private let edgeWidth: CGFloat = 44.0
|
||||
private func edgeWidth(width: CGFloat) -> CGFloat {
|
||||
return min(44.0, floor(width / 6.0))
|
||||
}
|
||||
|
||||
private let leftFadeImage = generateImage(CGSize(width: 64.0, height: 1.0), opaque: false, rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
@ -153,11 +155,11 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
let size = strongSelf.bounds
|
||||
|
||||
var highlightedSide: Bool?
|
||||
if point.x < edgeWidth && strongSelf.canGoToPreviousItem() {
|
||||
if point.x < edgeWidth(width: size.width) && strongSelf.canGoToPreviousItem() {
|
||||
if strongSelf.items.count > 1 {
|
||||
highlightedSide = false
|
||||
}
|
||||
} else if point.x > size.width - edgeWidth && strongSelf.canGoToNextItem() {
|
||||
} else if point.x > size.width - edgeWidth(width: size.width) && strongSelf.canGoToNextItem() {
|
||||
if strongSelf.items.count > 1 {
|
||||
highlightedSide = true
|
||||
}
|
||||
@ -180,11 +182,11 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
|
||||
var highlightedSide: Bool?
|
||||
if let point = point {
|
||||
if point.x < edgeWidth && strongSelf.canGoToPreviousItem() {
|
||||
if point.x < edgeWidth(width: size.width) && strongSelf.canGoToPreviousItem() {
|
||||
if strongSelf.items.count > 1 {
|
||||
highlightedSide = false
|
||||
}
|
||||
} else if point.x > size.width - edgeWidth && strongSelf.canGoToNextItem() {
|
||||
} else if point.x > size.width - edgeWidth(width: size.width) && strongSelf.canGoToNextItem() {
|
||||
if strongSelf.items.count > 1 {
|
||||
highlightedSide = true
|
||||
}
|
||||
@ -233,9 +235,9 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
if case .tap = gesture {
|
||||
let size = self.bounds.size
|
||||
if location.x < edgeWidth && self.canGoToPreviousItem() {
|
||||
if location.x < edgeWidth(width: size.width) && self.canGoToPreviousItem() {
|
||||
self.goToPreviousItem()
|
||||
} else if location.x > size.width - edgeWidth && self.canGoToNextItem() {
|
||||
} else if location.x > size.width - edgeWidth(width: size.width) && self.canGoToNextItem() {
|
||||
self.goToNextItem()
|
||||
}
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ final class MutableBasicPeerView: MutablePostboxView {
|
||||
fileprivate var peer: Peer?
|
||||
fileprivate var notificationSettings: PeerNotificationSettings?
|
||||
fileprivate var isContact: Bool
|
||||
fileprivate var groupId: PeerGroupId?
|
||||
|
||||
init(postbox: Postbox, peerId: PeerId) {
|
||||
self.peerId = peerId
|
||||
self.peer = postbox.peerTable.get(peerId)
|
||||
self.notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId)
|
||||
self.isContact = postbox.contactsTable.isContact(peerId: self.peerId)
|
||||
self.groupId = postbox.chatListIndexTable.get(peerId: peerId).inclusion.groupId
|
||||
}
|
||||
|
||||
func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool {
|
||||
@ -30,6 +32,13 @@ final class MutableBasicPeerView: MutablePostboxView {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
if transaction.currentUpdatedChatListInclusions[self.peerId] != nil {
|
||||
let groupId = postbox.chatListIndexTable.get(peerId: peerId).inclusion.groupId
|
||||
if self.groupId != groupId {
|
||||
self.groupId = groupId
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
@ -43,10 +52,12 @@ public final class BasicPeerView: PostboxView {
|
||||
public let peer: Peer?
|
||||
public let notificationSettings: PeerNotificationSettings?
|
||||
public let isContact: Bool
|
||||
public let groupId: PeerGroupId?
|
||||
|
||||
init(_ view: MutableBasicPeerView) {
|
||||
self.peer = view.peer
|
||||
self.notificationSettings = view.notificationSettings
|
||||
self.isContact = view.isContact
|
||||
self.groupId = view.groupId
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,19 @@ import Foundation
|
||||
|
||||
public enum UnreadMessageCountsItem: Equatable {
|
||||
case total(ValueBoxKey?)
|
||||
case totalInGroup(PeerGroupId)
|
||||
case peer(PeerId)
|
||||
}
|
||||
|
||||
private enum MutableUnreadMessageCountsItemEntry {
|
||||
case total((ValueBoxKey, PreferencesEntry?)?, ChatListTotalUnreadState)
|
||||
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
|
||||
case peer(PeerId, CombinedPeerReadState?)
|
||||
}
|
||||
|
||||
public enum UnreadMessageCountsItemEntry {
|
||||
case total(PreferencesEntry?, ChatListTotalUnreadState)
|
||||
case totalInGroup(PeerGroupId, ChatListTotalUnreadState)
|
||||
case peer(PeerId, CombinedPeerReadState?)
|
||||
}
|
||||
|
||||
@ -23,6 +26,8 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
switch item {
|
||||
case let .total(preferencesKey):
|
||||
return .total(preferencesKey.flatMap({ ($0, postbox.preferencesTable.get(key: $0)) }), postbox.messageHistoryMetadataTable.getChatListTotalUnreadState())
|
||||
case let .totalInGroup(groupId):
|
||||
return .totalInGroup(groupId, ChatListTotalUnreadState(absoluteCounters: [:], filteredCounters: [:]))
|
||||
case let .peer(peerId):
|
||||
return .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
|
||||
}
|
||||
@ -57,6 +62,8 @@ final class MutableUnreadMessageCountsView: MutablePostboxView {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
case let .totalInGroup(groupId, state):
|
||||
break
|
||||
case let .peer(peerId, _):
|
||||
if transaction.alteredInitialPeerCombinedReadStates[peerId] != nil {
|
||||
self.entries[i] = .peer(peerId, postbox.readStateTable.getCombinedState(peerId))
|
||||
@ -82,6 +89,8 @@ public final class UnreadMessageCountsView: PostboxView {
|
||||
switch entry {
|
||||
case let .total(keyAndValue, state):
|
||||
return .total(keyAndValue?.1, state)
|
||||
case let .totalInGroup(groupId, state):
|
||||
return .totalInGroup(groupId, state)
|
||||
case let .peer(peerId, count):
|
||||
return .peer(peerId, count)
|
||||
}
|
||||
@ -103,7 +112,7 @@ public final class UnreadMessageCountsView: PostboxView {
|
||||
public func count(for item: UnreadMessageCountsItem) -> Int32? {
|
||||
for entry in self.entries {
|
||||
switch entry {
|
||||
case .total:
|
||||
case .total, .totalInGroup:
|
||||
break
|
||||
case let .peer(peerId, state):
|
||||
if case .peer(peerId) = item {
|
||||
|
@ -315,6 +315,7 @@ public final class SegmentedControlNode: ASDisplayNode, UIGestureRecognizerDeleg
|
||||
|
||||
for i in 0 ..< self.itemNodes.count {
|
||||
let itemNode = self.itemNodes[i]
|
||||
let _ = itemNode.measure(itemSize)
|
||||
transition.updateFrame(node: itemNode, frame: CGRect(origin: CGPoint(x: itemSize.width * CGFloat(i), y: (size.height - itemSize.height) / 2.0), size: itemSize))
|
||||
|
||||
let isSelected = selectedIndex == i
|
||||
|
@ -878,6 +878,7 @@ private final class SettingsControllerImpl: ItemListController, SettingsControll
|
||||
|
||||
private final class SettingsTabBarContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = true
|
||||
let ignoreContentTouches: Bool = true
|
||||
|
||||
private let controller: ViewController
|
||||
private let sourceNode: ContextExtractedContentContainingNode
|
||||
|
@ -6,6 +6,7 @@ import Postbox
|
||||
|
||||
final class ChatMessageContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = false
|
||||
|
||||
private weak var chatNode: ChatControllerNode?
|
||||
private let message: Message
|
||||
|
@ -8,6 +8,7 @@ import AppBundle
|
||||
|
||||
private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let location: CGPoint
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
private let requestDismiss: () -> Void
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
@ -16,8 +17,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
private let animatedStickerNode: AnimatedStickerNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
init(text: String, location: CGPoint, requestDismiss: @escaping () -> Void) {
|
||||
init(text: String, location: CGPoint, shouldDismissOnTouch: @escaping (CGPoint) -> Bool, requestDismiss: @escaping () -> Void) {
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
self.requestDismiss = requestDismiss
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
@ -90,7 +92,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
eventIsPresses = event.type == .presses
|
||||
}
|
||||
if event.type == .touches || eventIsPresses {
|
||||
//self.requestDismiss()
|
||||
if self.shouldDismissOnTouch(point) {
|
||||
self.requestDismiss()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -119,6 +123,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
public final class TooltipScreen: ViewController {
|
||||
private let text: String
|
||||
private let location: CGPoint
|
||||
private let shouldDismissOnTouch: (CGPoint) -> Bool
|
||||
|
||||
private var controllerNode: TooltipScreenNode {
|
||||
return self.displayNode as! TooltipScreenNode
|
||||
@ -127,9 +132,10 @@ public final class TooltipScreen: ViewController {
|
||||
private var validLayout: ContainerViewLayout?
|
||||
private var isDismissed: Bool = false
|
||||
|
||||
public init(text: String, location: CGPoint) {
|
||||
public init(text: String, location: CGPoint, shouldDismissOnTouch: @escaping (CGPoint) -> Bool) {
|
||||
self.text = text
|
||||
self.location = location
|
||||
self.shouldDismissOnTouch = shouldDismissOnTouch
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -151,7 +157,7 @@ public final class TooltipScreen: ViewController {
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = TooltipScreenNode(text: self.text, location: self.location, requestDismiss: { [weak self] in
|
||||
self.displayNode = TooltipScreenNode(text: self.text, location: self.location, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user