mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Monoforums
This commit is contained in:
parent
c984cb956b
commit
5d2b252850
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user