Update dependencies
This commit is contained in:
708
Cargo.lock
generated
708
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
15
Cargo.toml
15
Cargo.toml
@ -5,12 +5,15 @@ authors = ["Joakim Hulthe <joakim@hulthe.net"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
seed = "0.8.0"
|
seed = "0.10.0"
|
||||||
#wasm-bindgen = "0.2.70"
|
serde = { version = "1.0.0", features = ['derive'] }
|
||||||
serde = { version = "1", features = ['derive'] }
|
serde_json = "1.0.0"
|
||||||
serde_json = "1"
|
rand = "0.8.5"
|
||||||
anyhow = "*"
|
gloo-console = "0.3.0"
|
||||||
rand = "0.8.4"
|
gloo-net = "0.4.0"
|
||||||
|
csv = "1.2.2"
|
||||||
|
thiserror = "1.0.48"
|
||||||
|
wasm-bindgen = "0.2.87"
|
||||||
|
|
||||||
[dependencies.css_typegen]
|
[dependencies.css_typegen]
|
||||||
git = "https://github.com/hulthe/css_typegen.git"
|
git = "https://github.com/hulthe/css_typegen.git"
|
||||||
|
|||||||
38
src/app.rs
38
src/app.rs
@ -1,15 +1,16 @@
|
|||||||
use crate::css::C;
|
use crate::css::C;
|
||||||
use crate::custom_list::{fetch_custom_song_list, fetch_custom_song_list_index, CustomLists};
|
use crate::custom_list::{fetch_custom_song_list, fetch_custom_song_list_index, CustomLists};
|
||||||
|
use crate::fetch::fetch_list_of;
|
||||||
use crate::fuzzy::FuzzyScore;
|
use crate::fuzzy::FuzzyScore;
|
||||||
use crate::query::ParsedQuery;
|
use crate::query::ParsedQuery;
|
||||||
use crate::song::Song;
|
use crate::song::Song;
|
||||||
use anyhow::anyhow;
|
use gloo_console::error;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use seed::app::cmds::timeout;
|
use seed::app::cmds::timeout;
|
||||||
use seed::browser::util::document;
|
use seed::browser::util::document;
|
||||||
use seed::{attrs, button, div, empty, error, img, input, p, span, C, IF};
|
use seed::prelude::*;
|
||||||
use seed::{log, prelude::*};
|
use seed::{attrs, button, div, empty, img, input, p, span, C, IF};
|
||||||
use std::cmp::Reverse;
|
use std::cmp::Reverse;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use web_sys::Element;
|
use web_sys::Element;
|
||||||
@ -194,12 +195,9 @@ pub fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
autotype_song(model, orders);
|
autotype_song(model, orders);
|
||||||
}
|
}
|
||||||
Msg::Scroll => {
|
Msg::Scroll => {
|
||||||
let (scroll, max_scroll) = match get_scroll() {
|
let Some((scroll, max_scroll)) = get_scroll() else {
|
||||||
Ok(v) => v,
|
error!("Failed to get song list element by id:", SONG_LIST_ID);
|
||||||
Err(e) => {
|
|
||||||
error!(e);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let scroll_left: i32 = max_scroll - scroll;
|
let scroll_left: i32 = max_scroll - scroll;
|
||||||
@ -344,18 +342,10 @@ pub fn view(model: &Model) -> Vec<Node<Msg>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_songs() -> Option<Msg> {
|
async fn fetch_songs() -> Option<Msg> {
|
||||||
let response = match fetch("/songs").await.and_then(|r| r.check_status()) {
|
let mut songs: Vec<Song> = match fetch_list_of("/songs").await {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log!("error fetching songs", e);
|
error!("Error fetching songs:", e);
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut songs: Vec<Song> = match response.json().await {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
log!("error parsing songs", e);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -374,22 +364,20 @@ pub fn autotype_song(model: &mut Model, orders: &mut impl Orders<Msg>) {
|
|||||||
|
|
||||||
const SONG_LIST_ID: &str = "song_list";
|
const SONG_LIST_ID: &str = "song_list";
|
||||||
|
|
||||||
fn get_song_list_element() -> anyhow::Result<Element> {
|
fn get_song_list_element() -> Option<Element> {
|
||||||
document()
|
document().get_element_by_id(SONG_LIST_ID)
|
||||||
.get_element_by_id(SONG_LIST_ID)
|
|
||||||
.ok_or_else(|| anyhow!("Failed to access song list element"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_to_top() {
|
fn scroll_to_top() {
|
||||||
if let Ok(elem) = get_song_list_element() {
|
if let Some(elem) = get_song_list_element() {
|
||||||
elem.scroll_to_with_x_and_y(0.0, 0.0);
|
elem.scroll_to_with_x_and_y(0.0, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_scroll() -> anyhow::Result<(i32, i32)> {
|
fn get_scroll() -> Option<(i32, i32)> {
|
||||||
let list = get_song_list_element()?;
|
let list = get_song_list_element()?;
|
||||||
let scroll = list.scroll_top();
|
let scroll = list.scroll_top();
|
||||||
let height = list.client_height();
|
let height = list.client_height();
|
||||||
let max = (list.scroll_height() - height).max(0);
|
let max = (list.scroll_height() - height).max(0);
|
||||||
Ok((scroll, max))
|
Some((scroll, max))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,19 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use seed::{log, prelude::fetch};
|
use gloo_console::error;
|
||||||
|
|
||||||
use crate::app::{Loading, Msg};
|
use crate::{
|
||||||
|
app::{Loading, Msg},
|
||||||
|
fetch::fetch_list_of,
|
||||||
|
};
|
||||||
|
|
||||||
pub type CustomLists = HashMap<String, Loading<HashSet<String>>>;
|
pub type CustomLists = HashMap<String, Loading<HashSet<String>>>;
|
||||||
|
|
||||||
pub async fn fetch_custom_song_list_index() -> Option<Msg> {
|
pub async fn fetch_custom_song_list_index() -> Option<Msg> {
|
||||||
let response = match fetch("/custom/lists").await.and_then(|r| r.check_status()) {
|
let custom_lists: Vec<String> = match fetch_list_of("/custom/lists").await {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log!("error fetching custom song list index", e);
|
error!("Failed fetching custom song list index:", e);
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let custom_lists: Vec<String> = match response.json().await {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
log!("error parsing custom song list index", e);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -27,21 +22,10 @@ pub async fn fetch_custom_song_list_index() -> Option<Msg> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_custom_song_list(list: String) -> Option<Msg> {
|
pub async fn fetch_custom_song_list(list: String) -> Option<Msg> {
|
||||||
let response = match fetch(format!("/custom/list/{list}"))
|
let song_hashes: HashSet<String> = match fetch_list_of(format!("/custom/list/{list}")).await {
|
||||||
.await
|
Ok(response) => response.into_iter().collect(),
|
||||||
.and_then(|r| r.check_status())
|
|
||||||
{
|
|
||||||
Ok(response) => response,
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log!("error fetching custom song list", e);
|
error!("Failed fetching custom song list:", e);
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let song_hashes: HashSet<String> = match response.json().await {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
log!("error parsing custom song list", e);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
70
src/fetch.rs
Normal file
70
src/fetch.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use gloo_net::http::{Request, Response};
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use wasm_bindgen::{JsError, JsValue};
|
||||||
|
|
||||||
|
const HTTP_ACCEPT: &str = concat!("text/csv, ", "application/json;q=0.9");
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum FetchError {
|
||||||
|
/// The request returned a non-2XX status code.
|
||||||
|
#[error("server responded with {code} {text}")]
|
||||||
|
Status { code: u16, text: String },
|
||||||
|
|
||||||
|
/// The response contained an unrecognized or missing content type.
|
||||||
|
#[error("unknown content type {0:?}")]
|
||||||
|
UnknownContentType(Option<String>),
|
||||||
|
|
||||||
|
/// Another error occured.
|
||||||
|
#[error("{0}")]
|
||||||
|
Other(#[from] gloo_net::Error),
|
||||||
|
|
||||||
|
#[error("error deserializing csv: {0}")]
|
||||||
|
Csv(#[from] csv::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FetchError> for JsValue {
|
||||||
|
fn from(e: FetchError) -> Self {
|
||||||
|
JsError::new(&e.to_string()).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a GET request.
|
||||||
|
pub async fn fetch(url: impl AsRef<str>) -> Result<Response, FetchError> {
|
||||||
|
let response = Request::get(url.as_ref())
|
||||||
|
.header("accept", HTTP_ACCEPT)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !response.ok() {
|
||||||
|
return Err(FetchError::Status {
|
||||||
|
code: response.status(),
|
||||||
|
text: response.status_text(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a GET request and try to deserialize the response as a `Vec<T>`.
|
||||||
|
pub async fn fetch_list_of<T: DeserializeOwned>(
|
||||||
|
url: impl AsRef<str>,
|
||||||
|
) -> Result<Vec<T>, FetchError> {
|
||||||
|
let response = fetch(url.as_ref()).await?;
|
||||||
|
let headers = response.headers();
|
||||||
|
let content_type = headers.get("Content-Type").map(|s| s.to_lowercase());
|
||||||
|
|
||||||
|
match content_type.as_deref() {
|
||||||
|
Some("text/csv") => {
|
||||||
|
let text = response.text().await?;
|
||||||
|
let reader = csv::Reader::from_reader(Cursor::new(text)).into_deserialize();
|
||||||
|
let list = reader
|
||||||
|
.map(|r| r.map_err(FetchError::from))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
Ok(list)
|
||||||
|
}
|
||||||
|
Some("application/json") => Ok(response.json().await?),
|
||||||
|
_ => Err(FetchError::UnknownContentType(content_type)),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
mod app;
|
mod app;
|
||||||
mod css;
|
mod css;
|
||||||
mod custom_list;
|
mod custom_list;
|
||||||
|
mod fetch;
|
||||||
mod fuzzy;
|
mod fuzzy;
|
||||||
mod query;
|
mod query;
|
||||||
mod song;
|
mod song;
|
||||||
|
|||||||
Reference in New Issue
Block a user