Initial State
This commit is contained in:
commit
261577bfc7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/ui/target
|
1565
Cargo.lock
generated
Normal file
1565
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "kttd"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.6.18", features = ["tracing", "http2"] }
|
||||||
|
chrono = { version = "0.4.24", features = ["serde"] }
|
||||||
|
ctxerr = "0.2.5"
|
||||||
|
diesel = { version = "2.0.4", features = ["chrono"] }
|
||||||
|
diesel-async = { version = "0.2.2", features = ["postgres", "tokio-postgres", "deadpool"] }
|
||||||
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
|
tokio = { version = "1.28.0", features = ["parking_lot", "macros", "rt", "rt-multi-thread"] }
|
8
diesel.toml
Normal file
8
diesel.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
||||||
|
|
||||||
|
[migrations_directory]
|
||||||
|
dir = "migrations"
|
0
migrations/.keep
Normal file
0
migrations/.keep
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
6
migrations/00000000000000_diesel_initial_setup/down.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
|
||||||
|
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||||
|
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
36
migrations/00000000000000_diesel_initial_setup/up.sql
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Sets up a trigger for the given table to automatically set a column called
|
||||||
|
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||||
|
-- in the modified columns)
|
||||||
|
--
|
||||||
|
-- # Example
|
||||||
|
--
|
||||||
|
-- ```sql
|
||||||
|
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||||
|
--
|
||||||
|
-- SELECT diesel_manage_updated_at('users');
|
||||||
|
-- ```
|
||||||
|
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||||
|
BEGIN
|
||||||
|
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||||
|
BEGIN
|
||||||
|
IF (
|
||||||
|
NEW IS DISTINCT FROM OLD AND
|
||||||
|
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||||
|
) THEN
|
||||||
|
NEW.updated_at := current_timestamp;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
1
migrations/2023-05-11-145250_create_users/down.sql
Normal file
1
migrations/2023-05-11-145250_create_users/down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
10
migrations/2023-05-11-145250_create_users/up.sql
Normal file
10
migrations/2023-05-11-145250_create_users/up.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
image_id INTEGER,
|
||||||
|
first_name VARCHAR NOT NULL,
|
||||||
|
last_name VARCHAR NOT NULL,
|
||||||
|
email VARCHAR NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||||
|
)
|
1
migrations/2023-05-11-145320_create_items/down.sql
Normal file
1
migrations/2023-05-11-145320_create_items/down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
1
migrations/2023-05-11-145320_create_items/up.sql
Normal file
1
migrations/2023-05-11-145320_create_items/up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- Your SQL goes here
|
1
migrations/2023-05-11-145323_create_locations/down.sql
Normal file
1
migrations/2023-05-11-145323_create_locations/down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
1
migrations/2023-05-11-145323_create_locations/up.sql
Normal file
1
migrations/2023-05-11-145323_create_locations/up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- Your SQL goes here
|
1
migrations/2023-05-11-145327_create_tasks/down.sql
Normal file
1
migrations/2023-05-11-145327_create_tasks/down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
1
migrations/2023-05-11-145327_create_tasks/up.sql
Normal file
1
migrations/2023-05-11-145327_create_tasks/up.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
-- Your SQL goes here
|
35
src/api/mod.rs
Normal file
35
src/api/mod.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
mod user;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
routing::{get, post},
|
||||||
|
Router,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::db::DbPool;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ApiState {
|
||||||
|
pub pool: DbPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_server(state: ApiState) {
|
||||||
|
// initialize tracing
|
||||||
|
// tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
// build our application with a route
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/", get(root))
|
||||||
|
.route("/users", post(user::create_user))
|
||||||
|
.route("/users", get(user::list_user))
|
||||||
|
.with_state(state);
|
||||||
|
|
||||||
|
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic handler that responds with a static string
|
||||||
|
async fn root() -> &'static str {
|
||||||
|
"Hello, World!"
|
||||||
|
}
|
47
src/api/user.rs
Normal file
47
src/api/user.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use axum::{extract::State, http::StatusCode, Json};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::db::{
|
||||||
|
models::user::{NewUser, User},
|
||||||
|
repos,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::ApiState;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ApiUser {
|
||||||
|
first_name: String,
|
||||||
|
last_name: String,
|
||||||
|
email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_user(
|
||||||
|
State(s): State<ApiState>,
|
||||||
|
Json(payload): Json<ApiUser>,
|
||||||
|
) -> Result<Json<User>, (StatusCode, String)> {
|
||||||
|
let user = repos::user::create_user(
|
||||||
|
&s.pool,
|
||||||
|
NewUser {
|
||||||
|
first_name: &payload.first_name,
|
||||||
|
last_name: &payload.last_name,
|
||||||
|
email: &payload.email,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(internal_error)?;
|
||||||
|
|
||||||
|
Ok(Json(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_user(State(s): State<ApiState>) -> (StatusCode, Json<Vec<User>>) {
|
||||||
|
let user = repos::user::list_users(&s.pool).await.unwrap();
|
||||||
|
|
||||||
|
(StatusCode::CREATED, Json(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn internal_error<E>(err: E) -> (StatusCode, String)
|
||||||
|
where
|
||||||
|
E: std::error::Error,
|
||||||
|
{
|
||||||
|
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
|
||||||
|
}
|
13
src/db/mod.rs
Normal file
13
src/db/mod.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
pub mod models;
|
||||||
|
pub mod repos;
|
||||||
|
pub mod schema;
|
||||||
|
|
||||||
|
use diesel_async::pg::AsyncPgConnection;
|
||||||
|
use diesel_async::pooled_connection::deadpool::Pool;
|
||||||
|
use diesel_async::pooled_connection::AsyncDieselConnectionManager;
|
||||||
|
|
||||||
|
pub type DbPool = Pool<AsyncPgConnection>;
|
||||||
|
|
||||||
|
pub async fn connect<S: Into<String>>(durl: S) -> Result<DbPool, crate::error::Error> {
|
||||||
|
Ok(DbPool::builder(AsyncDieselConnectionManager::new(durl)).build()?)
|
||||||
|
}
|
0
src/db/models/image.rs
Normal file
0
src/db/models/image.rs
Normal file
0
src/db/models/item.rs
Normal file
0
src/db/models/item.rs
Normal file
0
src/db/models/location.rs
Normal file
0
src/db/models/location.rs
Normal file
6
src/db/models/mod.rs
Normal file
6
src/db/models/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod image;
|
||||||
|
pub mod item;
|
||||||
|
pub mod location;
|
||||||
|
pub mod role;
|
||||||
|
pub mod tag;
|
||||||
|
pub mod user;
|
0
src/db/models/role.rs
Normal file
0
src/db/models/role.rs
Normal file
0
src/db/models/tag.rs
Normal file
0
src/db/models/tag.rs
Normal file
24
src/db/models/user.rs
Normal file
24
src/db/models/user.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use diesel::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::db::schema::users;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone, Queryable, Selectable, Identifiable)]
|
||||||
|
#[diesel(table_name = users)]
|
||||||
|
pub struct User {
|
||||||
|
pub id: i32,
|
||||||
|
pub image_id: Option<i32>,
|
||||||
|
pub first_name: String,
|
||||||
|
pub last_name: String,
|
||||||
|
pub email: String,
|
||||||
|
pub created_at: chrono::NaiveDateTime,
|
||||||
|
pub updated_at: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Insertable)]
|
||||||
|
#[diesel(table_name = users)]
|
||||||
|
pub struct NewUser<'a> {
|
||||||
|
pub first_name: &'a str,
|
||||||
|
pub last_name: &'a str,
|
||||||
|
pub email: &'a str,
|
||||||
|
}
|
0
src/db/repos/items.rs
Normal file
0
src/db/repos/items.rs
Normal file
0
src/db/repos/location.rs
Normal file
0
src/db/repos/location.rs
Normal file
3
src/db/repos/mod.rs
Normal file
3
src/db/repos/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod items;
|
||||||
|
pub mod location;
|
||||||
|
pub mod user;
|
25
src/db/repos/user.rs
Normal file
25
src/db/repos/user.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use diesel::prelude::*;
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
|
use crate::db::models::user::NewUser;
|
||||||
|
use crate::db::schema::users;
|
||||||
|
use crate::db::{models::user::User, DbPool};
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
pub async fn create_user<'a>(pool: &DbPool, user: NewUser<'a>) -> Result<User, Error> {
|
||||||
|
let mut conn = pool.get().await?;
|
||||||
|
|
||||||
|
Ok(diesel::insert_into(users::table)
|
||||||
|
.values(&user)
|
||||||
|
.get_result(&mut conn)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list_users(pool: &DbPool) -> Result<Vec<User>, Error> {
|
||||||
|
let mut conn = pool.get().await?;
|
||||||
|
|
||||||
|
Ok(users::table
|
||||||
|
.select(User::as_select())
|
||||||
|
.load(&mut conn)
|
||||||
|
.await?)
|
||||||
|
}
|
108
src/db/schema.rs
Normal file
108
src/db/schema.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
diesel::table! {
|
||||||
|
users {
|
||||||
|
id -> Int4,
|
||||||
|
image_id -> Nullable<Int4>,
|
||||||
|
first_name -> VarChar,
|
||||||
|
last_name -> VarChar,
|
||||||
|
email -> VarChar,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
user_schedule {
|
||||||
|
id -> Int4,
|
||||||
|
user_id -> Int4,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
items {
|
||||||
|
id -> Int4,
|
||||||
|
title -> VarChar,
|
||||||
|
image_id -> Nullable<Int4>,
|
||||||
|
location_id -> Nullable<Int4>,
|
||||||
|
description -> Text,
|
||||||
|
created_by -> Int4,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
images {
|
||||||
|
id -> Int4,
|
||||||
|
title -> VarChar,
|
||||||
|
url -> VarChar,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
item_image {
|
||||||
|
id -> Int4,
|
||||||
|
item_id -> Int4,
|
||||||
|
image_id -> Int4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
locations {
|
||||||
|
id -> Int4,
|
||||||
|
parent_id -> Nullable<Int4>,
|
||||||
|
image_id -> Nullable<Int4>,
|
||||||
|
title -> VarChar,
|
||||||
|
description -> Text,
|
||||||
|
created_by -> Int4,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
location_image {
|
||||||
|
id -> Int4,
|
||||||
|
location_id -> Int4,
|
||||||
|
image_id -> Int4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
tasks {
|
||||||
|
id -> Int4,
|
||||||
|
active -> Bool,
|
||||||
|
title -> Varchar,
|
||||||
|
description -> Text,
|
||||||
|
assigned_to -> Nullable<Int4>,
|
||||||
|
estimate_time -> Int4,
|
||||||
|
estimate_period -> Int4,
|
||||||
|
estimate_start_date -> Nullable<Timestamp>,
|
||||||
|
estimate_end_date -> Nullable<Timestamp>,
|
||||||
|
created_by -> Int4,
|
||||||
|
created_at -> Timestamp,
|
||||||
|
updated_at -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
item_task {
|
||||||
|
id -> Int4,
|
||||||
|
location_id -> Int4,
|
||||||
|
image_id -> Int4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diesel::joinable!(users -> images (image_id));
|
||||||
|
|
||||||
|
diesel::joinable!(item_image -> images (image_id));
|
||||||
|
diesel::joinable!(item_image -> items (item_id));
|
||||||
|
|
||||||
|
diesel::joinable!(location_image -> images (image_id));
|
||||||
|
diesel::joinable!(location_image -> locations (location_id));
|
||||||
|
|
||||||
|
diesel::allow_tables_to_appear_in_same_query!(items, item_image, images,);
|
||||||
|
diesel::allow_tables_to_appear_in_same_query!(items, location_image, locations,);
|
16
src/error.rs
Normal file
16
src/error.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use ctxerr::ctxerr;
|
||||||
|
use diesel::result;
|
||||||
|
use diesel_async::pooled_connection::deadpool::{BuildError, PoolError};
|
||||||
|
// use diesel_async::pooled_connection::PoolError;
|
||||||
|
|
||||||
|
#[ctxerr]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
#[error("PoolError: {0}")]
|
||||||
|
Pool(#[from] PoolError),
|
||||||
|
|
||||||
|
#[error("PoolBuildError: {0}")]
|
||||||
|
PoolBuild(#[from] BuildError),
|
||||||
|
|
||||||
|
#[error("DieselResultError: {0}")]
|
||||||
|
DieselResult(#[from] result::Error),
|
||||||
|
}
|
27
src/main.rs
Normal file
27
src/main.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
mod api;
|
||||||
|
mod db;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub use error::{Error, ErrorKind};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Error> {
|
||||||
|
let durl = std::env::var("DATABASE_URL")
|
||||||
|
.unwrap_or_else(|_| "postgres://postgres@127.0.0.1:5432/kttd".to_string());
|
||||||
|
|
||||||
|
let pool = db::connect(durl).await?;
|
||||||
|
|
||||||
|
// repos::user::create_user(
|
||||||
|
// &pool,
|
||||||
|
// db::models::user::NewUser {
|
||||||
|
// first_name: "Andrey",
|
||||||
|
// last_name: "Tkachenko",
|
||||||
|
// email: "andrey@aidev.ru",
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
api::start_server(api::ApiState { pool }).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
1658
ui/Cargo.lock
generated
Normal file
1658
ui/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
ui/Cargo.toml
Normal file
10
ui/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "ui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dioxus = "0.3.2"
|
||||||
|
dioxus-web = "0.3.1"
|
1029
ui/dist/assets/dioxus/dioxus.js
vendored
Normal file
1029
ui/dist/assets/dioxus/dioxus.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
ui/dist/assets/dioxus/dioxus_bg.wasm
vendored
Normal file
BIN
ui/dist/assets/dioxus/dioxus_bg.wasm
vendored
Normal file
Binary file not shown.
180
ui/dist/assets/dioxus/snippets/dioxus-interpreter-js-1676574062e4c953/inline0.js
vendored
Normal file
180
ui/dist/assets/dioxus/snippets/dioxus-interpreter-js-1676574062e4c953/inline0.js
vendored
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
let m,p,ls,lss,sp,d,t,c,s,sl,op,i,e,z,index,bubbles,n,len,field,root,text,ns,value,ptr,many,event_name,tmpl_id,id;const ns_cache = [];const evt = [];const attr = [];
|
||||||
|
class ListenerMap {
|
||||||
|
constructor(root) {
|
||||||
|
// bubbling events can listen at the root element
|
||||||
|
this.global = {};
|
||||||
|
// non bubbling events listen at the element the listener was created at
|
||||||
|
this.local = {};
|
||||||
|
this.root = null;
|
||||||
|
this.handler = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(event_name, element, bubbles) {
|
||||||
|
if (bubbles) {
|
||||||
|
if (this.global[event_name] === undefined) {
|
||||||
|
this.global[event_name] = {};
|
||||||
|
this.global[event_name].active = 1;
|
||||||
|
this.root.addEventListener(event_name, this.handler);
|
||||||
|
} else {
|
||||||
|
this.global[event_name].active++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
if (!this.local[id]) {
|
||||||
|
this.local[id] = {};
|
||||||
|
}
|
||||||
|
element.addEventListener(event_name, this.handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(element, event_name, bubbles) {
|
||||||
|
if (bubbles) {
|
||||||
|
this.global[event_name].active--;
|
||||||
|
if (this.global[event_name].active === 0) {
|
||||||
|
this.root.removeEventListener(event_name, this.global[event_name].callback);
|
||||||
|
delete this.global[event_name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
delete this.local[id][event_name];
|
||||||
|
if (this.local[id].length === 0) {
|
||||||
|
delete this.local[id];
|
||||||
|
}
|
||||||
|
element.removeEventListener(event_name, this.handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAllNonBubbling(element) {
|
||||||
|
const id = element.getAttribute("data-dioxus-id");
|
||||||
|
delete this.local[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function SetAttributeInner(node, field, value, ns) {
|
||||||
|
const name = field;
|
||||||
|
if (ns === "style") {
|
||||||
|
// ????? why do we need to do this
|
||||||
|
if (node.style === undefined) {
|
||||||
|
node.style = {};
|
||||||
|
}
|
||||||
|
node.style[name] = value;
|
||||||
|
} else if (ns !== null && ns !== undefined && ns !== "") {
|
||||||
|
node.setAttributeNS(ns, name, value);
|
||||||
|
} else {
|
||||||
|
switch (name) {
|
||||||
|
case "value":
|
||||||
|
if (value !== node.value) {
|
||||||
|
node.value = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "checked":
|
||||||
|
node.checked = value === "true";
|
||||||
|
break;
|
||||||
|
case "selected":
|
||||||
|
node.selected = value === "true";
|
||||||
|
break;
|
||||||
|
case "dangerous_inner_html":
|
||||||
|
node.innerHTML = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
|
||||||
|
if (value === "false" && bool_attrs.hasOwnProperty(name)) {
|
||||||
|
node.removeAttribute(name);
|
||||||
|
} else {
|
||||||
|
node.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function LoadChild(ptr, len) {
|
||||||
|
// iterate through each number and get that child
|
||||||
|
node = stack[stack.length - 1];
|
||||||
|
ptr_end = ptr + len;
|
||||||
|
for (; ptr < ptr_end; ptr++) {
|
||||||
|
end = m.getUint8(ptr);
|
||||||
|
for (node = node.firstChild; end > 0; end--) {
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
const listeners = new ListenerMap();
|
||||||
|
let nodes = [];
|
||||||
|
let stack = [];
|
||||||
|
const templates = {};
|
||||||
|
let node, els, end, ptr_end, k;
|
||||||
|
export function save_template(nodes, tmpl_id) {
|
||||||
|
templates[tmpl_id] = nodes;
|
||||||
|
}
|
||||||
|
export function set_node(id, node) {
|
||||||
|
nodes[id] = node;
|
||||||
|
}
|
||||||
|
export function initilize(root, handler) {
|
||||||
|
listeners.handler = handler;
|
||||||
|
nodes = [root];
|
||||||
|
stack = [root];
|
||||||
|
listeners.root = root;
|
||||||
|
}
|
||||||
|
function AppendChildren(id, many){
|
||||||
|
root = nodes[id];
|
||||||
|
els = stack.splice(stack.length-many);
|
||||||
|
for (k = 0; k < many; k++) {
|
||||||
|
root.appendChild(els[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const bool_attrs = {
|
||||||
|
allowfullscreen: true,
|
||||||
|
allowpaymentrequest: true,
|
||||||
|
async: true,
|
||||||
|
autofocus: true,
|
||||||
|
autoplay: true,
|
||||||
|
checked: true,
|
||||||
|
controls: true,
|
||||||
|
default: true,
|
||||||
|
defer: true,
|
||||||
|
disabled: true,
|
||||||
|
formnovalidate: true,
|
||||||
|
hidden: true,
|
||||||
|
ismap: true,
|
||||||
|
itemscope: true,
|
||||||
|
loop: true,
|
||||||
|
multiple: true,
|
||||||
|
muted: true,
|
||||||
|
nomodule: true,
|
||||||
|
novalidate: true,
|
||||||
|
open: true,
|
||||||
|
playsinline: true,
|
||||||
|
readonly: true,
|
||||||
|
required: true,
|
||||||
|
reversed: true,
|
||||||
|
selected: true,
|
||||||
|
truespeed: true,
|
||||||
|
};
|
||||||
|
export function create(r){d=r;c=new TextDecoder('utf-8',{fatal:true})}export function update_memory(r){m=new DataView(r.buffer)}export function set_buffer(b){m=new DataView(b)}export function run(){t=m.getUint8(d,true);if(t&1){ls=m.getUint32(d+1,true)}p=ls;if(t&2){lss=m.getUint32(d+5,true)}if(t&4){sl=m.getUint32(d+9,true);if(t&8){sp=lss;s="";e=sp+(sl/4|0)*4;while(sp<e){t=m.getUint32(sp,true);s+=String.fromCharCode(t&255,(t&65280)>>8,(t&16711680)>>16,t>>24);sp+=4}while(sp<lss+sl){s+=String.fromCharCode(m.getUint8(sp++));}}else{s=c.decode(new DataView(m.buffer,lss,sl))}sp=0}for(;;){op=m.getUint32(p,true);p+=4;z=0;while(z++<4){switch(op&255){case 0:{AppendChildren(root, stack.length-1);}break;case 1:{stack.push(nodes[m.getUint32(p,true)]);}p+=4;break;case 2:id=m.getUint32(p,true);p += 4;{AppendChildren(id, m.getUint32(p,true));}p+=4;break;case 3:{stack.pop();}break;case 4:id=m.getUint32(p,true);p += 4;{root = nodes[id]; els = stack.splice(stack.length-m.getUint32(p,true)); if (root.listening) { listeners.removeAllNonBubbling(root); } root.replaceWith(...els);}p+=4;break;case 5:id=m.getUint32(p,true);p += 4;{nodes[id].after(...stack.splice(stack.length-m.getUint32(p,true)));}p+=4;break;case 6:id=m.getUint32(p,true);p += 4;{nodes[id].before(...stack.splice(stack.length-m.getUint32(p,true)));}p+=4;break;case 7:{node = nodes[m.getUint32(p,true)]; if (node !== undefined) { if (node.listening) { listeners.removeAllNonBubbling(node); } node.remove(); }}p+=4;break;case 8:{stack.push(document.createTextNode(s.substring(sp,sp+=m.getUint32(p,true))));}p+=4;break;case 9:text=s.substring(sp,sp+=m.getUint32(p,true));p += 4;{node = document.createTextNode(text); nodes[m.getUint32(p,true)] = node; stack.push(node);}p+=4;break;case 10:{node = document.createElement('pre'); node.hidden = true; stack.push(node); nodes[m.getUint32(p,true)] = node;}p+=4;break;case 11:id=m.getUint32(p,true);p += 4;i=m.getUint32(p,true);if((i&128)!=0){event_name=s.substring(sp,sp+=(i>>>8)&255);evt[i&127]=event_name;}else{event_name=evt[i&127];}node = nodes[id]; if(node.listening){node.listening += 1;}else{node.listening = 1;} node.setAttribute('data-dioxus-id', `${id}`); listeners.create(event_name, node, (i>>>16)&255);p+=3;break;case 12:i=m.getUint32(p,true);p += 3;if((i&128)!=0){event_name=s.substring(sp,sp+=(i>>>8)&255);evt[i&127]=event_name;}else{event_name=evt[i&127];}bubbles=(i>>>16)&255;{node = nodes[m.getUint32(p,true)]; node.listening -= 1; node.removeAttribute('data-dioxus-id'); listeners.remove(node, event_name, bubbles);}p+=4;break;case 13:id=m.getUint32(p,true);p += 4;{nodes[id].textContent = s.substring(sp,sp+=m.getUint32(p,true));}p+=4;break;case 14:i=m.getUint32(p,true);p += 4;if((i&128)!=0){ns=s.substring(sp,sp+=(i>>>8)&255);ns_cache[i&127]=ns;}else{ns=ns_cache[i&127];}if((i&8388608)!=0){field=s.substring(sp,sp+=i>>>24);attr[(i>>>16)&127]=field;}else{field=attr[(i>>>16)&127];}id=m.getUint32(p,true);p += 4;{node = nodes[id]; SetAttributeInner(node, field, s.substring(sp,sp+=m.getUint32(p,true)), ns);}p+=4;break;case 15:i=m.getUint32(p,true);p += 4;if((i&128)!=0){ns=s.substring(sp,sp+=(i>>>8)&255);ns_cache[i&127]=ns;}else{ns=ns_cache[i&127];}if((i&8388608)!=0){field=s.substring(sp,sp+=i>>>24);attr[(i>>>16)&127]=field;}else{field=attr[(i>>>16)&127];}{name = field;
|
||||||
|
node = nodes[m.getUint32(p,true)];
|
||||||
|
if (ns == "style") {
|
||||||
|
node.style.removeProperty(name);
|
||||||
|
} else if (ns !== null && ns !== undefined && ns !== "") {
|
||||||
|
node.removeAttributeNS(ns, name);
|
||||||
|
} else if (name === "value") {
|
||||||
|
node.value = "";
|
||||||
|
} else if (name === "checked") {
|
||||||
|
node.checked = false;
|
||||||
|
} else if (name === "selected") {
|
||||||
|
node.selected = false;
|
||||||
|
} else if (name === "dangerous_inner_html") {
|
||||||
|
node.innerHTML = "";
|
||||||
|
} else {
|
||||||
|
node.removeAttribute(name);
|
||||||
|
}}p+=4;break;case 16:len=m.getUint8(p,true);p += 1;ptr=m.getUint32(p,true);p += 4;{nodes[m.getUint32(p,true)] = LoadChild(ptr, len);}p+=4;break;case 17:len=m.getUint8(p,true);p += 1;value=s.substring(sp,sp+=m.getUint32(p,true));p += 4;ptr=m.getUint32(p,true);p += 4;{
|
||||||
|
node = LoadChild(ptr, len);
|
||||||
|
if (node.nodeType == Node.TEXT_NODE) {
|
||||||
|
node.textContent = value;
|
||||||
|
} else {
|
||||||
|
let text = document.createTextNode(value);
|
||||||
|
node.replaceWith(text);
|
||||||
|
node = text;
|
||||||
|
}
|
||||||
|
nodes[m.getUint32(p,true)] = node;
|
||||||
|
}p+=4;break;case 18:len=m.getUint8(p,true);p += 1;ptr=m.getUint32(p,true);p += 4;{els = stack.splice(stack.length - m.getUint32(p,true)); node = LoadChild(ptr, len); node.replaceWith(...els);}p+=4;break;case 19:tmpl_id=m.getUint32(p,true);p += 4;index=m.getUint32(p,true);p += 4;{node = templates[tmpl_id][index].cloneNode(true); nodes[m.getUint32(p,true)] = node; stack.push(node);}p+=4;break;case 20:return true;}op>>>=8;}}}
|
46
ui/dist/index.html
vendored
Normal file
46
ui/dist/index.html
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>dioxus | ⛺</title>
|
||||||
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="main"></div>
|
||||||
|
<script type="module">
|
||||||
|
import init from "/./assets/dioxus/dioxus.js";
|
||||||
|
init("/./assets/dioxus/dioxus_bg.wasm").then(wasm => {
|
||||||
|
if (wasm.__wbindgen_start == undefined) {
|
||||||
|
wasm.main();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html><script>// Dioxus-CLI
|
||||||
|
// https://github.com/DioxusLabs/cli
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
var url = protocol + '//' + window.location.host + '/_dioxus/ws';
|
||||||
|
var poll_interval = 8080;
|
||||||
|
var reload_upon_connect = () => {
|
||||||
|
window.setTimeout(
|
||||||
|
() => {
|
||||||
|
var ws = new WebSocket(url);
|
||||||
|
ws.onopen = () => window.location.reload();
|
||||||
|
ws.onclose = reload_upon_connect;
|
||||||
|
},
|
||||||
|
poll_interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
var ws = new WebSocket(url);
|
||||||
|
ws.onmessage = (ev) => {
|
||||||
|
if (ev.data == "reload") {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onclose = reload_upon_connect;
|
||||||
|
})()</script>
|
19
ui/src/main.rs
Normal file
19
ui/src/main.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#![allow(non_snake_case)]
|
||||||
|
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// launch the web app
|
||||||
|
dioxus_web::launch(App);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a component that renders a div with the text "Hello, world!"
|
||||||
|
fn App(cx: Scope) -> Element {
|
||||||
|
let mut count = use_state(cx, || 0);
|
||||||
|
|
||||||
|
cx.render(rsx! {
|
||||||
|
h1 { "High-Five counter: {count}" }
|
||||||
|
button { onclick: move |_| count += 1, "Up high!" }
|
||||||
|
button { onclick: move |_| count -= 1, "Down low!" }
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user