diff --git a/OpoLua.xcodeproj/project.pbxproj b/OpoLua.xcodeproj/project.pbxproj index 81a2d4fe..14f3d8cc 100644 --- a/OpoLua.xcodeproj/project.pbxproj +++ b/OpoLua.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ D82639362783CAE500AB4086 /* TimerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82639352783CAE500AB4086 /* TimerRequest.swift */; }; D82A37B827A9B43F00D8F5F4 /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82A37B727A9B43F00D8F5F4 /* DirectoryMonitor.swift */; }; D82D2FCC27AB4FBC001A4283 /* RecursiveDirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82D2FCB27AB4FBC001A4283 /* RecursiveDirectoryMonitor.swift */; }; - D82D2FCE27AC3E62001A4283 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = D82D2FCD27AC3E62001A4283 /* Error.swift */; }; D82D76082C9142740040265E /* src in Resources */ = {isa = PBXBuildFile; fileRef = D82D76072C9142740040265E /* src */; }; D84ADB162771D937001ABFB4 /* Device.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84ADB152771D937001ABFB4 /* Device.swift */; }; D84ADB182771DAB9001ABFB4 /* NSNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84ADB172771DAB9001ABFB4 /* NSNotification.swift */; }; @@ -92,6 +91,7 @@ D8CCACAE27B2FE3D008C8122 /* NSLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CCACAD27B2FE3D008C8122 /* NSLock.swift */; }; D8CCACB027B2FE59008C8122 /* NSRecursiveLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8CCACAF27B2FE59008C8122 /* NSRecursiveLock.swift */; }; D8D184F527BACAB5004DDFFD /* SoundViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D184F427BACAB5004DDFFD /* SoundViewController.swift */; }; + D8D22C1F2CA4E6B1003E2D5E /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D22C1E2CA4E6AF003E2D5E /* Metadata.swift */; }; D8D44C49279A0B87007340B5 /* TaskManagerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D44C48279A0B87007340B5 /* TaskManagerViewController.swift */; }; D8D50117275928CF008C1BC9 /* Scheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D50116275928CF008C1BC9 /* Scheduler.swift */; }; D8D6701527A72B2100D12E96 /* DestinationPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D6701427A72B2100D12E96 /* DestinationPickerViewController.swift */; }; @@ -163,7 +163,6 @@ D82639352783CAE500AB4086 /* TimerRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerRequest.swift; sourceTree = ""; }; D82A37B727A9B43F00D8F5F4 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = ""; }; D82D2FCB27AB4FBC001A4283 /* RecursiveDirectoryMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecursiveDirectoryMonitor.swift; sourceTree = ""; }; - D82D2FCD27AC3E62001A4283 /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; D82D76072C9142740040265E /* src */ = {isa = PBXFileReference; lastKnownFileType = folder; path = src; sourceTree = ""; }; D84ADB152771D937001ABFB4 /* Device.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Device.swift; sourceTree = ""; }; D84ADB172771DAB9001ABFB4 /* NSNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSNotification.swift; sourceTree = ""; }; @@ -221,6 +220,7 @@ D8CCACAD27B2FE3D008C8122 /* NSLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLock.swift; sourceTree = ""; }; D8CCACAF27B2FE59008C8122 /* NSRecursiveLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSRecursiveLock.swift; sourceTree = ""; }; D8D184F427BACAB5004DDFFD /* SoundViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundViewController.swift; sourceTree = ""; }; + D8D22C1E2CA4E6AF003E2D5E /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = ""; }; D8D44C48279A0B87007340B5 /* TaskManagerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskManagerViewController.swift; sourceTree = ""; }; D8D50116275928CF008C1BC9 /* Scheduler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Scheduler.swift; sourceTree = ""; }; D8D6701427A72B2100D12E96 /* DestinationPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestinationPickerViewController.swift; sourceTree = ""; }; @@ -309,6 +309,7 @@ D884A3D6277914CF008954A0 /* Installer.swift */, D86724F12771672A009C7246 /* InterpreterThread.swift */, D8FE6D902C92251B0058CE2E /* Legal.swift */, + D8D22C1E2CA4E6AF003E2D5E /* Metadata.swift */, D884A3D02778E2CB008954A0 /* ObjectFileSystem.swift */, D8C3BD662774BF1F003B1AD0 /* OpoLuaError.swift */, D86724EF27715B88009C7246 /* Program.swift */, @@ -370,7 +371,6 @@ D889134E274C181F00794D55 /* CGPoint.swift */, D87A62732752A67500075CB8 /* CGSize.swift */, D8EB6FF9275995DB00FC139F /* Dictionary.swift */, - D82D2FCD27AC3E62001A4283 /* Error.swift */, D89A60792756A2CC00C9757F /* FileManager.swift */, D80925602772939A00121E12 /* Graphics+CoreGraphics.swift */, D8CCACAD27B2FE3D008C8122 /* NSLock.swift */, @@ -629,7 +629,6 @@ D889134A274BF3B000794D55 /* TranslucentNavigationController.swift in Sources */, DB9533FE27AC06A1001CF58D /* FlagSet.swift in Sources */, D89A607E2756A98200C9757F /* ViewController.swift in Sources */, - D82D2FCE27AC3E62001A4283 /* Error.swift in Sources */, D88A472227B82C720094A438 /* ApplicationIdentifier.swift in Sources */, D8C43DF527C3EFEF00944A11 /* RootViewController.swift in Sources */, D8F5CADB279D07A700A2DC3F /* ResourceViewController.swift in Sources */, @@ -686,6 +685,7 @@ D8FE6D912C92251B0058CE2E /* Legal.swift in Sources */, D80925612772939A00121E12 /* Graphics+CoreGraphics.swift in Sources */, D84ADB182771DAB9001ABFB4 /* NSNotification.swift in Sources */, + D8D22C1F2CA4E6B1003E2D5E /* Metadata.swift in Sources */, D88A472427B83D6E0094A438 /* FileMetadataCache.swift in Sources */, D8891342274BF21900794D55 /* ChoiceViewController.swift in Sources */, D8647C702793540500DCC261 /* TaskManager.swift in Sources */, diff --git a/OpoLua/AppDelegate.swift b/OpoLua/AppDelegate.swift index 384007e7..6a1fe99a 100644 --- a/OpoLua/AppDelegate.swift +++ b/OpoLua/AppDelegate.swift @@ -112,10 +112,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { rootViewController.showSettings() } - func install(url: URL, preferredDestinationUrl: URL? = nil) { + func install(url: URL, preferredDestinationUrl: URL? = nil, sourceUrl: URL? = nil) { let installerViewController = InstallerViewController(settings: settings, url: url, - preferredDestinationUrl: preferredDestinationUrl) + preferredDestinationUrl: preferredDestinationUrl, + sourceUrl: sourceUrl) installerViewController.installerDelegate = self rootViewController.present(installerViewController, animated: true) } diff --git a/OpoLua/Extensions/Error.swift b/OpoLua/Extensions/Error.swift deleted file mode 100644 index 51cb5dfa..00000000 --- a/OpoLua/Extensions/Error.swift +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2021-2024 Jason Morley, Tom Sutcliffe -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import Foundation - -extension Error { - - private static func description(details: String) -> String { - return "## Description\n\n_Please provide details of the program you were running, and what you were doing when you encountered the error._\n\n## Details\n\n```\n\(details)\n```" - } - - var gitHubIssueUrl: URL? { - if let _ = self as? OpoInterpreter.BinaryDatabaseError { - return nil - } else if let _ = self as? OpoInterpreter.LeaveError { - return nil - } else if let _ = self as? OpoInterpreter.NativeBinaryError { - return nil - } else if let unimplementedOperation = self as? OpoInterpreter.UnimplementedOperationError { - return URL.gitHubIssue(title: "Unimplemented Operation: \(unimplementedOperation.operation)", - description: Self.description(details: unimplementedOperation.detail), - labels: ["facerake", "bug"]) - } else if let interpreterError = self as? OpoInterpreter.InterpreterError { - return URL.gitHubIssue(title: "Internal Error: \(interpreterError.message)", - description: Self.description(details: interpreterError.detail), - labels: ["internal-error", "bug"]) - } - return nil - } - -} diff --git a/OpoLua/Extensions/FileManager.swift b/OpoLua/Extensions/FileManager.swift index 851558e0..1198713e 100644 --- a/OpoLua/Extensions/FileManager.swift +++ b/OpoLua/Extensions/FileManager.swift @@ -194,28 +194,7 @@ extension FileManager { } func isSystem(at url: URL) throws -> Bool { - guard url.lastPathComponent.hasSuffix(".system") else { - return false - } - - let contents = try contentsOfDirectory(atPath: url.path).map { url.appendingPathComponent($0) } - let drives: Set = ["c", "m"] - - // Ensure there only folders named for valid drive letters present. - // N.B. This implementation is intetnionally strict. We can relax it as and when we find we need to. - for url in contents { - let name = url.lastPathComponent - if name.starts(with: ".") { - // Ignore hidden files. - continue - } - guard url.isDirectory, - drives.contains(name.lowercased()) - else { - return false - } - } - return true + return url.lastPathComponent.hasSuffix(".system") } func detectSystemFileSystem(for url: URL) throws -> FileSystem? { diff --git a/OpoLua/Extensions/URL.swift b/OpoLua/Extensions/URL.swift index ac75ce72..dd3e20fa 100644 --- a/OpoLua/Extensions/URL.swift +++ b/OpoLua/Extensions/URL.swift @@ -23,7 +23,28 @@ import UIKit extension URL { - static func gitHubIssue(title: String, description: String, labels: [String]) -> URL? { + private static func description(details: String, title: String, sourceUrl: URL?) -> String { + return """ +## Description + +_Please provide details of the program you were running, and what you were doing when you encountered the error._ + +## Metadata + +| Key | Value | +| --- | --- | +| **Title** | \(title) | +| **Source URL** | \(sourceUrl?.absoluteString ?? "*unknown*") | + +## Details + +``` +\(details) +``` +""" + } + + private static func gitHubIssueURL(title: String, description: String, labels: [String]) -> URL? { var components = URLComponents() components.scheme = "https" components.host = "github.com" @@ -36,6 +57,27 @@ extension URL { return components.url } + static func gitHubIssueURL(for error: Error, title: String, sourceUrl: URL?) -> URL? { + if let _ = error as? OpoInterpreter.BinaryDatabaseError { + return nil + } else if let _ = error as? OpoInterpreter.LeaveError { + return nil + } else if let _ = error as? OpoInterpreter.NativeBinaryError { + return nil + } else if let unimplementedOperation = error as? OpoInterpreter.UnimplementedOperationError { + let description = description(details: unimplementedOperation.detail, title: title, sourceUrl: sourceUrl) + return URL.gitHubIssueURL(title: "[\(title)] Unimplemented Operation: \(unimplementedOperation.operation)", + description: description, + labels: ["facerake", "bug"]) + } else if let interpreterError = error as? OpoInterpreter.InterpreterError { + let description = description(details: interpreterError.detail, title: title, sourceUrl: sourceUrl) + return URL.gitHubIssueURL(title: "[\(title)] Internal Error: \(interpreterError.message)", + description: description, + labels: ["internal-error", "bug"]) + } + return nil + } + var localizedName: String { if self == FileManager.default.documentsUrl { return UIDevice.current.localizedDocumentsName diff --git a/OpoLua/Model/Installer.swift b/OpoLua/Model/Installer.swift index dee93efe..848a32c0 100644 --- a/OpoLua/Model/Installer.swift +++ b/OpoLua/Model/Installer.swift @@ -34,14 +34,16 @@ class Installer { let url: URL let destinationUrl: URL - let fileSystem: FileSystem + let sourceUrl: URL? + let fileSystem: SystemFileSystem let psilua = PsiLuaEnv() weak var delegate: InstallerDelegate? - init(url: URL, destinationUrl: URL) { + init(url: URL, destinationUrl: URL, sourceUrl: URL?) { self.url = url self.destinationUrl = destinationUrl + self.sourceUrl = sourceUrl self.fileSystem = SystemFileSystem(rootUrl: destinationUrl) } @@ -49,6 +51,7 @@ class Installer { DispatchQueue.global().async { do { try self.fileSystem.prepare() + self.fileSystem.metadata = Metadata(sourceUrl: self.sourceUrl) try self.psilua.installSisFile(path: self.url.path, handler: self) let item: Directory.Item? if let systemType = try Directory.Item.system(url: self.destinationUrl, env: self.psilua) { diff --git a/OpoLua/Model/Metadata.swift b/OpoLua/Model/Metadata.swift new file mode 100644 index 00000000..e2d3b795 --- /dev/null +++ b/OpoLua/Model/Metadata.swift @@ -0,0 +1,44 @@ +// Copyright (c) 2021-2024 Jason Morley, Tom Sutcliffe +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation + +struct Metadata: Codable { + + let sourceUrl: URL? + + init(sourceUrl: URL? = nil) { + self.sourceUrl = sourceUrl + } + + init(contentsOf url: URL) throws { + let data = try Data(contentsOf: url) + let decoder = JSONDecoder() + self = try decoder.decode(Metadata.self, from: data) + } + + func write(to url: URL) throws { + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(self) + try data.write(to: url, options: .atomic) + } + +} diff --git a/OpoLua/Model/Program.swift b/OpoLua/Model/Program.swift index 3b5fbcbc..be924d0c 100644 --- a/OpoLua/Model/Program.swift +++ b/OpoLua/Model/Program.swift @@ -135,6 +135,13 @@ class Program { return applicationMetadata?.uid3 } + var metadata: Metadata { + guard let systemFileSystem = fileSystem as? SystemFileSystem else { + return Metadata() + } + return systemFileSystem.metadata + } + lazy private var fileSystem: FileSystem = { do { return try FileManager.default.detectSystemFileSystem(for: url) ?? ObjectFileSystem(objectUrl: url) @@ -436,6 +443,8 @@ extension Program: InterpreterThreadDelegate { return fileSystem.guestPath(for: url) } + // TODO: Result might be better as an actual result. Oh. That's a Tom Thing. + // TODO: Unclear to me whether this would be the right place to handle the error or not. func interpreter(_ interpreter: InterpreterThread, didFinishWithResult result: Error?) { DispatchQueue.main.sync { interpreter.handler = nil diff --git a/OpoLua/Model/SystemFileSystem.swift b/OpoLua/Model/SystemFileSystem.swift index 933c62ee..c639efa8 100644 --- a/OpoLua/Model/SystemFileSystem.swift +++ b/OpoLua/Model/SystemFileSystem.swift @@ -29,6 +29,19 @@ class SystemFileSystem: FileSystem { self.rootUrl = rootUrl } + var metadata: Metadata { + get { + return (try? Metadata(contentsOf: rootUrl.appendingPathComponent("manifest.json"))) ?? Metadata() + } + set { + do { + try newValue.write(to: rootUrl.appendingPathComponent("manifest.json")) + } catch { + print("Failed to save file system metadata with error \(error).") + } + } + } + func prepare() throws { let fileManager = FileManager.default try fileManager.createDirectory(at: rootUrl, withIntermediateDirectories: true) diff --git a/OpoLua/Utilities/RaiseGitHubIssueActivity.swift b/OpoLua/Utilities/RaiseGitHubIssueActivity.swift index 0125554a..29de32a2 100644 --- a/OpoLua/Utilities/RaiseGitHubIssueActivity.swift +++ b/OpoLua/Utilities/RaiseGitHubIssueActivity.swift @@ -26,7 +26,7 @@ class RaiseGitHubIssueActivity: UIActivity { return .action } - let error: Error + let url: URL var activityItems = [Any]() override var activityTitle: String? { @@ -41,8 +41,8 @@ class RaiseGitHubIssueActivity: UIActivity { return UIActivity.ActivityType(rawValue: "org.opolua.action.raise-github-issue") } - init(error: Error) { - self.error = error + init(url: URL) { + self.url = url super.init() } @@ -62,9 +62,6 @@ class RaiseGitHubIssueActivity: UIActivity { defer { activityDidFinish(true) } - guard let url = error.gitHubIssueUrl else { - return - } UIApplication.shared.open(url) } diff --git a/OpoLua/View Controllers/BrowserViewController.swift b/OpoLua/View Controllers/BrowserViewController.swift index b2fc88b9..235786ba 100644 --- a/OpoLua/View Controllers/BrowserViewController.swift +++ b/OpoLua/View Controllers/BrowserViewController.swift @@ -124,9 +124,10 @@ extension BrowserViewController: PsionSoftwareIndexViewControllerDelegate { psionSoftwareIndexViewController.dismiss(animated: true) } - func psionSoftwareIndexViewController(psionSoftwareIndexViewController: PsionSoftwareIndexViewController, didSelectURL url: URL) { + func psionSoftwareIndexViewController(psionSoftwareIndexViewController: PsionSoftwareIndexViewController, + didSelectItem item: SoftwareIndexView.Item) { psionSoftwareIndexViewController.dismiss(animated: true) { - AppDelegate.shared.install(url: url) + AppDelegate.shared.install(url: item.url, sourceUrl: item.sourceURL) } } diff --git a/OpoLua/View Controllers/ErrorViewController.swift b/OpoLua/View Controllers/ErrorViewController.swift index 2fafb953..59dc98dd 100644 --- a/OpoLua/View Controllers/ErrorViewController.swift +++ b/OpoLua/View Controllers/ErrorViewController.swift @@ -28,7 +28,8 @@ protocol ErrorViewControllerDelegate: AnyObject { class ErrorViewController: UIViewController { - var error: Error + let error: Error + let activities: [UIActivity] weak var delegate: ErrorViewControllerDelegate? @@ -65,8 +66,9 @@ class ErrorViewController: UIViewController { return barButtonItem }() - init(error: Error, screenshot: UIImage) { + init(error: Error, screenshot: UIImage, activities: [UIActivity]) { self.error = error + self.activities = activities super.init(nibName: nil, bundle: nil) view.backgroundColor = .systemBackground isModalInPresentation = true @@ -127,14 +129,7 @@ class ErrorViewController: UIViewController { guard let text = textView.text else { return } - var activities: [UIActivity] = [] - if error.gitHubIssueUrl != nil { - // TODO: Support raising issues for all errors shown in the error view controller #210 - // https://github.com/inseven/opolua/issues/210 - activities.append(RaiseGitHubIssueActivity(error: error)) - } - let activityViewController = UIActivityViewController(activityItems: [text], - applicationActivities: activities) + let activityViewController = UIActivityViewController(activityItems: [text], applicationActivities: activities) self.present(activityViewController, animated: true) } diff --git a/OpoLua/View Controllers/Installer/InstallerViewController.swift b/OpoLua/View Controllers/Installer/InstallerViewController.swift index 5d0976e6..1a5ab7bd 100644 --- a/OpoLua/View Controllers/Installer/InstallerViewController.swift +++ b/OpoLua/View Controllers/Installer/InstallerViewController.swift @@ -33,14 +33,16 @@ class InstallerViewController: UINavigationController { private let settings: Settings private var item: ManagedItem + private var sourceUrl: URL? private var installer: Installer? private var destinationUrl: URL? weak var installerDelegate: InstallerViewControllerDelegate? - init(settings: Settings, url: URL, preferredDestinationUrl: URL? = nil) { + init(settings: Settings, url: URL, preferredDestinationUrl: URL? = nil, sourceUrl: URL?) { self.settings = settings self.item = ManagedItem(url: url) + self.sourceUrl = sourceUrl let introductionViewController = InstallerIntroductionViewController(settings: settings, preferredDestinationUrl: preferredDestinationUrl) super.init(rootViewController: introductionViewController) @@ -71,7 +73,7 @@ class InstallerViewController: UINavigationController { return } - let installer = Installer(url: item.url, destinationUrl: systemUrl) + let installer = Installer(url: item.url, destinationUrl: systemUrl, sourceUrl: sourceUrl) self.installer = installer installer.delegate = self installer.run() diff --git a/OpoLua/View Controllers/ProgramViewController.swift b/OpoLua/View Controllers/ProgramViewController.swift index 87c3a697..07167507 100644 --- a/OpoLua/View Controllers/ProgramViewController.swift +++ b/OpoLua/View Controllers/ProgramViewController.swift @@ -594,8 +594,18 @@ extension ProgramViewController: ProgramLifecycleObserver { zoomInButton.isEnabled = false zoomOutButton.isEnabled = false + // Generate the GitHub issue URL and sharing activities. + let gitHubIssueUrl = URL.gitHubIssueURL(for: error, + title: program.title, + sourceUrl: program.metadata.sourceUrl) + let activities: [UIActivity] = if let gitHubIssueUrl { + [RaiseGitHubIssueActivity(url: gitHubIssueUrl)] + } else { + [] + } + let showErrorDetails: () -> Void = { - let viewController = ErrorViewController(error: error, screenshot: screenshot) + let viewController = ErrorViewController(error: error, screenshot: screenshot, activities: activities) viewController.delegate = self let navigationController = UINavigationController(rootViewController: viewController) self.present(navigationController, animated: true) @@ -607,19 +617,19 @@ extension ProgramViewController: ProgramLifecycleObserver { } let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { action in + alert.addAction(UIAlertAction(title: "OK", style: .cancel) { action in self.navigationController?.popViewController(animated: true) - })) - alert.addAction(UIAlertAction(title: "Show Details", style: .default, handler: { action in + }) + alert.addAction(UIAlertAction(title: "Show Details", style: .default) { action in showErrorDetails() - })) - if let gitHubIssueUrl = error.gitHubIssueUrl { - alert.addAction(UIAlertAction(title: "Raise GitHub Issue", style: .default, handler: { action in + }) + if let gitHubIssueUrl { + alert.addAction(UIAlertAction(title: "Raise GitHub Issue", style: .default) { action in UIApplication.shared.open(gitHubIssueUrl) self.navigationController?.popViewController(animated: true) - })) + }) } - present(alert, animated: true, completion: nil) + present(alert, animated: true) } func program(_ program: Program, didUpdateTitle title: String) { diff --git a/dependencies/PsionSoftwareIndexSwift b/dependencies/PsionSoftwareIndexSwift index ac82f9ce..61bc7808 160000 --- a/dependencies/PsionSoftwareIndexSwift +++ b/dependencies/PsionSoftwareIndexSwift @@ -1 +1 @@ -Subproject commit ac82f9ce57633e849e233d91637c6ac4ea2505d8 +Subproject commit 61bc7808c183e3160ce02dc07d742667c21122e1