Folder improvements

This commit is contained in:
Ali 2023-04-01 23:18:58 +04:00
parent 195b4a0159
commit dfe8e80232
6 changed files with 141 additions and 71 deletions

View File

@ -1547,7 +1547,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
for filter in filters { for filter in filters {
if filter.id == filterId, case let .filter(_, title, _, data) = filter { if filter.id == filterId, case let .filter(_, title, _, data) = filter {
if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty { if !data.includePeers.peers.isEmpty && data.categories.isEmpty && !data.excludeRead && !data.excludeMuted && !data.excludeArchived && data.excludePeers.isEmpty {
items.append(.action(ContextMenuActionItem(text: "Share", textColor: .primary, badge: ContextMenuActionBadge(value: "NEW", color: .accent, style: .label), icon: { theme in items.append(.action(ContextMenuActionItem(text: "Share", textColor: .primary, badge: data.hasSharedLinks ? nil : ContextMenuActionBadge(value: "NEW", color: .accent, style: .label), icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Link"), color: theme.contextMenu.primaryColor)
}, action: { c, f in }, action: { c, f in
c.dismiss(completion: { c.dismiss(completion: {

View File

@ -1044,7 +1044,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var includePeers = ChatListFilterIncludePeers() var includePeers = ChatListFilterIncludePeers()
includePeers.setPeers(state.additionallyIncludePeers) includePeers.setPeers(state.additionallyIncludePeers)
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers)) let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, hasSharedLinks: currentPreset?.data?.hasSharedLinks ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
if let data = filter.data { if let data = filter.data {
switch chatListFilterType(data) { switch chatListFilterType(data) {
case .generic: case .generic:
@ -1189,7 +1189,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
let state = stateValue.with { $0 } let state = stateValue.with { $0 }
var includePeers = ChatListFilterIncludePeers() var includePeers = ChatListFilterIncludePeers()
includePeers.setPeers(state.additionallyIncludePeers) includePeers.setPeers(state.additionallyIncludePeers)
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers)) let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, hasSharedLinks: currentPreset?.data?.hasSharedLinks ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
let _ = (context.engine.peers.currentChatListFilters() let _ = (context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).start(next: { filters in |> deliverOnMainQueue).start(next: { filters in
@ -1211,7 +1211,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
let state = stateValue.with { $0 } let state = stateValue.with { $0 }
var includePeers = ChatListFilterIncludePeers() var includePeers = ChatListFilterIncludePeers()
includePeers.setPeers(state.additionallyIncludePeers) includePeers.setPeers(state.additionallyIncludePeers)
let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers)) let filter: ChatListFilter = .filter(id: currentPreset?.id ?? -1, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, hasSharedLinks: currentPreset?.data?.hasSharedLinks ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
let _ = (context.engine.peers.currentChatListFilters() let _ = (context.engine.peers.currentChatListFilters()
|> deliverOnMainQueue).start(next: { filters in |> deliverOnMainQueue).start(next: { filters in
@ -1328,9 +1328,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
previousLink = updatedLink previousLink = updatedLink
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
guard var links else { var links = links ?? []
return
}
if let updatedLink { if let updatedLink {
if let index = links.firstIndex(where: { $0.link == updatedLink.link }) { if let index = links.firstIndex(where: { $0.link == updatedLink.link }) {
@ -1356,9 +1354,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: link, linkUpdated: { updatedLink in pushControllerImpl?(folderInviteLinkListController(context: context, filterId: currentPreset.id, title: currentPreset.title, allPeerIds: state.additionallyIncludePeers, currentInvitation: link, linkUpdated: { updatedLink in
if updatedLink != link { if updatedLink != link {
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
guard var links else { var links = links ?? []
return
}
if let updatedLink { if let updatedLink {
if let index = links.firstIndex(where: { $0.link == link.link }) { if let index = links.firstIndex(where: { $0.link == link.link }) {
@ -1381,9 +1377,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
removeLink: { link in removeLink: { link in
if let currentPreset { if let currentPreset {
let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in let _ = (sharedLinks.get() |> take(1) |> deliverOnMainQueue).start(next: { links in
guard var links else { var links = links ?? []
return
}
if let index = links.firstIndex(where: { $0.link == link.link }) { if let index = links.firstIndex(where: { $0.link == link.link }) {
links.remove(at: index) links.remove(at: index)
@ -1407,7 +1401,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
if currentPreset == nil { if currentPreset == nil {
filterId = context.engine.peers.generateNewChatListFilterId(filters: filters) filterId = context.engine.peers.generateNewChatListFilterId(filters: filters)
} }
var updatedFilter: ChatListFilter = .filter(id: filterId, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers)) var updatedFilter: ChatListFilter = .filter(id: filterId, title: state.name, emoticon: currentPreset?.emoticon, data: ChatListFilterData(isShared: currentPreset?.data?.isShared ?? false, hasSharedLinks: currentPreset?.data?.hasSharedLinks ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
var filters = filters var filters = filters
if let _ = currentPreset { if let _ = currentPreset {
@ -1554,7 +1548,7 @@ func chatListFilterPresetController(context: AccountContext, currentPreset: Chat
var includePeers = ChatListFilterIncludePeers() var includePeers = ChatListFilterIncludePeers()
includePeers.setPeers(state.additionallyIncludePeers) includePeers.setPeers(state.additionallyIncludePeers)
let filter: ChatListFilter = .filter(id: currentPreset.id, title: state.name, emoticon: currentPreset.emoticon, data: ChatListFilterData(isShared: currentPreset.data?.isShared ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers)) let filter: ChatListFilter = .filter(id: currentPreset.id, title: state.name, emoticon: currentPreset.emoticon, data: ChatListFilterData(isShared: currentPreset.data?.isShared ?? false, hasSharedLinks: currentPreset.data?.hasSharedLinks ?? false, categories: state.includeCategories, excludeMuted: state.excludeMuted, excludeRead: state.excludeRead, excludeArchived: state.excludeArchived, includePeers: includePeers, excludePeers: state.additionallyExcludePeers))
if currentPresetWithoutPinnedPeers != filter { if currentPresetWithoutPinnedPeers != filter {
displaySaveAlert() displaySaveAlert()
return false return false

View File

@ -30,7 +30,7 @@ private final class FolderInviteLinkListControllerArguments {
let copyLink: (String) -> Void let copyLink: (String) -> Void
let mainLinkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void let mainLinkContextAction: (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void
let peerAction: (EnginePeer, Bool) -> Void let peerAction: (EnginePeer, Bool) -> Void
let generateLink: () -> Void let toggleAllSelected: () -> Void
init( init(
context: AccountContext, context: AccountContext,
@ -39,7 +39,7 @@ private final class FolderInviteLinkListControllerArguments {
copyLink: @escaping (String) -> Void, copyLink: @escaping (String) -> Void,
mainLinkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void, mainLinkContextAction: @escaping (ExportedChatFolderLink?, ASDisplayNode, ContextGesture?) -> Void,
peerAction: @escaping (EnginePeer, Bool) -> Void, peerAction: @escaping (EnginePeer, Bool) -> Void,
generateLink: @escaping () -> Void toggleAllSelected: @escaping () -> Void
) { ) {
self.context = context self.context = context
self.shareMainLink = shareMainLink self.shareMainLink = shareMainLink
@ -47,7 +47,7 @@ private final class FolderInviteLinkListControllerArguments {
self.copyLink = copyLink self.copyLink = copyLink
self.mainLinkContextAction = mainLinkContextAction self.mainLinkContextAction = mainLinkContextAction
self.peerAction = peerAction self.peerAction = peerAction
self.generateLink = generateLink self.toggleAllSelected = toggleAllSelected
} }
} }
@ -68,7 +68,7 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
case mainLinkHeader(String) case mainLinkHeader(String)
case mainLink(link: ExportedChatFolderLink?, isGenerating: Bool) case mainLink(link: ExportedChatFolderLink?, isGenerating: Bool)
case peersHeader(String) case peersHeader(String, String?)
case peer(index: Int, peer: EnginePeer, isSelected: Bool, disabledReasonText: String?) case peer(index: Int, peer: EnginePeer, isSelected: Bool, disabledReasonText: String?)
case peersInfo(String) case peersInfo(String)
@ -137,8 +137,8 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
} else { } else {
return false return false
} }
case let .peersHeader(text): case let .peersHeader(text, action):
if case .peersHeader(text) = rhs { if case .peersHeader(text, action) = rhs {
return true return true
} else { } else {
return false return false
@ -177,8 +177,6 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
}, shareAction: { }, shareAction: {
if let link { if let link {
arguments.copyLink(link.link) arguments.copyLink(link.link)
} else {
arguments.generateLink()
} }
}, secondaryAction: { }, secondaryAction: {
if let link { if let link {
@ -191,8 +189,10 @@ private enum InviteLinksListEntry: ItemListNodeEntry {
arguments.openMainLink(link.link) arguments.openMainLink(link.link)
} }
}) })
case let .peersHeader(text): case let .peersHeader(text, action):
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section) return ItemListSectionHeaderItem(presentationData: presentationData, text: text, actionText: action, action: action == nil ? nil : {
arguments.toggleAllSelected()
}, sectionId: self.section)
case let .peersInfo(text): case let .peersInfo(text):
return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section) return ItemListTextItem(presentationData: presentationData, text: .markdown(text), sectionId: self.section)
case let .peer(_, peer, isSelected, disabledReasonText): case let .peer(_, peer, isSelected, disabledReasonText):
@ -239,6 +239,9 @@ private func folderInviteLinkListControllerEntries(
let peersHeaderString: String let peersHeaderString: String
let canShareChats = !allPeers.allSatisfy({ !canShareLinkToPeer(peer: $0) }) let canShareChats = !allPeers.allSatisfy({ !canShareLinkToPeer(peer: $0) })
let allSelected = allPeers.filter({ canShareLinkToPeer(peer: $0) }).allSatisfy({ state.selectedPeerIds.contains($0.id) })
var selectAllString: String?
if !canShareChats { if !canShareChats {
infoString = "You can only share groups and channels in which you are allowed to create invite links." infoString = "You can only share groups and channels in which you are allowed to create invite links."
@ -247,12 +250,21 @@ private func folderInviteLinkListControllerEntries(
} else if state.selectedPeerIds.isEmpty { } else if state.selectedPeerIds.isEmpty {
chatCountString = "Anyone with this link can add **\(title)** folder and the chats selected below." chatCountString = "Anyone with this link can add **\(title)** folder and the chats selected below."
peersHeaderString = "CHATS" peersHeaderString = "CHATS"
if allPeers.count > 1 {
selectAllString = allSelected ? "DESELECT ALL" : "SELECT ALL"
}
} else if state.selectedPeerIds.count == 1 { } else if state.selectedPeerIds.count == 1 {
chatCountString = "Anyone with this link can add **\(title)** folder and the 1 chat selected below." chatCountString = "Anyone with this link can add **\(title)** folder and the 1 chat selected below."
peersHeaderString = "1 CHAT SELECTED" peersHeaderString = "1 CHAT SELECTED"
if allPeers.count > 1 {
selectAllString = allSelected ? "DESELECT ALL" : "SELECT ALL"
}
} else { } else {
chatCountString = "Anyone with this link can add **\(title)** folder and the \(state.selectedPeerIds.count) chats selected below." chatCountString = "Anyone with this link can add **\(title)** folder and the \(state.selectedPeerIds.count) chats selected below."
peersHeaderString = "\(state.selectedPeerIds.count) CHATS SELECTED" peersHeaderString = "\(state.selectedPeerIds.count) CHATS SELECTED"
if allPeers.count > 1 {
selectAllString = allSelected ? "DESELECT ALL" : "SELECT ALL"
}
} }
entries.append(.header(chatCountString)) entries.append(.header(chatCountString))
@ -263,7 +275,7 @@ private func folderInviteLinkListControllerEntries(
entries.append(.mainLink(link: state.currentLink, isGenerating: state.generatingLink)) entries.append(.mainLink(link: state.currentLink, isGenerating: state.generatingLink))
} }
entries.append(.peersHeader(peersHeaderString)) entries.append(.peersHeader(peersHeaderString, selectAllString))
var sortedPeers: [EnginePeer] = [] var sortedPeers: [EnginePeer] = []
for peer in allPeers.filter({ canShareLinkToPeer(peer: $0) }) { for peer in allPeers.filter({ canShareLinkToPeer(peer: $0) }) {
@ -338,6 +350,20 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
var didDisplayAddPeerNotice: Bool = false var didDisplayAddPeerNotice: Bool = false
var combinedPeerIds: [EnginePeer.Id] = []
if let currentInvitation {
for peerId in currentInvitation.peerIds {
if !combinedPeerIds.contains(peerId) {
combinedPeerIds.append(peerId)
}
}
}
for peerId in allPeerIds {
if !combinedPeerIds.contains(peerId) {
combinedPeerIds.append(peerId)
}
}
let arguments = FolderInviteLinkListControllerArguments(context: context, shareMainLink: { inviteLink in let arguments = FolderInviteLinkListControllerArguments(context: context, shareMainLink: { inviteLink in
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData) let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.completed = { peerIds in shareController.completed = { peerIds in
@ -500,47 +526,33 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
dismissTooltipsImpl?() dismissTooltipsImpl?()
displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil)) displayTooltipImpl?(.peers(context: context, peers: [peer], title: nil, text: text, customUndoText: nil))
} }
}, generateLink: { }, toggleAllSelected: {
let currentState = stateValue.with({ $0 }) let _ = (context.engine.data.get(
if !currentState.generatingLink { EngineDataList(combinedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))
updateState { state in )
var state = state |> deliverOnMainQueue).start(next: { allPeers in
let allPeers = allPeers.compactMap({ $0 })
state.generatingLink = true let selectablePeers = allPeers.filter({ canShareLinkToPeer(peer: $0) })
let state = stateValue.with({ $0 })
return state let allSelected = selectablePeers.allSatisfy({ state.selectedPeerIds.contains($0.id) })
}
actionsDisposable.add((context.engine.peers.exportChatFolder(filterId: filterId, title: "", peerIds: Array(currentState.selectedPeerIds))
|> deliverOnMainQueue).start(next: { result in
linkUpdated(result)
updateState { state in updateState { state in
var state = state var state = state
state.generatingLink = false if allSelected {
state.currentLink = result state.selectedPeerIds.removeAll()
} else {
state.selectedPeerIds.removeAll()
for peer in selectablePeers {
state.selectedPeerIds.insert(peer.id)
}
}
return state return state
} }
}, error: { _ in
}))
}
}) })
})
var combinedPeerIds: [EnginePeer.Id] = []
if let currentInvitation {
for peerId in currentInvitation.peerIds {
if !combinedPeerIds.contains(peerId) {
combinedPeerIds.append(peerId)
}
}
}
for peerId in allPeerIds {
if !combinedPeerIds.contains(peerId) {
combinedPeerIds.append(peerId)
}
}
let allPeers = context.engine.data.subscribe( let allPeers = context.engine.data.subscribe(
EngineDataList(combinedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))) EngineDataList(combinedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:)))

View File

@ -45,17 +45,21 @@ public class ItemListSectionHeaderItem: ListViewItem, ItemListItem {
let multiline: Bool let multiline: Bool
let activityIndicator: ItemListSectionHeaderActivityIndicator let activityIndicator: ItemListSectionHeaderActivityIndicator
let accessoryText: ItemListSectionHeaderAccessoryText? let accessoryText: ItemListSectionHeaderAccessoryText?
let actionText: String?
let action: (() -> Void)?
public let sectionId: ItemListSectionId public let sectionId: ItemListSectionId
public let isAlwaysPlain: Bool = true public let isAlwaysPlain: Bool = true
public init(presentationData: ItemListPresentationData, text: String, badge: String? = nil, multiline: Bool = false, activityIndicator: ItemListSectionHeaderActivityIndicator = .none, accessoryText: ItemListSectionHeaderAccessoryText? = nil, sectionId: ItemListSectionId) { public init(presentationData: ItemListPresentationData, text: String, badge: String? = nil, multiline: Bool = false, activityIndicator: ItemListSectionHeaderActivityIndicator = .none, accessoryText: ItemListSectionHeaderAccessoryText? = nil, actionText: String? = nil, action: (() -> Void)? = nil, sectionId: ItemListSectionId) {
self.presentationData = presentationData self.presentationData = presentationData
self.text = text self.text = text
self.badge = badge self.badge = badge
self.multiline = multiline self.multiline = multiline
self.activityIndicator = activityIndicator self.activityIndicator = activityIndicator
self.accessoryText = accessoryText self.accessoryText = accessoryText
self.actionText = actionText
self.action = action
self.sectionId = sectionId self.sectionId = sectionId
} }
@ -104,6 +108,8 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode {
private let accessoryTextNode: TextNode private let accessoryTextNode: TextNode
private var accessoryImageNode: ASImageNode? private var accessoryImageNode: ASImageNode?
private var activityIndicator: ActivityIndicator? private var activityIndicator: ActivityIndicator?
private var actionNode: TextNode?
private var actionButtonNode: HighlightableButtonNode?
private let activateArea: AccessibilityAreaNode private let activateArea: AccessibilityAreaNode
@ -130,6 +136,7 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode {
public func asyncLayout() -> (_ item: ItemListSectionHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { public func asyncLayout() -> (_ item: ItemListSectionHeaderItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
let makeActionLayout = TextNode.asyncLayout(self.actionNode)
let makeBadgeTextLayout = TextNode.asyncLayout(self.badgeTextNode) let makeBadgeTextLayout = TextNode.asyncLayout(self.badgeTextNode)
let makeAccessoryTextLayout = TextNode.asyncLayout(self.accessoryTextNode) let makeAccessoryTextLayout = TextNode.asyncLayout(self.accessoryTextNode)
@ -152,6 +159,13 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode {
textRightInset += badgeLayoutAndApply.0.size.width + badgeSpacing textRightInset += badgeLayoutAndApply.0.size.width + badgeSpacing
} }
var actionLayoutAndApply: (TextNodeLayout, () -> TextNode)?
if let actionText = item.actionText {
let actionLayoutAndApplyValue = makeActionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: actionText, font: titleFont, textColor: item.presentationData.theme.list.itemAccentColor), backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
actionLayoutAndApply = actionLayoutAndApplyValue
textRightInset += actionLayoutAndApplyValue.0.size.width + 2.0
}
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.text, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.text, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: item.multiline ? 0 : 1, truncationType: .end, constrainedSize: CGSize(width: params.width - params.leftInset - params.rightInset - textRightInset, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
var accessoryTextString: NSAttributedString? var accessoryTextString: NSAttributedString?
var accessoryIcon: UIImage? var accessoryIcon: UIImage?
@ -195,6 +209,39 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode {
strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: titleLayout.size) strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 7.0), size: titleLayout.size)
if let (actionLayout, actionApply) = actionLayoutAndApply {
let actionButtonNode: HighlightableButtonNode
if let current = strongSelf.actionButtonNode {
actionButtonNode = current
} else {
actionButtonNode = HighlightableButtonNode()
strongSelf.actionButtonNode = actionButtonNode
actionButtonNode.hitTestSlop = UIEdgeInsets(top: -4.0, left: -4.0, bottom: -4.0, right: -4.0)
strongSelf.addSubnode(actionButtonNode)
actionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.actionButtonPressed), forControlEvents: .touchUpInside)
}
let actionNode = actionApply()
if strongSelf.actionNode !== actionNode {
strongSelf.actionNode?.removeFromSupernode()
strongSelf.actionNode = actionNode
actionButtonNode.addSubnode(actionNode)
}
actionButtonNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - actionLayout.size.width, y: 7.0), size: actionLayout.size)
actionNode.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: actionLayout.size)
} else {
if let actionNode = strongSelf.actionNode {
strongSelf.actionNode = nil
actionNode.removeFromSupernode()
}
if let actionButtonNode = strongSelf.actionButtonNode {
strongSelf.actionButtonNode = nil
actionButtonNode.removeFromSupernode()
}
}
if let badgeLayoutAndApply { if let badgeLayoutAndApply {
let badgeTextNode = badgeLayoutAndApply.1() let badgeTextNode = badgeLayoutAndApply.1()
let badgeSideInset: CGFloat = 4.0 let badgeSideInset: CGFloat = 4.0
@ -296,6 +343,10 @@ public class ItemListSectionHeaderItemNode: ListViewItemNode {
} }
} }
@objc private func actionButtonPressed() {
self.item?.action?()
}
override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4) self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
} }

View File

@ -175,6 +175,7 @@ extension ChatListFilterIncludePeers {
public struct ChatListFilterData: Equatable, Hashable { public struct ChatListFilterData: Equatable, Hashable {
public var isShared: Bool public var isShared: Bool
public var hasSharedLinks: Bool
public var categories: ChatListFilterPeerCategories public var categories: ChatListFilterPeerCategories
public var excludeMuted: Bool public var excludeMuted: Bool
public var excludeRead: Bool public var excludeRead: Bool
@ -184,6 +185,7 @@ public struct ChatListFilterData: Equatable, Hashable {
public init( public init(
isShared: Bool, isShared: Bool,
hasSharedLinks: Bool,
categories: ChatListFilterPeerCategories, categories: ChatListFilterPeerCategories,
excludeMuted: Bool, excludeMuted: Bool,
excludeRead: Bool, excludeRead: Bool,
@ -192,6 +194,7 @@ public struct ChatListFilterData: Equatable, Hashable {
excludePeers: [PeerId] excludePeers: [PeerId]
) { ) {
self.isShared = isShared self.isShared = isShared
self.hasSharedLinks = hasSharedLinks
self.categories = categories self.categories = categories
self.excludeMuted = excludeMuted self.excludeMuted = excludeMuted
self.excludeRead = excludeRead self.excludeRead = excludeRead
@ -250,6 +253,7 @@ public enum ChatListFilter: Codable, Equatable {
let data = ChatListFilterData( let data = ChatListFilterData(
isShared: try container.decodeIfPresent(Bool.self, forKey: "isShared") ?? false, isShared: try container.decodeIfPresent(Bool.self, forKey: "isShared") ?? false,
hasSharedLinks: try container.decodeIfPresent(Bool.self, forKey: "hasSharedLinks") ?? false,
categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")), categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")),
excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0, excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0,
excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0, excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0,
@ -280,6 +284,7 @@ public enum ChatListFilter: Codable, Equatable {
try container.encodeIfPresent(emoticon, forKey: "emoticon") try container.encodeIfPresent(emoticon, forKey: "emoticon")
try container.encode(data.isShared, forKey: "isShared") try container.encode(data.isShared, forKey: "isShared")
try container.encode(data.hasSharedLinks, forKey: "hasSharedLinks")
try container.encode(data.categories.rawValue, forKey: "categories") try container.encode(data.categories.rawValue, forKey: "categories")
try container.encode((data.excludeMuted ? 1 : 0) as Int32, forKey: "excludeMuted") try container.encode((data.excludeMuted ? 1 : 0) as Int32, forKey: "excludeMuted")
try container.encode((data.excludeRead ? 1 : 0) as Int32, forKey: "excludeRead") try container.encode((data.excludeRead ? 1 : 0) as Int32, forKey: "excludeRead")
@ -303,6 +308,7 @@ extension ChatListFilter {
emoticon: emoticon, emoticon: emoticon,
data: ChatListFilterData( data: ChatListFilterData(
isShared: false, isShared: false,
hasSharedLinks: false,
categories: ChatListFilterPeerCategories(apiFlags: flags), categories: ChatListFilterPeerCategories(apiFlags: flags),
excludeMuted: (flags & (1 << 11)) != 0, excludeMuted: (flags & (1 << 11)) != 0,
excludeRead: (flags & (1 << 12)) != 0, excludeRead: (flags & (1 << 12)) != 0,
@ -344,13 +350,14 @@ extension ChatListFilter {
} }
) )
) )
case let .dialogFilterCommunity(_, id, title, emoticon, pinnedPeers, includePeers): case let .dialogFilterCommunity(flags, id, title, emoticon, pinnedPeers, includePeers):
self = .filter( self = .filter(
id: id, id: id,
title: title, title: title,
emoticon: emoticon, emoticon: emoticon,
data: ChatListFilterData( data: ChatListFilterData(
isShared: true, isShared: true,
hasSharedLinks: (flags & (1 << 26)) != 0,
categories: [], categories: [],
excludeMuted: false, excludeMuted: false,
excludeRead: false, excludeRead: false,
@ -1078,7 +1085,8 @@ public struct ChatListFeaturedFilter: Codable, Equatable {
self.title = try container.decode(String.self, forKey: "title") self.title = try container.decode(String.self, forKey: "title")
self.description = try container.decode(String.self, forKey: "description") self.description = try container.decode(String.self, forKey: "description")
self.data = ChatListFilterData( self.data = ChatListFilterData(
isShared: try container.decodeIfPresent(Bool.self, forKey: "isShared") ?? false, isShared: false,
hasSharedLinks: false,
categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")), categories: ChatListFilterPeerCategories(rawValue: try container.decode(Int32.self, forKey: "categories")),
excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0, excludeMuted: (try container.decode(Int32.self, forKey: "excludeMuted")) != 0,
excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0, excludeRead: (try container.decode(Int32.self, forKey: "excludeRead")) != 0,

View File

@ -539,24 +539,29 @@ private struct FirstTimeFolderUpdatesKey: Hashable {
private var firstTimeFolderUpdates = Set<FirstTimeFolderUpdatesKey>() private var firstTimeFolderUpdates = Set<FirstTimeFolderUpdatesKey>()
func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> Signal<Never, NoError> { func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> ChatListFiltersState in return account.postbox.transaction { transaction -> (ChatListFiltersState, AppConfiguration) in
return _internal_currentChatListFiltersState(transaction: transaction) return (_internal_currentChatListFiltersState(transaction: transaction), currentAppConfiguration(transaction: transaction))
} }
|> mapToSignal { state -> Signal<Never, NoError> in |> mapToSignal { state, appConfig -> Signal<Never, NoError> in
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970) let timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
let key = FirstTimeFolderUpdatesKey(accountId: account.id, folderId: folderId) let key = FirstTimeFolderUpdatesKey(accountId: account.id, folderId: folderId)
if firstTimeFolderUpdates.contains(key) { if firstTimeFolderUpdates.contains(key) {
if let current = state.updates.first(where: { $0.folderId == folderId }) { if let current = state.updates.first(where: { $0.folderId == folderId }) {
let updateInterval: Int32 var updateInterval: Int32 = 3600
if let data = appConfig.data {
if let value = data["community_update_period"] as? Double {
updateInterval = Int32(value)
}
}
#if DEBUG #if DEBUG
if "".isEmpty {
updateInterval = 5 updateInterval = 5
#else }
updateInterval = 60 * 60
#endif #endif
if current.timestamp + updateInterval >= timestamp { if current.timestamp + updateInterval >= timestamp {
return .complete() return .complete()
} }
} }