diff --git a/src/api/quotes.rs b/src/api/quotes.rs index 49df416..708becc 100644 --- a/src/api/quotes.rs +++ b/src/api/quotes.rs @@ -33,7 +33,7 @@ pub async fn get_by_id( #[derive(Deserialize)] pub struct QuoteLineForm { pub content: String, - pub name_id: Uuid, + pub name_ids: Vec, } #[derive(Deserialize)] @@ -55,8 +55,11 @@ pub async fn create( let mut lines = Vec::with_capacity(form.lines.len()); for l in form.lines { - let name = Name::get_by_id(&mut tx, l.name_id).await?; - lines.push((l.content, name)); + let mut names = Vec::with_capacity(l.name_ids.len()); + for id in l.name_ids { + names.push(Name::get_by_id(&mut tx, id).await?); + } + lines.push((l.content, names)); } let q = Quote::create( diff --git a/src/quotes/mod.rs b/src/quotes/mod.rs index eed5268..434a0a3 100644 --- a/src/quotes/mod.rs +++ b/src/quotes/mod.rs @@ -23,7 +23,7 @@ pub struct Quote { #[derive(Serialize)] pub struct QuoteLine { pub id: Uuid, - pub attribution: Name, + pub attribution: Vec, pub content: String, } @@ -86,18 +86,25 @@ impl Quote { .fetch_all(&mut *conn) .await?; - let mut lines = Vec::with_capacity(line_rows.len()); + let mut lines: Vec = Vec::new(); for r in line_rows { - lines.push(QuoteLine { - id: r.try_get("id")?, - content: r.try_get("content")?, - attribution: Name { - id: r.try_get("name_id")?, - is_primary: r.try_get("is_primary")?, - person_id: r.try_get("person_id")?, - name: r.try_get("name")?, - }, - }); + let line_id: Uuid = r.try_get("id")?; + let name = Name { + id: r.try_get("name_id")?, + is_primary: r.try_get("is_primary")?, + person_id: r.try_get("person_id")?, + name: r.try_get("name")?, + }; + + if let Some(last) = lines.last_mut().filter(|l| l.id == line_id) { + last.attribution.push(name); + } else { + lines.push(QuoteLine { + id: line_id, + content: r.try_get("content")?, + attribution: vec![name], + }); + } } Ok(Quote { @@ -169,7 +176,7 @@ impl Quote { pub async fn create( conn: &mut PgConnection, - lines: Vec<(String, Name)>, + lines: Vec<(String, Vec)>, timestamp: NaiveDateTime, context: Option, location: Option, @@ -181,7 +188,7 @@ impl Quote { } let quote_id = Uuid::now_v7(); - let lines: Vec<(Uuid, String, Name)> = lines + let lines: Vec<(Uuid, String, Vec)> = lines .into_iter() .map(|(c, a)| (Uuid::now_v7(), c, a)) .collect(); @@ -215,16 +222,18 @@ impl Quote { .execute(&mut *conn) .await?; - sqlx::query( - r#" - INSERT INTO line_authors (line_id, name_id) - VALUES ($1, $2) - "#, - ) - .bind(id) - .bind(attr.id) - .execute(&mut *conn) - .await?; + for a in attr { + sqlx::query( + r#" + INSERT INTO line_authors (line_id, name_id) + VALUES ($1, $2) + "#, + ) + .bind(id) + .bind(a.id) + .execute(&mut *conn) + .await?; + } } Ok(Quote { diff --git a/src/web/components/quote.rs b/src/web/components/quote.rs index 6f159a3..2ca5166 100644 --- a/src/web/components/quote.rs +++ b/src/web/components/quote.rs @@ -9,7 +9,9 @@ pub fn quote(quote: &Quote) -> Markup { (PreEscaped(icons::QUOTE)) } @for (i, line) in quote.lines.iter().enumerate() { - @let show_author = i == quote.lines.len()-1 || quote.lines[i+1].attribution.id != line.attribution.id; + @let is_last = i == quote.lines.len() - 1; + @let show_author = is_last || !line.attribution.iter().map(|a| a.id) + .eq(quote.lines[i + 1].attribution.iter().map(|a| a.id)); div class="mb-2" { span class="flex flex-row gap-2 relative" { span class="scale-x-[.65] scale-y-[.5] absolute opacity-[.3]"{ @@ -19,7 +21,7 @@ pub fn quote(quote: &Quote) -> Markup { } @if show_author { p class="text-sm italic ml-3 flex flex-row gap-1.5 text-neutral-400" { - "— " (line.attribution.name) + "— " (line.attribution.iter().map(|a| a.name.clone()).collect::>().join(", ")) } } } diff --git a/src/web/pages/quotes/add.rs b/src/web/pages/quotes/add.rs index 0d96983..9cd6a9b 100644 --- a/src/web/pages/quotes/add.rs +++ b/src/web/pages/quotes/add.rs @@ -153,7 +153,12 @@ pub async fn form( for nid in form.authors { authors.push(Name::get_by_id(&mut *tx, nid).await.unwrap()); } - let lines = form.lines.into_iter().zip(authors).collect(); + let lines = form + .lines + .into_iter() + .zip(authors) + .map(|(l, a)| (l, vec![a])) + .collect(); let timestamp = match NaiveDateTime::parse_from_str(&form.time, "%Y-%m-%dT%H:%M") { Ok(ts) => ts,