Skip to content

Commit

Permalink
✨ Add image reader experience options (#453)
Browse files Browse the repository at this point in the history
* wip: start support for double spread reading

* minimally functional double spread, wip book pref

* wip: redesign a bit and big refactor of image-based reader components

* wip: add scaling during lunch

* simplify scaling configuration options

* wip: fix some smells

* try standardize names

* fix some colors

* messing around

* wip: gradient support and misc theme fixes

* wip pumpkin theme

* refactor tailwind to use ts

* add more tests

* wip fix scroll reading and fix test

* update defaults in library settings

* add migration:

* remove old store and auto-submit reader defaults

* prevent stalling scroll in footer
  • Loading branch information
aaronleopold authored Sep 28, 2024
1 parent 72d7263 commit 185badd
Show file tree
Hide file tree
Showing 178 changed files with 3,273 additions and 906 deletions.
1 change: 0 additions & 1 deletion apps/desktop/tailwind.config.js

This file was deleted.

7 changes: 7 additions & 0 deletions apps/desktop/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import stumpPreset, { getContent } from '@stump/components/tailwind'
import type { Config } from 'tailwindcss'

export default {
content: getContent({ relativePath: 'packages/desktop' }),
presets: [stumpPreset],
} satisfies Config
17 changes: 11 additions & 6 deletions apps/expo/src/components/reader/image/ImageBasedReader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export default function ImageBasedReader({ book, initialPage, incognito }: Props
const [imageSizes, setImageHeights] = useState<Record<number, ImageDimension>>({})

// const lastPrefetchStart = useRef(0)
const readerMode = useReaderStore((state) => state.mode)
// FIXME: useBookPreferences and get the reader mode from there
// const readerMode = useReaderStore((state) => state.mode)
const readerMode = 'paged'

const deviceOrientation = width > height ? 'landscape' : 'portrait'

Expand Down Expand Up @@ -166,14 +168,17 @@ const Page = React.memo(
}: PageProps) => {
const insets = useSafeAreaInsets()

const { showToolBar, setShowToolBar } = useReaderStore((state) => ({
setShowToolBar: state.setShowToolBar,
showToolBar: state.showToolBar,
const {
settings: { showToolBar },
setSettings,
} = useReaderStore((state) => ({
setSettings: state.setSettings,
settings: state.settings,
}))

const handlePress = useCallback(() => {
setShowToolBar(!showToolBar)
}, [showToolBar, setShowToolBar])
setSettings({ showToolBar: !showToolBar })
}, [showToolBar, setSettings])

/**
* A memoized value that represents the size(s) of the image dimensions for the current page.
Expand Down
18 changes: 18 additions & 0 deletions apps/server/src/routers/api/v1/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,15 @@ async fn create_library(
library_config::generate_file_hashes::set(
library_config.generate_file_hashes,
),
library_config::default_reading_dir::set(
library_config.default_reading_dir.to_string(),
),
library_config::default_reading_image_scale_fit::set(
library_config.default_reading_image_scale_fit.to_string(),
),
library_config::default_reading_mode::set(
library_config.default_reading_mode.to_string(),
),
library_config::library_pattern::set(
library_config.library_pattern.to_string(),
),
Expand Down Expand Up @@ -1523,6 +1532,15 @@ async fn update_library(
library_config::process_metadata::set(
library_config.process_metadata,
),
library_config::default_reading_dir::set(
library_config.default_reading_dir.to_string(),
),
library_config::default_reading_image_scale_fit::set(
library_config.default_reading_image_scale_fit.to_string(),
),
library_config::default_reading_mode::set(
library_config.default_reading_mode.to_string(),
),
library_config::generate_file_hashes::set(
library_config.generate_file_hashes,
),
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routers/api/v1/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ async fn get_media_by_id(
query = query.with(if params.load_library.unwrap_or_default() {
media::series::fetch()
.with(series::metadata::fetch())
.with(series::library::fetch())
.with(series::library::fetch().with(library::config::fetch()))
} else {
media::series::fetch().with(series::metadata::fetch())
});
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/routers/api/v1/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ async fn update_preferences(
input.preferred_layout_mode.to_owned(),
),
user_preferences::app_theme::set(input.app_theme.to_owned()),
user_preferences::enable_gradients::set(input.enable_gradients),
user_preferences::app_font::set(input.app_font.to_string()),
user_preferences::primary_navigation_mode::set(
input.primary_navigation_mode.to_owned(),
Expand Down Expand Up @@ -559,6 +560,7 @@ pub struct UpdateUserPreferences {
pub primary_navigation_mode: String,
pub layout_max_width_px: Option<i32>,
pub app_theme: String,
pub enable_gradients: bool,
pub app_font: SupportedFont,
pub show_query_indicator: bool,
pub enable_live_refetch: bool,
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/utils/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub fn create_prisma_user(user: &User, hashed_pass: String) -> user::Data {
primary_navigation_mode: user_preferences.primary_navigation_mode,
layout_max_width_px: user_preferences.layout_max_width_px,
app_theme: user_preferences.app_theme,
enable_gradients: user_preferences.enable_gradients,
app_font: user_preferences.app_font.to_string(),
show_query_indicator: user_preferences.show_query_indicator,
enable_live_refetch: user_preferences.enable_live_refetch,
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"nuke": "rimraf node_modules"
},
"dependencies": {
"@stump/client": "*",
"@stump/browser": "*",
"@stump/client": "*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.23.0",
Expand Down
1 change: 0 additions & 1 deletion apps/web/tailwind.config.js

This file was deleted.

7 changes: 7 additions & 0 deletions apps/web/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import stumpPreset, { getContent } from '@stump/components/tailwind'
import type { Config } from 'tailwindcss'

export default {
content: getContent({ relativePath: 'packages/web' }),
presets: [stumpPreset],
} satisfies Config
1 change: 1 addition & 0 deletions apps/web/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import tsconfigPaths from 'vite-plugin-tsconfig-paths'

// https://www.npmjs.com/package/vite-plugin-node-polyfills
import { name, version } from './package.json'

// https://vitejs.dev/config/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- AlterTable
ALTER TABLE "media" ADD COLUMN "deleted_at" DATETIME;

-- RedefineTables
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_library_configs" (
"id" TEXT NOT NULL PRIMARY KEY,
"convert_rar_to_zip" BOOLEAN NOT NULL DEFAULT false,
"hard_delete_conversions" BOOLEAN NOT NULL DEFAULT false,
"default_reading_dir" TEXT NOT NULL DEFAULT 'ltr',
"default_reading_mode" TEXT NOT NULL DEFAULT 'paged',
"default_reading_image_scale_fit" TEXT NOT NULL DEFAULT 'height',
"generate_file_hashes" BOOLEAN NOT NULL DEFAULT false,
"process_metadata" BOOLEAN NOT NULL DEFAULT true,
"library_pattern" TEXT NOT NULL DEFAULT 'SERIES_BASED',
"thumbnail_config" BLOB,
"ignore_rules" BLOB,
"library_id" TEXT
);
INSERT INTO "new_library_configs" ("convert_rar_to_zip", "generate_file_hashes", "hard_delete_conversions", "id", "ignore_rules", "library_id", "library_pattern", "process_metadata", "thumbnail_config") SELECT "convert_rar_to_zip", "generate_file_hashes", "hard_delete_conversions", "id", "ignore_rules", "library_id", "library_pattern", "process_metadata", "thumbnail_config" FROM "library_configs";
DROP TABLE "library_configs";
ALTER TABLE "new_library_configs" RENAME TO "library_configs";
CREATE TABLE "new_user_preferences" (
"id" TEXT NOT NULL PRIMARY KEY,
"preferred_layout_mode" TEXT NOT NULL DEFAULT 'GRID',
"locale" TEXT NOT NULL DEFAULT 'en',
"app_theme" TEXT NOT NULL DEFAULT 'LIGHT',
"app_font" TEXT NOT NULL DEFAULT 'inter',
"primary_navigation_mode" TEXT NOT NULL DEFAULT 'SIDEBAR',
"layout_max_width_px" INTEGER DEFAULT 1280,
"show_query_indicator" BOOLEAN NOT NULL DEFAULT false,
"enable_live_refetch" BOOLEAN NOT NULL DEFAULT false,
"enable_discord_presence" BOOLEAN NOT NULL DEFAULT false,
"enable_compact_display" BOOLEAN NOT NULL DEFAULT false,
"enable_gradients" BOOLEAN NOT NULL DEFAULT false,
"enable_double_sidebar" BOOLEAN NOT NULL DEFAULT true,
"enable_replace_primary_sidebar" BOOLEAN NOT NULL DEFAULT false,
"enable_hide_scrollbar" BOOLEAN NOT NULL DEFAULT false,
"prefer_accent_color" BOOLEAN NOT NULL DEFAULT true,
"show_thumbnails_in_headers" BOOLEAN NOT NULL DEFAULT false,
"navigation_arrangement" BLOB,
"home_arrangement" BLOB,
"user_id" TEXT
);
INSERT INTO "new_user_preferences" ("app_font", "app_theme", "enable_compact_display", "enable_discord_presence", "enable_double_sidebar", "enable_hide_scrollbar", "enable_live_refetch", "enable_replace_primary_sidebar", "home_arrangement", "id", "layout_max_width_px", "locale", "navigation_arrangement", "prefer_accent_color", "preferred_layout_mode", "primary_navigation_mode", "show_query_indicator", "show_thumbnails_in_headers", "user_id") SELECT "app_font", "app_theme", "enable_compact_display", "enable_discord_presence", "enable_double_sidebar", "enable_hide_scrollbar", "enable_live_refetch", "enable_replace_primary_sidebar", "home_arrangement", "id", "layout_max_width_px", "locale", "navigation_arrangement", "prefer_accent_color", "preferred_layout_mode", "primary_navigation_mode", "show_query_indicator", "show_thumbnails_in_headers", "user_id" FROM "user_preferences";
DROP TABLE "user_preferences";
ALTER TABLE "new_user_preferences" RENAME TO "user_preferences";
CREATE UNIQUE INDEX "user_preferences_user_id_key" ON "user_preferences"("user_id");
PRAGMA foreign_key_check;
PRAGMA foreign_keys=ON;
20 changes: 12 additions & 8 deletions core/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,15 @@ model Library {
}

model LibraryConfig {
id String @id @default(uuid())
library_pattern String @default("SERIES_BASED") // SERIES_BASED or COLLECTION_BASED
// Scanning opt-in settings
convert_rar_to_zip Boolean @default(false)
hard_delete_conversions Boolean @default(false)
generate_file_hashes Boolean @default(false)
process_metadata Boolean @default(true)
id String @id @default(uuid())
convert_rar_to_zip Boolean @default(false)
hard_delete_conversions Boolean @default(false)
default_reading_dir String @default("ltr") // ltr or rtl
default_reading_mode String @default("paged") // paged or continuous:(horizontal|vertical)
default_reading_image_scale_fit String @default("height") // height, width, none (original)
generate_file_hashes Boolean @default(false)
process_metadata Boolean @default(true)
library_pattern String @default("SERIES_BASED") // SERIES_BASED or COLLECTION_BASED
thumbnail_config Bytes? // { size_factor: "...", format: "...", quality: ... }
ignore_rules Bytes? // ["glob1", "glob2", ...]
Expand Down Expand Up @@ -190,7 +192,8 @@ model Media {
pages Int
updated_at DateTime @updatedAt
created_at DateTime @default(now())
modified_at DateTime?
modified_at DateTime? // last modified date of the file
deleted_at DateTime?
hash String? // This is **not** an integrity check(sum), and is not used to verify the file contents.
path String
status String @default("READY") // UNKNOWN, READY, UNSUPPORTED, ERROR, MISSING
Expand Down Expand Up @@ -713,6 +716,7 @@ model UserPreferences {
enable_live_refetch Boolean @default(false)
enable_discord_presence Boolean @default(false)
enable_compact_display Boolean @default(false)
enable_gradients Boolean @default(false) // Some themes have gradients, not all
enable_double_sidebar Boolean @default(true)
enable_replace_primary_sidebar Boolean @default(false)
Expand Down
98 changes: 98 additions & 0 deletions core/src/db/entity/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,104 @@ pub enum LayoutMode {
TABLE,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, Type, ToSchema)]
pub enum ReadingDirection {
#[default]
#[serde(rename = "ltr")]
LeftToRight,
#[serde(rename = "rtl")]
RightToLeft,
}

impl FromStr for ReadingDirection {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ltr" => Ok(ReadingDirection::LeftToRight),
"rtl" => Ok(ReadingDirection::RightToLeft),
_ => Err(format!("\"{s}\" is not a valid reading direction")),
}
}
}

impl fmt::Display for ReadingDirection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ReadingDirection::LeftToRight => write!(f, "ltr"),
ReadingDirection::RightToLeft => write!(f, "rtl"),
}
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, Type, ToSchema)]
pub enum ReadingMode {
#[default]
#[serde(rename = "paged")]
Paged,
#[serde(rename = "continuous:vertical")]
ContinuousVertical,
#[serde(rename = "continuous:horizontal")]
ContinuousHorizontal,
}

impl FromStr for ReadingMode {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"paged" => Ok(ReadingMode::Paged),
"continuous:vertical" => Ok(ReadingMode::ContinuousVertical),
"continuous:horizontal" => Ok(ReadingMode::ContinuousHorizontal),
_ => Err(format!("\"{s}\" is not a valid reader mode")),
}
}
}

impl fmt::Display for ReadingMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ReadingMode::Paged => write!(f, "paged"),
ReadingMode::ContinuousVertical => write!(f, "continuous:vertical"),
ReadingMode::ContinuousHorizontal => write!(f, "continuous:horizontal"),
}
}
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, Type, ToSchema)]
pub enum ReadingImageScaleFit {
#[default]
#[serde(rename = "height")]
Height,
#[serde(rename = "width")]
Width,
#[serde(rename = "none", alias = "original")]
None,
}

impl FromStr for ReadingImageScaleFit {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"height" => Ok(ReadingImageScaleFit::Height),
"width" => Ok(ReadingImageScaleFit::Width),
"none" | "original" => Ok(ReadingImageScaleFit::None),
_ => Err(format!("\"{s}\" is not a valid image scale fit")),
}
}
}

impl fmt::Display for ReadingImageScaleFit {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ReadingImageScaleFit::Height => write!(f, "height"),
ReadingImageScaleFit::Width => write!(f, "width"),
ReadingImageScaleFit::None => write!(f, "none"),
}
}
}

/// A struct representing a sort order for a column using react-table (tanstack)
#[derive(Default, Clone, Debug, Deserialize, Serialize, Type, ToSchema)]
pub struct ReactTableColumnSort {
Expand Down
26 changes: 25 additions & 1 deletion core/src/db/entity/library/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use specta::Type;
use utoipa::ToSchema;

use crate::{filesystem::image::ImageProcessorOptions, prisma::library_config};
use crate::{
db::entity::common::{ReadingDirection, ReadingImageScaleFit, ReadingMode},
filesystem::image::ImageProcessorOptions,
prisma::library_config,
};

use super::{IgnoreRules, LibraryPattern};

Expand All @@ -16,6 +22,12 @@ pub struct LibraryConfig {
pub process_metadata: bool,
pub library_pattern: LibraryPattern,
pub thumbnail_config: Option<ImageProcessorOptions>,
#[serde(default)] // TODO: remove this after update with experimental
pub default_reading_dir: ReadingDirection,
#[serde(default)] // TODO: remove this after update with experimental
pub default_reading_mode: ReadingMode,
#[serde(default)]
pub default_reading_image_scale_fit: ReadingImageScaleFit,
#[serde(default)]
pub ignore_rules: IgnoreRules,
// TODO(prisma-nested-create): Refactor once nested create is supported
Expand All @@ -40,6 +52,18 @@ impl From<library_config::Data> for LibraryConfig {
generate_file_hashes: data.generate_file_hashes,
process_metadata: data.process_metadata,
library_pattern: LibraryPattern::from(data.library_pattern),
default_reading_dir: ReadingDirection::from_str(
data.default_reading_dir.as_str(),
)
.unwrap_or_default(),
default_reading_mode: ReadingMode::from_str(
data.default_reading_mode.as_str(),
)
.unwrap_or_default(),
default_reading_image_scale_fit: ReadingImageScaleFit::from_str(
data.default_reading_image_scale_fit.as_str(),
)
.unwrap_or_default(),
thumbnail_config: data.thumbnail_config.map(|config| {
ImageProcessorOptions::try_from(config).unwrap_or_default()
}),
Expand Down
Loading

0 comments on commit 185badd

Please sign in to comment.