damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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  */