LoadScript.swift (5452B)
1 // 2 // LoadScript.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-07-04. 6 // 7 8 import SwiftUI 9 10 struct ScriptLoaded { 11 let script: NostrScript 12 let state: LoadedState 13 } 14 15 enum LoadedState { 16 case loaded 17 case running 18 case ran(NostrScriptRunResult) 19 } 20 21 enum LoadScriptState { 22 case not_loaded 23 case loading 24 case loaded(ScriptLoaded) 25 case failed(NostrScriptLoadErr) 26 27 static func loaded(script: NostrScript) -> LoadScriptState { 28 return .loaded(ScriptLoaded(script: script, state: .loaded)) 29 } 30 } 31 32 class ScriptModel: ObservableObject { 33 var data: [UInt8] 34 @Published var state: LoadScriptState 35 36 init(data: [UInt8], state: LoadScriptState) { 37 self.data = data 38 self.state = state 39 } 40 41 @MainActor 42 func run() async { 43 guard case .loaded(let script) = state else { 44 return 45 } 46 self.state = .loaded(.init(script: script.script, state: .running)) 47 48 let t = Task.detached { 49 return script.script.run() 50 } 51 52 let res = await t.value 53 self.state = .loaded(.init(script: script.script, state: .ran(res))) 54 } 55 56 @MainActor 57 func load(pool: RelayPool) async { 58 guard case .not_loaded = state else { 59 return 60 } 61 self.state = .loading 62 let script = NostrScript(pool: pool, data: self.data) 63 let t = Task.detached { 64 print("loading script") 65 return script.load() 66 } 67 68 let load_err = await t.value 69 70 let t2 = Task { @MainActor in 71 if let load_err { 72 self.state = .failed(load_err) 73 return 74 } 75 76 self.state = .loaded(script: script) 77 } 78 79 await t2.value 80 } 81 } 82 83 struct LoadScript: View { 84 let pool: RelayPool 85 86 @ObservedObject var model: ScriptModel 87 88 func ScriptView(_ script: ScriptLoaded) -> some View { 89 ScrollView { 90 VStack { 91 let imports = script.script.imports() 92 93 let nounString = pluralizedString(key: "imports_count", count: imports.count) 94 let nounText = Text(nounString).font(.title) 95 Text("\(Text(verbatim: imports.count.formatted())) \(nounText)", comment: "Sentence composed of 2 variables to describe how many imports were performed from loading a NostrScript. In source English, the first variable is the number of imports, and the second variable is 'Import' or 'Imports'.") 96 97 ForEach(imports.indices, id: \.self) { ind in 98 Text(imports[ind]) 99 } 100 101 switch script.state { 102 case .loaded: 103 BigButton(NSLocalizedString("Run", comment: "Button that runs a NostrScript.")) { 104 Task { 105 await model.run() 106 } 107 } 108 case .running: 109 Text("Running...", comment: "Indication that the execution of a NostrScript is running.") 110 case .ran(let result): 111 switch result { 112 case .runtime_err(let errs): 113 Text("Runtime error", comment: "Indication that a runtime error occurred when running a NostrScript.") 114 .font(.title2) 115 ForEach(errs.indices, id: \.self) { ind in 116 Text(verbatim: errs[ind]) 117 } 118 case .suspend: 119 Text("Ran to suspension.", comment: "Indication that a NostrScript was run until it reached a suspended state.") 120 case .finished(let code): 121 Text("Executed successfully, returned with code \(code.description)", comment: "Indication that the execution of running a NostrScript finished successfully, while providing a numeric return code.") 122 } 123 } 124 } 125 } 126 } 127 128 var body: some View { 129 Group { 130 switch self.model.state { 131 case .not_loaded: 132 ProgressView() 133 .progressViewStyle(.circular) 134 case .loading: 135 ProgressView() 136 .progressViewStyle(.circular) 137 case .loaded(let loaded): 138 ScriptView(loaded) 139 case .failed(let load_err): 140 VStack(spacing: 20) { 141 Text("NostrScript Error", comment: "Text indicating that there was an error with loading NostrScript. There is a more descriptive error message shown separately underneath.") 142 .font(.title) 143 switch load_err { 144 case .parse: 145 Text("Failed to parse", comment: "NostrScript error message when it fails to parse a script.") 146 case .module_init: 147 Text("Failed to initialize", comment: "NostrScript error message when it fails to initialize a module.") 148 } 149 } 150 } 151 } 152 .task { 153 await model.load(pool: self.pool) 154 } 155 .navigationTitle(NSLocalizedString("NostrScript", comment: "Navigation title for the view showing NostrScript.")) 156 } 157 } 158 159 160 /* 161 #Preview { 162 LoadScript() 163 } 164 */