import Foundation import UIKit import Display import TelegramCore import Postbox import SwiftSignalKit import TelegramPresentationData import TelegramBaseController import AccountContext import AlertUI import PresentationDataUtils import ChatPresentationInterfaceState import ChatNavigationButton import CounterControllerTitleView import AdminUserActionsSheet public final class ChatRecentActionsController: TelegramBaseController { private var controllerNode: ChatRecentActionsControllerNode { return self.displayNode as! ChatRecentActionsControllerNode } private let context: AccountContext private let peer: Peer private let initialAdminPeerId: PeerId? private var presentationData: PresentationData private var presentationDataPromise = Promise() override public var updatedPresentationData: (PresentationData, Signal) { return (self.presentationData, self.presentationDataPromise.get()) } private var presentationDataDisposable: Disposable? private var didSetPresentationData = false private var panelInteraction: ChatPanelInterfaceInteraction! private let titleView: CounterControllerTitleView private var rightBarButton: ChatNavigationButton? private var adminsDisposable: Disposable? public init(context: AccountContext, peer: Peer, adminPeerId: PeerId?) { self.context = context self.peer = peer self.initialAdminPeerId = adminPeerId self.presentationData = context.sharedContext.currentPresentationData.with { $0 } self.titleView = CounterControllerTitleView(theme: self.presentationData.theme) super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .specific(size: .compact), locationBroadcastPanelSource: .none, groupCallPanelSource: .none) self.automaticallyControlPresentationContextLayout = false self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in }, deleteSelectedMessages: { }, reportSelectedMessages: { }, reportMessages: { _, _ in }, blockMessageAuthor: { _, _ in }, deleteMessages: { _, _, f in f(.default) }, forwardSelectedMessages: { }, forwardCurrentForwardMessages: { }, forwardMessages: { _ in }, updateForwardOptionsState: { _ in }, presentForwardOptions: { _ in }, presentReplyOptions: { _ in }, presentLinkOptions: { _ in }, shareSelectedMessages: { }, updateTextInputStateAndMode: { _ in }, updateInputModeAndDismissedButtonKeyboardMessageId: { _ in }, openStickers: { }, editMessage: { }, beginMessageSearch: { _, _ in }, dismissMessageSearch: { }, updateMessageSearch: { _ in }, openSearchResults: { }, navigateMessageSearch: { _ in }, openCalendarSearch: { }, toggleMembersSearch: { _ in }, navigateToMessage: { _, _, _, _ in }, navigateToChat: { _ in }, navigateToProfile: { _ in }, openPeerInfo: { }, togglePeerNotifications: { }, sendContextResult: { _, _, _, _ in return false }, sendBotCommand: { _, _ in }, sendShortcut: { _ in }, openEditShortcuts: { }, sendBotStart: { _ in }, botSwitchChatWithPayload: { _, _ in }, beginMediaRecording: { _ in }, finishMediaRecording: { _ in }, stopMediaRecording: { }, lockMediaRecording: { }, resumeMediaRecording: { }, deleteRecordedMedia: { }, sendRecordedMedia: { _, _ in }, displayRestrictedInfo: { _, _ in }, displayVideoUnmuteTip: { _ in }, switchMediaRecordingMode: { }, setupMessageAutoremoveTimeout: { }, sendSticker: { _, _, _, _, _, _ in return false }, unblockPeer: { }, pinMessage: { _, _ in }, unpinMessage: { _, _, _ in }, unpinAllMessages: { }, openPinnedList: { _ in }, shareAccountContact: { }, reportPeer: { }, presentPeerContact: { }, dismissReportPeer: { }, deleteChat: { }, beginCall: { _ in }, toggleMessageStickerStarred: { _ in }, presentController: { _, _ in }, presentControllerInCurrent: { _, _ in }, getNavigationController: { return nil }, presentGlobalOverlayController: { _, _ in }, navigateFeed: { }, openGrouping: { }, toggleSilentPost: { }, requestUnvoteInMessage: { _ in }, requestStopPollInMessage: { _ in }, updateInputLanguage: { _ in }, unarchiveChat: { }, openLinkEditing: { }, reportPeerIrrelevantGeoLocation: { }, displaySlowmodeTooltip: { _, _ in }, displaySendMessageOptions: { _, _ in }, openScheduledMessages: { }, openPeersNearby: { }, displaySearchResultsTooltip: { _, _ in }, unarchivePeer: { }, scrollToTop: { }, viewReplies: { _, _ in }, activatePinnedListPreview: { _, _ in }, joinGroupCall: { _ in }, presentInviteMembers: { }, presentGigagroupHelp: { }, editMessageMedia: { _, _ in }, updateShowCommands: { _ in }, updateShowSendAsPeers: { _ in }, openInviteRequests: { }, openSendAsPeer: { _, _ in }, presentChatRequestAdminInfo: { }, displayCopyProtectionTip: { _, _ in }, openWebView: { _, _, _, _ in }, updateShowWebView: { _ in }, insertText: { _ in }, backwardsDeleteText: { }, restartTopic: { }, toggleTranslation: { _ in }, changeTranslationLanguage: { _ in }, addDoNotTranslateLanguage: { _ in }, hideTranslationPanel: { }, openPremiumGift: { }, openPremiumRequiredForMessaging: { }, openBoostToUnrestrict: { }, updateVideoTrimRange: { _, _, _, _ in }, updateHistoryFilter: { _ in }, updateDisplayHistoryFilterAsList: { _ in }, requestLayout: { _ in }, chatController: { return nil }, statuses: nil) self.navigationItem.titleView = self.titleView let rightBarButton = ChatNavigationButton(action: .search(hasTags: false), buttonItem: UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.activateSearch))) self.rightBarButton = rightBarButton self.titleView.title = CounterControllerTitle(title: EnginePeer(peer).compactDisplayTitle, counter: self.presentationData.strings.Channel_AdminLog_TitleAllEvents) let themeEmoticon = self.context.account.postbox.peerView(id: peer.id) |> map { view -> String? in let cachedData = view.cachedData if let cachedData = cachedData as? CachedUserData { return cachedData.themeEmoticon } else if let cachedData = cachedData as? CachedGroupData { return cachedData.themeEmoticon } else if let cachedData = cachedData as? CachedChannelData { return cachedData.themeEmoticon } else { return nil } } |> distinctUntilChanged self.presentationDataDisposable = combineLatest(queue: Queue.mainQueue(), context.sharedContext.presentationData, context.engine.themes.getChatThemes(accountManager: context.sharedContext.accountManager, onlyCached: true), themeEmoticon).startStrict(next: { [weak self] presentationData, chatThemes, themeEmoticon in if let strongSelf = self { let previousTheme = strongSelf.presentationData.theme let previousStrings = strongSelf.presentationData.strings var presentationData = presentationData if let themeEmoticon = themeEmoticon, let theme = chatThemes.first(where: { $0.emoticon == themeEmoticon }) { if let theme = makePresentationTheme(cloudTheme: theme, dark: presentationData.theme.overallDarkAppearance) { presentationData = presentationData.withUpdated(theme: theme) presentationData = presentationData.withUpdated(chatWallpaper: theme.chat.defaultWallpaper) } } let isFirstTime = !strongSelf.didSetPresentationData strongSelf.presentationData = presentationData strongSelf.presentationDataPromise.set(.single(presentationData)) strongSelf.didSetPresentationData = true if isFirstTime || previousTheme !== presentationData.theme || previousStrings !== presentationData.strings { strongSelf.updateThemeAndStrings() } } }) } required public init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { self.presentationDataDisposable?.dispose() self.adminsDisposable?.dispose() } private func updateThemeAndStrings() { self.titleView.theme = self.presentationData.theme self.updateTitle() let rightButton = ChatNavigationButton(action: .search(hasTags: false), buttonItem: UIBarButtonItem(image: PresentationResourcesRootController.navigationCompactSearchIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.activateSearch))) self.navigationItem.setRightBarButton(rightButton.buttonItem, animated: false) self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationBar?.updatePresentationData(NavigationBarPresentationData(presentationData: self.presentationData)) self.controllerNode.updatePresentationData(self.presentationData) } override public func loadDisplayNode() { self.displayNode = ChatRecentActionsControllerNode(context: self.context, controller: self, peer: self.peer, presentationData: self.presentationData, pushController: { [weak self] c in (self?.navigationController as? NavigationController)?.pushViewController(c) }, presentController: { [weak self] c, t, a in self?.present(c, in: t, with: a, blockInteraction: true) }, getNavigationController: { [weak self] in return self?.navigationController as? NavigationController }) self.controllerNode.isEmptyUpdated = { [weak self] isEmpty in guard let self, let rightBarButton = self.rightBarButton else { return } self.navigationItem.setRightBarButton(isEmpty ? nil : rightBarButton.buttonItem, animated: true) } if let adminPeerId = self.initialAdminPeerId { self.controllerNode.updateFilter(events: .all, adminPeerIds: [adminPeerId]) self.updateTitle() } self.displayNodeDidLoad() } override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) var childrenLayout = layout childrenLayout.intrinsicInsets.bottom += 49.0 self.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition) self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) } @objc private func activateSearch() { if let navigationBar = self.navigationBar { if !(navigationBar.contentNode is ChatRecentActionsSearchNavigationContentNode) { let searchNavigationNode = ChatRecentActionsSearchNavigationContentNode(theme: self.presentationData.theme, strings: self.presentationData.strings, cancel: { [weak self] in self?.deactivateSearch() }) navigationBar.setContentNode(searchNavigationNode, animated: true) searchNavigationNode.setQueryUpdated({ [weak self] query in self?.controllerNode.updateSearchQuery(query) self?.updateTitle() }) searchNavigationNode.activate() } } } private func deactivateSearch() { self.controllerNode.updateSearchQuery("") self.navigationBar?.setContentNode(nil, animated: true) self.updateTitle() } private var adminsPromise: Promise<[RenderedChannelParticipant]?>? func openFilterSetup() { if self.adminsPromise == nil { self.adminsPromise = Promise() let (disposable, _) = self.context.peerChannelMemberCategoriesContextsManager.admins(engine: self.context.engine, postbox: self.context.account.postbox, network: self.context.account.network, accountPeerId: self.context.account.peerId, peerId: self.peer.id) { membersState in if case .loading = membersState.loadingState, membersState.list.isEmpty { self.adminsPromise?.set(.single(nil)) } else { self.adminsPromise?.set(.single(membersState.list)) } } self.adminsDisposable = disposable } guard let adminsPromise = self.adminsPromise else { return } let _ = (adminsPromise.get() |> filter { $0 != nil } |> take(1) |> deliverOnMainQueue).start(next: { [weak self] result in guard let self else { return } var adminPeers: [EnginePeer] = [] if let result { for participant in result { adminPeers.append(EnginePeer(participant.peer)) } } let controller = RecentActionsSettingsSheet( context: self.context, peer: EnginePeer(self.peer), adminPeers: adminPeers, initialValue: RecentActionsSettingsSheet.Value( events: self.controllerNode.filter.events, admins: self.controllerNode.filter.adminPeerIds ), completion: { [weak self] result in guard let self else { return } self.controllerNode.updateFilter(events: result.events, adminPeerIds: result.admins) self.updateTitle() } ) self.push(controller) }) } private func updateTitle() { let title = EnginePeer(self.peer).compactDisplayTitle let subtitle: String if self.controllerNode.filter.isEmpty { subtitle = self.presentationData.strings.Channel_AdminLog_TitleAllEvents } else { subtitle = self.presentationData.strings.Channel_AdminLog_TitleSelectedEvents } self.titleView.title = CounterControllerTitle(title: title, counter: subtitle) } }