Monoforums

This commit is contained in:
Isaac 2025-05-27 02:19:48 +08:00
parent c984cb956b
commit 5d2b252850
9 changed files with 331 additions and 149 deletions

View File

@ -50,7 +50,7 @@ public final class PeerSelectionControllerParams {
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
public let filter: ChatListNodePeersFilter public let filter: ChatListNodePeersFilter
public let requestPeerType: [ReplyMarkupButtonRequestPeerType]? public let requestPeerType: [ReplyMarkupButtonRequestPeerType]?
public let forumPeerId: EnginePeer.Id? public let forumPeerId: (id: EnginePeer.Id, isMonoforum: Bool)?
public let hasFilters: Bool public let hasFilters: Bool
public let hasChatListSelector: Bool public let hasChatListSelector: Bool
public let hasContactSelector: Bool public let hasContactSelector: Bool
@ -72,7 +72,7 @@ public final class PeerSelectionControllerParams {
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
filter: ChatListNodePeersFilter = [.onlyWriteable], filter: ChatListNodePeersFilter = [.onlyWriteable],
requestPeerType: [ReplyMarkupButtonRequestPeerType]? = nil, requestPeerType: [ReplyMarkupButtonRequestPeerType]? = nil,
forumPeerId: EnginePeer.Id? = nil, forumPeerId: (id: EnginePeer.Id, isMonoforum: Bool)? = nil,
hasFilters: Bool = false, hasFilters: Bool = false,
hasChatListSelector: Bool = true, hasChatListSelector: Bool = true,
hasContactSelector: Bool = true, hasContactSelector: Bool = true,

View File

@ -284,9 +284,14 @@ public final class AvatarNode: ASDisplayNode {
public static func addAvatarBubblePath(context: CGContext, rect: CGRect) { public static func addAvatarBubblePath(context: CGContext, rect: CGRect) {
if let path = try? convertSvgPath("M60,30.274903 C60,46.843446 46.568544,60.274904 30,60.274904 C13.431458,60.274904 0,46.843446 0,30.274903 C0,23.634797 2.158635,17.499547 5.810547,12.529785 L6.036133,12.226074 C6.921364,10.896042 7.367402,8.104698 5.548828,5.316895 C3.606939,2.340088 1.186019,0.979668 2.399414,0.470215 C3.148032,0.156204 7.572027,0.000065 10.764648,1.790527 C12.148517,2.56662 13.2296,3.342422 14.09224,4.039734 C14.42622,4.309704 14.892063,4.349773 15.265962,4.138523 C19.618079,1.679604 24.644722,0.274902 30,0.274902 C46.568544,0.274902 60,13.70636 60,30.274903 Z ") { if let path = try? convertSvgPath("M60,30.274903 C60,46.843446 46.568544,60.274904 30,60.274904 C13.431458,60.274904 0,46.843446 0,30.274903 C0,23.634797 2.158635,17.499547 5.810547,12.529785 L6.036133,12.226074 C6.921364,10.896042 7.367402,8.104698 5.548828,5.316895 C3.606939,2.340088 1.186019,0.979668 2.399414,0.470215 C3.148032,0.156204 7.572027,0.000065 10.764648,1.790527 C12.148517,2.56662 13.2296,3.342422 14.09224,4.039734 C14.42622,4.309704 14.892063,4.349773 15.265962,4.138523 C19.618079,1.679604 24.644722,0.274902 30,0.274902 C46.568544,0.274902 60,13.70636 60,30.274903 Z ") {
var transform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 60.274904) let sx = rect.width / 60.0
transform = CGAffineTransformScale(transform, rect.width / 60.0, rect.height / 60.0) let sy = rect.height / 60.0
transform = CGAffineTransformTranslate(transform, rect.minX, rect.minY) var transform = CGAffineTransform(
a: sx, b: 0.0,
c: 0.0, d: -sy,
tx: rect.minX,
ty: rect.minY + rect.height
)
let transformedPath = path.copy(using: &transform)! let transformedPath = path.copy(using: &transform)!
context.addPath(transformedPath) context.addPath(transformedPath)
} }
@ -950,6 +955,10 @@ public final class AvatarNode: ASDisplayNode {
context.beginPath() context.beginPath()
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height), cornerRadius: floor(bounds.size.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height), cornerRadius: floor(bounds.size.width * 0.25)).cgPath)
context.clip() context.clip()
} else if case .bubble = parameters.clipStyle {
context.beginPath()
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: bounds.size.height))
context.clip()
} }
} else { } else {
colors = grayscaleColors colors = grayscaleColors

View File

@ -215,7 +215,14 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
context.clip() context.clip()
case .bubble: case .bubble:
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) let rect = CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
AvatarNode.addAvatarBubblePath(context: context, rect: rect)
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
context.clip() context.clip()
} }
@ -285,8 +292,14 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
context.fillPath() context.fillPath()
case .bubble: case .bubble:
context.beginPath() let rect = CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
AvatarNode.addAvatarBubblePath(context: context, rect: rect)
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
context.clip() context.clip()
} }
} }
@ -305,8 +318,14 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
context.fillPath() context.fillPath()
case .bubble: case .bubble:
context.beginPath() let rect = CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
AvatarNode.addAvatarBubblePath(context: context, rect: rect)
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
context.clip() context.clip()
} }
} }
@ -346,8 +365,14 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath) context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
context.fillPath() context.fillPath()
case .bubble: case .bubble:
context.beginPath() let rect = CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset)) context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
AvatarNode.addAvatarBubblePath(context: context, rect: rect)
context.translateBy(x: rect.midX, y: rect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -rect.midX, y: -rect.midY)
context.clip() context.clip()
} }
} }

View File

@ -468,7 +468,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
interaction: nodeInteraction interaction: nodeInteraction
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence): case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence):
let itemPeer = peer.chatMainPeer let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer? var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
chatPeer = peer chatPeer = peer
@ -643,7 +643,7 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
animationRenderer: nodeInteraction.animationRenderer animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case .peerType: case .peerType:
let itemPeer = peer.chatMainPeer let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer? var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
chatPeer = peer chatPeer = peer
@ -867,7 +867,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
interaction: nodeInteraction interaction: nodeInteraction
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence): case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence):
let itemPeer = peer.chatMainPeer let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer? var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
chatPeer = peer chatPeer = peer
@ -993,7 +993,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
animationRenderer: nodeInteraction.animationRenderer animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint) ), directionHint: entry.directionHint)
case .peerType: case .peerType:
let itemPeer = peer.chatMainPeer let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer? var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] { if let peer = peer.peers[peer.peerId] {
chatPeer = peer chatPeer = peer

View File

@ -1211,7 +1211,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
strongSelf.contextSourceNode.contentRect = extractedRect strongSelf.contextSourceNode.contentRect = extractedRect
switch item.peer { switch item.peer {
case let .peer(peer, _): case let .peer(peer, chatPeer):
if let peer = peer { if let peer = peer {
var overrideImage: AvatarNodeImageOverride? var overrideImage: AvatarNodeImageOverride?
if peer.id == item.context.account.peerId, case let .generalSearch(isSavedMessages) = item.peerMode, case .treatSelfAsSaved = item.aliasHandling { if peer.id == item.context.account.peerId, case let .generalSearch(isSavedMessages) = item.peerMode, case .treatSelfAsSaved = item.aliasHandling {
@ -1233,8 +1233,16 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
if case .app(true) = item.peerMode { if case .app(true) = item.peerMode {
clipStyle = .roundedRect clipStyle = .roundedRect
displayDimensions = CGSize(width: displayDimensions.width, height: displayDimensions.width * 1.2) displayDimensions = CGSize(width: displayDimensions.width, height: displayDimensions.width * 1.2)
} else if case let .channel(channel) = peer, channel.isForumOrMonoForum { } else if case let .channel(channel) = peer {
clipStyle = .roundedRect if case let .channel(chatPeer) = chatPeer, chatPeer.isMonoForum {
clipStyle = .bubble
} else {
if channel.isForum {
clipStyle = .roundedRect
} else {
clipStyle = .round
}
}
} else { } else {
clipStyle = .round clipStyle = .round
} }

View File

@ -1924,109 +1924,72 @@ public final class ChatSideTopicsPanel: Component {
} }
component.updateTopicId(topicId, direction) component.updateTopicId(topicId, direction)
} }
let itemContextGesture: ((ContextGesture, ContextExtractedContentContainingNode) -> Void)? = (self.isReordering || component.isMonoforum) ? nil : { [weak self] gesture, sourceNode in var itemContextGesture: ((ContextGesture, ContextExtractedContentContainingNode) -> Void)?
guard let self, let component = self.component else { if !self.isReordering && component.isMonoforum {
return itemContextGesture = { [weak self] gesture, sourceNode in
}
guard let controller = component.controller() else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
if let listView = self.list.view as? AsyncListComponent.View {
listView.stopScrolling()
}
let topicId: Int64
switch item.item.id {
case let .chatList(peerId):
topicId = peerId.toInt64()
case let .forum(topicIdValue):
topicId = topicIdValue
}
var isPinned = false
if case let .forum(pinnedIndex, _, _, _, _) = item.item.index {
if case .index = pinnedIndex {
isPinned = true
}
}
let isClosed = item.item.threadData?.isClosed
let threadData = item.item.threadData
let _ = (chatForumTopicMenuItems(
context: component.context,
peerId: component.peerId,
threadId: topicId,
isPinned: isPinned,
isClosed: isClosed,
chatListController: controller,
joined: true,
canSelect: false,
customEdit: { [weak self] contextController in
contextController.dismiss(completion: {
guard let self, let component = self.component, let threadData else {
return
}
let editController = component.context.sharedContext.makeEditForumTopicScreen(
context: component.context,
peerId: component.peerId,
threadId: topicId,
threadInfo: threadData.info,
isHidden: threadData.isHidden
)
component.controller()?.push(editController)
})
},
customPinUnpin: { [weak self] contextController in
guard let self, let component = self.component else {
contextController.dismiss(completion: {})
return
}
self.isTogglingPinnedItem = true
self.dismissContextControllerOnNextUpdate = contextController
let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: component.peerId, threadId: topicId)
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak contextController] error in
guard let self, let component = self.component else {
contextController?.dismiss(completion: {})
return
}
switch error {
case let .limitReached(count):
contextController?.dismiss(completion: {})
if let controller = component.controller() {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let text = presentationData.strings.ChatList_MaxThreadPinsFinalText(Int32(count))
controller.present(textAlertController(context: component.context, title: presentationData.strings.Premium_LimitReached, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true), in: .window(.root))
}
default:
break
}
})
},
reorder: { [weak self] in
guard let self else {
return
}
self.updateIsReordering(isReordering: true)
}
)
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak sourceNode, weak gesture] items in
guard let self, let component = self.component else { guard let self, let component = self.component else {
return return
} }
guard let controller = component.controller() else { guard let controller = component.controller() else {
return return
} }
guard let sourceNode else {
return let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
if let listView = self.list.view as? AsyncListComponent.View {
listView.stopScrolling()
} }
let topicId: Int64
switch item.item.id {
case let .chatList(peerId):
topicId = peerId.toInt64()
case let .forum(topicIdValue):
topicId = topicIdValue
}
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: presentationData.strings.ChatList_Context_Delete, textColor: .destructive, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Delete"), color: theme.contextMenu.destructiveColor) }, action: { [weak self] c, _ in
guard let self else {
return
}
c?.dismiss(completion: { [weak self] in
guard let self, let component = self.component, let controller = component.controller() else {
return
}
let actionSheet = ActionSheetController(presentationData: presentationData)
var items: [ActionSheetItem] = []
items.append(ActionSheetTextItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationText, parseMarkdown: true))
items.append(ActionSheetButtonItem(title: presentationData.strings.ChatList_DeleteTopicConfirmationAction, color: .destructive, action: { [weak self, weak actionSheet] in
actionSheet?.dismissAnimated()
guard let self, let component = self.component else {
return
}
if component.topicId == topicId {
component.updateTopicId(nil, false)
}
let _ = component.context.engine.peers.removeForumChannelThread(id: component.peerId, threadId: topicId).startStandalone(completed: {
})
}))
actionSheet.setItemGroups([
ActionSheetItemGroup(items: items),
ActionSheetItemGroup(items: [
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
actionSheet?.dismissAnimated()
})
])
])
controller.present(actionSheet, in: .window(.root))
})
})))
let contextController = ContextController( let contextController = ContextController(
presentationData: presentationData, presentationData: presentationData,
source: .extracted(ItemExtractedContentSource( source: .extracted(ItemExtractedContentSource(
@ -2039,7 +2002,125 @@ public final class ChatSideTopicsPanel: Component {
gesture: gesture gesture: gesture
) )
controller.presentInGlobalOverlay(contextController) controller.presentInGlobalOverlay(contextController)
}) }
} else if !self.isReordering {
itemContextGesture = { [weak self] gesture, sourceNode in
guard let self, let component = self.component else {
return
}
guard let controller = component.controller() else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with({ $0 })
if let listView = self.list.view as? AsyncListComponent.View {
listView.stopScrolling()
}
let topicId: Int64
switch item.item.id {
case let .chatList(peerId):
topicId = peerId.toInt64()
case let .forum(topicIdValue):
topicId = topicIdValue
}
var isPinned = false
if case let .forum(pinnedIndex, _, _, _, _) = item.item.index {
if case .index = pinnedIndex {
isPinned = true
}
}
let isClosed = item.item.threadData?.isClosed
let threadData = item.item.threadData
let _ = (chatForumTopicMenuItems(
context: component.context,
peerId: component.peerId,
threadId: topicId,
isPinned: isPinned,
isClosed: isClosed,
chatListController: controller,
joined: true,
canSelect: false,
customEdit: { [weak self] contextController in
contextController.dismiss(completion: {
guard let self, let component = self.component, let threadData else {
return
}
let editController = component.context.sharedContext.makeEditForumTopicScreen(
context: component.context,
peerId: component.peerId,
threadId: topicId,
threadInfo: threadData.info,
isHidden: threadData.isHidden
)
component.controller()?.push(editController)
})
},
customPinUnpin: { [weak self] contextController in
guard let self, let component = self.component else {
contextController.dismiss(completion: {})
return
}
self.isTogglingPinnedItem = true
self.dismissContextControllerOnNextUpdate = contextController
let _ = (component.context.engine.peers.toggleForumChannelTopicPinned(id: component.peerId, threadId: topicId)
|> deliverOnMainQueue).startStandalone(error: { [weak self, weak contextController] error in
guard let self, let component = self.component else {
contextController?.dismiss(completion: {})
return
}
switch error {
case let .limitReached(count):
contextController?.dismiss(completion: {})
if let controller = component.controller() {
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
let text = presentationData.strings.ChatList_MaxThreadPinsFinalText(Int32(count))
controller.present(textAlertController(context: component.context, title: presentationData.strings.Premium_LimitReached, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], parseMarkdown: true), in: .window(.root))
}
default:
break
}
})
},
reorder: { [weak self] in
guard let self else {
return
}
self.updateIsReordering(isReordering: true)
}
)
|> take(1)
|> deliverOnMainQueue).startStandalone(next: { [weak self, weak sourceNode, weak gesture] items in
guard let self, let component = self.component else {
return
}
guard let controller = component.controller() else {
return
}
guard let sourceNode else {
return
}
let contextController = ContextController(
presentationData: presentationData,
source: .extracted(ItemExtractedContentSource(
sourceNode: sourceNode,
containerView: self,
keepInPlace: false
)),
items: .single(ContextController.Items(content: .list(items))),
recognizer: nil,
gesture: gesture
)
controller.presentInGlobalOverlay(contextController)
})
}
} }
switch component.location { switch component.location {

View File

@ -526,7 +526,7 @@ public func threadNotificationExceptionsScreen(context: AccountContext, peerId:
}, addException: { }, addException: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader] let filter: ChatListNodePeersFilter = [.excludeRecent, .doNotSearchMessages, .removeSearchHeader]
let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, forumPeerId: peerId, hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle)) let controller = context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: context, filter: filter, forumPeerId: (peerId, false), hasContactSelector: false, title: presentationData.strings.Notifications_AddExceptionTitle))
controller.peerSelected = { [weak controller] _, threadId in controller.peerSelected = { [weak controller] _, threadId in
guard let threadId = threadId else { guard let threadId = threadId else {
return return

View File

@ -22,7 +22,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
public var peerSelected: ((EnginePeer, Int64?) -> Void)? public var peerSelected: ((EnginePeer, Int64?) -> Void)?
public var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.SendParameters?) -> Void)? public var multiplePeersSelected: (([EnginePeer], [EnginePeer.Id: EnginePeer], NSAttributedString, AttachmentTextInputPanelSendMode, ChatInterfaceForwardOptionsState?, ChatSendMessageActionSheetController.SendParameters?) -> Void)?
private let filter: ChatListNodePeersFilter private let filter: ChatListNodePeersFilter
private let forumPeerId: EnginePeer.Id? private let forumPeerId: (id: EnginePeer.Id, isMonoforum: Bool)?
private let selectForumThreads: Bool private let selectForumThreads: Bool
private let attemptSelection: ((EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void)? private let attemptSelection: ((EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void)?
@ -277,28 +277,45 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
self.peerSelectionNode.requestOpenPeer = { [weak self] peer, threadId in self.peerSelectionNode.requestOpenPeer = { [weak self] peer, threadId in
if let strongSelf = self, let peerSelected = strongSelf.peerSelected { if let strongSelf = self, let peerSelected = strongSelf.peerSelected {
if case let .channel(peer) = peer, peer.isForumOrMonoForum, threadId == nil, strongSelf.selectForumThreads { if case let .channel(peer) = peer, peer.isForumOrMonoForum, threadId == nil, strongSelf.selectForumThreads {
let controller = PeerSelectionControllerImpl( let mainPeer: Signal<EnginePeer?, NoError>
PeerSelectionControllerParams( if peer.isMonoForum, let linkedMonoforumId = peer.linkedMonoforumId {
context: strongSelf.context, mainPeer = self.context.engine.data.get(
updatedPresentationData: nil, TelegramEngine.EngineData.Item.Peer.Peer(id: linkedMonoforumId)
filter: strongSelf.filter,
forumPeerId: peer.id,
hasFilters: false,
hasChatListSelector: false,
hasContactSelector: false,
hasGlobalSearch: false,
title: EnginePeer(peer).compactDisplayTitle,
attemptSelection: strongSelf.attemptSelection,
createNewGroup: nil,
pretendPresentedInModal: false,
multipleSelection: false,
forwardedMessageIds: [],
hasTypeHeaders: false,
selectForumThreads: false
) )
) } else {
controller.peerSelected = strongSelf.peerSelected mainPeer = .single(nil)
strongSelf.push(controller) }
let _ = (mainPeer |> deliverOnMainQueue).startStandalone(next: { [weak strongSelf] mainPeer in
guard let strongSelf else {
return
}
let displayPeer = mainPeer ?? EnginePeer(peer)
let controller = PeerSelectionControllerImpl(
PeerSelectionControllerParams(
context: strongSelf.context,
updatedPresentationData: nil,
filter: strongSelf.filter,
forumPeerId: (peer.id, peer.isMonoForum),
hasFilters: false,
hasChatListSelector: false,
hasContactSelector: false,
hasGlobalSearch: false,
title: displayPeer.compactDisplayTitle,
attemptSelection: strongSelf.attemptSelection,
createNewGroup: nil,
pretendPresentedInModal: false,
multipleSelection: false,
forwardedMessageIds: [],
hasTypeHeaders: false,
selectForumThreads: false
)
)
controller.peerSelected = strongSelf.peerSelected
strongSelf.push(controller)
})
} else { } else {
peerSelected(peer, threadId) peerSelected(peer, threadId)
} }
@ -322,7 +339,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
context: strongSelf.context, context: strongSelf.context,
updatedPresentationData: nil, updatedPresentationData: nil,
filter: strongSelf.filter, filter: strongSelf.filter,
forumPeerId: peer.id, forumPeerId: (peer.id, peer.isMonoForum),
hasFilters: false, hasFilters: false,
hasChatListSelector: false, hasChatListSelector: false,
hasContactSelector: false, hasContactSelector: false,

View File

@ -33,7 +33,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
private let presentInGlobalOverlay: (ViewController, Any?) -> Void private let presentInGlobalOverlay: (ViewController, Any?) -> Void
private let dismiss: () -> Void private let dismiss: () -> Void
private let filter: ChatListNodePeersFilter private let filter: ChatListNodePeersFilter
private let forumPeerId: EnginePeer.Id? private let forumPeerId: (id: EnginePeer.Id, isMonoforum: Bool)?
private let hasGlobalSearch: Bool private let hasGlobalSearch: Bool
private let forwardedMessageIds: [EngineMessage.Id] private let forwardedMessageIds: [EngineMessage.Id]
private let hasTypeHeaders: Bool private let hasTypeHeaders: Bool
@ -109,7 +109,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
return (self.presentationData, self.presentationDataPromise.get()) return (self.presentationData, self.presentationDataPromise.get())
} }
init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: EnginePeer.Id?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, hasCreation: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) { init(context: AccountContext, controller: PeerSelectionControllerImpl, presentationData: PresentationData, filter: ChatListNodePeersFilter, forumPeerId: (id: EnginePeer.Id, isMonoforum: Bool)?, hasFilters: Bool, hasChatListSelector: Bool, hasContactSelector: Bool, hasGlobalSearch: Bool, forwardedMessageIds: [EngineMessage.Id], hasTypeHeaders: Bool, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, hasCreation: Bool, createNewGroup: (() -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, dismiss: @escaping () -> Void) {
self.context = context self.context = context
self.controller = controller self.controller = controller
self.present = present self.present = present
@ -193,8 +193,12 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
let chatListLocation: ChatListControllerLocation let chatListLocation: ChatListControllerLocation
if let forumPeerId = self.forumPeerId { if let (forumPeerId, isMonoforum) = self.forumPeerId {
chatListLocation = .forum(peerId: forumPeerId) if isMonoforum {
chatListLocation = .savedMessagesChats(peerId: forumPeerId)
} else {
chatListLocation = .forum(peerId: forumPeerId)
}
} else { } else {
chatListLocation = .chatList(groupId: .root) chatListLocation = .chatList(groupId: .root)
} }
@ -248,12 +252,46 @@ final class PeerSelectionControllerNode: ASDisplayNode {
} }
self.chatListNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in self.chatListNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in
self?.chatListNode?.clearHighlightAnimated(true) guard let self else {
self?.requestOpenPeer?(peer, threadId) return
}
if let (peerId, isMonoforum) = self.forumPeerId, isMonoforum {
let _ = (self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
)
|> deliverOnMainQueue).startStandalone(next: { [weak self] mainPeer in
guard let self, let mainPeer else {
return
}
self.chatListNode?.clearHighlightAnimated(true)
self.requestOpenPeer?(mainPeer, peer.id.toInt64())
})
} else {
self.chatListNode?.clearHighlightAnimated(true)
self.requestOpenPeer?(peer, threadId)
}
} }
self.mainContainerNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in self.mainContainerNode?.peerSelected = { [weak self] peer, threadId, _, _, _ in
self?.chatListNode?.clearHighlightAnimated(true) guard let self else {
self?.requestOpenPeer?(peer, threadId) return
}
if let (peerId, isMonoforum) = self.forumPeerId, isMonoforum {
let _ = (self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
)
|> deliverOnMainQueue).startStandalone(next: { [weak self] mainPeer in
guard let self, let mainPeer else {
return
}
self.chatListNode?.clearHighlightAnimated(true)
self.requestOpenPeer?(mainPeer, peer.id.toInt64())
})
} else {
self.chatListNode?.clearHighlightAnimated(true)
self.requestOpenPeer?(peer, threadId)
}
} }
self.chatListNode?.disabledPeerSelected = { [weak self] peer, threadId, reason in self.chatListNode?.disabledPeerSelected = { [weak self] peer, threadId, reason in
@ -1212,8 +1250,12 @@ final class PeerSelectionControllerNode: ASDisplayNode {
self.mainContainerNode?.accessibilityElementsHidden = true self.mainContainerNode?.accessibilityElementsHidden = true
let chatListLocation: ChatListControllerLocation let chatListLocation: ChatListControllerLocation
if let forumPeerId = self.forumPeerId { if let (forumPeerId, isMonoforum) = self.forumPeerId {
chatListLocation = .forum(peerId: forumPeerId) if isMonoforum {
chatListLocation = .savedMessagesChats(peerId: forumPeerId)
} else {
chatListLocation = .forum(peerId: forumPeerId)
}
} else { } else {
chatListLocation = .chatList(groupId: EngineChatList.Group(.root)) chatListLocation = .chatList(groupId: EngineChatList.Group(.root))
} }