Skip to content

Commit

Permalink
fix metadata updates & check sync responses
Browse files Browse the repository at this point in the history
  • Loading branch information
littlebenlittle committed Jun 7, 2024
1 parent a8cf520 commit 0ceed58
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 124 deletions.
45 changes: 45 additions & 0 deletions ui/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,48 @@ pub use http::*;
mod mock;
#[cfg(feature = "demo")]
pub use mock::*;

use crate::{
data::{SyncResponse, ID},
log, MediaCollection,
};
use std::collections::HashMap;

pub async fn sync_local(media: MediaCollection) -> anyhow::Result<SyncResponse> {
let storage = web_sys::window().unwrap().local_storage().unwrap().unwrap();
let mut missing = HashMap::new();
// TODO this is not efficient, but not worth optimizing yet
if media.len() > 0 {
// empty storage
// localStorage **updates its keys** when an item
// is removed, so count backwards
for i in (0..storage.length().unwrap()).rev() {
let key = storage.key(i).unwrap().unwrap();
if key.starts_with("media/") {
storage.remove_item(&key).unwrap();
}
}
// populate storage from memory
for (id, val) in media.iter() {
let key = format!("media/{}", id);
let sval = serde_json::to_string(&val).unwrap();
storage.set_item(&key, &sval).unwrap();
}
} else {
// populate memory from storage
for i in 0..storage.length().unwrap() {
let key = storage.key(i).unwrap().unwrap();
if let Some(id) = key.strip_prefix("media/").map(|k| ID::from(k)) {
if media.get(&id).is_none() {
let storage_val = storage.get_item(&key).unwrap().unwrap();
let val = serde_json::from_str(&storage_val).unwrap();
missing.insert(id, val);
}
}
}
}
return Ok(SyncResponse {
missing,
unknown: vec![],
});
}
42 changes: 1 addition & 41 deletions ui/src/client/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use js_sys::wasm_bindgen::JsValue;
use serde::{Deserialize, Serialize};
use sha2::Digest;
use wasm_bindgen_futures::JsFuture;
use std::fmt;

// Origin of the current page.
#[inline]
Expand Down Expand Up @@ -48,8 +49,6 @@ enum MediaFormat {
Mp4,
}

use std::{collections::HashMap, fmt};

impl fmt::Display for MediaFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand Down Expand Up @@ -302,45 +301,6 @@ fn new_ws(ws_path: &str) -> anyhow::Result<WebSocket> {
Ok(WebSocket::open(&url)?)
}

pub async fn sync_local(media: MediaCollection) -> anyhow::Result<SyncResponse> {
let storage = web_sys::window().unwrap().local_storage().unwrap().unwrap();
let mut missing = HashMap::new();
// TODO this is not efficient, but not worth optimizing yet
if media.len() > 0 {
// empty storage
// localStorage **updates its keys** when an item
// is removed, so count backwards
for i in (0..storage.length().unwrap()).rev() {
let key = storage.key(i).unwrap().unwrap();
if key.starts_with("media/") {
storage.remove_item(&key).unwrap();
}
}
// populate storage from memory
for (id, val) in media.iter() {
let key = format!("media/{}", id);
let sval = serde_json::to_string(&val).unwrap();
storage.set_item(&key, &sval).unwrap();
}
} else {
// populate memory from storage
for i in 0..storage.length().unwrap() {
let key = storage.key(i).unwrap().unwrap();
if let Some(id) = key.strip_prefix("media/").map(|k| ID::from(k)) {
if media.get(&id).is_none() {
let storage_val = storage.get_item(&key).unwrap().unwrap();
let val = serde_json::from_str(&storage_val).unwrap();
missing.insert(id, val);
}
}
}
}
return Ok(SyncResponse {
missing,
unknown: vec![],
});
}

pub async fn sync_remote(media: MediaCollection) -> anyhow::Result<SyncResponse> {
let url = format!("{}/api/sync/media", origin());
let res = gloo_net::http::Request::post(&url)
Expand Down
9 changes: 1 addition & 8 deletions ui/src/client/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,13 @@ fn unknown(n: usize) -> Vec<ID> {
return unknown;
}

pub async fn sync_local(_media: MediaCollection) -> anyhow::Result<SyncResponse> {
pub async fn sync_remote(_media: MediaCollection) -> anyhow::Result<SyncResponse> {
Ok(crate::data::SyncResponse {
missing: missing(12),
unknown: vec![],
})
}

pub async fn sync_remote(_media: MediaCollection) -> anyhow::Result<SyncResponse> {
Ok(crate::data::SyncResponse {
missing: missing(3),
unknown: unknown(2),
})
}

// non-resumable wrapper around resumable upload
pub async fn upload<'a>(_file: &'a web_sys::File) -> anyhow::Result<()> {
anyhow::bail!("uploads not available for mock client");
Expand Down
20 changes: 19 additions & 1 deletion ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub fn App() -> impl IntoView {
// update the selected media when the selected id changes
create_effect(move |_| {
if let Some(id) = selected_id() {
log!("selected {}", id);
set_selected_media(media.get().get(&id).cloned())
}
});
Expand All @@ -79,6 +80,7 @@ pub fn App() -> impl IntoView {
create_effect(move |_| {
if let Some(m) = updated_media() {
if let Some(id) = selected_id.get_untracked() {
log!("updated {}", id);
set_media.update(|ms| {
ms.insert(id, m);
})
Expand Down Expand Up @@ -136,7 +138,23 @@ pub fn App() -> impl IntoView {
create_effect(move |_| {
sync_local_action.version().get();
if let Some(Some(res)) = sync_local_action.value().get() {
set_media.update(|media| sync(res, media))
let m = media.get_untracked();
let mut should_update = false;
for (id, v) in res.missing.iter() {
if m.get(id) != Some(v) {
should_update = true;
break;
}
}
for id in res.unknown.iter() {
if m.get(id) != None {
should_update = true;
break;
}
}
if should_update {
set_media.update(|media| sync(res, media))
}
}
});

Expand Down
116 changes: 46 additions & 70 deletions ui/src/pages/player/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,24 @@ pub fn Info() -> impl IntoView {
});
view! {
<div id="video-info">
<ClickToEdit
sig=title
children=move |tr| {
view! { <h3 on:click=move |_| tr.notify()>{title()}</h3> }
}
/>
<ClickToEdit sig=title/>

<table>
<tr class="editable">
<td>"Shortname"</td>
<ClickToEdit
sig=shortname
children=move |tr| {
view! {
<td on:click=move |_| tr.notify()>
<span>{shortname()}</span>
</td>
}
}
/>
<td>
<ClickToEdit sig=shortname/>

</td>

</tr>

<tr class="editable">
<td>"Format"</td>
<ClickToEdit
sig=format
children=move |tr| {
view! {
<td on:click=move |_| tr.notify()>
<span>{format()}</span>
</td>
}
}
/>
<td>
<ClickToEdit sig=format/>

</td>

</tr>

Expand All @@ -81,58 +64,51 @@ pub fn Info() -> impl IntoView {
}

#[component]
fn ClickToEdit<F, IV>(sig: RwSignal<String>, children: F) -> impl IntoView
where
F: 'static + Fn(Trigger) -> IV,
IV: IntoView,
{
fn ClickToEdit(sig: RwSignal<String>) -> impl IntoView {
let (edit, set_edit) = create_signal(false);
let val = create_rw_signal(sig.get_untracked());
let trigger = create_trigger();
let node = create_node_ref::<html::Input>();
create_effect(move |x| {
trigger.track();
if x.is_some() {
// triggered rather than run by default
set_edit(true);
let n = node.get().unwrap();
// n.focus().expect("input.focus");
n.select();
}
});
let _ = on_click_outside(node, move |_| {
set_edit(false);
sig.set(val.get());
if edit.get_untracked() {
set_edit(false);
let val = val.get_untracked();
if sig.get() != val {
sig.set(val);
}
}
});
view! {
{move || {
if edit() {
view! {
<input
node_ref=node
type="text"
value=val.get_untracked()
on:input=move |e| {
val.set(event_target_value(&e));
}
<input
hidden=move || !edit()
node_ref=node
type="text"
value=val.get_untracked()
on:input=move |e| {
val.set(event_target_value(&e));
}

on:keydown=move |e| {
if e.key() == "Enter" {
// order matter; set_edit will be disposed
// if sig is triggered first
set_edit(false);
sig.set(val.get());
} else if e.key() == "Escape" {
set_edit(false);
val.set(sig.get());
}
}
/>
on:keydown=move |e| {
if e.key() == "Enter" {
set_edit(false);
let val = val.get_untracked();
if sig.get() != val {
sig.set(val);
}
} else if e.key() == "Escape" {
set_edit(false);
val.set(sig.get());
}
.into_view()
} else {
children(trigger).into_view()
}
}}
/>

<span
hidden=move || edit()
on:click=move |_| {
set_edit(true);
node.get().unwrap().select();
}
>
{move || sig.get()}
</span>
}
}
4 changes: 2 additions & 2 deletions ui/src/pages/player/selector.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::Context;
use crate::{log, data::ID};
use crate::{data::ID, log};
use leptos::*;
use leptos_router::*;

Expand All @@ -11,7 +11,7 @@ pub fn MediaSelector() -> impl IntoView {
let is_eq = move |i: &ID| id().map(|id| id == *i).unwrap_or(false);
let sorted_media = move || {
let mut list: Vec<_> = ctx.media.get().into_iter().collect();
list.sort_by(|(_, a), (_, b)| a.shortname.cmp(&b.shortname));
list.sort_by(|(_, a), (_, b)| a.shortname.to_lowercase().cmp(&b.shortname.to_lowercase()));
list
};
view! {
Expand Down
8 changes: 6 additions & 2 deletions ui/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ main {
// text-align: center;
display: flex;
flex-direction: column;
align-items: center;
// justify-content: center;
// align-items: center;
justify-content: center;
// border: #50b060 solid 3px;
// border-radius: 8px;
}
Expand Down Expand Up @@ -290,6 +290,10 @@ nav ul {
text-wrap: nowrap;
text-overflow: ellipsis;
overflow: hidden;

span {
width: 100%;
}
}

td:first-child {
Expand Down

0 comments on commit 0ceed58

Please sign in to comment.