Added the import/cleanup functionality

This commit is contained in:
Connor Johnstone
2026-03-24 15:58:14 -04:00
parent 1431cd2fbc
commit 823ef15022
11 changed files with 392 additions and 27 deletions

View File

@@ -143,6 +143,27 @@ pub fn album_page(props: &Props) -> Html {
})
};
let on_unwatch_click = {
let detail = detail.clone();
let mbid = t.recording_mbid.clone();
Callback::from(move |_: MouseEvent| {
let detail = detail.clone();
let mbid = mbid.clone();
let idx = idx;
wasm_bindgen_futures::spawn_local(async move {
if api::unwatch_track(&mbid).await.is_ok() {
if let Some(ref d) = *detail {
let mut updated = d.clone();
if let Some(track) = updated.tracks.get_mut(idx) {
track.status = None;
}
detail.set(Some(updated));
}
}
});
})
};
html! {
<>
<tr>
@@ -162,6 +183,11 @@ pub fn album_page(props: &Props) -> Html {
onclick={on_watch_click}>
{ "Watch" }
</button>
} else {
<button class="btn btn-sm btn-secondary"
onclick={on_unwatch_click}>
{ "Unwatch" }
</button>
}
<button class="btn btn-sm btn-secondary"
onclick={on_lyrics_click}>

View File

@@ -106,9 +106,40 @@ pub fn artist_page(props: &Props) -> Html {
};
let watch_all_btn = {
let artist_status = d.artist_status.clone();
let show = artist_status != "owned";
if show {
let is_watched = d.artist_status == "owned"
|| d.artist_status == "partial"
|| d.artist_status == "wanted";
if is_watched {
// Unwatch All
let artist_id_num = d.artist.id;
let artist_name = d.artist.name.clone();
let message = message.clone();
let error = error.clone();
let fetch = fetch.clone();
let artist_id = id.clone();
html! {
<button class="btn btn-sm btn-secondary"
onclick={Callback::from(move |_: MouseEvent| {
let artist_name = artist_name.clone();
let message = message.clone();
let error = error.clone();
let fetch = fetch.clone();
let artist_id = artist_id.clone();
wasm_bindgen_futures::spawn_local(async move {
match api::unwatch_artist(artist_id_num).await {
Ok(_) => {
message.set(Some(format!("Unwatched {artist_name}")));
fetch.emit(artist_id);
}
Err(e) => error.set(Some(e.0)),
}
});
})}>
{ "Unwatch All" }
</button>
}
} else {
// Watch All
let artist_name = d.artist.name.clone();
let artist_mbid = d.artist.musicbrainz_id.clone();
let message = message.clone();
@@ -140,8 +171,6 @@ pub fn artist_page(props: &Props) -> Html {
{ "Watch All" }
</button>
}
} else {
html! {}
}
};
@@ -191,6 +220,33 @@ pub fn artist_page(props: &Props) -> Html {
}
};
let remove_btn = {
let artist_id_num = d.artist.id;
if artist_id_num > 0 {
let error = error.clone();
html! {
<button class="btn btn-sm btn-danger"
onclick={Callback::from(move |_: MouseEvent| {
let error = error.clone();
wasm_bindgen_futures::spawn_local(async move {
if let Err(e) = api::delete_artist(artist_id_num).await {
error.set(Some(e.0));
return;
}
// Navigate back to library
if let Some(window) = web_sys::window() {
let _ = window.location().set_href("/library");
}
});
})}>
{ "Remove" }
</button>
}
} else {
html! {}
}
};
html! {
<div>
if let Some(ref banner) = d.artist_banner {
@@ -249,6 +305,7 @@ pub fn artist_page(props: &Props) -> Html {
<div class="flex gap-1">
{ watch_all_btn }
{ monitor_btn }
{ remove_btn }
</div>
</div>
if let Some(ref bio) = d.artist_bio {
@@ -315,7 +372,7 @@ pub fn artist_page(props: &Props) -> Html {
let tc = album.track_count;
// Watch button for unwatched albums
// Watch/Unwatch toggle for albums
let watch_btn = if is_unwatched {
let artist_name = d.artist.name.clone();
let album_title = album.title.clone();
@@ -351,7 +408,34 @@ pub fn artist_page(props: &Props) -> Html {
</button>
}
} else {
html! {}
let album_title = album.title.clone();
let album_mbid = album.mbid.clone();
let message = message.clone();
let error = error.clone();
let fetch = fetch.clone();
let artist_id = id.clone();
html! {
<button class="btn btn-sm btn-secondary"
onclick={Callback::from(move |_: MouseEvent| {
let album_title = album_title.clone();
let album_mbid = album_mbid.clone();
let message = message.clone();
let error = error.clone();
let fetch = fetch.clone();
let artist_id = artist_id.clone();
wasm_bindgen_futures::spawn_local(async move {
match api::unwatch_album(&album_mbid).await {
Ok(_) => {
message.set(Some(format!("Unwatched '{album_title}'")));
fetch.emit(artist_id);
}
Err(e) => error.set(Some(e.0)),
}
});
})}>
{ "Unwatch" }
</button>
}
};
html! {

View File

@@ -10,16 +10,25 @@ pub fn library_page() -> Html {
let artists = use_state(|| None::<Vec<ArtistListItem>>);
let error = use_state(|| None::<String>);
{
let fetch_artists = {
let artists = artists.clone();
let error = error.clone();
use_effect_with((), move |_| {
Callback::from(move |_: ()| {
let artists = artists.clone();
let error = error.clone();
wasm_bindgen_futures::spawn_local(async move {
match api::list_artists(200, 0).await {
Ok(a) => artists.set(Some(a)),
Err(e) => error.set(Some(e.0)),
}
});
})
};
{
let fetch = fetch_artists.clone();
use_effect_with((), move |_| {
fetch.emit(());
});
}
@@ -49,10 +58,25 @@ pub fn library_page() -> Html {
<th>{ "Owned" }</th>
<th>{ "Watched" }</th>
<th>{ "Tracks" }</th>
<th></th>
</tr>
</thead>
<tbody>
{ for artists.iter().map(|a| html! {
{ for artists.iter().map(|a| {
let artist_id = a.id;
let error = error.clone();
let fetch = fetch_artists.clone();
let on_remove = Callback::from(move |_: MouseEvent| {
let error = error.clone();
let fetch = fetch.clone();
wasm_bindgen_futures::spawn_local(async move {
match api::delete_artist(artist_id).await {
Ok(_) => fetch.emit(()),
Err(e) => error.set(Some(e.0)),
}
});
});
html! {
<tr>
<td>
<Link<Route> to={Route::Artist { id: a.id.to_string() }}>
@@ -90,7 +114,13 @@ pub fn library_page() -> Html {
{ a.total_items }
}
</td>
<td>
<button class="btn btn-sm btn-danger" onclick={on_remove}>
{ "Remove" }
</button>
</td>
</tr>
}
})}
</tbody>
</table>

View File

@@ -453,6 +453,18 @@ pub fn settings_page() -> Html {
}
})} />
</div>
<div class="form-group">
<label>{ "Concurrency" }</label>
<input type="number" min="1" max="16" value={c.tagging.concurrency.to_string()}
oninput={let config = config.clone(); Callback::from(move |e: InputEvent| {
let input: HtmlInputElement = e.target_unchecked_into();
if let Ok(v) = input.value().parse::<usize>() {
let mut cfg = (*config).clone().unwrap();
cfg.tagging.concurrency = v;
config.set(Some(cfg));
}
})} />
</div>
</div>
// Downloads