Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.DS_Store
/generated/
/target/
/web-distribution/
/web-distribution/
.codex/
99 changes: 85 additions & 14 deletions definy-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,19 @@ fn read_ssr_state() -> Option<(
>,
)>,
bool,
Vec<Vec<u8>>,
)> {
let text = SSR_INITIAL_STATE_TEXT.as_ref()?.to_string();
let decoded = definy_ui::decode_ssr_state(text.as_str())?;
Some(
(
decoded
.event_binaries
.into_iter()
.map(|bytes| {
let hash: [u8; 32] = <sha2::Sha256 as sha2::Digest>::digest(&bytes).into();
(hash, definy_event::verify_and_deserialize(&bytes))
})
.collect(),
decoded.has_more,
),
)
let event_binaries = decoded.event_binaries;
let events = event_binaries
.iter()
.map(|bytes| {
let hash: [u8; 32] = <sha2::Sha256 as sha2::Digest>::digest(bytes).into();
(hash, definy_event::verify_and_deserialize(bytes))
})
.collect();
Some((events, decoded.has_more, event_binaries))
}

impl narumincho_vdom_client::App<AppState> for DefinyApp {
Expand All @@ -81,6 +78,7 @@ impl narumincho_vdom_client::App<AppState> for DefinyApp {
) -> AppState {
let fire = std::rc::Rc::clone(fire);
let ssr_state = read_ssr_state();
let ssr_event_binaries = ssr_state.as_ref().map(|(_, _, binaries)| binaries.clone());
let has_ssr_events = ssr_state.is_some();

let fire_for_keydown = std::rc::Rc::clone(&fire);
Expand Down Expand Up @@ -113,7 +111,62 @@ impl narumincho_vdom_client::App<AppState> for DefinyApp {

let filter_for_fetch = definy_ui::event_filter_from_query_str(&filter_query);
wasm_bindgen_futures::spawn_local(async move {
if let Some(ssr_event_binaries) = ssr_event_binaries {
let event_pairs = ssr_event_binaries
.into_iter()
.map(|bytes| {
let hash: [u8; 32] =
<sha2::Sha256 as sha2::Digest>::digest(&bytes).into();
(hash, bytes)
})
.collect::<Vec<_>>();
let _ = definy_ui::indexed_db::store_events(&event_pairs).await;
}
if !has_ssr_events {
if let Ok(cached_event_binaries) =
definy_ui::indexed_db::load_event_binaries().await
{
let mut cached_events = cached_event_binaries
.into_iter()
.map(|bytes| {
let hash: [u8; 32] =
<sha2::Sha256 as sha2::Digest>::digest(&bytes).into();
let event = definy_event::verify_and_deserialize(&bytes);
(hash, event)
})
.collect::<Vec<_>>();
cached_events.sort_by(|a, b| {
let a_time = match &a.1 {
Ok((_, event)) => event.time,
Err(_) => chrono::DateTime::<chrono::Utc>::MIN_UTC,
};
let b_time = match &b.1 {
Ok((_, event)) => event.time,
Err(_) => chrono::DateTime::<chrono::Utc>::MIN_UTC,
};
b_time.cmp(&a_time)
});
fire(Box::new(move |state| {
let mut event_cache = state.event_cache.clone();
let mut event_hashes = Vec::new();
for (hash, event) in &cached_events {
event_cache.insert(*hash, event.clone());
event_hashes.push(*hash);
}
AppState {
event_cache,
event_list_state: definy_ui::EventListState {
event_hashes,
current_offset: 0,
page_size: 20,
is_loading: true,
has_more: state.event_list_state.has_more,
filter_event_type: state.event_list_state.filter_event_type,
},
..state.clone()
}
}));
}
let events = definy_ui::fetch::get_events(
filter_for_fetch,
Some(20),
Expand Down Expand Up @@ -149,6 +202,23 @@ impl narumincho_vdom_client::App<AppState> for DefinyApp {
..state.clone()
}));
}
let local_events = definy_ui::indexed_db::load_event_records().await;
fire(Box::new(move |state| {
let mut next = state.clone();
match local_events {
Ok(records) => {
definy_ui::replace_local_event_records(&mut next, records);
next.local_event_queue.is_loading = false;
next.local_event_queue.last_error = None;
}
Err(error) => {
next.local_event_queue.is_loading = false;
next.local_event_queue.last_error =
Some(format!("Failed to load local events: {error:?}"));
}
}
next
}));
});

let location = {
Expand All @@ -164,7 +234,8 @@ impl narumincho_vdom_client::App<AppState> for DefinyApp {
definy_ui::Location::from_url(&pathname)
};

let (events, is_loading, has_more) = if let Some((ssr_events, has_more)) = ssr_state {
let (events, is_loading, has_more) =
if let Some((ssr_events, has_more, _)) = ssr_state {
// SSRが送ってきた状態をそのまま採用
(ssr_events, false, has_more)
} else {
Expand Down
9 changes: 9 additions & 0 deletions definy-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ web-sys = { version = "0.3.85", features = [
"HtmlTextAreaElement",
"Navigator",
"Clipboard",
"DomStringList",
"IdbDatabase",
"IdbFactory",
"IdbObjectStore",
"IdbOpenDbRequest",
"IdbRequest",
"IdbTransaction",
"IdbTransactionMode",
"IdbVersionChangeEvent",
"Request",
"RequestInit",
"Response",
Expand Down
48 changes: 22 additions & 26 deletions definy-ui/main.css
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');

:root {
--background: #0a1118;
--surface: rgba(16, 26, 38, 0.74);
--surface-hover: rgba(26, 39, 54, 0.9);
--surface-opaque: #121f2d;
--background: #10161b;
--surface: rgba(20, 28, 35, 0.88);
--surface-hover: rgba(28, 38, 46, 0.92);
--surface-opaque: #162129;

--primary-gradient: linear-gradient(135deg, #0ea5e9 0%, #22c55e 100%);
--primary-hover-gradient: linear-gradient(135deg, #38bdf8 0%, #4ade80 100%);
--primary: #38bdf8;
--primary-content: #f8fafc;
--primary-gradient: none;
--primary-hover-gradient: none;
--primary: #7cc0d8;
--primary-content: #f1f6fb;

--error: #f87171;
--error-bg: rgba(248, 113, 113, 0.14);
--error: #f28b82;
--error-bg: rgba(242, 139, 130, 0.12);

--border: rgba(167, 186, 210, 0.2);
--border-strong: rgba(167, 186, 210, 0.38);
--border-focus: rgba(56, 189, 248, 0.45);
--border: rgba(160, 176, 192, 0.18);
--border-strong: rgba(160, 176, 192, 0.32);
--border-focus: rgba(124, 192, 216, 0.4);

--text: #e6eef8;
--text-secondary: #a7bad2;
--text: #e1e8ef;
--text-secondary: #a3b2c2;

--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 18px;
--radius-full: 9999px;

--shadow-sm: 0 4px 12px rgba(7, 12, 20, 0.2);
--shadow-md: 0 12px 20px rgba(7, 12, 20, 0.3);
--shadow-lg: 0 20px 32px rgba(3, 8, 16, 0.4);
--shadow-glow: 0 0 0 3px rgba(56, 189, 248, 0.16);
--shadow-sm: 0 4px 10px rgba(7, 12, 18, 0.18);
--shadow-md: 0 10px 18px rgba(7, 12, 18, 0.26);
--shadow-lg: 0 18px 28px rgba(5, 10, 16, 0.34);
--shadow-glow: 0 0 0 3px rgba(124, 192, 216, 0.14);

--glass-blur: blur(16px);
--content-max-width: 920px;
Expand All @@ -41,11 +41,7 @@

body {
background-color: var(--background);
background-image:
radial-gradient(circle at 14% 8%, rgba(14, 165, 233, 0.14) 0%, transparent 42%),
radial-gradient(circle at 88% 78%, rgba(34, 197, 94, 0.14) 0%, transparent 40%),
linear-gradient(180deg, rgba(255, 255, 255, 0.01), rgba(255, 255, 255, 0));
background-attachment: fixed;
background-image: none;
color: var(--text);
font-family: 'Outfit', system-ui, -apple-system, sans-serif;
margin: 0;
Expand Down Expand Up @@ -81,7 +77,7 @@ textarea {
}

button {
background: var(--primary-gradient);
background: var(--primary);
color: var(--primary-content);
border: 1px solid transparent;
border-radius: var(--radius-full);
Expand All @@ -98,7 +94,7 @@ button {
}

button:hover {
background: var(--primary-hover-gradient);
background: #8ad0e6;
transform: translateY(-1px);
box-shadow: 0 12px 22px rgba(14, 165, 233, 0.26);
}
Expand Down
83 changes: 54 additions & 29 deletions definy-ui/src/account_detail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub fn account_detail_view(
return state;
}
let filter = state.event_list_state.filter_event_type;
let force_offline = state.force_offline;
wasm_bindgen_futures::spawn_local(async move {
let event_binary = match definy_event::sign_and_serialize(
definy_event::event::Event {
Expand Down Expand Up @@ -107,37 +108,61 @@ pub fn account_detail_view(
}
};

if fetch::post_event(event_binary.as_slice()).await.is_ok() {
if let Ok(events) =
fetch::get_events(filter, Some(20), Some(0)).await
{
set_state_for_async(Box::new(move |state| {
let events_len = events.len();
let mut event_cache = state.event_cache.clone();
let mut event_hashes = Vec::new();
for (hash, event) in events {
event_cache.insert(hash, event);
event_hashes.push(hash);
}
AppState {
event_cache,
event_list_state: crate::EventListState {
event_hashes,
current_offset: 0,
page_size: 20,
is_loading: false,
has_more: events_len == 20,
filter_event_type: filter,
},
profile_name_input: String::new(),
..state.clone()
match fetch::post_event_with_queue(
event_binary.as_slice(),
force_offline,
)
.await
{
Ok(record) => {
let status = record.status.clone();
if status == crate::local_event::LocalEventStatus::Sent {
if let Ok(events) =
fetch::get_events(filter, Some(20), Some(0)).await
{
set_state_for_async(Box::new(move |state| {
let events_len = events.len();
let mut event_cache = state.event_cache.clone();
let mut event_hashes = Vec::new();
for (hash, event) in events {
event_cache.insert(hash, event);
event_hashes.push(hash);
}
let mut next = state.clone();
next.event_cache = event_cache;
next.event_list_state = crate::EventListState {
event_hashes,
current_offset: 0,
page_size: 20,
is_loading: false,
has_more: events_len == 20,
filter_event_type: filter,
};
next.profile_name_input = String::new();
crate::app_state::upsert_local_event_record(
&mut next,
record,
);
next
}));
}
}));
} else {
set_state_for_async(Box::new(move |state| {
let mut next = state.clone();
next.profile_name_input = String::new();
crate::app_state::upsert_local_event_record(
&mut next,
record,
);
next
}));
}
}
Err(_) => {
web_sys::console::log_1(
&"Failed to post change profile event".into(),
);
}
} else {
web_sys::console::log_1(
&"Failed to post change profile event".into(),
);
}
});
state
Expand Down
Loading