Skip to content

Commit

Permalink
Merge pull request #32 from picture-pro/28-add-public-purchase-page
Browse files Browse the repository at this point in the history
28 add public purchase page
  • Loading branch information
johnbchron authored Feb 23, 2024
2 parents 8040388 + 8025c6a commit 3e89f11
Show file tree
Hide file tree
Showing 19 changed files with 571 additions and 70 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ SURREALDB_ROOT_PASS=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=
APP_BASE_URL=
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ simple_logger = "4.2.0"
surrealdb = { version = "1" }
thiserror = "1"
chrono = { version = "0.4", features = [ "serde" ] }
timeago = { version = "0.4", default-features = false, features = [ "chrono" ] }
tokio = { version = "1", features = ["full"] }
tower = { version = "0.4", features = ["full"] }
tower-http = { version = "0.5", features = ["full"] }
Expand Down
20 changes: 20 additions & 0 deletions crates/bl/src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ pub async fn fetch_user_owned_photo_groups(
Ok(groups)
}

#[instrument]
pub async fn fetch_photo_group(
photo_group_id: core_types::PhotoGroupRecordId,
) -> Result<Option<core_types::PhotoGroup>> {
let client = SurrealRootClient::new()
.await
.wrap_err("Failed to create surreal client")?;
client
.use_ns("main")
.use_db("main")
.await
.wrap_err("Failed to use surreal namespace/database")?;

let group = core_types::PhotoGroup::fetch(photo_group_id, &client)
.await
.wrap_err("Failed to fetch photo group")?;

Ok(group)
}

#[instrument]
pub async fn fetch_user(
user_id: core_types::UserRecordId,
Expand Down
1 change: 0 additions & 1 deletion crates/core_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ mod price;
#[cfg(feature = "ssr")]
pub(crate) mod ssr;

#[cfg(feature = "ssr")]
pub use ulid::Ulid;

#[cfg(feature = "ssr")]
Expand Down
2 changes: 2 additions & 0 deletions crates/site-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ cfg-if.workspace = true
thiserror.workspace = true
serde.workspace = true
bytes = { workspace = true, optional = true }
chrono = { workspace = true }
timeago = { workspace = true }
tracing = { workspace = true, optional = true }

validator = "0.16.1"
Expand Down
58 changes: 2 additions & 56 deletions crates/site-app/src/components/gallery.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use leptos::*;

use crate::components::user::UserName;
use crate::components::photo_group::PhotoGroup;

#[component]
pub fn Gallery() -> impl IntoView {
Expand Down Expand Up @@ -47,7 +47,7 @@ fn PhotoGroupList(groups: Vec<core_types::PhotoGroup>) -> impl IntoView {
}

view! {
<div class="flex-1 flex flex-col gap-4 justify-stretch max-w-lg">
<div class="flex-1 flex flex-col gap-4 items-stretch lg:max-w-lg">
{ groups.into_iter().map(|group| {
view! {
<PhotoGroup group=group />
Expand All @@ -59,60 +59,6 @@ fn PhotoGroupList(groups: Vec<core_types::PhotoGroup>) -> impl IntoView {
.into_view()
}

#[component]
fn PhotoGroup(group: core_types::PhotoGroup) -> impl IntoView {
let status_element = match group.status {
core_types::PhotoGroupStatus::OwnershipForSale { digital_price } => view! {
<p class="text-2xl tracking-tight text-base-content">
"For Sale: "
<span class="font-semibold">
{ format!("${:.2}", digital_price.0) }
</span>
</p>
}
.into_view(),
_ => view! {
<p class="text-xl font-semibold tracking-tight text-base-content/80">
"Not For Sale"
</p>
}
.into_view(),
};

view! {
<div class="flex p-4 gap-4 bg-base-100 rounded-box shadow">
{ group.photos.into_iter().map(|photo_id| {
view! {
<crate::components::photo::Photo photo_id=photo_id />
}
.into_view()
}).collect::<Vec<_>>() }
<div class="flex-1 flex flex-col gap-2">
{ status_element }
<div class="flex-1" />
<div class="flex flex-col gap-1">
<p class="text-xs text-base-content/80">
"Owned by "<UserName id={group.owner} />
</p>
<p class="text-xs text-base-content/80">
"Photographed by "<UserName id={group.photographer} />
</p>
</div>
</div>
<div class="flex flex-col items-end justify-between">
// context menu ellipsis
<button class="
d-btn d-btn-ghost d-btn-circle d-btn-sm text-xl font-bold
justify-center items-center text-center
">"⋮"</button>
<button class="d-btn d-btn-primary d-btn-sm text-lg font-semibold tracking-tight">
"Share"
</button>
</div>
</div>
}
}

#[cfg_attr(feature = "ssr", tracing::instrument)]
#[server]
pub async fn fetch_user_photo_groups(
Expand Down
11 changes: 11 additions & 0 deletions crates/site-app/src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod form;
pub mod gallery;
pub mod navigation;
pub mod photo;
pub mod photo_group;
pub mod photo_upload;
pub mod user;

Expand Down Expand Up @@ -33,4 +34,14 @@ pub mod basic {
</a>
}
}

#[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());

view! {
<span>{ formatted }</span>
}
}
}
112 changes: 112 additions & 0 deletions crates/site-app/src/components/photo_group.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use leptos::*;

use crate::components::user::UserName;

#[component]
pub fn EllipsisButton() -> impl IntoView {
view! {
<button class="
d-btn d-btn-ghost d-btn-circle d-btn-sm text-xl font-bold
justify-center items-center text-center
">"⋮"</button>
}
}

#[component]
pub fn PhotoGroup(
group: core_types::PhotoGroup,
#[prop(default = false)] read_only: bool,
#[prop(default = "")] extra_class: &'static str,
) -> impl IntoView {
let status_element = match group.status {
core_types::PhotoGroupStatus::OwnershipForSale { digital_price } => view! {
<p class="text-2xl tracking-tight text-base-content">
"For Sale: "
<span class="font-semibold">
{ format!("${:.2}", digital_price.0) }
</span>
</p>
}
.into_view(),
_ => view! {
<p class="text-xl font-semibold tracking-tight text-base-content/80">
"Not For Sale"
</p>
}
.into_view(),
};

let share_url = format!(
"{}/photo/{}",
std::env::var("APP_BASE_URL").expect("APP_BASE_URL not set"),
group.id.0
);

let photos_element = view! {
{ group.photos.clone().into_iter().map(|photo_id| {
view! {
<crate::components::photo::Photo photo_id=photo_id />
}
.into_view()
}).collect::<Vec<_>>() }
}
.into_view();

let owned_by_element = view! {
"Owned by "<UserName id={group.owner} />
}
.into_view();

let uploaded_by_element = view! {
"Uploaded by "<UserName id={group.photographer} />
}
.into_view();

let created_at_element = view! {
<crate::components::basic::TimeAgo time={group.meta.created_at} />
}
.into_view();

view! {
<div class={format!(
"grid grid-cols-[auto_1fr] p-6 gap-4 bg-base-100 rounded-box shadow {extra_class}"
)}>
<div class={format!(
"col-start-1 col-span-1 row-start-1 flex flex-col justify-center xs:px-4 {adjusted_for_action}",
adjusted_for_action = if !read_only { "row-span-1 sm:row-span-2" } else { "row-span-2" },
)}>
{ photos_element }
</div>
<div class="col-start-2 col-span-1 row-start-1 row-span-1 flex flex-row justify-between gap-4">
{ status_element }
<crate::components::photo_group::EllipsisButton />
</div>
<div class={format!(
"row-start-2 row-span-1 flex flex-row items-end justify-between gap-4 {adjusted_for_action}",
adjusted_for_action = if !read_only { "col-start-1 col-span-2 sm:col-start-2 sm:col-span-1" } else { "col-start-2 col-span-1" },
)}>
<div class="flex flex-col gap-1">
<p class="text-xs text-base-content/80">
{ owned_by_element }
</p>
<p class="text-xs text-base-content/80">
{ uploaded_by_element }
", "
{ created_at_element }
</p>
</div>
{ match read_only {
false => view! {
<a
class="d-btn d-btn-primary d-btn-sm text-lg font-semibold tracking-tight"
href={ share_url }
>
"Share"
</a>
}.into_view(),
true => ().into_view()
} }
</div>
</div>
}
}
8 changes: 6 additions & 2 deletions crates/site-app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ use leptos_meta::*;
use leptos_router::*;

use crate::{
components::navigation::navigate_to,
components::navigation::reload,
error_template::{AppError, ErrorTemplate},
pages::Footer,
utils::authenticated_user,
};

Expand Down Expand Up @@ -39,7 +40,10 @@ pub fn App() -> impl IntoView {
<Route path="/dashboard" view=pages::dashboard::DashboardPage/>
<Route path="/login" view=pages::auth::login_page::LoginPage/>
<Route path="/signup" view=pages::auth::signup_page::SignupPage/>
<Route path="/photo/:id" view=pages::purchase::PurchasePage/>
<Route path="/photo" view=pages::purchase::error::PurchasePageNoId/>
</Routes>
<Footer/>
</div>
</Router>
}
Expand Down Expand Up @@ -88,7 +92,7 @@ pub fn LogoutButton(class: Option<String>) -> impl IntoView {

create_effect(move |_| {
if matches!(logout_value(), Some(Ok(_))) {
navigate_to("/");
reload();
}
});

Expand Down
6 changes: 3 additions & 3 deletions crates/site-app/src/pages/dashboard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ pub fn DashboardPage() -> impl IntoView {
}

view! {
<PageWrapper bg_color="" shadow="">
<PageWrapper backed=false>
<p class="text-4xl font-semibold tracking-tight">"Marketplace Photos"</p>
<div class="flex flex-row justify-between gap-4 items-start">
<crate::components::gallery::Gallery />
<div class="flex flex-col lg:flex-row-reverse items-stretch lg:justify-between gap-4 items-start">
<crate::components::photo_upload::PhotoUpload />
<crate::components::gallery::Gallery />
</div>
</PageWrapper>
}
Expand Down
Loading

0 comments on commit 3e89f11

Please sign in to comment.