Using a service worker to keep the app working when offline is really nice. One of the main counterpoints of LiveView is that you need to be connected to the server at all times and this shows you can still make LiveView work in those cases with a little bit of effort! Super cool!
WOW. Excellent solution, clear explanation, and beautiful presentation (and app!). This is an excellent tactic for offline capability in liveview, and the svelte integration also allows for beautiful client-side reactivity and components, all while managing state gracefully. Well done!! I hope you make more videos like this in the future!!
This was very interesting! Saw on your site you only started coding a few years ago so this is quite impressive. Think you've got a promising career ahead of you!
It would be great to see offline support built into Phoenix itself, but I think it's unlikely. Working offline inherently means using JavaScript, so the only way I can imagine this being accomplished in Phoenix would be to have a bunch of Elixir wrappers around Javascript. That sounds like a lot of additional overhead that the Phoenix team probably won't want to take on, but I'd definitely be very happy if I see it :)
Just one question: You showed how CRDT works when connected to the websocket, and you briefly mentioned that Yjs is able to run both on the client and on the server. I'm missing the part how CRDT works on the server in case of an offline client that reconnects. I mean, how does the central Postgres DB know which state to store in case of a reconnecting client with a different state?
Good question! When reconnecting, each client will send its current state back up to the server. For example, if we have the following states when disconnected: - client #1, `state A` - client #2, `state B` - server, `state C` When reconnecting, client #1 and client #2 will send their states up to the server. The order in which the states are sent to the server won't matter. On the server, Yjs will take care of merging the states, so the resulting state on the server will be `state A` + `state B` + `state C`. So let's just call it `state ABC` for short. Then, `state ABC` will be sent from the server down to the clients. On client #1, Yjs will take care of merging the states so the state will be: `state A` + `state ABC` which will just equal `state ABC`. The same thing happens on client #2. Yjs will merge `state ABC` + `state B` which will again equal `state ABC` So now we should end up with the same `state ABC` everywhere. Remember to note, that thanks to the Yjs CRDT algorithm, these updates could have been sent in any order to each other, and even sent multiple times, and the resulting state will always end up being `state ABC`. Sorry, it was a little hard explaining via text, but I hope this helped! Let me know if things are still not quite clear and I can try to explain further or make another video with some diagrams :)
@@thisistonydang Thanks for the reply, it's a bit clearer now, but I don't understand how Yjs works on the server. I mean, is there an Elixir port of Yjs, or there's a separate Nodejs server running whose sole job is to run Yjs?
@@FrancoBugnano There isn't a port of Yjs in Elixir. You'll have to use one of the ports that is available (github.com/yjs/yjs?tab=readme-ov-file#Ports ) or have a separate Nodejs server. In my case, I am using a Cloudflare Worker function so that I can run Yjs in JavaScript. After receiving a state update from a client, I'm sending the client state and current server state to my Cloudflare function to do the merging, which will then send it back to my Phoenix server. This is not the most efficient solution since you have to do a round trip to another server, but it was the quickest solution that got the job done for me :) You can definitely just use one of the available ports or just use nodejs and run Yjs on the same server as Elixir as well.
Hmm, I'm not sure why the app.css wouldn't show up after deployment. I just added some instructions to the repo for deployment and running the project locally. Hopefully it helps! github.com/tonydangblog/liveview-svelte-pwa
I've examined the implementation and noticed that the Yjs document is stored in a PostgreSQL database directly. I'm curious if there's a straightforward method for decoding/encoding Yjs documents and validating changes in fields?
In order to be able to validate the data, you'll have to run Yjs server-side. In my original implementation of the project, I was running Yjs server-side by using a Cloudflare Worker. However, I've since removed the Cloudflare Worker because I wanted to remove the dependency of using an additional server. To be able to run Yjs on the same Phoenix server, there are some possibilities that I haven't tried yet myself: 1. Use Rustler with the Yjs Rust port 2. Use Elixir's `Port` to run JS Yjs 3. Compile Yjs to WASM and run with Extism So to answer your question- yes, it's possible to decode the Yjs document server-side for validation, but it's not super straightforward.
Using a service worker to keep the app working when offline is really nice. One of the main counterpoints of LiveView is that you need to be connected to the server at all times and this shows you can still make LiveView work in those cases with a little bit of effort! Super cool!
Thanks so much for going over this! Immensely valuable.
WOW. Excellent solution, clear explanation, and beautiful presentation (and app!). This is an excellent tactic for offline capability in liveview, and the svelte integration also allows for beautiful client-side reactivity and components, all while managing state gracefully.
Well done!! I hope you make more videos like this in the future!!
very clear, and capable solution. It'd be great if this was all "built-in" to Liveview, as it'd be a great functionality to have simple offline apps!
Amazing solution. PWA
Quite an elite stack!
This was very interesting! Saw on your site you only started coding a few years ago so this is quite impressive. Think you've got a promising career ahead of you!
Thank you for the kind words! I very much appreciate it 🙏
Very well explained
Love it love it love it, thanks for the great video!
Very cool project! Speak very clearly, even my English listening is not very good, I can understand you completely.
Very clever approach. Your presentation was very thorough and clearly presented. Thank you for sharing!
Fantastic project; super clear presentation. Thanks for this!
Super cool video!
Awesome work Tony! 😍 Subbed. 🔔
Be interesting to see if Chris McCord takes anything from this concept to extend Phoenix itself to enable offline
It would be great to see offline support built into Phoenix itself, but I think it's unlikely. Working offline inherently means using JavaScript, so the only way I can imagine this being accomplished in Phoenix would be to have a bunch of Elixir wrappers around Javascript. That sounds like a lot of additional overhead that the Phoenix team probably won't want to take on, but I'd definitely be very happy if I see it :)
Just one question: You showed how CRDT works when connected to the websocket, and you briefly mentioned that Yjs is able to run both on the client and on the server. I'm missing the part how CRDT works on the server in case of an offline client that reconnects. I mean, how does the central Postgres DB know which state to store in case of a reconnecting client with a different state?
Good question! When reconnecting, each client will send its current state back up to the server. For example, if we have the following states when disconnected:
- client #1, `state A`
- client #2, `state B`
- server, `state C`
When reconnecting, client #1 and client #2 will send their states up to the server. The order in which the states are sent to the server won't matter. On the server, Yjs will take care of merging the states, so the resulting state on the server will be `state A` + `state B` + `state C`. So let's just call it `state ABC` for short.
Then, `state ABC` will be sent from the server down to the clients. On client #1, Yjs will take care of merging the states so the state will be: `state A` + `state ABC` which will just equal `state ABC`.
The same thing happens on client #2. Yjs will merge `state ABC` + `state B` which will again equal `state ABC`
So now we should end up with the same `state ABC` everywhere.
Remember to note, that thanks to the Yjs CRDT algorithm, these updates could have been sent in any order to each other, and even sent multiple times, and the resulting state will always end up being `state ABC`.
Sorry, it was a little hard explaining via text, but I hope this helped! Let me know if things are still not quite clear and I can try to explain further or make another video with some diagrams :)
@@thisistonydang Thanks for the reply, it's a bit clearer now, but I don't understand how Yjs works on the server. I mean, is there an Elixir port of Yjs, or there's a separate Nodejs server running whose sole job is to run Yjs?
@@FrancoBugnano There isn't a port of Yjs in Elixir. You'll have to use one of the ports that is available (github.com/yjs/yjs?tab=readme-ov-file#Ports ) or have a separate Nodejs server.
In my case, I am using a Cloudflare Worker function so that I can run Yjs in JavaScript. After receiving a state update from a client, I'm sending the client state and current server state to my Cloudflare function to do the merging, which will then send it back to my Phoenix server.
This is not the most efficient solution since you have to do a round trip to another server, but it was the quickest solution that got the job done for me :)
You can definitely just use one of the available ports or just use nodejs and run Yjs on the same server as Elixir as well.
@@thisistonydang Thanks for the explaination, now everything is clear😀
Really cool!
I have an issue while deploying the app. For some reason the app.css is not in assets after deployment.
Hmm, I'm not sure why the app.css wouldn't show up after deployment. I just added some instructions to the repo for deployment and running the project locally. Hopefully it helps! github.com/tonydangblog/liveview-svelte-pwa
I've examined the implementation and noticed that the Yjs document is stored in a PostgreSQL database directly. I'm curious if there's a straightforward method for decoding/encoding Yjs documents and validating changes in fields?
In order to be able to validate the data, you'll have to run Yjs server-side. In my original implementation of the project, I was running Yjs server-side by using a Cloudflare Worker. However, I've since removed the Cloudflare Worker because I wanted to remove the dependency of using an additional server.
To be able to run Yjs on the same Phoenix server, there are some possibilities that I haven't tried yet myself:
1. Use Rustler with the Yjs Rust port
2. Use Elixir's `Port` to run JS Yjs
3. Compile Yjs to WASM and run with Extism
So to answer your question- yes, it's possible to decode the Yjs document server-side for validation, but it's not super straightforward.