Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

100 overhaul visual style #104

Merged
merged 15 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
547 changes: 211 additions & 336 deletions Cargo.lock

Large diffs are not rendered by default.

34 changes: 25 additions & 9 deletions crates/artifact/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,46 @@ pub fn object_store_from_env(
Ok(Box::new(object_store))
}

fn cache_dir() -> std::path::PathBuf { std::env::temp_dir() }
fn cache_path(id: &str) -> std::path::PathBuf { cache_dir().join(id) }
/// Get the cache directory for artifacts.
///
/// By default, this is the system's temporary directory. It can be overridden
/// by setting the `TMPDIR` environment variable. If the directory does not
/// exist, it will be created.
fn get_cache_dir() -> Result<std::path::PathBuf> {
let cache_dir = std::env::temp_dir();
if !cache_dir.exists() {
tracing::debug!("creating cache directory");
std::fs::create_dir_all(&cache_dir)
.wrap_err("Failed to create cache directory")?;
}
Ok(cache_dir)
}
/// Get the path to the cached artifact.
///
/// The parent directory will be created in [`get_cache_dir`] if it does not
/// exist.
fn cache_path(id: &str) -> Result<std::path::PathBuf> {
get_cache_dir().map(|d| d.join(id))
}

#[instrument(skip(object_store))]
pub async fn download_artifact(
object_store: ObjectStoreGenerator,
id: &str,
) -> Result<bytes::Bytes> {
let cache_path = cache_path(id);
let cache_path = cache_path(id)?;
if cache_path.exists() {
tracing::debug!("using cached artifact instead of downloading");
let contents = tokio::fs::read(cache_path)
.await
.wrap_err("Failed to read cached artifact")?;
return Ok(contents.into());
}

let object_store = object_store()?;
tracing::debug!("downloading uncached artifact");
let contents = inner_download_artifact(&*object_store, id).await?;

if !cache_dir().exists() {
tokio::fs::create_dir_all(cache_dir())
.await
.wrap_err("Failed to create cache directory")?;
}
tokio::fs::write(&cache_path, &contents)
.await
.wrap_err("Failed to write cached artifact")?;
Expand Down Expand Up @@ -73,7 +89,7 @@ pub async fn upload_artifact(
id: &str,
contents: bytes::Bytes,
) -> Result<()> {
let cache_path = cache_path(id);
let cache_path = cache_path(id)?;
tokio::fs::write(&cache_path, &contents)
.await
.wrap_err("Failed to write cached artifact")?;
Expand Down
4 changes: 2 additions & 2 deletions crates/bl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ edition = "2021"
[dependencies]
core_types = { path = "../core_types" }
leptos.workspace = true
bytes.workspace = true
serde.workspace = true
thiserror.workspace = true
http.workspace = true
Expand All @@ -19,6 +18,7 @@ artifact = { path = "../artifact", optional = true }
rmp-serde = "1.1.2"

base64 = { workspace = true, optional = true }
bytes = { workspace = true, optional = true }
chrono = { workspace = true, optional = true }
color-eyre = { workspace = true, optional = true }
image = { workspace = true, optional = true }
Expand All @@ -37,5 +37,5 @@ hydrate = [ "leptos/hydrate" ]
ssr = [
"core_types/ssr", "leptos/ssr", "dep:clients", "dep:artifact", "dep:base64",
"dep:color-eyre", "dep:image", "dep:qrcode", "dep:rayon", "dep:surrealdb",
"dep:tokio", "dep:tracing", "dep:kamadak-exif", "dep:chrono",
"dep:tokio", "dep:tracing", "dep:kamadak-exif", "dep:chrono", "dep:bytes",
]
4 changes: 2 additions & 2 deletions crates/site-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ serde.workspace = true
bytes = { workspace = true, optional = true }
time = { workspace = true, optional = true }
chrono = { workspace = true }
timeago = { workspace = true }
timeago = { workspace = true, optional = true }
tower-sessions = { workspace = true, optional = true }
tracing = { workspace = true, optional = true }

Expand All @@ -39,6 +39,6 @@ hydrate = ["bl/hydrate", "leptos/hydrate", "leptos_meta/hydrate", "leptos_router
ssr = [
"bl/ssr", "leptos/ssr", "leptos/tracing", "leptos_meta/ssr", "leptos_router/ssr",
"dep:leptos_axum", "dep:auth", "dep:bytes", "dep:tracing", "dep:tower-sessions",
"dep:time",
"dep:time", "dep:timeago",
]

22 changes: 19 additions & 3 deletions crates/site-app/src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,27 @@ pub mod basic {

#[component]
pub fn TimeAgo(time: chrono::DateTime<chrono::Utc>) -> impl IntoView {
let formatter = timeago::Formatter::new();
let formatted = formatter.convert_chrono(time, chrono::Utc::now());
let formatted_time = create_blocking_resource(move || time, format_timeago);

view! {
<span>{ formatted }</span>
<Suspense fallback=|| view!{}>
{ move || match formatted_time() {
Some(Ok(time)) => view! {
<span>{ time }</span>
}.into_view(),
_ => view! {
<span>...</span>
}.into_view()
}}
</Suspense>
}
}

#[server]
async fn format_timeago(
date: chrono::DateTime<chrono::Utc>,
) -> Result<String, ServerFnError> {
let formatter = timeago::Formatter::new();
Ok(formatter.convert_chrono(date, chrono::Utc::now()))
}
}
9 changes: 7 additions & 2 deletions crates/site-app/src/components/photo_deck.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use leptos::*;

use crate::components::photo::PhotoSize;

#[component]
pub fn PhotoDeck(ids: Vec<core_types::PhotoRecordId>) -> impl IntoView {
pub fn PhotoDeck(
ids: Vec<core_types::PhotoRecordId>,
#[prop(default = PhotoSize::Regular)] size: PhotoSize,
) -> impl IntoView {
view! {
{ ids.into_iter().map(|photo_id| {
view! {
<crate::components::photo::Photo photo_id=photo_id />
<crate::components::photo::Photo photo_id=photo_id size=size />
}
.into_view()
}).collect::<Vec<_>>() }
Expand Down
72 changes: 34 additions & 38 deletions crates/site-app/src/components/photo_upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ fn round_with_scale(input: f32, scale: f32) -> f32 {
}

/// Converts a price to a reasonable multiple.
#[allow(illegal_floating_point_literal_pattern)]
fn sensible_price(input: f32) -> f32 {
match input {
0.0..=1.0 => round_with_scale(input, 0.05),
Expand Down Expand Up @@ -72,47 +71,44 @@ pub fn PhotoUpload() -> impl IntoView {
});

view! {
<div class="d-card bg-base-100 shadow max-w-sm">
<div class="d-card-body gap-4">
<p class="text-2xl font-semibold tracking-tight">"Upload Photo"</p>

// price input
<div class="flex flex-row gap-4 items-center">
<label for="price">"Price"</label>
<input
type="range" class="d-range" id="price" name="price"
min={MIN_PRICE.log10()} max={MAX_PRICE.log10()} step=0.01
on:input=move |e: Event| {
set_logarithmic_price(event_target_value(&e).parse::<f32>().unwrap());
}
value={DEFAULT_PRICE.log10()}
prop:value=logarithmic_price
/>
<p class="min-w-[4rem] text-right">{move || format!("${:.2}", price())}</p>
</div>

// file input
<div class="flex flex-col p-8 gap-4 rounded-box bg-base-100 shadow max-w-sm">
<p class="text-2xl font-semibold tracking-tight">"Upload Photo"</p>

// price input
<div class="flex flex-row gap-4 items-center">
<label for="price">"Price"</label>
<input
type="file" class="d-file-input d-file-input-bordered w-full"
name="photo" accept="image/*" capture="camera" multiple="multiple"
required=true on:input=move |e: Event| {
let target = e.target().unwrap().dyn_into::<HtmlInputElement>().unwrap();
set_files(target.files());
type="range" class="d-range" id="price" name="price"
min={MIN_PRICE.log10()} max={MAX_PRICE.log10()} step=0.01
on:input=move |e: Event| {
set_logarithmic_price(event_target_value(&e).parse::<f32>().unwrap());
}
value={DEFAULT_PRICE.log10()}
prop:value=logarithmic_price
/>
<p class="min-w-[4rem] text-right">{move || format!("${:.2}", price())}</p>
</div>

// upload button
<div class="d-form-control mt-6">
<button
class="d-btn d-btn-primary w-full"
disabled=move || pending() || files().is_none()
on:click=move |_| upload_action.dispatch((files().unwrap(), price()))
>
{ move || if pending() { view!{ <span class="d-loading d-loading-spinner" /> }.into_view() } else { view! {}.into_view() } }
"Upload"
</button>
</div>

// file input
<input
type="file" class="d-file-input d-file-input-bordered w-full"
name="photo" accept="image/*" capture="camera" multiple="multiple"
required=true on:input=move |e: Event| {
let target = e.target().unwrap().dyn_into::<HtmlInputElement>().unwrap();
set_files(target.files());
}
/>

// upload button
<div class="d-form-control mt-6">
<button
class="d-btn d-btn-primary w-full"
disabled=move || pending() || files().is_none()
on:click=move |_| upload_action.dispatch((files().unwrap(), price()))
>
{ move || if pending() { view!{ <span class="d-loading d-loading-spinner" /> }.into_view() } else { view! {}.into_view() } }
"Upload"
</button>
</div>
</div>
}
Expand Down
20 changes: 8 additions & 12 deletions crates/site-app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ pub fn App() -> impl IntoView {
view! {
<Style>{include_str!("../style/fonts.css")}</Style>
<Stylesheet id="leptos" href="/pkg/site.css"/>
<Link rel="preload" href="fonts/inter.ttf" as_="font" type_="font/ttf" crossorigin="anonymous" />
<Link rel="preload" href="/fonts/inter.ttf" as_="font" type_="font/ttf" crossorigin="anonymous" />

<Html lang="en" attr:data-theme="wireframe" />
<Html lang="en" attr:data-theme="dark" />

// set the metadata
<Title text="PicturePro"/>
Expand Down Expand Up @@ -59,21 +59,17 @@ pub fn App() -> impl IntoView {
#[component]
pub fn Navbar() -> impl IntoView {
let current_user = authenticated_user();
let home_url = if current_user.is_some() {
"/dashboard"
} else {
"/"
};
let button_class = "d-btn d-btn-neutral d-btn-sm";

let user_area = match current_user {
Some(_user) => view! {
<a class="d-btn d-btn-neutral d-btn-sm" href="/dashboard">Dashboard</a>
<LogoutButton class={Some("d-btn d-btn-neutral d-btn-sm".into())} />
<a class={button_class} href="/dashboard">Dashboard</a>
<LogoutButton class={Some(button_class.into())} />
}
.into_view(),
None => view! {
<a class="d-btn d-btn-neutral d-btn-sm" href="/login">Login</a>
<a class="d-btn d-btn-neutral d-btn-sm" href="/signup">Sign Up</a>
<a class={button_class} href="/login">Login</a>
<a class={button_class} href="/signup">Sign Up</a>
}
.into_view(),
};
Expand All @@ -82,7 +78,7 @@ pub fn Navbar() -> impl IntoView {
<div class="bg-base-100 w-full shadow">
<div class="d-navbar md:container md:mx-auto">
<div class="flex-1">
<a class="d-btn d-btn-ghost text-xl d-btn-sm" href={home_url}>PicturePro</a>
<a class="d-btn d-btn-ghost text-xl d-btn-sm" href="/">PicturePro</a>
</div>
<div class="flex-none flex flex-row items-center gap-2">
{user_area}
Expand Down
Loading
Loading