mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Topics
This commit is contained in:
parent
9a013c85c9
commit
99396ec7d4
@ -509,12 +509,7 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
|
|
||||||
var items: [ContextMenuItem] = []
|
var items: [ContextMenuItem] = []
|
||||||
|
|
||||||
var canManage: Bool = false
|
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.hasPermission(.pinMessages) {
|
||||||
canManage = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if canManage {
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: isPinned ? "Unpin" : "Pin", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
@ -714,7 +709,15 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
|||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
|
|
||||||
if canManage || threadData.isOwnedByMe {
|
var canManage = false
|
||||||
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
|
canManage = true
|
||||||
|
} else if threadData.isOwnedByMe {
|
||||||
|
canManage = true
|
||||||
|
}
|
||||||
|
if canManage {
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
items.append(.action(ContextMenuActionItem(text: threadData.isClosed ? "Restart" : "Close", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: threadData.isClosed ? "Chat/Context Menu/Play": "Chat/Context Menu/Pause"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
items.append(.action(ContextMenuActionItem(text: threadData.isClosed ? "Restart" : "Close", icon: { theme in generateTintedImage(image: UIImage(bundleImageName: threadData.isClosed ? "Chat/Context Menu/Play": "Chat/Context Menu/Pause"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||||
f(.default)
|
f(.default)
|
||||||
|
@ -1590,10 +1590,14 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
|
|
||||||
controller.completion = { title, fileId in
|
controller.completion = { [weak controller] title, fileId in
|
||||||
|
controller?.isInProgress = true
|
||||||
|
|
||||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId)
|
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId)
|
||||||
|> deliverOnMainQueue).start(next: { topicId in
|
|> deliverOnMainQueue).start(next: { topicId in
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start()
|
||||||
|
}, error: { _ in
|
||||||
|
controller?.isInProgress = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
strongSelf.push(controller)
|
strongSelf.push(controller)
|
||||||
@ -2631,12 +2635,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
|
|
||||||
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
let controller = ForumCreateTopicScreen(context: context, peerId: peerId, mode: .create)
|
||||||
controller.navigationPresentation = .modal
|
controller.navigationPresentation = .modal
|
||||||
controller.completion = { title, fileId in
|
|
||||||
|
controller.completion = { [weak controller] title, fileId in
|
||||||
|
controller?.isInProgress = true
|
||||||
|
|
||||||
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId)
|
let _ = (context.engine.peers.createForumChannelTopic(id: peerId, title: title, iconColor: ForumCreateTopicScreen.iconColors.randomElement()!, iconFileId: fileId)
|
||||||
|> deliverOnMainQueue).start(next: { topicId in
|
|> deliverOnMainQueue).start(next: { topicId in
|
||||||
if let navigationController = (sourceController.navigationController as? NavigationController) {
|
if let navigationController = (sourceController.navigationController as? NavigationController) {
|
||||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start()
|
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: topicId, messageId: nil, navigationController: navigationController, activateInput: .text).start()
|
||||||
}
|
}
|
||||||
|
}, error: { _ in
|
||||||
|
controller?.isInProgress = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sourceController.push(controller)
|
sourceController.push(controller)
|
||||||
@ -3709,6 +3718,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
|||||||
var items: [ActionSheetItem] = []
|
var items: [ActionSheetItem] = []
|
||||||
|
|
||||||
//TODO:localize
|
//TODO:localize
|
||||||
|
items.append(ActionSheetTextItem(title: "This will delete the topic with all its messages", parseMarkdown: true))
|
||||||
items.append(ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in
|
items.append(ActionSheetButtonItem(title: "Delete", color: .destructive, action: { [weak self, weak actionSheet] in
|
||||||
actionSheet?.dismissAnimated()
|
actionSheet?.dismissAnimated()
|
||||||
self?.commitDeletePeerThread(peerId: peerId, threadId: threadId)
|
self?.commitDeletePeerThread(peerId: peerId, threadId: threadId)
|
||||||
|
@ -330,10 +330,10 @@ private func groupReferenceRevealOptions(strings: PresentationStrings, theme: Pr
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isPinned: Bool, isEditing: Bool, canManage: Bool) -> [ItemListRevealOption] {
|
private func forumRevealOptions(strings: PresentationStrings, theme: PresentationTheme, isMuted: Bool?, isClosed: Bool, isPinned: Bool, isEditing: Bool, canPin: Bool, canManage: Bool) -> [ItemListRevealOption] {
|
||||||
var options: [ItemListRevealOption] = []
|
var options: [ItemListRevealOption] = []
|
||||||
if !isEditing {
|
if !isEditing {
|
||||||
if canManage {
|
if canPin {
|
||||||
if isPinned {
|
if isPinned {
|
||||||
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
|
options.append(ItemListRevealOption(key: RevealOptionKey.unpin.rawValue, title: strings.DialogList_Unpin, icon: unpinIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor))
|
||||||
} else {
|
} else {
|
||||||
@ -1852,16 +1852,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
|||||||
if case .forum = item.chatListLocation {
|
if case .forum = item.chatListLocation {
|
||||||
if case let .chat(itemPeer) = contentPeer, case let .channel(channel) = itemPeer.peer {
|
if case let .chat(itemPeer) = contentPeer, case let .channel(channel) = itemPeer.peer {
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
|
canManage = true
|
||||||
|
} else if let threadInfo = threadInfo, threadInfo.isOwner {
|
||||||
canManage = true
|
canManage = true
|
||||||
} else if let threadInfo {
|
|
||||||
canManage = threadInfo.isOwner
|
|
||||||
}
|
}
|
||||||
var isClosed = false
|
var isClosed = false
|
||||||
if let threadInfo {
|
if let threadInfo {
|
||||||
isClosed = threadInfo.isClosed
|
isClosed = threadInfo.isClosed
|
||||||
}
|
}
|
||||||
peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canManage: canManage)
|
peerRevealOptions = forumRevealOptions(strings: item.presentationData.strings, theme: item.presentationData.theme, isMuted: (currentMutedIconImage != nil), isClosed: isClosed, isPinned: isPinned, isEditing: item.editing, canPin: channel.hasPermission(.pinMessages), canManage: canManage)
|
||||||
peerLeftRevealOptions = []
|
peerLeftRevealOptions = []
|
||||||
} else {
|
} else {
|
||||||
peerRevealOptions = []
|
peerRevealOptions = []
|
||||||
|
@ -269,8 +269,13 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
switch action.action {
|
||||||
|
case .topicCreated, .topicEdited:
|
||||||
|
hideAuthor = false
|
||||||
default:
|
default:
|
||||||
hideAuthor = true
|
hideAuthor = true
|
||||||
|
}
|
||||||
if let (text, textSpoilers, customEmojiRangesValue) = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: true) {
|
if let (text, textSpoilers, customEmojiRangesValue) = plainServiceMessageString(strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: message, accountPeerId: accountPeerId, forChatList: true) {
|
||||||
messageText = text
|
messageText = text
|
||||||
spoilers = textSpoilers
|
spoilers = textSpoilers
|
||||||
|
@ -226,7 +226,7 @@ func _internal_createForumChannelTopic(account: Account, peerId: PeerId, title:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let topicId = topicId {
|
if let topicId = topicId {
|
||||||
return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [])
|
return resolveForumThreads(postbox: account.postbox, network: account.network, ids: [MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: topicId))])
|
||||||
|> castError(CreateForumChannelTopicError.self)
|
|> castError(CreateForumChannelTopicError.self)
|
||||||
|> map { _ -> Int64 in
|
|> map { _ -> Int64 in
|
||||||
return topicId
|
return topicId
|
||||||
|
@ -31,6 +31,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
|
||||||
"//submodules/Components/PagerComponent:PagerComponent",
|
"//submodules/Components/PagerComponent:PagerComponent",
|
||||||
"//submodules/PremiumUI",
|
"//submodules/PremiumUI",
|
||||||
|
"//submodules/ProgressNavigationButtonNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -15,6 +15,7 @@ import MultilineTextComponent
|
|||||||
import EmojiStatusComponent
|
import EmojiStatusComponent
|
||||||
import Postbox
|
import Postbox
|
||||||
import PremiumUI
|
import PremiumUI
|
||||||
|
import ProgressNavigationButtonNode
|
||||||
|
|
||||||
private final class TitleFieldComponent: Component {
|
private final class TitleFieldComponent: Component {
|
||||||
typealias EnvironmentType = Empty
|
typealias EnvironmentType = Empty
|
||||||
@ -162,6 +163,8 @@ private final class TitleFieldComponent: Component {
|
|||||||
placeholderComponentView.frame = CGRect(origin: CGPoint(x: 62.0, y: floorToScreenPixels((availableSize.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize)
|
placeholderComponentView.frame = CGRect(origin: CGPoint(x: 62.0, y: floorToScreenPixels((availableSize.height - placeholderSize.height) / 2.0) + 1.0 - UIScreenPixel), size: placeholderSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.placeholderView.view?.isHidden = !component.text.isEmpty
|
||||||
|
|
||||||
let iconSize = self.iconView.update(
|
let iconSize = self.iconView.update(
|
||||||
transition: .easeInOut(duration: 0.2),
|
transition: .easeInOut(duration: 0.2),
|
||||||
component: AnyComponent(EmojiStatusComponent(
|
component: AnyComponent(EmojiStatusComponent(
|
||||||
@ -782,10 +785,32 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
|
|||||||
case edit(topic: EngineMessageHistoryThread.Info)
|
case edit(topic: EngineMessageHistoryThread.Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let context: AccountContext
|
||||||
|
private let mode: Mode
|
||||||
|
|
||||||
|
private var doneBarItem: UIBarButtonItem?
|
||||||
|
|
||||||
private var state: (String, Int64?) = ("", nil)
|
private var state: (String, Int64?) = ("", nil)
|
||||||
public var completion: (String, Int64?) -> Void = { _, _ in }
|
public var completion: (String, Int64?) -> Void = { _, _ in }
|
||||||
|
|
||||||
|
public var isInProgress: Bool = false {
|
||||||
|
didSet {
|
||||||
|
if self.isInProgress != oldValue {
|
||||||
|
if self.isInProgress {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||||
|
} else {
|
||||||
|
//TODO:localize
|
||||||
|
self.navigationItem.rightBarButtonItem = self.doneBarItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode) {
|
public init(context: AccountContext, peerId: EnginePeer.Id, mode: ForumCreateTopicScreen.Mode) {
|
||||||
|
self.context = context
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
var titleUpdatedImpl: ((String) -> Void)?
|
var titleUpdatedImpl: ((String) -> Void)?
|
||||||
var iconUpdatedImpl: ((Int64?) -> Void)?
|
var iconUpdatedImpl: ((Int64?) -> Void)?
|
||||||
var openPremiumImpl: (() -> Void)?
|
var openPremiumImpl: (() -> Void)?
|
||||||
@ -805,9 +830,11 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
|
|||||||
case .create:
|
case .create:
|
||||||
title = "New Topic"
|
title = "New Topic"
|
||||||
doneTitle = "Create"
|
doneTitle = "Create"
|
||||||
case .edit:
|
case let .edit(topic):
|
||||||
title = "Edit Topic"
|
title = "Edit Topic"
|
||||||
doneTitle = "Done"
|
doneTitle = "Done"
|
||||||
|
|
||||||
|
self.state = (topic.title, topic.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.title = title
|
self.title = title
|
||||||
@ -815,20 +842,21 @@ public class ForumCreateTopicScreen: ViewControllerComponentContainer {
|
|||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||||
|
|
||||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: doneTitle, style: .done, target: self, action: #selector(self.createPressed))
|
self.doneBarItem = UIBarButtonItem(title: doneTitle, style: .done, target: self, action: #selector(self.createPressed))
|
||||||
self.navigationItem.rightBarButtonItem?.isEnabled = false
|
self.navigationItem.rightBarButtonItem = self.doneBarItem
|
||||||
|
self.doneBarItem?.isEnabled = false
|
||||||
|
|
||||||
if case .edit = mode {
|
if case .edit = mode {
|
||||||
self.navigationItem.rightBarButtonItem?.isEnabled = true
|
self.doneBarItem?.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
titleUpdatedImpl = { [weak self] title in
|
titleUpdatedImpl = { [weak self] title in
|
||||||
guard let strongSelf = self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.navigationItem.rightBarButtonItem?.isEnabled = !title.isEmpty
|
self.doneBarItem?.isEnabled = !title.isEmpty
|
||||||
|
|
||||||
strongSelf.state = (title, strongSelf.state.1)
|
self.state = (title, self.state.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
iconUpdatedImpl = { [weak self] fileId in
|
iconUpdatedImpl = { [weak self] fileId in
|
||||||
|
@ -3038,7 +3038,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
case let .replyThread(replyThreadMessage):
|
case let .replyThread(replyThreadMessage):
|
||||||
let peerId = replyThreadMessage.messageId.peerId
|
let peerId = replyThreadMessage.messageId.peerId
|
||||||
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, animated: true, completion: nil)
|
strongSelf.navigateToMessage(from: nil, to: .index(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp - Int32(NSTimeZone.local.secondsFromGMT()))), scrollPosition: .bottom(0.0), rememberInStack: false, forceInCurrentChat: true, animated: true, completion: nil)
|
||||||
case .feed:
|
case .feed:
|
||||||
//TODO:implement
|
//TODO:implement
|
||||||
break
|
break
|
||||||
|
@ -262,7 +262,9 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS
|
|||||||
if let threadData = chatPresentationInterfaceState.threadData {
|
if let threadData = chatPresentationInterfaceState.threadData {
|
||||||
if threadData.isClosed {
|
if threadData.isClosed {
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
canManage = true
|
canManage = true
|
||||||
} else if threadData.isOwn {
|
} else if threadData.isOwn {
|
||||||
canManage = true
|
canManage = true
|
||||||
|
@ -162,7 +162,9 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState
|
|||||||
if let threadData = chatPresentationInterfaceState.threadData {
|
if let threadData = chatPresentationInterfaceState.threadData {
|
||||||
if threadData.isClosed {
|
if threadData.isClosed {
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
canManage = true
|
canManage = true
|
||||||
} else if threadData.isOwn {
|
} else if threadData.isOwn {
|
||||||
canManage = true
|
canManage = true
|
||||||
|
@ -61,7 +61,9 @@ func titlePanelForChatPresentationInterfaceState(_ chatPresentationInterfaceStat
|
|||||||
if let threadData = chatPresentationInterfaceState.threadData {
|
if let threadData = chatPresentationInterfaceState.threadData {
|
||||||
if threadData.isClosed {
|
if threadData.isClosed {
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
canManage = true
|
canManage = true
|
||||||
} else if threadData.isOwn {
|
} else if threadData.isOwn {
|
||||||
canManage = true
|
canManage = true
|
||||||
|
@ -102,7 +102,9 @@ private func peerButtons(_ state: ChatPresentationInterfaceState) -> [ChatReport
|
|||||||
if let threadData = state.threadData {
|
if let threadData = state.threadData {
|
||||||
if threadData.isClosed {
|
if threadData.isClosed {
|
||||||
var canManage = false
|
var canManage = false
|
||||||
if channel.hasPermission(.pinMessages) {
|
if channel.flags.contains(.isCreator) {
|
||||||
|
canManage = true
|
||||||
|
} else if channel.adminRights != nil {
|
||||||
canManage = true
|
canManage = true
|
||||||
} else if threadData.isOwn {
|
} else if threadData.isOwn {
|
||||||
canManage = true
|
canManage = true
|
||||||
@ -597,7 +599,7 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
|||||||
if let renderedPeer = interfaceState.renderedPeer {
|
if let renderedPeer = interfaceState.renderedPeer {
|
||||||
chatPeer = renderedPeer.peers[renderedPeer.peerId]
|
chatPeer = renderedPeer.peers[renderedPeer.peerId]
|
||||||
}
|
}
|
||||||
if let chatPeer = chatPeer, let invitedBy = interfaceState.contactStatus?.invitedBy {
|
if let chatPeer = chatPeer, (updatedButtons.contains(.block) || updatedButtons.contains(.reportSpam) || updatedButtons.contains(.reportUserSpam)), let invitedBy = interfaceState.contactStatus?.invitedBy {
|
||||||
var inviteInfoTransition = transition
|
var inviteInfoTransition = transition
|
||||||
let inviteInfoNode: ChatInfoTitlePanelInviteInfoNode
|
let inviteInfoNode: ChatInfoTitlePanelInviteInfoNode
|
||||||
if let current = self.inviteInfoNode {
|
if let current = self.inviteInfoNode {
|
||||||
|
@ -1189,7 +1189,7 @@ func peerInfoHeaderButtons(peer: Peer?, cachedData: CachedPeerData?, isOpenedFro
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func peerInfoCanEdit(peer: Peer?, cachedData: CachedPeerData?, isContact: Bool?) -> Bool {
|
func peerInfoCanEdit(peer: Peer?, threadData: MessageHistoryThreadData?, cachedData: CachedPeerData?, isContact: Bool?) -> Bool {
|
||||||
if let user = peer as? TelegramUser {
|
if let user = peer as? TelegramUser {
|
||||||
if user.isDeleted {
|
if user.isDeleted {
|
||||||
return false
|
return false
|
||||||
@ -1199,6 +1199,17 @@ func peerInfoCanEdit(peer: Peer?, cachedData: CachedPeerData?, isContact: Bool?)
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else if let peer = peer as? TelegramChannel {
|
} else if let peer = peer as? TelegramChannel {
|
||||||
|
if peer.flags.contains(.isForum) {
|
||||||
|
if peer.flags.contains(.isCreator) {
|
||||||
|
return true
|
||||||
|
} else if let threadData = threadData, threadData.isOwnedByMe {
|
||||||
|
return true
|
||||||
|
} else if let _ = peer.adminRights {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if peer.flags.contains(.isCreator) {
|
if peer.flags.contains(.isCreator) {
|
||||||
return true
|
return true
|
||||||
} else if peer.hasPermission(.changeInfo) {
|
} else if peer.hasPermission(.changeInfo) {
|
||||||
@ -1207,6 +1218,7 @@ func peerInfoCanEdit(peer: Peer?, cachedData: CachedPeerData?, isContact: Bool?)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
} else if let peer = peer as? TelegramGroup {
|
} else if let peer = peer as? TelegramGroup {
|
||||||
if case .creator = peer.role {
|
if case .creator = peer.role {
|
||||||
return true
|
return true
|
||||||
|
@ -8183,7 +8183,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
leftNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .qrCode, isForExpandedView: false))
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .search, isForExpandedView: true))
|
||||||
} else if peerInfoCanEdit(peer: self.data?.peer, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
} else if peerInfoCanEdit(peer: self.data?.peer, threadData: self.data?.threadData, cachedData: self.data?.cachedData, isContact: self.data?.isContact) {
|
||||||
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .edit, isForExpandedView: false))
|
||||||
}
|
}
|
||||||
if self.state.selectedMessageIds == nil {
|
if self.state.selectedMessageIds == nil {
|
||||||
@ -9602,6 +9602,31 @@ func presentAddMembersImpl(context: AccountContext, updatedPresentationData: (in
|
|||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
}, completed: {
|
}, completed: {
|
||||||
contactsController?.dismiss()
|
contactsController?.dismiss()
|
||||||
|
|
||||||
|
let mappedPeerIds: [EnginePeer.Id] = peers.compactMap { peer -> EnginePeer.Id? in
|
||||||
|
switch peer {
|
||||||
|
case let .peer(id):
|
||||||
|
return id
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !mappedPeerIds.isEmpty {
|
||||||
|
let _ = (context.engine.data.get(EngineDataMap(mappedPeerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init(id:))))
|
||||||
|
|> deliverOnMainQueue).start(next: { maybePeers in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let peers = maybePeers.compactMap { $0.value }
|
||||||
|
|
||||||
|
//TODO:localize
|
||||||
|
let text: String
|
||||||
|
if peers.count == 1 {
|
||||||
|
text = "**\(peers[0].displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))** added to the group."
|
||||||
|
} else {
|
||||||
|
text = "**\(peers.count)** members added to the group."
|
||||||
|
}
|
||||||
|
parentController?.present(UndoOverlayController(presentationData: presentationData, content: .peers(context: context, peers: peers, title: nil, text: text, customUndoText: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||||
|
})
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
contactsController.dismissed = {
|
contactsController.dismissed = {
|
||||||
|
@ -27,6 +27,7 @@ swift_library(
|
|||||||
"//submodules/AvatarNode:AvatarNode",
|
"//submodules/AvatarNode:AvatarNode",
|
||||||
"//submodules/AccountContext:AccountContext",
|
"//submodules/AccountContext:AccountContext",
|
||||||
"//submodules/ComponentFlow:ComponentFlow",
|
"//submodules/ComponentFlow:ComponentFlow",
|
||||||
|
"//submodules/AnimatedAvatarSetNode:AnimatedAvatarSetNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -42,6 +42,7 @@ public enum UndoOverlayContent {
|
|||||||
case image(image: UIImage, text: String)
|
case image(image: UIImage, text: String)
|
||||||
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
case notificationSoundAdded(title: String, text: String, action: (() -> Void)?)
|
||||||
case universal(animation: String, scale: CGFloat, colors: [String: UIColor], title: String?, text: String, customUndoText: String?)
|
case universal(animation: String, scale: CGFloat, colors: [String: UIColor], title: String?, text: String, customUndoText: String?)
|
||||||
|
case peers(context: AccountContext, peers: [EnginePeer], title: String?, text: String, customUndoText: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UndoOverlayAction {
|
public enum UndoOverlayAction {
|
||||||
|
@ -17,6 +17,7 @@ import AnimationUI
|
|||||||
import StickerResources
|
import StickerResources
|
||||||
import AvatarNode
|
import AvatarNode
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import AnimatedAvatarSetNode
|
||||||
|
|
||||||
final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||||
private let elevatedLayout: Bool
|
private let elevatedLayout: Bool
|
||||||
@ -25,6 +26,8 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
private let timerTextNode: ImmediateTextNode
|
private let timerTextNode: ImmediateTextNode
|
||||||
private let avatarNode: AvatarNode?
|
private let avatarNode: AvatarNode?
|
||||||
private let iconNode: ASImageNode?
|
private let iconNode: ASImageNode?
|
||||||
|
private var multiAvatarsNode: AnimatedAvatarSetNode?
|
||||||
|
private var multiAvatarsSize: CGSize?
|
||||||
private var iconImageSize: CGSize?
|
private var iconImageSize: CGSize?
|
||||||
private let iconCheckNode: RadialStatusNode?
|
private let iconCheckNode: RadialStatusNode?
|
||||||
private let animationNode: AnimationNode?
|
private let animationNode: AnimationNode?
|
||||||
@ -872,6 +875,44 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
|
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
|
||||||
displayUndo = true
|
displayUndo = true
|
||||||
self.originalRemainingSeconds = 5
|
self.originalRemainingSeconds = 5
|
||||||
|
case let .peers(context, peers, title, text, customUndoText):
|
||||||
|
self.avatarNode = nil
|
||||||
|
let multiAvatarsNode = AnimatedAvatarSetNode()
|
||||||
|
self.multiAvatarsNode = multiAvatarsNode
|
||||||
|
let avatarsContext = AnimatedAvatarSetContext()
|
||||||
|
self.multiAvatarsSize = multiAvatarsNode.update(context: context, content: avatarsContext.update(peers: peers, animated: false), itemSize: CGSize(width: 28.0, height: 28.0), animated: false, synchronousLoad: false)
|
||||||
|
|
||||||
|
self.iconNode = nil
|
||||||
|
self.iconCheckNode = nil
|
||||||
|
self.animationNode = nil
|
||||||
|
self.animatedStickerNode = nil
|
||||||
|
if let title = title {
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(14.0), textColor: .white)
|
||||||
|
} else {
|
||||||
|
self.titleNode.attributedText = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||||
|
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||||
|
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||||
|
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in
|
||||||
|
return ("URL", contents)
|
||||||
|
}), textAlignment: .natural)
|
||||||
|
self.textNode.attributedText = attributedText
|
||||||
|
|
||||||
|
if text.contains("](") {
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
}
|
||||||
|
self.originalRemainingSeconds = isUserInteractionEnabled ? 5 : 3
|
||||||
|
|
||||||
|
self.textNode.maximumNumberOfLines = 5
|
||||||
|
|
||||||
|
if let customUndoText = customUndoText {
|
||||||
|
undoText = customUndoText
|
||||||
|
displayUndo = true
|
||||||
|
} else {
|
||||||
|
displayUndo = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.remainingSeconds = self.originalRemainingSeconds
|
self.remainingSeconds = self.originalRemainingSeconds
|
||||||
@ -900,7 +941,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
switch content {
|
switch content {
|
||||||
case .removedChat:
|
case .removedChat:
|
||||||
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
self.panelWrapperNode.addSubnode(self.timerTextNode)
|
||||||
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal:
|
case .archivedChat, .hidArchive, .revealedArchive, .autoDelete, .succeed, .emoji, .swipeToReply, .actionSucceeded, .stickersModified, .chatAddedToFolder, .chatRemovedFromFolder, .messagesUnpinned, .setProximityAlert, .invitedToVoiceChat, .linkCopied, .banned, .importedMessage, .audioRate, .forward, .gigagroupConversion, .linkRevoked, .voiceChatRecording, .voiceChatFlag, .voiceChatCanSpeak, .copy, .mediaSaved, .paymentSent, .image, .inviteRequestSent, .notificationSoundAdded, .universal, .peers:
|
||||||
if self.textNode.tapAttributeAction != nil || displayUndo {
|
if self.textNode.tapAttributeAction != nil || displayUndo {
|
||||||
self.isUserInteractionEnabled = true
|
self.isUserInteractionEnabled = true
|
||||||
} else {
|
} else {
|
||||||
@ -927,6 +968,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.animationNode?.isUserInteractionEnabled = false
|
self.animationNode?.isUserInteractionEnabled = false
|
||||||
self.iconCheckNode?.isUserInteractionEnabled = false
|
self.iconCheckNode?.isUserInteractionEnabled = false
|
||||||
self.avatarNode?.isUserInteractionEnabled = false
|
self.avatarNode?.isUserInteractionEnabled = false
|
||||||
|
self.multiAvatarsNode?.isUserInteractionEnabled = false
|
||||||
self.slotMachineNode?.isUserInteractionEnabled = false
|
self.slotMachineNode?.isUserInteractionEnabled = false
|
||||||
self.animatedStickerNode?.isUserInteractionEnabled = false
|
self.animatedStickerNode?.isUserInteractionEnabled = false
|
||||||
|
|
||||||
@ -938,6 +980,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.animatedStickerNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.slotMachineNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
|
self.avatarNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
|
self.multiAvatarsNode.flatMap(self.panelWrapperNode.addSubnode)
|
||||||
self.panelWrapperNode.addSubnode(self.buttonNode)
|
self.panelWrapperNode.addSubnode(self.buttonNode)
|
||||||
self.panelWrapperNode.addSubnode(self.titleNode)
|
self.panelWrapperNode.addSubnode(self.titleNode)
|
||||||
self.panelWrapperNode.addSubnode(self.textNode)
|
self.panelWrapperNode.addSubnode(self.textNode)
|
||||||
@ -1088,7 +1131,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
if iconSize.width > leftInset {
|
if iconSize.width > leftInset {
|
||||||
leftInset = iconSize.width - 8.0
|
leftInset = iconSize.width - 8.0
|
||||||
}
|
}
|
||||||
|
} else if let multiAvatarsSize = self.multiAvatarsSize {
|
||||||
|
leftInset = 13.0 + multiAvatarsSize.width + 20.0
|
||||||
}
|
}
|
||||||
|
|
||||||
let rightInset: CGFloat = 16.0
|
let rightInset: CGFloat = 16.0
|
||||||
var contentHeight: CGFloat = 20.0
|
var contentHeight: CGFloat = 20.0
|
||||||
|
|
||||||
@ -1228,6 +1274,11 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
|||||||
let avatarSize: CGFloat = 30.0
|
let avatarSize: CGFloat = 30.0
|
||||||
transition.updateFrame(node: avatarNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - avatarSize) / 2.0), y: floor((contentHeight - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)))
|
transition.updateFrame(node: avatarNode, frame: CGRect(origin: CGPoint(x: floor((leftInset - avatarSize) / 2.0), y: floor((contentHeight - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let multiAvatarsNode = self.multiAvatarsNode, let multiAvatarsSize = self.multiAvatarsSize {
|
||||||
|
let avatarsFrame = CGRect(origin: CGPoint(x: 13.0, y: floor((contentHeight - multiAvatarsSize.height) / 2.0) + verticalOffset), size: multiAvatarsSize)
|
||||||
|
transition.updateFrame(node: multiAvatarsNode, frame: avatarsFrame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateIn(asReplacement: Bool) {
|
func animateIn(asReplacement: Bool) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user