github.com/pojntfx/hydrapp/hydrapp@v0.0.0-20240516002902-d08759d6ca9f/pkg/generators/App.tsx.tpl (about) 1 import { ILocalContext, IRemoteContext, Registry } from "@pojntfx/panrpc"; 2 import { JSONParser } from "@streamparser/json-whatwg"; 3 import { useEffect, useState } from "react"; 4 import useAsyncEffect from "use-async"; 5 6 class Local { 7 async ExampleNotification(ctx: ILocalContext, msg: string) { 8 if ("Notification" in window && Notification.permission !== "granted") { 9 await Notification.requestPermission(); 10 } 11 12 if ("Notification" in window) { 13 new Notification(msg); 14 } else { 15 alert(msg); 16 } 17 } 18 } 19 20 class Remote { 21 async ExamplePrintString(ctx: IRemoteContext, msg: string) { 22 return; 23 } 24 25 async ExamplePrintStruct(ctx: IRemoteContext, input: any) { 26 return; 27 } 28 29 async ExampleReturnError(ctx: IRemoteContext) { 30 return; 31 } 32 33 async ExampleReturnString(ctx: IRemoteContext): Promise<string> { 34 return ""; 35 } 36 37 async ExampleReturnStruct(ctx: IRemoteContext): Promise<any> { 38 return {}; 39 } 40 41 async ExampleReturnStringAndError(ctx: IRemoteContext): Promise<string> { 42 return ""; 43 } 44 45 async ExampleCallback(ctx: IRemoteContext) { 46 return; 47 } 48 49 async ExampleClosure( 50 ctx: IRemoteContext, 51 length: number, 52 onIteration: (ctx: ILocalContext, i: number, b: string) => Promise<string> 53 ): Promise<number> { 54 return 0; 55 } 56 } 57 58 const App = () => { 59 const [clients, setClients] = useState(0); 60 useEffect(() => console.log(clients, "clients connected"), [clients]); 61 62 const [reconnect, setReconnect] = useState(false); 63 const [registry] = useState( 64 new Registry( 65 new Local(), 66 new Remote(), 67 68 { 69 onClientConnect: () => setClients((v) => v + 1), 70 onClientDisconnect: () => 71 setClients((v) => { 72 if (v === 1) { 73 setReconnect(true); 74 } 75 76 return v - 1; 77 }), 78 } 79 ) 80 ); 81 82 useAsyncEffect(async () => { 83 if (reconnect) { 84 await new Promise((r) => { 85 setTimeout(r, 100); 86 }); 87 88 setReconnect(false); 89 90 return () => {}; 91 } 92 93 const addr = 94 new URLSearchParams(window.location.search).get("socketURL") || 95 "ws://localhost:1337"; 96 97 const socket = new WebSocket(addr); 98 99 socket.addEventListener("error", (e) => { 100 console.error("Disconnected with error, reconnecting:", e); 101 102 setReconnect(true); 103 }); 104 105 await new Promise<void>((res, rej) => { 106 socket.addEventListener("open", () => res()); 107 socket.addEventListener("error", rej); 108 }); 109 110 const encoder = new WritableStream({ 111 write(chunk) { 112 socket.send(JSON.stringify(chunk)); 113 }, 114 }); 115 116 const parser = new JSONParser({ 117 paths: ["$"], 118 separator: "", 119 }); 120 const parserWriter = parser.writable.getWriter(); 121 const parserReader = parser.readable.getReader(); 122 const decoder = new ReadableStream({ 123 start(controller) { 124 parserReader 125 .read() 126 .then(async function process({ done, value }) { 127 if (done) { 128 controller.close(); 129 130 return; 131 } 132 133 controller.enqueue(value?.value); 134 135 parserReader 136 .read() 137 .then(process) 138 .catch((e) => controller.error(e)); 139 }) 140 .catch((e) => controller.error(e)); 141 }, 142 }); 143 socket.addEventListener("message", (m) => 144 parserWriter.write(m.data as string) 145 ); 146 socket.addEventListener("close", () => { 147 parserReader.cancel(); 148 parserWriter.abort(); 149 }); 150 151 registry.linkStream( 152 encoder, 153 decoder, 154 155 (v) => v, 156 (v) => v 157 ); 158 159 console.log("Connected to", addr); 160 161 return () => socket.close(); 162 }, [reconnect]); 163 164 return clients > 0 ? ( 165 <main> 166 <h1>hydrapp React and panrpc Example</h1> 167 168 <div> 169 <button 170 onClick={() => 171 registry.forRemotes(async (_, remote) => { 172 try { 173 await remote.ExamplePrintString( 174 undefined, 175 prompt("String to print")! 176 ); 177 } catch (e) { 178 alert(JSON.stringify((e as Error).message)); 179 } 180 }) 181 } 182 > 183 Print string 184 </button> 185 186 <button 187 onClick={() => 188 registry.forRemotes(async (_, remote) => { 189 try { 190 await remote.ExamplePrintStruct(undefined, { 191 name: prompt("Name to print")!, 192 }); 193 } catch (e) { 194 alert(JSON.stringify((e as Error).message)); 195 } 196 }) 197 } 198 > 199 Print struct 200 </button> 201 202 <button 203 onClick={() => 204 registry.forRemotes(async (_, remote) => { 205 try { 206 await remote.ExampleReturnError(undefined); 207 } catch (e) { 208 alert(JSON.stringify((e as Error).message)); 209 } 210 }) 211 } 212 > 213 Return error 214 </button> 215 216 <button 217 onClick={() => 218 registry.forRemotes(async (_, remote) => { 219 try { 220 const res = await remote.ExampleReturnString(undefined); 221 222 alert(JSON.stringify(res)); 223 } catch (e) { 224 alert(JSON.stringify((e as Error).message)); 225 } 226 }) 227 } 228 > 229 Return string 230 </button> 231 232 <button 233 onClick={() => 234 registry.forRemotes(async (_, remote) => { 235 try { 236 const res = await remote.ExampleReturnStruct(undefined); 237 238 alert(JSON.stringify(res)); 239 } catch (e) { 240 alert(JSON.stringify((e as Error).message)); 241 } 242 }) 243 } 244 > 245 Return struct 246 </button> 247 248 <button 249 onClick={() => 250 registry.forRemotes(async (_, remote) => { 251 try { 252 await remote.ExampleReturnStringAndError(undefined); 253 } catch (e) { 254 alert(JSON.stringify((e as Error).message)); 255 } 256 }) 257 } 258 > 259 Return string and error 260 </button> 261 262 <button 263 onClick={() => 264 registry.forRemotes(async (_, remote) => { 265 try { 266 await remote.ExampleCallback(undefined); 267 } catch (e) { 268 alert(JSON.stringify((e as Error).message)); 269 } 270 }) 271 } 272 > 273 Get three time notifications (with callback) 274 </button> 275 276 <button 277 onClick={() => 278 registry.forRemotes(async (_, remote) => { 279 try { 280 await remote.ExampleClosure(undefined, 3, async (_, i, b) => { 281 if ( 282 "Notification" in window && 283 Notification.permission !== "granted" 284 ) { 285 await Notification.requestPermission(); 286 } 287 288 if ("Notification" in window) { 289 new Notification(`In iteration ${i} ${b}`); 290 } else { 291 alert(`In iteration ${i} ${b}`); 292 } 293 294 return "This is from the frontend"; 295 }); 296 } catch (e) { 297 alert(JSON.stringify((e as Error).message)); 298 } 299 }) 300 } 301 > 302 Get three iteration notifications (with closure) 303 </button> 304 </div> 305 </main> 306 ) : ( 307 "Connecting ..." 308 ); 309 }; 310 311 export default App; 312