Finished media_slideshow

This commit is contained in:
Franklin 2023-03-25 13:12:51 -04:00
parent 474cbf3817
commit 66e932a6a0
9 changed files with 297 additions and 29 deletions

View File

@ -0,0 +1,43 @@
.loading-container{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 50vh;
}.lds-facebook {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-facebook div {
display: inline-block;
position: absolute;
left: 8px;
width: 16px;
background: #5D6A73;
animation: lds-facebook 1.2s cubic-bezier(0, 0.5, 0.5, 1) infinite;
}
.lds-facebook div:nth-child(1) {
left: 8px;
animation-delay: -0.24s;
}
.lds-facebook div:nth-child(2) {
left: 32px;
animation-delay: -0.12s;
}
.lds-facebook div:nth-child(3) {
left: 56px;
animation-delay: 0;
}
@keyframes lds-facebook {
0% {
top: 8px;
height: 64px;
}
50%, 100% {
top: 24px;
height: 32px;
}
}

View File

@ -0,0 +1,83 @@
.media-slideshow-frame {
height: 360px;
background-color: gray;
width: 100%;
position: relative;
}
.media-slideshow-photo {
background-color: gray;
object-fit: cover;
width: 100%;
height: 100%;
display: none;
}
.media-slideshow-video {
background-color: gray;
width: 100%;
height: 100%;
display: none;
object-fit: cover;
}
.media-slideshow-arrow-left {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
margin-top: -22px;
padding: 16px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
transition: 0.6s ease;
}
.media-slideshow-arrow-right {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
margin-top: -22px;
padding: 16px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
right: 0;
border-radius: 3px 0 0 3px;
transition: 0.6s ease;
}
.media-slideshow-arrow-left:hover, .media-slideshow-arrow-right:hover {
background-color: rgba(0,0,0,0.8);
}
/* Number text (1/3 etc) */
.media-slideshow-number-text {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
background-color: rgba(0,0,0,0.8);
border-radius: 2px;
}
/* Fading animation */
.fade {
animation-name: fade;
animation-duration: 1.5s;
}
@keyframes fade {
from {opacity: .4}
to {opacity: 1}
}

View File

@ -18,15 +18,10 @@ Divide the Details page into 3 main sections:
justify-content: stretch; justify-content: stretch;
align-items: center; align-items: center;
width: 100%; width: 100%;
} gap: 10px;
.details-head-image-frame {
display: flex;
height: 300px;
width: 100%;
background-color: gray;
} }
.details-head-title { .details-head-title {
font-size: 25px; font-size: 25px;
} }

View File

@ -2,6 +2,7 @@
.navbar-background { .navbar-background {
border-top: 3px solid #04B2D9; border-top: 3px solid #04B2D9;
position: fixed; position: fixed;
z-index: 1;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

@ -10,7 +10,8 @@
<link data-trunk type="text/css" href="css/search.css" rel="css" /> <link data-trunk type="text/css" href="css/search.css" rel="css" />
<link data-trunk type="text/css" href="css/project_card.css" rel="css" /> <link data-trunk type="text/css" href="css/project_card.css" rel="css" />
<link data-trunk type="text/css" href="css/details.css" rel="css" /> <link data-trunk type="text/css" href="css/details.css" rel="css" />
<link data-trunk type="text/css" href="css/components/loading.css" rel="css" />
<link data-trunk type="text/css" href="css/components/media_slideshow.css" rel="css" />
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Sacramento" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Sacramento" rel="stylesheet">

View File

@ -0,0 +1,72 @@
use jl_types::domain::media::Media;
use yew::prelude::*;
#[function_component(MediaSlideshow)]
pub fn media_slideshow(props: &MediaSlideshowProps) -> Html {
let current_media_index = use_state(|| 0);
let total_media_count = props.media_list.len();
let previous_click_cb = {
let current_media_index = current_media_index.clone();
let total_media_count = total_media_count.clone();
Callback::from(move |_| {
if *current_media_index > 0 {
current_media_index.set(*current_media_index -1);
} else {
current_media_index.set(total_media_count - 1);
}
})
};
let next_click_cb = {
let current_media_index = current_media_index.clone();
let total_media_count = total_media_count.clone();
Callback::from(move |_| {
if *current_media_index >= total_media_count - 1 {
current_media_index.set(0);
} else {
current_media_index.set(*current_media_index + 1);
}
})
};
html! {
<div class={"media-slideshow-frame"}>
{
props.media_list.iter().enumerate().map(|(index, media)| {
match media {
Media::Photo(url) => html! {
<img class={"media-slideshow-photo fade"} style={if *current_media_index == index {
"display: block;"
} else {
"display: none;"
}} src={url.clone()} />
},
Media::Video(url) => html! {
<iframe class={"media-slideshow-video fade"} style={if *current_media_index == index {
"display: block;"
} else {
"display: none;"
}}
src={format!("{url}?autoplay=1&mute=1")}>
</iframe>
}
}
}).collect::<Html>()
}
<div class={"media-slideshow-arrow-left"} onclick={previous_click_cb}>
<i class="fa-solid fa-chevron-left"></i>
</div>
<div class={"media-slideshow-arrow-right"} onclick={next_click_cb}>
<i class="fa-solid fa-chevron-right"></i>
</div>
<div class={"media-slideshow-number-text"}>{format!("{}/{}", *current_media_index + 1, total_media_count)}</div>
</div>
}
}
#[derive(Properties, PartialEq, PartialOrd)]
pub struct MediaSlideshowProps {
pub media_list: Vec<Media>,
}

View File

@ -1,3 +1,4 @@
pub mod nav_bar; pub mod nav_bar;
pub mod search_filter; pub mod search_filter;
pub mod project_card; pub mod project_card;
pub mod media_slideshow;

View File

@ -1,25 +1,80 @@
use jl_types::dto::listing::Listing;
use log::{error, info};
use uuid::Uuid; use uuid::Uuid;
use yew::prelude::*; use yew::prelude::*;
use crate::components::nav_bar::NavigationBar; use crate::{components::{nav_bar::NavigationBar, media_slideshow::MediaSlideshow}, api::backend::get_project_listing};
#[function_component(DetailsPage)] #[function_component(DetailsPage)]
pub fn details_page(_props: &DetailsPageProps) -> Html { pub fn details_page(props: &DetailsPageProps) -> Html {
//TODO: query backend
let finished_loading = use_state(|| false);
let listing_handle: UseStateHandle<Listing> = use_state(|| Listing::default());
use_state(|| {
let finished_loading = finished_loading.clone();
let listing_handle = listing_handle.clone();
let project_id = props.project_id.clone();
wasm_bindgen_futures::spawn_local(async move {
match get_project_listing(&project_id).await {
Ok(listing) => {
listing_handle.set(listing);
finished_loading.set(true);
info!("Finshed loading listing");
},
Err(error) => {
error!("Error fetching listing. Error: {:?}", error);
},
};
});
});
let listing = (*listing_handle).clone();
let project_title = format!("{} en {}, {}", &listing.project.project_type, &listing.location.district, &listing.location.city);
let project_description = &listing.project.description;
let project_media_list = listing.project.media.media_list;
/*let project_price = format!("Desde ${} USD", match listing.project.starts_from {
Some(price) => {
let price_separated = price.separate_with_commas();
if price_separated.contains(".") {
price_separated
} else {
format!("{price_separated}.00")
}
},
None => "0.00".into()
});*/
let project_condition = &listing.project.project_condition.to_string();
let project_est_finish_date = &listing.project.finish_date.format("%m/%Y");
html!{ html!{
<> <>
<NavigationBar/> <NavigationBar/>
<div class={"page-container"}> <div class={"page-container"}>
{
if *finished_loading {
html!{
<div class={"details-container"}> <div class={"details-container"}>
<div class={"details-head"}> <div class={"details-head"}>
<div class={"details-head-image-frame"}> <MediaSlideshow media_list={project_media_list}/>
<div class={"details-head-title"}>{project_title}</div>
</div>
<div class={"details-head-title"}>{"Suites by refa piantini"}</div>
<div class={"details-head-price"}>{"RD$123,130.00"}</div> <div class={"details-head-price"}>{"RD$123,130.00"}</div>
<div>{"Andres Julio Aybar #39"}</div> <div>{project_description}</div>
<div>{"Descripción"}</div>
</div> </div>
</div> </div>
}
} else {
html!{
<div class={"loading-container"}>
<div class="lds-facebook"><div></div><div></div><div></div></div>
</div>
}
}
}
</div> </div>
</> </>
} }

View File

@ -14,6 +14,7 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)])); let districts_handle = use_state(|| Vec::from([OptionWrapper::new(None)]));
let search_results_handle: UseStateHandle<Vec<ProjectCardDto>> = use_state(|| Vec::new()); let search_results_handle: UseStateHandle<Vec<ProjectCardDto>> = use_state(|| Vec::new());
let page_counter: UseStateHandle<i64> = use_state(|| 1); let page_counter: UseStateHandle<i64> = use_state(|| 1);
let finished_loading = use_state(|| false);
let mut filters = Vec::new(); let mut filters = Vec::new();
if props.project_state.eq(&ProjectState::Finished) { if props.project_state.eq(&ProjectState::Finished) {
@ -25,6 +26,7 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
let cities_handle = cities_handle.clone(); let cities_handle = cities_handle.clone();
let search_results_handle = search_results_handle.clone(); let search_results_handle = search_results_handle.clone();
let page_counter = page_counter.clone(); let page_counter = page_counter.clone();
let finished_loading = finished_loading.clone();
wasm_bindgen_futures::spawn_local(async move { wasm_bindgen_futures::spawn_local(async move {
match get_all_cities().await { match get_all_cities().await {
Ok(cities) => { Ok(cities) => {
@ -36,7 +38,8 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
}; };
match get_all_projects_with_filters_paged(&(*page_counter), filters).await { match get_all_projects_with_filters_paged(&(*page_counter), filters).await {
Ok(projects) => { Ok(projects) => {
search_results_handle.set(projects) search_results_handle.set(projects);
finished_loading.set(true);
}, },
Err(error) => info!("Error in loading projects: {error}"), Err(error) => info!("Error in loading projects: {error}"),
}; };
@ -233,9 +236,23 @@ pub fn search_page(props: &SearchPageProperties) -> Html {
<div class={"project-search-divider"}/> <div class={"project-search-divider"}/>
</div> </div>
{
if *finished_loading {
html!{
<div class={"project-search-results-container"}> // Search Results Content <div class={"project-search-results-container"}> // Search Results Content
{(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()} {(*search_results_handle).clone().into_iter().map(|project| html!{<ProjectCard {project}/>}).collect::<Html>()}
</div> </div>
}
} else {
html!{
<div class={"project-search-results-container"}> // Search Results Content
<div class="lds-facebook"><div></div><div></div><div></div></div>
</div>
}
}
}
</div> </div>
</> </>
} }