mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
[WIP] Topics
This commit is contained in:
parent
8838fe095c
commit
d91c4a4b02
@ -748,7 +748,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
editItem.accessibilityLabel = strongSelf.presentationData.strings.Common_Edit
|
||||
strongSelf.navigationItem.setRightBarButton(editItem, animated: true)
|
||||
case .forum:
|
||||
strongSelf.navigationItem.setRightBarButton(strongSelf.moreBarButtonItem, animated: true)
|
||||
if strongSelf.navigationItem.rightBarButtonItem !== strongSelf.moreBarButtonItem {
|
||||
strongSelf.navigationItem.setRightBarButton(strongSelf.moreBarButtonItem, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,6 +344,7 @@ private final class InnerActionsContainerNode: ASDisplayNode {
|
||||
|
||||
final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
private let presentationData: PresentationData
|
||||
private let shadowNode: ASImageNode
|
||||
private var effectView: UIVisualEffectView?
|
||||
private let highlightBackgroundNode: ASDisplayNode
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
@ -368,6 +369,13 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
self.tip = tip
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.shadowNode = ASImageNode()
|
||||
self.shadowNode.displaysAsynchronously = false
|
||||
self.shadowNode.displayWithoutProcessing = true
|
||||
self.shadowNode.image = UIImage(bundleImageName: "Components/Context Menu/Shadow")?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 60)
|
||||
self.shadowNode.contentMode = .scaleToFill
|
||||
self.shadowNode.isHidden = true
|
||||
|
||||
self.highlightBackgroundNode = ASDisplayNode()
|
||||
self.highlightBackgroundNode.isAccessibilityElement = false
|
||||
self.highlightBackgroundNode.alpha = 0.0
|
||||
@ -408,6 +416,12 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
self.targetSelectionIndex = nil
|
||||
icon = nil
|
||||
isUserInteractionEnabled = text != nil
|
||||
case let .notificationTopicExceptions(text, action):
|
||||
self.action = action
|
||||
self.text = text
|
||||
self.targetSelectionIndex = nil
|
||||
icon = nil
|
||||
isUserInteractionEnabled = action != nil
|
||||
}
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
@ -499,7 +513,14 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
switch presentation {
|
||||
case .inline:
|
||||
self.shadowNode.isHidden = true
|
||||
case .modal:
|
||||
self.shadowNode.isHidden = false
|
||||
}
|
||||
|
||||
switch widthClass {
|
||||
case .compact:
|
||||
if let effectView = self.effectView {
|
||||
@ -609,6 +630,9 @@ final class InnerTextSelectionTipContainerNode: ASDisplayNode {
|
||||
func setActualSize(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.highlightBackgroundNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
transition.updateFrame(node: self.shadowNode, frame: bounds.insetBy(dx: -30.0, dy: -30.0))
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
@ -774,7 +798,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
||||
self.textSelectionTipNodeDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, constrainedWidth: CGFloat, constrainedHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
func updateLayout(widthClass: ContainerViewLayoutSizeClass, presentation: ContextControllerActionsStackNode.Presentation, constrainedWidth: CGFloat, constrainedHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var widthClass = widthClass
|
||||
if !self.blurBackground {
|
||||
widthClass = .regular
|
||||
@ -829,7 +853,7 @@ final class ContextActionsContainerNode: ASDisplayNode {
|
||||
|
||||
if let textSelectionTipNode = self.textSelectionTipNode {
|
||||
contentSize.height += 8.0
|
||||
let textSelectionTipSize = textSelectionTipNode.updateLayout(widthClass: widthClass, width: actionsSize.width, transition: transition)
|
||||
let textSelectionTipSize = textSelectionTipNode.updateLayout(widthClass: widthClass, presentation: presentation, width: actionsSize.width, transition: transition)
|
||||
transition.updateFrame(node: textSelectionTipNode, frame: CGRect(origin: CGPoint(x: 0.0, y: contentSize.height), size: textSelectionTipSize))
|
||||
textSelectionTipNode.setActualSize(size: textSelectionTipSize, transition: transition)
|
||||
contentSize.height += textSelectionTipSize.height
|
||||
|
@ -1606,7 +1606,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: actionsContainerTransition)
|
||||
let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, presentation: .inline, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: actionsContainerTransition)
|
||||
let adjustedActionsSize = realActionsSize
|
||||
|
||||
self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize)
|
||||
@ -1708,7 +1708,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
constrainedActionsBottomInset = 0.0
|
||||
}
|
||||
|
||||
let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: constrainedActionsHeight, transition: actionsContainerTransition)
|
||||
let realActionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, presentation: .inline, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: constrainedActionsHeight, transition: actionsContainerTransition)
|
||||
let adjustedActionsSize = realActionsSize
|
||||
|
||||
self.actionsContainerNode.updateSize(containerSize: realActionsSize, contentSize: realActionsSize)
|
||||
@ -1867,7 +1867,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
constrainedWidth = floor(layout.size.width / 2.0)
|
||||
}
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: constrainedWidth - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: actionsContainerTransition)
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, presentation: .inline, constrainedWidth: constrainedWidth - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: actionsContainerTransition)
|
||||
let contentScale = (constrainedWidth - actionsSideInset * 2.0) / constrainedWidth
|
||||
var contentUnscaledSize: CGSize
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
@ -2357,7 +2357,8 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
case textSelection
|
||||
case messageViewsPrivacy
|
||||
case messageCopyProtection(isChannel: Bool)
|
||||
case animatedEmoji(text: String?, arguments: TextNodeWithEntities.Arguments?, file: TelegramMediaFile?, action: (() -> Void)?)
|
||||
case animatedEmoji(text: String?, arguments: TextNodeWithEntities.Arguments?, file: TelegramMediaFile?, action: (() -> Void)?)
|
||||
case notificationTopicExceptions(text: String, action: (() -> Void)?)
|
||||
|
||||
public static func ==(lhs: Tip, rhs: Tip) -> Bool {
|
||||
switch lhs {
|
||||
@ -2391,6 +2392,12 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .notificationTopicExceptions(text, _):
|
||||
if case .notificationTopicExceptions(text, _) = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -963,7 +963,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
return (size, apparentHeight)
|
||||
}
|
||||
|
||||
func updateTip(presentationData: PresentationData, width: CGFloat, transition: ContainedViewLayoutTransition) -> (node: InnerTextSelectionTipContainerNode, height: CGFloat)? {
|
||||
func updateTip(presentationData: PresentationData, presentation: ContextControllerActionsStackNode.Presentation, width: CGFloat, transition: ContainedViewLayoutTransition) -> (node: InnerTextSelectionTipContainerNode, height: CGFloat)? {
|
||||
if let tip = self.tip {
|
||||
var updatedTransition = transition
|
||||
if let tipNode = self.tipNode, tipNode.tip == tip {
|
||||
@ -985,7 +985,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if let tipNode = self.tipNode {
|
||||
let size = tipNode.updateLayout(widthClass: .compact, width: width, transition: updatedTransition)
|
||||
let size = tipNode.updateLayout(widthClass: .compact, presentation: presentation, width: width, transition: updatedTransition)
|
||||
return (tipNode, size.height)
|
||||
} else {
|
||||
return nil
|
||||
@ -1229,7 +1229,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
standardMaxWidth = 240.0
|
||||
standardMinWidth = standardMaxWidth
|
||||
|
||||
if let (tipNode, tipHeight) = itemContainer.updateTip(presentationData: presentationData, width: standardMaxWidth, transition: itemContainerTransition) {
|
||||
if let (tipNode, tipHeight) = itemContainer.updateTip(presentationData: presentationData, presentation: presentation, width: standardMaxWidth, transition: itemContainerTransition) {
|
||||
tip = TipLayout(tipNode: tipNode, tipHeight: tipHeight)
|
||||
additionalBottomInset = tipHeight + 10.0
|
||||
} else {
|
||||
@ -1256,7 +1256,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
if !itemContainer.node.wantsFullWidth {
|
||||
if let (tipNode, tipHeight) = itemContainer.updateTip(presentationData: presentationData, width: itemSize.size.width, transition: itemContainerTransition) {
|
||||
if let (tipNode, tipHeight) = itemContainer.updateTip(presentationData: presentationData, presentation: presentation, width: itemSize.size.width, transition: itemContainerTransition) {
|
||||
tip = TipLayout(tipNode: tipNode, tipHeight: tipHeight)
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ final class PeekControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
let actionsSideInset: CGFloat = layout.safeInsets.left + 11.0
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: .immediate)
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(widthClass: layout.metrics.widthClass, presentation: .inline, constrainedWidth: layout.size.width - actionsSideInset * 2.0, constrainedHeight: layout.size.height, transition: .immediate)
|
||||
|
||||
let containerFrame: CGRect
|
||||
let actionsFrame: CGRect
|
||||
|
@ -372,9 +372,9 @@ final class ChatListIndexTable: Table {
|
||||
} else {
|
||||
let previousCount: Int32
|
||||
if let previousSummary = alteredInitialPeerThreadsSummaries[peerId] {
|
||||
previousCount = previousSummary.unreadCount
|
||||
previousCount = previousSummary.effectiveUnreadCount
|
||||
} else {
|
||||
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0
|
||||
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
}
|
||||
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: previousCount, markedUnread: false))])
|
||||
}
|
||||
@ -382,9 +382,9 @@ final class ChatListIndexTable: Table {
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let previousCount: Int32
|
||||
if let previousSummary = alteredInitialPeerThreadsSummaries[peerId] {
|
||||
previousCount = previousSummary.unreadCount
|
||||
previousCount = previousSummary.effectiveUnreadCount
|
||||
} else {
|
||||
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0
|
||||
previousCount = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
}
|
||||
initialReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: previousCount, markedUnread: false))])
|
||||
} else {
|
||||
@ -394,7 +394,7 @@ final class ChatListIndexTable: Table {
|
||||
|
||||
let currentReadState: CombinedPeerReadState?
|
||||
if let peer = postbox.peerTable.get(peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
currentReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
currentReadState = postbox.readStateTable.getCombinedState(peerId)
|
||||
@ -627,7 +627,7 @@ final class ChatListIndexTable: Table {
|
||||
|
||||
let combinedState: CombinedPeerReadState?
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
combinedState = postbox.readStateTable.getCombinedState(peerId)
|
||||
@ -719,7 +719,7 @@ final class ChatListIndexTable: Table {
|
||||
|
||||
let combinedState: CombinedPeerReadState?
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0
|
||||
let count: Int32 = postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0
|
||||
combinedState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
combinedState = postbox.readStateTable.getCombinedState(peerId)
|
||||
|
@ -492,8 +492,8 @@ final class MutableChatListView {
|
||||
|
||||
let isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0
|
||||
isUnread = count > 0
|
||||
let hasUnmutedUnread = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.hasUnmutedUnread ?? false
|
||||
isUnread = hasUnmutedUnread
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(peer.id)?.isUnread ?? false
|
||||
}
|
||||
@ -570,8 +570,7 @@ final class MutableChatListView {
|
||||
|
||||
let isUnread: Bool
|
||||
if let peer = groupEntries[i].renderedPeers[j].peer.peer, postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0
|
||||
isUnread = count > 0
|
||||
isUnread = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.hasUnmutedUnread ?? false
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(groupEntries[i].renderedPeers[j].peer.peerId)?.isUnread ?? false
|
||||
}
|
||||
@ -664,7 +663,7 @@ final class MutableChatListView {
|
||||
|
||||
let readState: CombinedPeerReadState?
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: index.messageIndex.id.peerId)?.unreadCount ?? 0
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: index.messageIndex.id.peerId)?.totalUnreadCount ?? 0
|
||||
readState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
readState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)
|
||||
|
@ -61,7 +61,7 @@ private func mappedChatListFilterPredicate(postbox: PostboxImpl, currentTransact
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId) {
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
||||
}
|
||||
@ -412,7 +412,7 @@ private final class ChatListViewSpaceState {
|
||||
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)?.isUnread ?? false
|
||||
}
|
||||
@ -537,7 +537,7 @@ private final class ChatListViewSpaceState {
|
||||
if settingsChange != nil || transaction.updatedPeerThreadsSummaries.contains(entryNotificationsPeerId) {
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
||||
}
|
||||
@ -581,7 +581,7 @@ private final class ChatListViewSpaceState {
|
||||
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
||||
}
|
||||
@ -784,7 +784,7 @@ private final class ChatListViewSpaceState {
|
||||
if updatedMessageSummary != nil || updatedActionsSummary != nil {
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(entryPeer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: entryPeer.id)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(entryPeer.id)?.isUnread ?? false
|
||||
}
|
||||
@ -834,7 +834,7 @@ private final class ChatListViewSpaceState {
|
||||
|
||||
var isUnread: Bool
|
||||
if postbox.seedConfiguration.peerSummaryIsThreadBased(mainPeer) {
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.unreadCount ?? 0) > 0
|
||||
isUnread = (postbox.peerThreadsSummaryTable.get(peerId: peerId)?.effectiveUnreadCount ?? 0) > 0
|
||||
} else {
|
||||
isUnread = postbox.readStateTable.getCombinedState(peerId)?.isUnread ?? false
|
||||
}
|
||||
@ -913,7 +913,7 @@ private final class ChatListViewSpaceState {
|
||||
|
||||
var updatedReadState = readState
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.totalUnreadCount ?? 0
|
||||
updatedReadState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
updatedReadState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)
|
||||
@ -1516,7 +1516,7 @@ struct ChatListViewState {
|
||||
|
||||
let readState: CombinedPeerReadState?
|
||||
if let peer = postbox.peerTable.get(index.messageIndex.id.peerId), postbox.seedConfiguration.peerSummaryIsThreadBased(peer) {
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.unreadCount ?? 0
|
||||
let count = postbox.peerThreadsSummaryTable.get(peerId: peer.id)?.totalUnreadCount ?? 0
|
||||
readState = CombinedPeerReadState(states: [(0, .idBased(maxIncomingReadId: 0, maxOutgoingReadId: 0, maxKnownId: 0, count: count, markedUnread: false))])
|
||||
} else {
|
||||
readState = postbox.readStateTable.getCombinedState(index.messageIndex.id.peerId)
|
||||
|
@ -108,26 +108,75 @@ class PeerThreadCombinedStateTable: Table {
|
||||
}
|
||||
|
||||
struct StoredPeerThreadsSummary: Equatable, Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case unreadCount = "u"
|
||||
struct ThreadsTagSummary: Equatable, Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case tag = "t"
|
||||
case count = "c"
|
||||
}
|
||||
|
||||
var tag: MessageTags
|
||||
var count: Int32
|
||||
|
||||
init(tag: MessageTags, count: Int32) {
|
||||
self.tag = tag
|
||||
self.count = count
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.tag = MessageTags(rawValue: UInt32(bitPattern: try container.decode(Int32.self, forKey: .tag)))
|
||||
self.count = try container.decode(Int32.self, forKey: .count)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(Int32(bitPattern: self.tag.rawValue), forKey: .tag)
|
||||
try container.encode(self.count, forKey: .count)
|
||||
}
|
||||
}
|
||||
|
||||
var unreadCount: Int32
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case totalUnreadCount = "u"
|
||||
case hasUnmutedUnread = "h"
|
||||
case tagSummaries = "ts"
|
||||
}
|
||||
|
||||
init(unreadCount: Int32) {
|
||||
self.unreadCount = unreadCount
|
||||
var totalUnreadCount: Int32
|
||||
var hasUnmutedUnread: Bool
|
||||
var tagSummaries: [ThreadsTagSummary]
|
||||
|
||||
init(totalUnreadCount: Int32, hasUnmutedUnread: Bool, tagSummaries: [ThreadsTagSummary]) {
|
||||
self.totalUnreadCount = totalUnreadCount
|
||||
self.hasUnmutedUnread = hasUnmutedUnread
|
||||
self.tagSummaries = tagSummaries
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self.unreadCount = try container.decode(Int32.self, forKey: .unreadCount)
|
||||
self.totalUnreadCount = try container.decodeIfPresent(Int32.self, forKey: .totalUnreadCount) ?? 0
|
||||
self.hasUnmutedUnread = try container.decodeIfPresent(Bool.self, forKey: .hasUnmutedUnread) ?? false
|
||||
self.tagSummaries = try container.decodeIfPresent([ThreadsTagSummary].self, forKey: .tagSummaries) ?? []
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(self.unreadCount, forKey: .unreadCount)
|
||||
try container.encode(self.totalUnreadCount, forKey: .totalUnreadCount)
|
||||
try container.encode(self.hasUnmutedUnread, forKey: .hasUnmutedUnread)
|
||||
try container.encode(self.tagSummaries, forKey: .tagSummaries)
|
||||
}
|
||||
}
|
||||
|
||||
extension StoredPeerThreadsSummary {
|
||||
var effectiveUnreadCount: Int32 {
|
||||
if self.hasUnmutedUnread {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,11 +185,15 @@ class PeerThreadsSummaryTable: Table {
|
||||
return ValueBoxTable(id: id, keyType: .binary, compactValuesOnCreation: true)
|
||||
}
|
||||
|
||||
private let seedConfiguration: SeedConfiguration
|
||||
|
||||
private let sharedKey = ValueBoxKey(length: 8)
|
||||
|
||||
private(set) var updatedIds = Set<PeerId>()
|
||||
|
||||
override init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool) {
|
||||
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, seedConfiguration: SeedConfiguration) {
|
||||
self.seedConfiguration = seedConfiguration
|
||||
|
||||
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
||||
}
|
||||
|
||||
@ -172,20 +225,33 @@ class PeerThreadsSummaryTable: Table {
|
||||
}
|
||||
}
|
||||
|
||||
func update(peerIds: Set<PeerId>, indexTable: MessageHistoryThreadIndexTable, combinedStateTable: PeerThreadCombinedStateTable) -> [PeerId: StoredPeerThreadsSummary] {
|
||||
func update(peerIds: Set<PeerId>, indexTable: MessageHistoryThreadIndexTable, combinedStateTable: PeerThreadCombinedStateTable, tagsSummaryTable: MessageHistoryTagsSummaryTable) -> [PeerId: StoredPeerThreadsSummary] {
|
||||
var updatedInitialSummaries: [PeerId: StoredPeerThreadsSummary] = [:]
|
||||
|
||||
for peerId in peerIds {
|
||||
var unreadCount: Int32 = 0
|
||||
var totalUnreadCount: Int32 = 0
|
||||
var hasUnmutedUnread: Bool = false
|
||||
var tagSummaries: [StoredPeerThreadsSummary.ThreadsTagSummary] = []
|
||||
for item in indexTable.fetch(peerId: peerId, namespace: 0, start: .upperBound, end: .lowerBound, limit: 20) {
|
||||
if item.info.summary.mutedUntil == nil {
|
||||
unreadCount += item.info.summary.totalUnreadCount
|
||||
if item.info.summary.totalUnreadCount > 0 {
|
||||
totalUnreadCount += 1
|
||||
if item.info.summary.mutedUntil == nil {
|
||||
hasUnmutedUnread = true
|
||||
}
|
||||
}
|
||||
|
||||
for tag in self.seedConfiguration.messageTagsWithThreadSummary {
|
||||
if let value = tagsSummaryTable.get(MessageHistoryTagsSummaryKey(tag: tag, peerId: peerId, threadId: item.threadId, namespace: 0)) {
|
||||
tagSummaries.append(StoredPeerThreadsSummary.ThreadsTagSummary(tag: tag, count: value.count))
|
||||
}
|
||||
}
|
||||
|
||||
tagSummaries.removeAll()
|
||||
}
|
||||
let current = self.get(peerId: peerId)
|
||||
if current?.unreadCount != unreadCount {
|
||||
updatedInitialSummaries[peerId] = current ?? StoredPeerThreadsSummary(unreadCount: 0)
|
||||
self.set(peerId: peerId, state: StoredPeerThreadsSummary(unreadCount: unreadCount))
|
||||
if current?.totalUnreadCount != totalUnreadCount || current?.hasUnmutedUnread != hasUnmutedUnread || current?.tagSummaries != tagSummaries {
|
||||
updatedInitialSummaries[peerId] = current ?? StoredPeerThreadsSummary(totalUnreadCount: 0, hasUnmutedUnread: false, tagSummaries: [])
|
||||
self.set(peerId: peerId, state: StoredPeerThreadsSummary(totalUnreadCount: totalUnreadCount, hasUnmutedUnread: hasUnmutedUnread, tagSummaries: tagSummaries))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1546,7 +1546,7 @@ final class PostboxImpl {
|
||||
self.messageHistoryTagsTable = MessageHistoryTagsTable(valueBox: self.valueBox, table: MessageHistoryTagsTable.tableSpec(12), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
|
||||
self.messageHistoryThreadsTable = MessageHistoryThreadsTable(valueBox: self.valueBox, table: MessageHistoryThreadsTable.tableSpec(62), useCaches: useCaches)
|
||||
self.peerThreadCombinedStateTable = PeerThreadCombinedStateTable(valueBox: self.valueBox, table: PeerThreadCombinedStateTable.tableSpec(74), useCaches: useCaches)
|
||||
self.peerThreadsSummaryTable = PeerThreadsSummaryTable(valueBox: self.valueBox, table: PeerThreadsSummaryTable.tableSpec(75), useCaches: useCaches)
|
||||
self.peerThreadsSummaryTable = PeerThreadsSummaryTable(valueBox: self.valueBox, table: PeerThreadsSummaryTable.tableSpec(75), useCaches: useCaches, seedConfiguration: self.seedConfiguration)
|
||||
self.messageHistoryThreadTagsTable = MessageHistoryThreadTagsTable(valueBox: self.valueBox, table: MessageHistoryThreadTagsTable.tableSpec(71), useCaches: useCaches, seedConfiguration: self.seedConfiguration, summaryTable: self.messageHistoryTagsSummaryTable)
|
||||
self.messageHistoryThreadHoleIndexTable = MessageHistoryThreadHoleIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadHoleIndexTable.tableSpec(63), useCaches: useCaches, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration)
|
||||
self.messageHistoryThreadReverseIndexTable = MessageHistoryThreadReverseIndexTable(valueBox: self.valueBox, table: MessageHistoryThreadReverseIndexTable.tableSpec(72), useCaches: useCaches)
|
||||
@ -2050,7 +2050,7 @@ final class PostboxImpl {
|
||||
let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates(postbox: self, transaction: currentTransaction)
|
||||
|
||||
let updatedMessageThreadPeerIds = self.messageHistoryThreadIndexTable.replay(threadsTable: self.messageHistoryThreadsTable, namespaces: self.seedConfiguration.chatMessagesNamespaces, updatedIds: self.messageHistoryThreadsTable.updatedIds)
|
||||
let alteredInitialPeerThreadsSummaries = self.peerThreadsSummaryTable.update(peerIds: updatedMessageThreadPeerIds.union(self.currentUpdatedPeerThreadCombinedStates), indexTable: self.messageHistoryThreadIndexTable, combinedStateTable: self.peerThreadCombinedStateTable)
|
||||
let alteredInitialPeerThreadsSummaries = self.peerThreadsSummaryTable.update(peerIds: updatedMessageThreadPeerIds.union(self.currentUpdatedPeerThreadCombinedStates), indexTable: self.messageHistoryThreadIndexTable, combinedStateTable: self.peerThreadCombinedStateTable, tagsSummaryTable: self.messageHistoryTagsSummaryTable)
|
||||
|
||||
self.chatListIndexTable.commitWithTransaction(postbox: self, currentTransaction: currentTransaction, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, updatedPeers: updatedPeers, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, alteredInitialPeerThreadsSummaries: alteredInitialPeerThreadsSummaries, updatedTotalUnreadStates: &self.currentUpdatedTotalUnreadStates, updatedGroupTotalUnreadSummaries: &self.currentUpdatedGroupTotalUnreadSummaries, currentUpdatedGroupSummarySynchronizeOperations: &self.currentUpdatedGroupSummarySynchronizeOperations)
|
||||
|
||||
|
@ -62,6 +62,7 @@ public final class SeedConfiguration {
|
||||
public let upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]]
|
||||
public let messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]]
|
||||
public let messageTagsWithSummary: MessageTags
|
||||
public let messageTagsWithThreadSummary: MessageTags
|
||||
public let existingGlobalMessageTags: GlobalMessageTags
|
||||
public let peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace]
|
||||
public let peerSummaryCounterTags: (Peer, Bool) -> PeerSummaryCounterTags
|
||||
@ -85,6 +86,7 @@ public final class SeedConfiguration {
|
||||
messageThreadHoles: [PeerId.Namespace: [MessageId.Namespace]],
|
||||
existingMessageTags: MessageTags,
|
||||
messageTagsWithSummary: MessageTags,
|
||||
messageTagsWithThreadSummary: MessageTags,
|
||||
existingGlobalMessageTags: GlobalMessageTags,
|
||||
peerNamespacesRequiringMessageTextIndex: [PeerId.Namespace],
|
||||
peerSummaryCounterTags: @escaping (Peer, Bool) -> PeerSummaryCounterTags,
|
||||
@ -103,6 +105,7 @@ public final class SeedConfiguration {
|
||||
self.upgradedMessageHoles = upgradedMessageHoles
|
||||
self.messageThreadHoles = messageThreadHoles
|
||||
self.messageTagsWithSummary = messageTagsWithSummary
|
||||
self.messageTagsWithThreadSummary = messageTagsWithThreadSummary
|
||||
self.existingGlobalMessageTags = existingGlobalMessageTags
|
||||
self.peerNamespacesRequiringMessageTextIndex = peerNamespacesRequiringMessageTextIndex
|
||||
self.peerSummaryCounterTags = peerSummaryCounterTags
|
||||
|
@ -534,6 +534,38 @@ func _internal_loadMessageHistoryThreads(account: Account, peerId: PeerId) -> Si
|
||||
return signal
|
||||
}
|
||||
|
||||
func _internal_forumChannelTopicNotificationExceptions(account: Account, id: EnginePeer.Id) -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(id).flatMap(apiInputPeer)
|
||||
}
|
||||
|> mapToSignal { inputPeer -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> in
|
||||
guard let inputPeer = inputPeer else {
|
||||
return .single([])
|
||||
}
|
||||
return account.network.request(Api.functions.account.getNotifyExceptions(flags: 1 << 0, peer: Api.InputNotifyPeer.inputNotifyPeer(peer: inputPeer)))
|
||||
|> map { result -> [(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)] in
|
||||
var list: [(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)] = []
|
||||
for update in result.allUpdates {
|
||||
switch update {
|
||||
case let .updateNotifySettings(peer, notifySettings):
|
||||
switch peer {
|
||||
case let .notifyForumTopic(_, topMsgId):
|
||||
list.append((Int64(topMsgId), EnginePeer.NotificationSettings(TelegramPeerNotificationSettings(apiSettings: notifySettings))))
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|> `catch` { _ -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> in
|
||||
return .single([])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class ForumChannelTopics {
|
||||
private final class Impl {
|
||||
private let queue: Queue
|
||||
|
@ -45,6 +45,7 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
|
||||
messageThreadHoles: messageThreadHoles,
|
||||
existingMessageTags: MessageTags.all,
|
||||
messageTagsWithSummary: [.unseenPersonalMessage, .pinned, .video, .photo, .gif, .music, .voiceOrInstantVideo, .webPage, .file, .unseenReaction],
|
||||
messageTagsWithThreadSummary: [.unseenPersonalMessage, .unseenReaction],
|
||||
existingGlobalMessageTags: GlobalMessageTags.all,
|
||||
peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat],
|
||||
peerSummaryCounterTags: { peer, isContact in
|
||||
|
@ -828,6 +828,10 @@ public extension TelegramEngine {
|
||||
public func setForumChannelTopicPinned(id: EnginePeer.Id, threadId: Int64, isPinned: Bool) -> Signal<Never, SetForumChannelTopicPinnedError> {
|
||||
return _internal_setForumChannelTopicPinned(account: self.account, id: id, threadId: threadId, isPinned: isPinned)
|
||||
}
|
||||
|
||||
public func forumChannelTopicNotificationExceptions(id: EnginePeer.Id) -> Signal<[(threadId: Int64, notificationSettiongs: EnginePeer.NotificationSettings)], NoError> {
|
||||
return _internal_forumChannelTopicNotificationExceptions(account: self.account, id: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,9 @@ func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo,
|
||||
namespace = Namespaces.ItemCollection.CloudStickerPacks
|
||||
}
|
||||
id = _id
|
||||
default:
|
||||
assertionFailure()
|
||||
break
|
||||
case .name:
|
||||
namespace = info.id.namespace
|
||||
id = info.id.id
|
||||
}
|
||||
if let namespace = namespace, let id = id {
|
||||
if let entry = CodableEntry(CachedStickerPack(info: info, items: items, hash: info.hash)) {
|
||||
|
@ -1877,6 +1877,9 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
private weak var copyProtectionTooltipController: TooltipController?
|
||||
weak var emojiStatusSelectionController: ViewController?
|
||||
|
||||
private var forumTopicNotificationExceptions: [(threadId: Int64, EnginePeer.NotificationSettings)] = []
|
||||
private var forumTopicNotificationExceptionsDisposable: Disposable?
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
@ -3411,6 +3414,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.shareStatusDisposable?.dispose()
|
||||
self.customStatusDisposable?.dispose()
|
||||
self.refreshMessageTagStatsDisposable?.dispose()
|
||||
self.forumTopicNotificationExceptionsDisposable?.dispose()
|
||||
|
||||
self.copyProtectionTooltipController?.dismiss()
|
||||
}
|
||||
@ -3445,6 +3449,19 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self.groupMembersSearchContext = nil
|
||||
}
|
||||
}
|
||||
|
||||
if let channel = data.peer as? TelegramChannel, channel.flags.contains(.isForum) {
|
||||
if self.forumTopicNotificationExceptionsDisposable == nil {
|
||||
self.forumTopicNotificationExceptionsDisposable = (self.context.engine.peers.forumChannelTopicNotificationExceptions(id: channel.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] list in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.forumTopicNotificationExceptions = list
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
var updatedMemberCount: Int?
|
||||
if let data = self.data {
|
||||
@ -4125,10 +4142,23 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
], title: nil, text: strongSelf.presentationData.strings.PeerInfo_TooltipMutedForever), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
|
||||
})))
|
||||
|
||||
var tip: ContextController.Tip?
|
||||
if !self.forumTopicNotificationExceptions.isEmpty {
|
||||
//TODO:localize
|
||||
let text: String
|
||||
if self.forumTopicNotificationExceptions.count == 1 {
|
||||
text = "There is 1 topic that is listed as exception."
|
||||
} else {
|
||||
text = "There are \(self.forumTopicNotificationExceptions.count) topics that are listed as exceptions."
|
||||
}
|
||||
tip = .notificationTopicExceptions(text: text, action: {
|
||||
})
|
||||
}
|
||||
|
||||
self.view.endEditing(true)
|
||||
|
||||
if let sourceNode = self.headerNode.buttonNodes[.mute]?.referenceNode {
|
||||
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: sourceNode)), items: .single(ContextController.Items(content: .list(items), tip: tip)), gesture: gesture)
|
||||
contextController.dismissed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.state = strongSelf.state.withHighlightedButton(nil)
|
||||
|
Loading…
x
Reference in New Issue
Block a user