Skip to content

Commit

Permalink
feat: Can set book read status
Browse files Browse the repository at this point in the history
  • Loading branch information
phildenhoff committed Nov 29, 2024
1 parent d0ec630 commit 0097a1a
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 47 deletions.
18 changes: 17 additions & 1 deletion src-tauri/libcalibre/src/api/books.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ impl BooksHandler {
}
}

pub fn get_book_read_state_for_user(&self, book_id: i32) -> Result<Option<bool>, ()> {
pub fn get_book_read_state(&self, book_id: i32) -> Result<Option<bool>, ()> {
let mut connection = self.client.lock().unwrap();

let read_state_column_id = self.get_or_create_read_state_custom_column(&mut *connection)?;
Expand All @@ -248,6 +248,22 @@ impl BooksHandler {
Err(_) => Ok(Some(false)),
}
}

pub fn set_book_read_state(&mut self, book_id: i32, read_state: bool) -> Result<(), ()> {
let mut connection = self.client.lock().unwrap();

let read_state_column_id = self.get_or_create_read_state_custom_column(&mut *connection)?;
let value = if read_state { 1 } else { 0 };

sql_query(format!(
"INSERT OR REPLACE INTO custom_column_{read_state_column_id} (book, value) VALUES (?, ?)"
))
.bind::<Integer, _>(book_id)
.bind::<Integer, _>(value)
.execute(&mut *connection)
.map(|_| ())
.or(Err(()))
}
}

fn uuid_for_book(conn: &mut SqliteConnection, book_id: i32) -> Option<String> {
Expand Down
10 changes: 9 additions & 1 deletion src-tauri/libcalibre/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,17 @@ impl CalibreClient {
updates: UpdateLibraryEntryDto,
) -> Result<crate::BookWithAuthorsAndFiles, Box<dyn std::error::Error>> {
// Write new updates to book
let is_read = updates.book.is_read;
let book_update = UpdateBookData::try_from(updates.book).unwrap();
let _book = self.client_v2.books().update(book_id, book_update);

if is_read.is_some() {
let _set_book_result = self
.client_v2
.books()
.set_book_read_state(book_id, is_read.unwrap());
}

match updates.author_id_list {
Some(author_id_list) => {
// Unlink existing authors
Expand Down Expand Up @@ -228,7 +236,7 @@ impl CalibreClient {
let is_read = self
.client_v2
.books()
.get_book_read_state_for_user(book.id)
.get_book_read_state(book.id)
.unwrap_or(Some(false))
.unwrap_or(false);

Expand Down
2 changes: 2 additions & 0 deletions src-tauri/libcalibre/src/dtos/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct UpdateBookDto {
pub path: Option<String>,
pub flags: Option<i32>,
pub has_cover: Option<bool>,
pub is_read: Option<bool>,
}

impl UpdateBookDto {
Expand All @@ -34,6 +35,7 @@ impl UpdateBookDto {
path: None,
flags: None,
has_cover: None,
is_read: None,
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/libs/calibre/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub struct BookUpdate {
pub title: Option<String>,
pub timestamp: Option<NaiveDateTime>,
pub publication_date: Option<NaiveDateTime>,
pub is_read: Option<bool>,
// pub tags: Option<String>,
// pub ext_id_list: Option<Vec<String>>,
}
Expand All @@ -124,6 +125,7 @@ impl BookUpdate {
title: self.title.clone(),
timestamp: self.timestamp,
pubdate: self.publication_date,
is_read: self.is_read,
..UpdateBookDto::default()
},
author_id_list: self.author_id_list.clone(),
Expand Down
2 changes: 1 addition & 1 deletion src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ return await TAURI_INVOKE("plugin:tauri-specta|init_client", { libraryPath });
/** user-defined types **/

export type BookFile = { Local: LocalFile } | { Remote: RemoteFile }
export type BookUpdate = { author_id_list: string[] | null; title: string | null; timestamp: string | null; publication_date: string | null }
export type BookUpdate = { author_id_list: string[] | null; title: string | null; timestamp: string | null; publication_date: string | null; is_read: boolean | null }
export type CalibreClientConfig = { library_path: string }
/**
* Book identifiers, such as ISBN, DOI, Google Books ID, etc.
Expand Down
100 changes: 56 additions & 44 deletions src/components/pages/EditBook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Group,
Paper,
Stack,
Switch,
Text,
TextInput,
Textarea,
Expand Down Expand Up @@ -77,16 +78,8 @@ const Formats = ({
);
};

const Cover = ({
book,
style,
}: { book: LibraryBook } & HTMLProps<HTMLDivElement>) => {
return (
<div style={style}>
<Text size="xl">Cover</Text>
<BookCover book={book} />
</div>
);
const Cover = ({ book }: { book: LibraryBook } & HTMLProps<HTMLDivElement>) => {
return <BookCover book={book} disableFade />;
};

const formValuesFromBook = (book: LibraryBook) => ({
Expand All @@ -95,6 +88,7 @@ const formValuesFromBook = (book: LibraryBook) => ({
authorList: book.author_list.map((author) => author.name),
identifierList: book.identifier_list,
description: book.description ?? "",
isRead: book.is_read,
});

// How much an element has to be offset vertically to account for the lack of a
Expand Down Expand Up @@ -144,32 +138,61 @@ const EditBookForm = ({
author_id_list: authorIdsFromName,
timestamp: null,
publication_date: null,
is_read: form.values.isRead,
};

await onSave(bookUpdate);

form.resetDirty();
form.resetTouched();
})}
style={{
// Additional `flex: 1` on the form prevents the element from
// overflowing when a second+ author is selected
display: "grid",
gridTemplateColumns: "0.3fr 1.8fr",
gridTemplateRows: "1.4fr 1.4fr 0.2fr",
gridTemplateRows: "1.4fr 1.4fr",
gridTemplateAreas: `"Cover BookInfo"
"Format BookInfo"
"Buttons Buttons"`,
"Format BookInfo"`,
gap: "0px 1rem",
height: "100%",
}}
>
<Cover book={book} style={{ gridArea: "Cover" }} />
<div style={{ gridArea: "Cover" }}>
<Stack>
<Cover book={book} />
<Switch
label="Finished"
{...form.getInputProps("isRead", { type: "checkbox" })}
/>
</Stack>
</div>
<Formats book={book} style={{ gridArea: "Format" }} />
<Group
align="flex-start"
preventGrowOverflow
style={{ gridArea: "BookInfo" }}
>
<Stack flex={1}>
<Text size="xl">Book info</Text>
<Group flex={1} justify="space-between">
<Text size="xl" p="1" h="36">
Book info
</Text>
{form.isDirty() && form.isTouched() && (
<Group justify="space-between">
<Button
variant="subtle"
onClick={() => form.reset()}
color="red"
>
Clear
</Button>
<Button type="submit" component="button">
Save
</Button>
</Group>
)}
</Group>
<Group flex={1}>
<TextInput
label="Title"
Expand All @@ -190,22 +213,24 @@ const EditBookForm = ({
selectOptions={allAuthorNames}
{...form.getInputProps("authorList")}
/>
{form.values.identifierList.length > 0 && (
<Group flex={1}>
<Fieldset legend="Identifiers">
{form.values.identifierList.map(({ label, value }) => (
<Group key={`${label}-${value}`} flex={1} align="center">
<TextInput
flex={"15ch"}
label={label.toUpperCase()}
value={value}
disabled
/>
</Group>
))}
</Fieldset>
</Group>
)}
<Group flex={1}>
{form.values.identifierList.length > 0 && (
<Group flex={1}>
<Fieldset legend="Identifiers">
{form.values.identifierList.map(({ label, value }) => (
<Group key={`${label}-${value}`} flex={1} align="center">
<TextInput
flex={"15ch"}
label={label.toUpperCase()}
value={value}
disabled
/>
</Group>
))}
</Fieldset>
</Group>
)}
</Group>
{form.values.description.length > 0 && (
<Paper shadow="sm" p="lg">
<Text size="lg">Description</Text>
Expand All @@ -214,19 +239,6 @@ const EditBookForm = ({
)}
</Stack>
</Group>
<Group
justify="flex-end"
mt="md"
gap="80px"
style={{ gridArea: "3 / 1 / 4 / 3" }}
>
<Button variant="subtle" onClick={() => form.reset()} color="red">
Cancel
</Button>
<Button type="submit" component="button">
Save
</Button>
</Group>
</Form>
);
};

0 comments on commit 0097a1a

Please sign in to comment.