mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
319 lines
18 KiB
Swift
319 lines
18 KiB
Swift
import Foundation
|
|
import Display
|
|
import SwiftSignalKit
|
|
import Postbox
|
|
import TelegramCore
|
|
import MtProtoKitDynamic
|
|
|
|
private final class DebugControllerArguments {
|
|
let account: Account
|
|
let accountManager: AccountManager
|
|
let presentController: (ViewController, ViewControllerPresentationArguments?) -> Void
|
|
let pushController: (ViewController) -> Void
|
|
|
|
init(account: Account, accountManager: AccountManager, presentController: @escaping (ViewController, ViewControllerPresentationArguments?) -> Void, pushController: @escaping (ViewController) -> Void) {
|
|
self.account = account
|
|
self.accountManager = accountManager
|
|
self.presentController = presentController
|
|
self.pushController = pushController
|
|
}
|
|
}
|
|
|
|
private enum DebugControllerSection: Int32 {
|
|
case logs
|
|
case removal
|
|
case logging
|
|
case experiments
|
|
case info
|
|
}
|
|
|
|
private enum DebugControllerEntry: ItemListNodeEntry {
|
|
case sendLogs(PresentationTheme)
|
|
case sendOneLog(PresentationTheme)
|
|
case accounts(PresentationTheme)
|
|
case resetServerContacts(PresentationTheme)
|
|
case clearPaymentData(PresentationTheme)
|
|
case logToFile(PresentationTheme, Bool)
|
|
case logToConsole(PresentationTheme, Bool)
|
|
case redactSensitiveData(PresentationTheme, Bool)
|
|
case enableRaiseToSpeak(PresentationTheme, Bool)
|
|
case keepChatNavigationStack(PresentationTheme, Bool)
|
|
case clearTips(PresentationTheme)
|
|
case reimport(PresentationTheme)
|
|
case sendTthumb(PresentationTheme)
|
|
case previewTthumb(PresentationTheme)
|
|
case animatedStickers(PresentationTheme)
|
|
case versionInfo(PresentationTheme)
|
|
|
|
var section: ItemListSectionId {
|
|
switch self {
|
|
case .sendLogs, .sendOneLog:
|
|
return DebugControllerSection.logs.rawValue
|
|
case .accounts:
|
|
return DebugControllerSection.logs.rawValue
|
|
case .resetServerContacts, .clearPaymentData:
|
|
return DebugControllerSection.removal.rawValue
|
|
case .logToFile, .logToConsole, .redactSensitiveData:
|
|
return DebugControllerSection.logging.rawValue
|
|
case .enableRaiseToSpeak, .keepChatNavigationStack:
|
|
return DebugControllerSection.experiments.rawValue
|
|
case .clearTips, .reimport, .sendTthumb, .previewTthumb, .animatedStickers:
|
|
return DebugControllerSection.experiments.rawValue
|
|
case .versionInfo:
|
|
return DebugControllerSection.info.rawValue
|
|
}
|
|
}
|
|
|
|
var stableId: Int32 {
|
|
switch self {
|
|
case .sendLogs:
|
|
return 0
|
|
case .sendOneLog:
|
|
return 1
|
|
case .accounts:
|
|
return 2
|
|
case .resetServerContacts:
|
|
return 3
|
|
case .clearPaymentData:
|
|
return 4
|
|
case .logToFile:
|
|
return 5
|
|
case .logToConsole:
|
|
return 6
|
|
case .redactSensitiveData:
|
|
return 7
|
|
case .enableRaiseToSpeak:
|
|
return 8
|
|
case .keepChatNavigationStack:
|
|
return 9
|
|
case .clearTips:
|
|
return 10
|
|
case .reimport:
|
|
return 11
|
|
case .sendTthumb:
|
|
return 12
|
|
case .previewTthumb:
|
|
return 13
|
|
case .animatedStickers:
|
|
return 14
|
|
case .versionInfo:
|
|
return 15
|
|
}
|
|
}
|
|
|
|
static func <(lhs: DebugControllerEntry, rhs: DebugControllerEntry) -> Bool {
|
|
return lhs.stableId < rhs.stableId
|
|
}
|
|
|
|
func item(_ arguments: DebugControllerArguments) -> ListViewItem {
|
|
switch self {
|
|
case let .sendLogs(theme):
|
|
return ItemListDisclosureItem(theme: theme, title: "Send Logs", label: "", sectionId: self.section, style: .blocks, action: {
|
|
let _ = (Logger.shared.collectLogs()
|
|
|> deliverOnMainQueue).start(next: { logs in
|
|
let controller = PeerSelectionController(account: arguments.account)
|
|
controller.peerSelected = { [weak controller] peerId in
|
|
if let strongController = controller {
|
|
strongController.dismiss()
|
|
|
|
let messages = logs.map { (name, path) -> EnqueueMessage in
|
|
let id = arc4random64()
|
|
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
|
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
|
}
|
|
let _ = enqueueMessages(account: arguments.account, peerId: peerId, messages: messages).start()
|
|
}
|
|
}
|
|
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
|
})
|
|
})
|
|
case let .sendOneLog(theme):
|
|
return ItemListDisclosureItem(theme: theme, title: "Send Latest Log", label: "", sectionId: self.section, style: .blocks, action: {
|
|
let _ = (Logger.shared.collectLogs()
|
|
|> deliverOnMainQueue).start(next: { logs in
|
|
let controller = PeerSelectionController(account: arguments.account)
|
|
controller.peerSelected = { [weak controller] peerId in
|
|
if let strongController = controller {
|
|
strongController.dismiss()
|
|
|
|
let updatedLogs = logs.last.flatMap({ [$0] }) ?? []
|
|
|
|
let messages = updatedLogs.map { (name, path) -> EnqueueMessage in
|
|
let id = arc4random64()
|
|
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)])
|
|
return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
|
}
|
|
let _ = enqueueMessages(account: arguments.account, peerId: peerId, messages: messages).start()
|
|
}
|
|
}
|
|
arguments.presentController(controller, ViewControllerPresentationArguments(presentationAnimation: ViewControllerPresentationAnimation.modalSheet))
|
|
})
|
|
})
|
|
case let .accounts(theme):
|
|
return ItemListDisclosureItem(theme: theme, title: "Accounts", label: "", sectionId: self.section, style: .blocks, action: {
|
|
arguments.pushController(debugAccountsController(account: arguments.account, accountManager: arguments.accountManager))
|
|
})
|
|
case let .resetServerContacts(theme):
|
|
return ItemListActionItem(theme: theme, title: "Reset Server Contacts", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
|
let _ = resetSavedContacts(network: arguments.account.network).start()
|
|
})
|
|
case let .clearPaymentData(theme):
|
|
return ItemListActionItem(theme: theme, title: "Clear Payment Password", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
|
let _ = cacheTwoStepPasswordToken(postbox: arguments.account.postbox, token: nil).start()
|
|
})
|
|
case let .logToFile(theme, value):
|
|
return ItemListSwitchItem(theme: theme, title: "Log to File", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
let _ = updateLoggingSettings(accountManager: arguments.accountManager, {
|
|
$0.withUpdatedLogToFile(value)
|
|
}).start()
|
|
})
|
|
case let .logToConsole(theme, value):
|
|
return ItemListSwitchItem(theme: theme, title: "Log to Console", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
let _ = updateLoggingSettings(accountManager: arguments.accountManager, {
|
|
$0.withUpdatedLogToConsole(value)
|
|
}).start()
|
|
})
|
|
case let .redactSensitiveData(theme, value):
|
|
return ItemListSwitchItem(theme: theme, title: "Remove Sensitive Data", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
let _ = updateLoggingSettings(accountManager: arguments.accountManager, {
|
|
$0.withUpdatedRedactSensitiveData(value)
|
|
}).start()
|
|
})
|
|
case let .enableRaiseToSpeak(theme, value):
|
|
return ItemListSwitchItem(theme: theme, title: "Enable Raise to Speak", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
let _ = updateMediaInputSettingsInteractively(postbox: arguments.account.postbox, {
|
|
$0.withUpdatedEnableRaiseToSpeak(value)
|
|
}).start()
|
|
})
|
|
case let .keepChatNavigationStack(theme, value):
|
|
return ItemListSwitchItem(theme: theme, title: "Keep Chat Stack", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
|
let _ = updateExperimentalUISettingsInteractively(postbox: arguments.account.postbox, { settings in
|
|
var settings = settings
|
|
settings.keepChatNavigationStack = value
|
|
return settings
|
|
}).start()
|
|
})
|
|
case let .clearTips(theme):
|
|
return ItemListActionItem(theme: theme, title: "Clear Tips", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
|
let _ = (arguments.account.postbox.transaction { transaction -> Void in
|
|
transaction.clearNoticeEntries()
|
|
}).start()
|
|
})
|
|
case let .reimport(theme):
|
|
return ItemListActionItem(theme: theme, title: "Reimport Application Data", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
|
|
let appGroupName = "group.\(Bundle.main.bundleIdentifier!)"
|
|
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
|
|
|
guard let appGroupUrl = maybeAppGroupUrl else {
|
|
return
|
|
}
|
|
|
|
let statusPath = appGroupUrl.path + "/Documents/importcompleted"
|
|
if FileManager.default.fileExists(atPath: statusPath) {
|
|
let _ = try? FileManager.default.removeItem(at: URL(fileURLWithPath: statusPath))
|
|
exit(0)
|
|
}
|
|
})
|
|
case let .sendTthumb(theme):
|
|
return ItemListSwitchItem(theme: theme, title: "Send TThumb", value: GlobalExperimentalSettings.enableTinyThumbnails, sectionId: self.section, style: .blocks, updated: { value in
|
|
GlobalExperimentalSettings.enableTinyThumbnails = value
|
|
})
|
|
case let .previewTthumb(theme):
|
|
return ItemListSwitchItem(theme: theme, title: "Preview TThumb", value: GlobalExperimentalSettings.forceTinyThumbnailsPreview, sectionId: self.section, style: .blocks, updated: { value in
|
|
GlobalExperimentalSettings.forceTinyThumbnailsPreview = value
|
|
})
|
|
case let .animatedStickers(theme):
|
|
return ItemListSwitchItem(theme: theme, title: "Animated Stickers", value: GlobalExperimentalSettings.animatedStickers, sectionId: self.section, style: .blocks, updated: { value in
|
|
GlobalExperimentalSettings.animatedStickers = value
|
|
})
|
|
case let .versionInfo(theme):
|
|
let bundle = Bundle.main
|
|
let bundleId = bundle.bundleIdentifier ?? ""
|
|
let bundleVersion = bundle.infoDictionary?["CFBundleShortVersionString"] ?? ""
|
|
let bundleBuild = bundle.infoDictionary?[kCFBundleVersionKey as String] ?? ""
|
|
return ItemListTextItem(theme: theme, text: .plain("\(bundleId)\n\(bundleVersion) (\(bundleBuild))"), sectionId: self.section)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func debugControllerEntries(presentationData: PresentationData, loggingSettings: LoggingSettings, mediaInputSettings: MediaInputSettings, experimentalSettings: ExperimentalUISettings, hasLegacyAppData: Bool) -> [DebugControllerEntry] {
|
|
var entries: [DebugControllerEntry] = []
|
|
|
|
entries.append(.sendLogs(presentationData.theme))
|
|
entries.append(.sendOneLog(presentationData.theme))
|
|
entries.append(.accounts(presentationData.theme))
|
|
|
|
entries.append(.resetServerContacts(presentationData.theme))
|
|
entries.append(.clearPaymentData(presentationData.theme))
|
|
|
|
entries.append(.logToFile(presentationData.theme, loggingSettings.logToFile))
|
|
entries.append(.logToConsole(presentationData.theme, loggingSettings.logToConsole))
|
|
entries.append(.redactSensitiveData(presentationData.theme, loggingSettings.redactSensitiveData))
|
|
|
|
entries.append(.enableRaiseToSpeak(presentationData.theme, mediaInputSettings.enableRaiseToSpeak))
|
|
entries.append(.keepChatNavigationStack(presentationData.theme, experimentalSettings.keepChatNavigationStack))
|
|
entries.append(.clearTips(presentationData.theme))
|
|
if hasLegacyAppData {
|
|
entries.append(.reimport(presentationData.theme))
|
|
}
|
|
entries.append(.sendTthumb(presentationData.theme))
|
|
entries.append(.previewTthumb(presentationData.theme))
|
|
entries.append(.animatedStickers(presentationData.theme))
|
|
entries.append(.versionInfo(presentationData.theme))
|
|
|
|
return entries
|
|
}
|
|
|
|
public func debugController(account: Account, accountManager: AccountManager) -> ViewController {
|
|
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
|
var pushControllerImpl: ((ViewController) -> Void)?
|
|
|
|
let arguments = DebugControllerArguments(account: account, accountManager: accountManager, presentController: { controller, arguments in
|
|
presentControllerImpl?(controller, arguments)
|
|
}, pushController: { controller in
|
|
pushControllerImpl?(controller)
|
|
})
|
|
|
|
let appGroupName = "group.\(Bundle.main.bundleIdentifier!)"
|
|
let maybeAppGroupUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupName)
|
|
|
|
var hasLegacyAppData = false
|
|
if let appGroupUrl = maybeAppGroupUrl {
|
|
let statusPath = appGroupUrl.path + "/Documents/importcompleted"
|
|
hasLegacyAppData = FileManager.default.fileExists(atPath: statusPath)
|
|
}
|
|
|
|
let signal = combineLatest((account.applicationContext as! TelegramApplicationContext).presentationData, accountManager.sharedData(keys: Set([SharedDataKeys.loggingSettings])), account.postbox.preferencesView(keys: [ApplicationSpecificPreferencesKeys.mediaInputSettings, ApplicationSpecificPreferencesKeys.experimentalUISettings]))
|
|
|> map { presentationData, sharedData, preferencesView -> (ItemListControllerState, (ItemListNodeState<DebugControllerEntry>, DebugControllerEntry.ItemGenerationArguments)) in
|
|
let loggingSettings: LoggingSettings
|
|
if let value = sharedData.entries [SharedDataKeys.loggingSettings] as? LoggingSettings {
|
|
loggingSettings = value
|
|
} else {
|
|
loggingSettings = LoggingSettings.defaultSettings
|
|
}
|
|
|
|
let mediaInputSettings: MediaInputSettings
|
|
if let value = preferencesView.values[ApplicationSpecificPreferencesKeys.mediaInputSettings] as? MediaInputSettings {
|
|
mediaInputSettings = value
|
|
} else {
|
|
mediaInputSettings = MediaInputSettings.defaultSettings
|
|
}
|
|
|
|
let experimentalSettings: ExperimentalUISettings = (preferencesView.values[ApplicationSpecificPreferencesKeys.experimentalUISettings] as? ExperimentalUISettings) ?? ExperimentalUISettings.defaultSettings
|
|
|
|
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Debug"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
|
let listState = ItemListNodeState(entries: debugControllerEntries(presentationData: presentationData, loggingSettings: loggingSettings, mediaInputSettings: mediaInputSettings, experimentalSettings: experimentalSettings, hasLegacyAppData: hasLegacyAppData), style: .blocks)
|
|
|
|
return (controllerState, (listState, arguments))
|
|
}
|
|
|
|
let controller = ItemListController(account: account, state: signal)
|
|
presentControllerImpl = { [weak controller] c, a in
|
|
controller?.present(c, in: .window(.root), with: a)
|
|
}
|
|
pushControllerImpl = { [weak controller] c in
|
|
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
|
}
|
|
return controller
|
|
}
|