dailp/collection.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
//! Provides types for defining and structuring edited collections.
use uuid::Uuid;
use crate::AnnotatedDoc;
use {
crate::async_graphql::{self, dataloader::DataLoader, Context, FieldResult},
crate::slugify,
crate::Database,
crate::DocumentCollection,
crate::DocumentId,
};
/// Structure to represent an edited collection. Missing certain fields and chapters in it.
/// Used for sending data to the front end
#[derive(Debug, Clone, async_graphql::SimpleObject)]
#[graphql(complex)]
pub struct EditedCollection {
/// UUID for the collection
pub id: Uuid,
/// Full title of the collection
pub title: String,
/// Description of the collection (optional)
pub description: Option<String>,
/// ID of WordPress menu for navigating the collection
pub wordpress_menu_id: Option<i64>,
#[graphql(skip)]
/// URL slug for the collection, like "cwkw"
pub slug: String,
/// Cover image URL
pub thumbnail_url: Option<String>,
}
/// Input object for creating a new collection
#[derive(async_graphql::InputObject)]
pub struct EditedCollectionInput {
/// Full title of the collection
pub title: String,
/// URL slug for the collection, like "cwkw"
pub slug: String,
/// ID of WordPress menu for navigating the collection
pub wordpress_menu_id: Option<i64>,
}
/// Structure to represent a single chapter. Used to send data to the front end.
#[derive(Debug, Clone, async_graphql::SimpleObject)]
#[graphql(complex)]
pub struct CollectionChapter {
/// UUID for the chapter
pub id: Uuid,
/// Full title of the chapter
pub title: String,
/// ID of WordPress page with text of the chapter
pub wordpress_id: std::option::Option<i64>,
/// Order within the parent chapter or collection
pub index_in_parent: i64,
/// Whether the chapter is an "Intro" or "Body" chapter
pub section: CollectionSection,
/// Document id
#[graphql(skip)]
pub document_id: Option<DocumentId>,
#[graphql(skip)]
/// Full path of the chapter
pub path: Vec<String>,
}
/// Enum to represent the sections in an edited collection
#[derive(async_graphql::Enum, Clone, Copy, PartialEq, Eq, Debug, sqlx::Type)]
#[sqlx(type_name = "collection_section")]
pub enum CollectionSection {
/// Intro chapter
Intro,
/// Body chapter
Body,
/// Credit
Credit,
}
#[async_graphql::ComplexObject]
impl EditedCollection {
/// URL slug for the collection, like "cwkw"
async fn slug(&self) -> String {
slugify(&self.slug)
}
async fn chapters(&self, context: &Context<'_>) -> FieldResult<Option<Vec<CollectionChapter>>> {
Ok(context
.data::<DataLoader<Database>>()?
.load_one(crate::ChaptersInCollection(self.slug.clone()))
.await?)
}
}
#[async_graphql::ComplexObject]
impl CollectionChapter {
/// Full path of the chapter
async fn path(&self) -> Vec<String> {
self.path.iter().map(slugify).collect()
}
async fn slug(&self) -> String {
slugify(self.path.last().unwrap())
}
async fn document(&self, context: &Context<'_>) -> FieldResult<Option<AnnotatedDoc>> {
if let Some(doc_id) = &self.document_id {
Ok(context
.data::<DataLoader<Database>>()?
.load_one(*doc_id)
.await?)
} else {
Ok(None)
}
}
/// Breadcrumbs from the top-level archive down to where this document lives.
async fn breadcrumbs(
&self,
context: &async_graphql::Context<'_>,
) -> FieldResult<Vec<DocumentCollection>> {
Ok(context
.data::<DataLoader<Database>>()?
.loader()
.chapter_breadcrumbs(self.path.clone())
.await?)
}
}
/// Input for creating an edited collection
#[derive(async_graphql::InputObject)]
pub struct CreateEditedCollectionInput {
/// The title of the collection
pub title: String,
/// Description of the collection
pub description: String,
/// URL of the thumbnail image for the collection
pub thumbnail_url: String,
}