chore: move CLI into its own "studio" app
This commit is contained in:
1 parent
207784800d
commit
fd7933561f
37 files changed
-1481
No files matched your search
@@ -1 +0,0 @@
|
||||
.vscode/
|
||||
@@ -1,39 +0,0 @@
|
||||
[package]
|
||||
name = "dioxus-livehost"
|
||||
version = "0.0.0"
|
||||
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { path = "../core", version = "0.1.2" }
|
||||
dioxus-web = { path = "../web" }
|
||||
|
||||
|
||||
serde = { version = "1", features = ['derive'] }
|
||||
tide = "0.16.0"
|
||||
tide-websockets = "0.3.0"
|
||||
async-std = { version = "1.8.0", features = ["attributes"] }
|
||||
log = "0.4"
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
async-channel = "1.5.1"
|
||||
futures-lite = "1.11.2"
|
||||
anyhow = "1.0.35"
|
||||
|
||||
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
wasm-bindgen = { version = "0.2.71", optional = true }
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
wasm-bindgen-futures = { version = "0.4.20", optional = true }
|
||||
|
||||
|
||||
serde_millis = "0.1"
|
||||
serde_json = "1"
|
||||
strum = { version = "0.20", features = ["derive"] }
|
||||
|
||||
|
||||
[features]
|
||||
default = ["client", "server"]
|
||||
server = []
|
||||
client = []
|
||||
@@ -1,13 +0,0 @@
|
||||
# Livehost
|
||||
|
||||
Stream events from a server to a client.
|
||||
|
||||
This crate provides drivers for Actix, Warp, and Tide to run coupled frontend and backend.
|
||||
|
||||
This comes in the form of two approaches:
|
||||
|
||||
- tight coupling: frontend and backend are locked together
|
||||
- loose coupling: hooks subscribe a component to a server using the suspense mechanism
|
||||
|
||||
Tight coupling is basically an implmentation of loose coupling where **all** events move through the backend connection. This coupling option has higher latency but is very simple to deploy. We use this approach for dioxus-webview where latency is minimal (hosted locally) and we want builds to be simple - no need to manually bundle a custom frontend because everything is server rendered.
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
pub fn main() {
|
||||
#[cfg(feature = "client")]
|
||||
wasm_bindgen_futures::spawn_local(async { client::app().await.unwrap() });
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async_std::task::block_on(async { server::app().await.expect("") });
|
||||
}
|
||||
|
||||
/// ===============================
|
||||
/// Common code (shared types)
|
||||
/// ===============================
|
||||
#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
|
||||
pub enum SelectedStream {
|
||||
Football,
|
||||
Hockey,
|
||||
Socker,
|
||||
}
|
||||
|
||||
/// Client-specific code
|
||||
#[cfg(feature = "client")]
|
||||
mod client {
|
||||
use super::*;
|
||||
use dioxus_core::prelude::*;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
pub async fn app() -> anyhow::Result<()> {
|
||||
Ok(dioxus_web::WebsysRenderer::start(APP).await)
|
||||
}
|
||||
|
||||
static APP: FC<()> = |cx| {
|
||||
todo!()
|
||||
// let (selected_stream, set_stream) = use_state(cx, || SelectedStream::Football);
|
||||
|
||||
// let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
|
||||
|
||||
// cx.render(rsx! {
|
||||
// div {
|
||||
// h1 { "Tide basic CRUD app" }
|
||||
// h2 { "Chosen stream: {selected_stream}" }
|
||||
// select {
|
||||
// value: {selected_stream.as_ref()}
|
||||
// "{selected_stream}"
|
||||
// {opts}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
};
|
||||
}
|
||||
|
||||
/// Server-specific code
|
||||
#[cfg(feature = "server")]
|
||||
mod server {
|
||||
use async_std::sync::RwLock;
|
||||
pub use log::info;
|
||||
use std::sync::Arc;
|
||||
use tide::Request;
|
||||
use tide_websockets::{Message, WebSocket, WebSocketConnection};
|
||||
|
||||
use crate::SelectedStream;
|
||||
|
||||
// type ServerRequest = Request<Arc<RwLock<()>>>;
|
||||
type ServerRequest = Request<()>;
|
||||
// type ServerRequest = Request<Arc<RwLock<ServerState>>>;
|
||||
|
||||
static CLIENT_PATH: &'static str = "";
|
||||
|
||||
pub async fn app() -> anyhow::Result<()> {
|
||||
let mut app = tide::new();
|
||||
|
||||
app.at("")
|
||||
.serve_dir(format!("{}/pkg", CLIENT_PATH))
|
||||
.expect("Cannot serve directory");
|
||||
|
||||
app.at("/updates").get(WebSocket::new(socket_handler));
|
||||
|
||||
let addr = "0.0.0.0:9001";
|
||||
log::info!("Congrats! Server is up and running at http://{}", addr);
|
||||
app.listen(addr).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn socket_handler(
|
||||
request: ServerRequest,
|
||||
stream: WebSocketConnection,
|
||||
) -> tide::Result<()> {
|
||||
// clone the receiver channel
|
||||
// subscribe to any updates
|
||||
// let receiver = request.state().read().await.receiver.clone();
|
||||
// while let Ok(evt) = receiver.recv().await {
|
||||
// let response_msg = serde_json::to_string(&evt)?;
|
||||
// stream.send_string(response_msg).await?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct SreamListProps {
|
||||
selected_stream: SelectedStream,
|
||||
}
|
||||
|
||||
static STREAM_LIST: FC<SreamListProps> = |cx| {
|
||||
//
|
||||
let g = match cx.selected_stream {
|
||||
SelectedStream::Football => cx.render(rsx! {
|
||||
li {
|
||||
"watch football!"
|
||||
}
|
||||
}),
|
||||
SelectedStream::Hockey => cx.render(rsx! {
|
||||
li {
|
||||
"watch football!"
|
||||
}
|
||||
}),
|
||||
SelectedStream::Socker => cx.render(rsx! {
|
||||
li {
|
||||
"watch football!"
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
pub fn main() {
|
||||
#[cfg(feature = "client")]
|
||||
wasm_bindgen_futures::spawn_local(async { client::app().await.unwrap() });
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
async_std::task::block_on(async { server::app().await.expect("") });
|
||||
}
|
||||
|
||||
/// ===============================
|
||||
/// Common code (shared types)
|
||||
/// ===============================
|
||||
#[derive(PartialEq, strum::EnumIter, strum::Display, strum::AsRefStr, strum::EnumString)]
|
||||
pub enum SelectedStream {
|
||||
Football,
|
||||
Hockey,
|
||||
Socker,
|
||||
}
|
||||
|
||||
/// Client-specific code
|
||||
#[cfg(feature = "client")]
|
||||
mod client {
|
||||
use super::*;
|
||||
use dioxus_core::prelude::*;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
pub async fn app() -> anyhow::Result<()> {
|
||||
Ok(dioxus_web::WebsysRenderer::start(APP).await)
|
||||
}
|
||||
|
||||
static APP: FC<()> = |cx| {
|
||||
todo!()
|
||||
// let (selected_stream, set_stream) = use_state(cx, || SelectedStream::Football);
|
||||
|
||||
// let opts = SelectedStream::iter().map(|name| rsx! { option { "{name}", value: "{name}" } });
|
||||
|
||||
// cx.render(rsx! {
|
||||
// div {
|
||||
// h1 { "Tide basic CRUD app" }
|
||||
// h2 { "Chosen stream: {selected_stream}" }
|
||||
// select {
|
||||
// value: {selected_stream.as_ref()}
|
||||
// "{selected_stream}"
|
||||
// {opts}
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
};
|
||||
}
|
||||
|
||||
/// Server-specific code
|
||||
#[cfg(feature = "server")]
|
||||
mod server {
|
||||
use async_std::sync::RwLock;
|
||||
pub use log::info;
|
||||
use std::sync::Arc;
|
||||
use tide::Request;
|
||||
use tide_websockets::{Message, WebSocket, WebSocketConnection};
|
||||
|
||||
use crate::SelectedStream;
|
||||
|
||||
// type ServerRequest = Request<Arc<RwLock<()>>>;
|
||||
type ServerRequest = Request<()>;
|
||||
// type ServerRequest = Request<Arc<RwLock<ServerState>>>;
|
||||
|
||||
static CLIENT_PATH: &'static str = "";
|
||||
|
||||
pub async fn app() -> anyhow::Result<()> {
|
||||
let mut app = tide::new();
|
||||
|
||||
app.at("")
|
||||
.serve_dir(format!("{}/pkg", CLIENT_PATH))
|
||||
.expect("Cannot serve directory");
|
||||
|
||||
app.at("/updates").get(WebSocket::new(socket_handler));
|
||||
|
||||
let addr = "0.0.0.0:9001";
|
||||
log::info!("Congrats! Server is up and running at http://{}", addr);
|
||||
app.listen(addr).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn socket_handler(
|
||||
request: ServerRequest,
|
||||
stream: WebSocketConnection,
|
||||
) -> tide::Result<()> {
|
||||
// clone the receiver channel
|
||||
// subscribe to any updates
|
||||
// let receiver = request.state().read().await.receiver.clone();
|
||||
// while let Ok(evt) = receiver.recv().await {
|
||||
// let response_msg = serde_json::to_string(&evt)?;
|
||||
// stream.send_string(response_msg).await?;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
#[derive(PartialEq, Props)]
|
||||
struct SreamListProps {
|
||||
selected_stream: SelectedStream,
|
||||
}
|
||||
|
||||
static STREAM_LIST: FC<SreamListProps> = |cx| {
|
||||
//
|
||||
match cx.selected_stream {
|
||||
SelectedStream::Football => cx.render(rsx! {
|
||||
li {
|
||||
"watch football!"
|
||||
}
|
||||
}),
|
||||
|
||||
_ => unimplemented!()
|
||||
// .render(cx),
|
||||
// SelectedStream::Hockey => rsx! {
|
||||
// li {
|
||||
// "watch football!"
|
||||
// }
|
||||
// }
|
||||
// .render(cx),
|
||||
// SelectedStream::Socker => rsx! {
|
||||
// li {
|
||||
// "watch football!"
|
||||
// }
|
||||
// }
|
||||
// .render(cx),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod ergorsx {
|
||||
|
||||
// struct Ncx {}
|
||||
|
||||
// struct VVNode {}
|
||||
// struct DTree {
|
||||
// // struct DTree<F: Fn(&Ncx) -> VVNode> {
|
||||
// caller: F,
|
||||
// }
|
||||
// impl<F: Fn(&Ncx) -> VVNode> DTree<F> {
|
||||
// fn new(f: F) -> Self {
|
||||
// Self { caller: f }
|
||||
// }
|
||||
// }
|
||||
|
||||
// trait Renderable {
|
||||
// fn render(self, nodecx: &Ncx) -> VVNode;
|
||||
// }
|
||||
|
||||
// impl<F: Fn(&Ncx) -> VVNode> Renderable for DTree<F> {
|
||||
// fn render(self, nodecx: &Ncx) -> VVNode {
|
||||
// (self.caller)(nodecx)
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn test() {
|
||||
// let t = 123;
|
||||
// let r = match t {
|
||||
// 123 => DTree::new(|f| VVNode {}).render(cx),
|
||||
// 456 => DTree::new(|f| VVNode {}).render(cx),
|
||||
// 789 => DTree::new(|f| VVNode {}).render(cx),
|
||||
// _ => unreachable!(),
|
||||
// };
|
||||
// }
|
||||
|
||||
// fn example() {
|
||||
// rsx! {
|
||||
// div {
|
||||
|
||||
// }
|
||||
// }.render(cx)
|
||||
// }
|
||||
|
||||
// fn example() {
|
||||
// cx.render(rsx!{
|
||||
// div {
|
||||
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
<!-- a js-only interpreter for the dioxus patch stream :) -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<meta charset="UTF-8" />
|
||||
<link
|
||||
href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div></div>
|
||||
</body>
|
||||
<script>
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.stack = [root];
|
||||
}
|
||||
|
||||
top() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class OPTABLE {
|
||||
// 0
|
||||
SetText(edit, interp) {
|
||||
interp.top(interp.stack).textContent = edit.text;
|
||||
}
|
||||
// 1
|
||||
RemoveSelfAndNextSiblings(edit) {
|
||||
const node = interp.pop();
|
||||
let sibling = node.nextSibling;
|
||||
while (sibling) {
|
||||
const temp = sibling.nextSibling;
|
||||
sibling.remove();
|
||||
sibling = temp;
|
||||
}
|
||||
node.remove();
|
||||
}
|
||||
|
||||
// 2
|
||||
ReplaceWith(edit, interp) {
|
||||
const newNode = interp.pop();
|
||||
const oldNode = interp.pop();
|
||||
oldNode.replaceWith(newNode);
|
||||
interp.stack.push(newNode);
|
||||
}
|
||||
|
||||
// 3
|
||||
SetAttribute(edit, interp) {
|
||||
const name = edit.name;
|
||||
const value = edit.value;
|
||||
const node = interp.top(interp.stack);
|
||||
node.setAttribute(name, value);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||
if ((name === "value", interp)) {
|
||||
node.value = value;
|
||||
}
|
||||
if ((name === "checked", interp)) {
|
||||
node.checked = true;
|
||||
}
|
||||
if ((name === "selected", interp)) {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 4
|
||||
RemoveAttribute(edit, interp) {
|
||||
const name = edit.name;
|
||||
const node = interp.top(interp.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if ((name === "value", interp)) {
|
||||
node.value = null;
|
||||
}
|
||||
if ((name === "checked", interp)) {
|
||||
node.checked = false;
|
||||
}
|
||||
if ((name === "selected", interp)) {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 5
|
||||
PushReverseChild(edit, interp) {
|
||||
const n = edit.n;
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[children.length - n - 1];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 6
|
||||
PopPushChild(edit, interp) {
|
||||
const n = edit.n;
|
||||
interp.pop();
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[n];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 7
|
||||
Pop(edit, interp) {
|
||||
interp.pop();
|
||||
}
|
||||
|
||||
// 8
|
||||
AppendChild(edit, interp) {
|
||||
console.log(interp.stack);
|
||||
const child = interp.pop();
|
||||
console.log(interp.stack);
|
||||
interp.top().appendChild(child);
|
||||
}
|
||||
|
||||
// 9
|
||||
CreateTextNode(edit, interp) {
|
||||
// interp.stack.push(document.createTextNode("asd"));
|
||||
console.log(interp.stack);
|
||||
|
||||
interp.stack.push(document.createTextNode(edit.text));
|
||||
console.log(interp.stack);
|
||||
}
|
||||
|
||||
// 10
|
||||
CreateElement(edit, interp) {
|
||||
const tagName = edit.tag_name;
|
||||
interp.stack.push(document.createElement(tagName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 11
|
||||
NewEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const a = mem32[i++];
|
||||
const b = mem32[i++];
|
||||
const el = interp.top(interp.stack);
|
||||
el.addEventListener(eventType, interp.eventHandler);
|
||||
el[`dodrio-a-${eventType}`] = a;
|
||||
el[`dodrio-b-${eventType}`] = b;
|
||||
}
|
||||
|
||||
// 12
|
||||
UpdateEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const el = interp.top(interp.stack);
|
||||
el[`dodrio-a-${eventType}`] = mem32[i++];
|
||||
el[`dodrio-b-${eventType}`] = mem32[i++];
|
||||
}
|
||||
|
||||
// 13
|
||||
RemoveEventListener(edit, interp) {
|
||||
// todo!
|
||||
const eventId = mem32[i++];
|
||||
const eventType = interp.getCachedString(eventId);
|
||||
const el = interp.top(interp.stack);
|
||||
el.removeEventListener(eventType, interp.eventHandler);
|
||||
}
|
||||
|
||||
// 14
|
||||
AddCachedString(edit, interp) {
|
||||
// todo!
|
||||
const pointer = mem32[i++];
|
||||
const length = mem32[i++];
|
||||
const id = mem32[i++];
|
||||
const str = string(mem8, pointer, length);
|
||||
interp.addCachedString(str, id);
|
||||
}
|
||||
|
||||
// 15
|
||||
DropCachedString(edit, interp) {
|
||||
// todo!
|
||||
const id = mem32[i++];
|
||||
interp.dropCachedString(id);
|
||||
}
|
||||
|
||||
// 16
|
||||
CreateElementNs(edit, interp) {
|
||||
// const tagNameId = mem32[i++];
|
||||
// const tagName = interp.getCachedString(tagNameId);
|
||||
// const nsId = mem32[i++];
|
||||
// const ns = interp.getCachedString(nsId);
|
||||
interp.stack.push(document.createElementNS(edit.ns, edit.tag_name));
|
||||
}
|
||||
|
||||
// 17
|
||||
SaveChildrenToTemporaries(edit, interp) {
|
||||
// let temp = mem32[i++];
|
||||
// const start = mem32[i++];
|
||||
// const end = mem32[i++];
|
||||
let temp = edit.temp;
|
||||
const start = edit.start;
|
||||
const end = edit.end;
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
for (let i = start; i < end; i++, interp) {
|
||||
interp.temporaries[temp++] = children[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 18
|
||||
PushChild(edit, interp) {
|
||||
const parent = interp.top(interp.stack);
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
const child = parent.childNodes[n];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 19
|
||||
PushTemporary(edit, interp) {
|
||||
// const temp = mem32[i++];
|
||||
const temp = edit.temp;
|
||||
interp.stack.push(interp.temporaries[temp]);
|
||||
}
|
||||
|
||||
// 20
|
||||
InsertBefore(edit, interp) {
|
||||
const before = interp.pop();
|
||||
const after = interp.pop();
|
||||
after.parentNode.insertBefore(before, after);
|
||||
interp.stack.push(before);
|
||||
}
|
||||
|
||||
// 21
|
||||
PopPushReverseChild(edit, interp) {
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
interp.pop();
|
||||
const parent = interp.top(interp.stack);
|
||||
const children = parent.childNodes;
|
||||
const child = children[children.length - n - 1];
|
||||
interp.stack.push(child);
|
||||
}
|
||||
|
||||
// 22
|
||||
RemoveChild(edit, interp) {
|
||||
// const n = mem32[i++];
|
||||
const n = edit.n;
|
||||
const parent = interp.top(interp.stack);
|
||||
const child = parent.childNodes[n];
|
||||
child.remove();
|
||||
}
|
||||
|
||||
// 23
|
||||
SetClass(edit, interp) {
|
||||
// const classId = mem32[i++];
|
||||
const className = edit.class_name;
|
||||
interp.top(interp.stack).className = className;
|
||||
}
|
||||
|
||||
// 24
|
||||
SaveTemplate(edit, interp) {
|
||||
const id = mem32[i++];
|
||||
const template = interp.top(interp.stack);
|
||||
interp.saveTemplate(id, template.cloneNode(true));
|
||||
}
|
||||
|
||||
// 25
|
||||
PushTemplate(edit, interp) {
|
||||
const id = mem32[i++];
|
||||
const template = interp.getTemplate(id);
|
||||
interp.stack.push(template.cloneNode(true));
|
||||
}
|
||||
}
|
||||
|
||||
const op_table = new OPTABLE();
|
||||
const interpreter = new Interpreter(window.document.body);
|
||||
|
||||
function EditListReceived(rawEditList) {
|
||||
let editList = JSON.parse(rawEditList);
|
||||
console.warn("hnelllo");
|
||||
editList.forEach(function (edit, index) {
|
||||
console.log(edit);
|
||||
op_table[edit.type](edit, interpreter);
|
||||
});
|
||||
}
|
||||
|
||||
external.invoke("initiate");
|
||||
</script>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
//! Request handler for actix
|
||||
@@ -1 +0,0 @@
|
||||
//! Request handler for rocket
|
||||
@@ -1 +0,0 @@
|
||||
//! Request handler for tide
|
||||
@@ -1 +0,0 @@
|
||||
//! Request handler for warp
|
||||
@@ -1,10 +0,0 @@
|
||||
mod handlers {
|
||||
|
||||
pub mod h_actix;
|
||||
|
||||
pub mod h_rocket;
|
||||
|
||||
pub mod h_tide;
|
||||
|
||||
pub mod h_warp;
|
||||
}
|
||||
Reference in new issue
Block a user