this repo has no description

errrerererere

+354 -22
+25
rust_demo_app/Cargo.lock
··· 86 "atrium-common", 87 "atrium-identity", 88 "atrium-oauth", 89 "console_error_panic_hook", 90 "gloo 0.11.0", 91 "gloo-net 0.5.0", 92 "gloo-utils 0.2.0", 93 "indexed_db_futures", 94 "js-sys", 95 "log", 96 "serde", 97 "serde-wasm-bindgen 0.6.5", 98 "serde_html_form", 99 "serde_json", 100 "serde_qs", 101 "types_demo", ··· 354 version = "1.1.9" 355 source = "registry+https://github.com/rust-lang/crates.io-index" 356 checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" 357 dependencies = [ 358 "serde", 359 ] ··· 3050 ] 3051 3052 [[package]] 3053 name = "serde_json" 3054 version = "1.0.140" 3055 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3690 name = "types_demo" 3691 version = "0.1.0" 3692 dependencies = [ 3693 "schemars", 3694 "serde", 3695 ]
··· 86 "atrium-common", 87 "atrium-identity", 88 "atrium-oauth", 89 + "chrono", 90 "console_error_panic_hook", 91 "gloo 0.11.0", 92 "gloo-net 0.5.0", 93 "gloo-utils 0.2.0", 94 "indexed_db_futures", 95 + "ipld-core", 96 "js-sys", 97 "log", 98 "serde", 99 "serde-wasm-bindgen 0.6.5", 100 "serde_html_form", 101 + "serde_ipld_dagcbor", 102 "serde_json", 103 "serde_qs", 104 "types_demo", ··· 357 version = "1.1.9" 358 source = "registry+https://github.com/rust-lang/crates.io-index" 359 checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" 360 + dependencies = [ 361 + "serde", 362 + ] 363 + 364 + [[package]] 365 + name = "cbor4ii" 366 + version = "0.2.14" 367 + source = "registry+https://github.com/rust-lang/crates.io-index" 368 + checksum = "b544cf8c89359205f4f990d0e6f3828db42df85b5dac95d09157a250eb0749c4" 369 dependencies = [ 370 "serde", 371 ] ··· 3062 ] 3063 3064 [[package]] 3065 + name = "serde_ipld_dagcbor" 3066 + version = "0.6.3" 3067 + source = "registry+https://github.com/rust-lang/crates.io-index" 3068 + checksum = "99600723cf53fb000a66175555098db7e75217c415bdd9a16a65d52a19dcc4fc" 3069 + dependencies = [ 3070 + "cbor4ii", 3071 + "ipld-core", 3072 + "scopeguard", 3073 + "serde", 3074 + ] 3075 + 3076 + [[package]] 3077 name = "serde_json" 3078 version = "1.0.140" 3079 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3714 name = "types_demo" 3715 version = "0.1.0" 3716 dependencies = [ 3717 + "atrium-api", 3718 "schemars", 3719 "serde", 3720 ]
+7 -1
rust_demo_app/api_demo/src/main.rs
··· 2 endpoint, ApiDescription, ClientErrorStatusCode, ConfigDropshot, ConfigLogging, 3 ConfigLoggingLevel, HttpError, HttpResponseOk, HttpServerStarter, RequestContext, 4 }; 5 6 use std::sync::{Arc, Mutex}; 7 - use types_demo::CounterState; 8 9 #[derive(Clone)] 10 struct AppState {
··· 2 endpoint, ApiDescription, ClientErrorStatusCode, ConfigDropshot, ConfigLogging, 3 ConfigLoggingLevel, HttpError, HttpResponseOk, HttpServerStarter, RequestContext, 4 }; 5 + use schemars::JsonSchema; 6 7 use std::sync::{Arc, Mutex}; 8 + 9 + // Define a simple CounterState struct for the API to use 10 + #[derive(serde::Serialize, Clone, Debug, JsonSchema)] 11 + struct CounterState { 12 + value: i32, 13 + } 14 15 #[derive(Clone)] 16 struct AppState {
+4
rust_demo_app/app_demo/Cargo.toml
··· 53 yew-hooks = "0.3.3" 54 55 gloo-utils = "0.2.0"
··· 53 yew-hooks = "0.3.3" 54 55 gloo-utils = "0.2.0" 56 + 57 + chrono = "0.4" 58 + serde_ipld_dagcbor = { version = "0.6.0", features = ["std"] } 59 + ipld-core = { version = "0.4.1", features = ["std"] }
+30
rust_demo_app/app_demo/src/app_routes.rs
···
··· 1 + use yew::prelude::*; 2 + use yew_router::prelude::*; 3 + 4 + // Import page components (adjust path if they move from main.rs) 5 + use crate::pages::callback::CallbackPage; 6 + use crate::pages::home::HomePage; 7 + use crate::pages::login::LoginPage; 8 + 9 + // Define the routes 10 + #[derive(Clone, Routable, PartialEq)] 11 + pub enum AppRoute { 12 + #[at("/")] 13 + Home, 14 + #[at("/login")] 15 + Login, 16 + #[at("/oauth/callback")] 17 + Callback, 18 + #[not_found] 19 + #[at("/404")] 20 + NotFound, 21 + } 22 + 23 + pub fn switch(routes: AppRoute) -> Html { 24 + match routes { 25 + AppRoute::Home => html! { <HomePage /> }, 26 + AppRoute::Login => html! { <LoginPage /> }, 27 + AppRoute::Callback => html! { <CallbackPage /> }, 28 + AppRoute::NotFound => html! { <h1>{ "404 Not Found" }</h1> }, 29 + } 30 + }
+11
rust_demo_app/app_demo/src/auth_store.rs
···
··· 1 + // Placeholder for auth_store module 2 + 3 + use atrium_api::types::string::Did; 4 + use serde::{Deserialize, Serialize}; 5 + use yewdux::prelude::*; 6 + 7 + // Yewdux Store for Authentication State 8 + #[derive(Default, Clone, PartialEq, Serialize, Deserialize, Store)] // Added Serialize/Deserialize based on at_2048 9 + pub struct AuthStore { 10 + pub did: Option<Did>, 11 + }
+3
rust_demo_app/app_demo/src/components.rs
···
··· 1 + // Placeholder for components module 2 + 3 + // Module for shared UI components (currently empty)
+12 -2
rust_demo_app/app_demo/src/main.rs
··· 3 use log::info; 4 use serde_json::Value; 5 use std::rc::Rc; 6 - use types_demo::CounterState; 7 use wasm_bindgen_futures::spawn_local; 8 use yew::prelude::*; 9 use yew_hooks::use_effect_once; ··· 13 // Import the Store trait 14 use atrium_common::store::Store; 15 16 mod atrium_stores; 17 mod idb; 18 mod oauth_client; 19 20 // Add pages module 21 - pub mod pages; 22 23 // Import page components 24 use crate::pages::callback::CallbackPage; ··· 32 use atrium_api::types::string::Did; 33 34 const API_ROOT: &str = ""; 35 36 // Define our application state 37 #[derive(Clone, Debug, PartialEq, Default)]
··· 3 use log::info; 4 use serde_json::Value; 5 use std::rc::Rc; 6 use wasm_bindgen_futures::spawn_local; 7 use yew::prelude::*; 8 use yew_hooks::use_effect_once; ··· 12 // Import the Store trait 13 use atrium_common::store::Store; 14 15 + mod app_routes; 16 mod atrium_stores; 17 + mod auth_store; 18 + mod components; 19 mod idb; 20 mod oauth_client; 21 + mod pages; 22 23 // Add pages module 24 + // pub mod pages; // Remove this duplicate 25 26 // Import page components 27 use crate::pages::callback::CallbackPage; ··· 35 use atrium_api::types::string::Did; 36 37 const API_ROOT: &str = ""; 38 + 39 + // Define CounterState struct for local use in app_demo 40 + #[derive(Clone, Debug, PartialEq, Default, serde::Deserialize, serde::Serialize)] 41 + pub struct CounterState { 42 + // Made pub for potential use in fetch_counter etc. if they move 43 + pub value: i32, 44 + } 45 46 // Define our application state 47 #[derive(Clone, Debug, PartialEq, Default)]
+193 -6
rust_demo_app/app_demo/src/pages/home.rs
··· 1 use crate::{ 2 fetch_counter, 3 - increment_counter, 4 reset_counter, 5 AppState, // Import reducer state 6 AuthStore, 7 }; 8 use yew::prelude::*; 9 - use yewdux::prelude::*; 10 11 #[function_component(HomePage)] 12 pub fn home_page() -> Html { ··· 14 let state = use_reducer(AppState::default); 15 let (auth_store, _) = use_store::<AuthStore>(); // Get auth state to check if logged in 16 17 // Effect to load counter on mount 18 { 19 let state = state.clone(); ··· 24 } 25 26 let on_increment = { 27 - let state = state.clone(); 28 Callback::from(move |_| { 29 - increment_counter(state.clone()); 30 }) 31 }; 32 ··· 55 } else if let Some(counter_data) = &state.counter { 56 <p class="text-3xl font-bold">{ counter_data.value }</p> 57 <div class="mt-4 space-x-2"> 58 - <button onclick={on_increment} class="btn btn-primary">{ "Increment" }</button> 59 - <button onclick={on_reset} class="btn btn-secondary">{ "Reset" }</button> 60 </div> 61 } else { 62 <p>{ "Counter state not available." }</p>
··· 1 use crate::{ 2 fetch_counter, 3 + oauth_client::get_oauth_client, 4 reset_counter, 5 AppState, // Import reducer state 6 AuthStore, 7 }; 8 + use atrium_api::agent::Agent; // Use Agent for XRPC calls 9 + use atrium_api::com::atproto::repo::{ 10 + get_record::ParametersData as GetRecordParametersData, 11 + put_record::InputData as PutRecordInputData, 12 + }; // Import specific data structs 13 + use atrium_api::types::string::{/* AtIdentifier, // Removed */ Datetime, Nsid, RecordKey}; 14 + use atrium_api::types::{DataModel, /* Object, // Removed */ Unknown}; 15 + use ipld_core::serde::to_ipld; 16 + use serde_json; // Added for record serialization 17 + use std::convert::TryFrom; // Added for try_from conversions 18 + use types_demo::dev::alternatebuild::rust_demo::count::{ 19 + /* Record as CountRecord, // Removed */ RecordData as CountRecordData, 20 + }; 21 use yew::prelude::*; 22 + use yewdux::prelude::*; // Added for serde_ipld_dagcbor::to_ipld 23 24 #[function_component(HomePage)] 25 pub fn home_page() -> Html { ··· 27 let state = use_reducer(AppState::default); 28 let (auth_store, _) = use_store::<AuthStore>(); // Get auth state to check if logged in 29 30 + // ATProto specific state 31 + let has_counted_record = use_state(|| false); 32 + let is_checking_record = use_state(|| true); 33 + let increment_button_disabled = use_state(|| true); 34 + let increment_button_text = use_state(|| "Increment".to_string()); 35 + 36 + // Effect to check for existing ATProto record 37 + { 38 + let auth_store_for_effect = auth_store.clone(); 39 + let has_counted_record = has_counted_record.clone(); 40 + let is_checking_record = is_checking_record.clone(); 41 + let increment_button_disabled = increment_button_disabled.clone(); 42 + let increment_button_text = increment_button_text.clone(); 43 + 44 + use_effect_with(auth_store_for_effect.did.clone(), move |did_option| { 45 + let did_option = did_option.clone(); 46 + let has_counted_record = has_counted_record.clone(); 47 + let is_checking_record = is_checking_record.clone(); 48 + let increment_button_disabled = increment_button_disabled.clone(); 49 + let increment_button_text = increment_button_text.clone(); 50 + 51 + is_checking_record.set(true); 52 + 53 + if let Some(did) = did_option.clone() { 54 + wasm_bindgen_futures::spawn_local(async move { 55 + let oauth_client = get_oauth_client().await; 56 + match oauth_client.restore(&did).await { 57 + Ok(session) => { 58 + let agent = Agent::new(session); 59 + let collection = 60 + Nsid::new(String::from("dev.alternatebuild.rust_demo.count")) 61 + .expect("Static NSID failed to parse"); 62 + let rkey = RecordKey::new(String::from("self")) 63 + .expect("Static RecordKey failed to parse"); 64 + let params = GetRecordParametersData { 65 + repo: did.into(), 66 + collection, 67 + rkey, 68 + cid: None, 69 + } 70 + .into(); 71 + 72 + match agent.api.com.atproto.repo.get_record(params).await { 73 + Ok(_) => { 74 + has_counted_record.set(true); 75 + increment_button_text.set("Already Counted".to_string()); 76 + increment_button_disabled.set(true); 77 + } 78 + Err(e) => { 79 + // Check if error is RecordNotFound 80 + // (Need to inspect the specific error type `e` from `get_record` result) 81 + // For now, assume any error means no record or inaccessible 82 + has_counted_record.set(false); 83 + increment_button_text.set("Increment".to_string()); 84 + increment_button_disabled.set(false); 85 + log::debug!("get_record failed (likely not found): {:?}", e); 86 + } 87 + } 88 + } 89 + Err(_e) => { 90 + has_counted_record.set(false); 91 + increment_button_text.set("Session error. Try login.".to_string()); 92 + increment_button_disabled.set(true); 93 + } 94 + } 95 + is_checking_record.set(false); 96 + }); 97 + } else { 98 + has_counted_record.set(false); 99 + increment_button_disabled.set(true); 100 + increment_button_text.set("Login to Count".to_string()); 101 + is_checking_record.set(false); 102 + } 103 + || () 104 + }); 105 + } 106 + 107 // Effect to load counter on mount 108 { 109 let state = state.clone(); ··· 114 } 115 116 let on_increment = { 117 + let auth_store_for_increment = auth_store.clone(); 118 + let has_counted_record_for_increment = has_counted_record.clone(); 119 + let increment_button_disabled_for_increment = increment_button_disabled.clone(); 120 + let increment_button_text_for_increment = increment_button_text.clone(); 121 + 122 Callback::from(move |_| { 123 + let auth_store = auth_store_for_increment.clone(); 124 + let has_counted_record = has_counted_record_for_increment.clone(); 125 + let increment_button_disabled = increment_button_disabled_for_increment.clone(); 126 + let increment_button_text = increment_button_text_for_increment.clone(); 127 + 128 + if !*has_counted_record && auth_store.did.is_some() { 129 + let did_for_put = auth_store.did.clone().unwrap(); 130 + wasm_bindgen_futures::spawn_local(async move { 131 + let oauth_client = get_oauth_client().await; 132 + match oauth_client.restore(&did_for_put).await { 133 + Ok(session) => { 134 + let agent = Agent::new(session); 135 + let record_body = CountRecordData { 136 + created_at: Datetime::now(), 137 + }; 138 + // Serialize record_body to Unknown via Ipld & DataModel 139 + let record_unknown = match serde_json::to_value(record_body) { 140 + Ok(value) => match to_ipld(&value) { 141 + Ok(ipld) => match DataModel::try_from(ipld) { 142 + Ok(data_model) => Unknown::Other(data_model), // Construct Unknown 143 + Err(e) => { 144 + log::error!( 145 + "Failed to convert Ipld to DataModel: {:?}", 146 + e 147 + ); 148 + increment_button_text 149 + .set("DataModel Error".to_string()); 150 + increment_button_disabled.set(true); 151 + return; 152 + } 153 + }, 154 + Err(e) => { 155 + log::error!( 156 + "Failed to convert serde_json::Value to Ipld: {:?}", 157 + e 158 + ); 159 + increment_button_text 160 + .set("Ipld Conversion Error".to_string()); 161 + increment_button_disabled.set(true); 162 + return; 163 + } 164 + }, 165 + Err(e) => { 166 + log::error!("Failed to serialize record body: {:?}", e); 167 + increment_button_text.set("Serialization Error".to_string()); 168 + increment_button_disabled.set(true); 169 + return; 170 + } 171 + }; 172 + 173 + let collection = 174 + Nsid::new(String::from("dev.alternatebuild.rust_demo.count")) 175 + .expect("Static NSID failed to parse"); 176 + let rkey = RecordKey::new(String::from("self")) 177 + .expect("Static RecordKey failed to parse"); 178 + let input = PutRecordInputData { 179 + repo: did_for_put.into(), 180 + collection, 181 + rkey, 182 + validate: Some(true), 183 + record: record_unknown, 184 + swap_commit: None, 185 + swap_record: None, 186 + } 187 + .into(); 188 + 189 + match agent.api.com.atproto.repo.put_record(input).await { 190 + // Pass input struct 191 + Ok(_) => { 192 + has_counted_record.set(true); 193 + increment_button_disabled.set(true); 194 + increment_button_text.set("Counted!".to_string()); 195 + } 196 + Err(_e) => { 197 + increment_button_text 198 + .set("Failed to count. Retry?".to_string()); 199 + increment_button_disabled.set(false); 200 + log::error!("put_record failed: {:?}", _e); 201 + } 202 + } 203 + } 204 + Err(_e) => { 205 + increment_button_text.set("Session error. Try login.".to_string()); 206 + increment_button_disabled.set(true); 207 + } 208 + } 209 + }); 210 + } 211 }) 212 }; 213 ··· 236 } else if let Some(counter_data) = &state.counter { 237 <p class="text-3xl font-bold">{ counter_data.value }</p> 238 <div class="mt-4 space-x-2"> 239 + if *is_checking_record { 240 + <p>{ "Checking your ATProto record..." }</p> 241 + } else { 242 + <button onclick={on_increment} disabled={*increment_button_disabled} class="btn btn-primary"> 243 + { &*increment_button_text } 244 + </button> 245 + } 246 + <button onclick={on_reset} class="btn btn-secondary">{ "Reset Shared Counter" }</button> 247 </div> 248 } else { 249 <p>{ "Counter state not available." }</p>
+27 -3
rust_demo_app/justfile
··· 9 # Run both API and frontend servers 10 dev: 11 #!/usr/bin/env sh 12 - trap 'kill $(jobs -p)' EXIT 13 - just api & 14 - just app
··· 9 # Run both API and frontend servers 10 dev: 11 #!/usr/bin/env sh 12 + # Start API in background and get its PID 13 + just api & 14 + API_PID=$! 15 + echo "API server started in background (PID: $API_PID)" 16 + 17 + # Define cleanup function 18 + cleanup() { 19 + echo "Shutting down API server (PID: $API_PID)..." 20 + # Use kill -0 to check if process exists before trying to kill 21 + if kill -0 $API_PID 2>/dev/null; then 22 + kill $API_PID 23 + wait $API_PID 2>/dev/null # Wait briefly for it to exit 24 + else 25 + echo "API server (PID: $API_PID) not found." 26 + fi 27 + # Exit script cleanly 28 + exit 0 29 + } 30 + 31 + # Trap signals to run cleanup 32 + trap cleanup INT TERM EXIT 33 + 34 + # Start frontend in foreground (script will block here until app stops) 35 + just app 36 + 37 + # Fallback cleanup if app exits normally (trap EXIT should handle Ctrl+C) 38 + cleanup
+1
rust_demo_app/types_demo/Cargo.toml
··· 6 [dependencies] 7 schemars = { version = "0.8.22", features = ["derive"] } 8 serde = { version = "1.0", features = ["derive"] }
··· 6 [dependencies] 7 schemars = { version = "0.8.22", features = ["derive"] } 8 serde = { version = "1.0", features = ["derive"] } 9 + atrium-api = { git = "https://github.com/atrium-rs/atrium" }
+1 -1
rust_demo_app/types_demo/lexicons/dev/alternatebuild/rustDemo/count.json rust_demo_app/types_demo/lexicons/dev/alternatebuild/rust_demo/count.json
··· 1 { 2 "lexicon": 1, 3 - "id": "dev.alternatebuild.rustDemo.count", 4 "defs": { 5 "main": { 6 "type": "record",
··· 1 { 2 "lexicon": 1, 3 + "id": "dev.alternatebuild.rust_demo.count", 4 "defs": { 5 "main": { 6 "type": "record",
+3
rust_demo_app/types_demo/src/dev.rs
···
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `dev` namespace. 3 + pub mod alternatebuild;
+4
rust_demo_app/types_demo/src/dev/alternatebuild.rs
···
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `dev.alternatebuild` namespace. 3 + 4 + pub mod rust_demo;
+9
rust_demo_app/types_demo/src/dev/alternatebuild/rust_demo.rs
···
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `dev.alternatebuild.rust_demo` namespace. 3 + pub mod count; 4 + #[derive(Debug)] 5 + pub struct Count; 6 + impl atrium_api::types::Collection for Count { 7 + const NSID: &'static str = "dev.alternatebuild.rust_demo.count"; 8 + type Record = count::Record; 9 + }
+14
rust_demo_app/types_demo/src/dev/alternatebuild/rust_demo/count.rs
···
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + //!Definitions for the `dev.alternatebuild.rust_demo.count` namespace. 3 + use atrium_api::types::TryFromUnknown; 4 + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 5 + #[serde(rename_all = "camelCase")] 6 + pub struct RecordData { 7 + pub created_at: atrium_api::types::string::Datetime, 8 + } 9 + pub type Record = atrium_api::types::Object<RecordData>; 10 + impl From<atrium_api::types::Unknown> for RecordData { 11 + fn from(value: atrium_api::types::Unknown) -> Self { 12 + Self::try_from_unknown(value).unwrap() 13 + } 14 + }
+3 -7
rust_demo_app/types_demo/src/lib.rs
··· 1 - use schemars::JsonSchema; 2 - use serde::{Deserialize, Serialize}; 3 - 4 - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] 5 - pub struct CounterState { 6 - pub value: i32, 7 - }
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 + pub mod record; 3 + pub mod dev;
+7 -2
rust_demo_app/types_demo/src/record.rs
··· 1 - // @generated - This file is generated by atrium-codegen. DO NOT EDIT. 2 //!A collection of known record types. 3 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 4 #[serde(tag = "$type")] 5 pub enum KnownRecord { 6 - #[serde(rename = "dev.alternatebuild.rustDemo.count")] 7 DevAlternatebuildRustDemoCount( 8 Box<crate::dev::alternatebuild::rust_demo::count::Record>, 9 ), ··· 20 KnownRecord::DevAlternatebuildRustDemoCount(Box::new(record_data.into())) 21 } 22 }
··· 1 + // @generated - This file is generated by esquema-codegen (forked from atrium-codegen). DO NOT EDIT. 2 //!A collection of known record types. 3 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] 4 #[serde(tag = "$type")] 5 pub enum KnownRecord { 6 + #[serde(rename = "dev.alternatebuild.rust_demo.count")] 7 DevAlternatebuildRustDemoCount( 8 Box<crate::dev::alternatebuild::rust_demo::count::Record>, 9 ), ··· 20 KnownRecord::DevAlternatebuildRustDemoCount(Box::new(record_data.into())) 21 } 22 } 23 + impl Into<atrium_api::types::Unknown> for KnownRecord { 24 + fn into(self) -> atrium_api::types::Unknown { 25 + atrium_api::types::TryIntoUnknown::try_into_unknown(&self).unwrap() 26 + } 27 + }