Skip to content

Commit

Permalink
feat: profile videos (#214)
Browse files Browse the repository at this point in the history
* added profile videos

* fix profile_post page

* fetch post using new cursor video api

* change route for profile post

* fix lint issues
  • Loading branch information
ravi-sawlani-yral authored Apr 30, 2024
1 parent e024805 commit ce56bb0
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 48 deletions.
3 changes: 2 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
menu::Menu,
post_view::{PostView, PostViewCtx},
privacy::PrivacyPolicy,
profile::ProfileView,
profile::{profile_post::ProfilePost, ProfileView},
refer_earn::ReferEarn,
root::RootPage,
terms::TermsOfService,
Expand Down Expand Up @@ -114,6 +114,7 @@ pub fn App() -> impl IntoView {
<GoogleAuthRedirectorRoute/>
<Route path="" view=BaseRoute>
<Route path="/hot-or-not/:canister_id/:post_id" view=PostView/>
<Route path="/profile/:canister_id/:post_id" view=ProfilePost/>
<Route path="/profile/:id" view=ProfileView/>
<Route path="/upload" view=UploadPostPage/>
<Route path="/error" view=ServerErrorPage/>
Expand Down
1 change: 1 addition & 0 deletions src/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ pub mod social;
pub mod spinner;
pub mod title;
pub mod toggle;
pub mod video_player;
2 changes: 1 addition & 1 deletion src/component/nav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub fn NavBar() -> impl IntoView {
"/upload" => 2,
"/wallet" | "/transactions" => 3,
"/menu" => 4,
s if s.starts_with("/hot-or-not") => {
s if s.starts_with("/hot-or-not") || s.starts_with("/profile") => {
home_path.set(path);
0
}
Expand Down
34 changes: 34 additions & 0 deletions src/component/video_player.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use leptos::*;
use leptos_dom::{html::Video, NodeRef};

#[component]
pub fn VideoPlayer(
#[prop(optional)] node_ref: NodeRef<Video>,
#[prop(into)] view_bg_url: Signal<Option<String>>,
#[prop(into)] view_video_url: Signal<Option<String>>,
) -> impl IntoView {
let muted = create_rw_signal(false);

view! {
<div class="w-full h-full absolute top-0 left-0 grid grid-cols-1 justify-items-center items-center cursor-pointer z-[3]">
<input
on:change=move |_| muted.update(|m| *m = !*m)
type="checkbox"
value=""
class="sr-only"
/>
<video
_ref=node_ref
class="object-contain h-dvh max-h-dvh cursor-pointer"
poster=view_bg_url
src=view_video_url
loop
muted
playsinline
disablepictureinpicture
disableremoteplayback
preload="auto"
></video>
</div>
}
}
2 changes: 1 addition & 1 deletion src/page/post_view/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod error;
mod overlay;
pub mod overlay;
pub mod video_iter;
mod video_loader;

Expand Down
23 changes: 2 additions & 21 deletions src/page/post_view/video_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
individual_user_template::PostViewDetailsFromFrontend,
utils::{bg_url, mp4_url},
},
component::feed_popup::FeedPopUp,
component::{feed_popup::FeedPopUp, video_player::VideoPlayer},
state::{
auth::account_connected_reader, canisters::unauth_canisters,
local_storage::use_referrer_store,
Expand Down Expand Up @@ -201,25 +201,6 @@ pub fn VideoView(idx: usize, muted: RwSignal<bool>) -> impl IntoView {
VideoWatched.send_event(vid_details, container_ref);

view! {
<label class="w-full h-full absolute top-0 left-0 grid grid-cols-1 justify-items-center items-center cursor-pointer z-[3]">
<input
on:change=move |_| muted.update(|m| *m = !*m)
type="checkbox"
value=""
class="sr-only"
/>
<video
_ref=container_ref
class="object-contain h-dvh max-h-dvh cursor-pointer"
poster=view_bg_url
src=view_video_url
loop
muted
playsinline
disablepictureinpicture
disableremoteplayback
preload="auto"
></video>
</label>
<VideoPlayer node_ref=container_ref view_bg_url = Signal::derive(view_bg_url) view_video_url=Signal::derive(view_video_url)/>
}
}
1 change: 1 addition & 0 deletions src/page/profile/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod ic;
mod posts;
pub mod profile_post;
mod speculation;

use candid::Principal;
Expand Down
57 changes: 34 additions & 23 deletions src/page/profile/posts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,43 @@ use crate::{
use super::ic::ProfileStream;

#[component]
fn Post(details: PostDetails, _ref: NodeRef<html::Div>) -> impl IntoView {
let bg = bg_url(&details.uid);
fn Post(details: PostDetails, user_canister: Principal, _ref: NodeRef<html::Div>) -> impl IntoView {
let image_error = create_rw_signal(false);

let handle_image_error =
move |_| image_error.update(|image_error| *image_error = !*image_error);

let profile_post_url = format!("/profile/{}/{}", user_canister, details.id);
view! {
<div _ref=_ref class="relative w-full basis-1/3 md:basis-1/4 xl:basis-1/5">
<div class="relative aspect-[9/16] h-full rounded-md border-white/20 m-2 border-[1px]">
<object type="image/jpeg" class="object-cover w-full h-full" data=bg>
<div class="h-full flex text-center flex-col place-content-center items-center text-white">
<Icon class="h-8 w-8" icon=icondata::TbCloudX/>
<span class="text-md">Not Available</span>
<a class = "h-full w-full" href = profile_post_url>
<Show when=image_error
fallback=move || view! {
<img class="object-cover w-full h-full" on:error=handle_image_error src=bg_url(details.uid.clone())/>
}
>
<div class="h-full flex text-center flex-col place-content-center items-center text-white">
<Icon class="h-8 w-8" icon=icondata::TbCloudX/>
<span class="text-md">Not Available</span>
</div>
</Show>

<div class="absolute bottom-1 left-1 grid grid-cols-2 items-center gap-1">
<Icon
class="h-5 w-5 p-1 text-primary-500 rounded-full bg-black/30"
icon=icondata::AiHeartOutlined
/>
<span class="text-white text-xs">{details.likes}</span>
</div>
<div class="absolute bottom-1 right-1 grid grid-cols-2 items-center gap-1">
<Icon
class="h-5 w-5 p-1 text-white rounded-full bg-black/30"
icon=icondata::AiEyeOutlined
/>
<span class="text-white text-xs">{details.views}</span>
</div>
</object>
<div class="absolute bottom-1 left-1 grid grid-cols-2 items-center gap-1">
<Icon
class="h-5 w-5 p-1 text-primary-500 rounded-full bg-black/30"
icon=icondata::AiHeartOutlined
/>
<span class="text-white text-xs">{details.likes}</span>
</div>
<div class="absolute bottom-1 right-1 grid grid-cols-2 items-center gap-1">
<Icon
class="h-5 w-5 p-1 text-white rounded-full bg-black/30"
icon=icondata::AiEyeOutlined
/>
<span class="text-white text-xs">{details.views}</span>
</div>
</a>
</div>
</div>
}
Expand All @@ -53,8 +64,8 @@ pub fn ProfilePosts(user_canister: Principal) -> impl IntoView {
provider
empty_graphic=NoMorePostsGraphic
empty_text="No Videos Uploaded yet".into()
children=|details, _ref| {
view! { <Post details=details _ref=_ref.unwrap_or_default()/> }
children=move |details, _ref| {
view! { <Post details=details user_canister=user_canister _ref=_ref.unwrap_or_default()/> }
}
/>
}
Expand Down
118 changes: 118 additions & 0 deletions src/page/profile/profile_post.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use candid::Principal;
use leptos::{html::Video, *};
use leptos_router::*;

use crate::{
canister::utils::{bg_url, mp4_url},
component::{back_btn::BackButton, spinner::FullScreenSpinner, video_player::VideoPlayer},
page::post_view::video_iter::get_post_uid,
state::canisters::{authenticated_canisters, unauth_canisters},
try_or_redirect_opt,
utils::route::failure_redirect,
};

use crate::page::post_view::overlay::{HomeButtonOverlay, VideoDetailsOverlay};

#[derive(Params, PartialEq)]
struct ProfileVideoParams {
canister_id: String,
post_id: u64,
}

#[component]
pub fn ProfilePost() -> impl IntoView {
let params = use_params::<ProfileVideoParams>();
let your_profile = create_rw_signal(false);

let canister_and_post = move || {
params.with(|p| {
let p = p.as_ref().ok()?;
let canister_id = Principal::from_text(&p.canister_id).ok()?;

Some((canister_id, p.post_id))
})
};

let auth_cans_res = authenticated_canisters();

let post_details = create_resource(canister_and_post, move |canister_and_post| async move {
let (creator_canister_id, post_id) = canister_and_post?;
let auth_canisters = leptos::untrack(|| auth_cans_res.get().transpose());
let auth_canisters = try_or_redirect_opt!(auth_canisters);
match auth_canisters {
Some(canisters) => {
if canisters.user_canister() == creator_canister_id {
your_profile.update(|yu| *yu = true);
}
match get_post_uid(&canisters, creator_canister_id, post_id).await {
Ok(pd) => pd,
Err(e) => {
failure_redirect(e);
None
}
}
}
None => {
let canisters = unauth_canisters();
match get_post_uid(&canisters, creator_canister_id, post_id).await {
Ok(pd) => pd,
Err(e) => {
failure_redirect(e);
None
}
}
}
}
// let post_details_for_frontend = auth_canisters(creator_canister_id).get_individual_post_details_by_id(post_id).await.unwrap();
// log::warn!("Fetched post");
// Some(PostDetails::from_canister_post(post_details_for_frontend))
});

let view_video_url = move || post_details.get().flatten().map(|pd| mp4_url(pd.uid));

let view_bg_url = move || post_details.get().flatten().map(|pd| bg_url(pd.uid));

let video_node_ref = create_node_ref::<Video>();

// Handles autoplay
create_effect(move |_| {
let Some(vid) = video_node_ref() else {
return;
};

log::warn!("set autoplay as true");
vid.set_autoplay(true);
_ = vid.play();
});

view! {
<Suspense fallback = FullScreenSpinner >
{move || {
post_details.get()
.flatten().map(|pd| {

Some(view!{
<div class ="absolute left-4 top-4 bg-transparent z-10 text-white">
<BackButton fallback="/".to_string()/>
</div>
<Show when=your_profile>
<HomeButtonOverlay/>
</Show>
<div class="snap-always snap-end w-full h-screen">
<div class="bg-transparent w-full h-full relative overflow-hidden">
<div
class="absolute top-0 left-0 bg-cover bg-center w-full h-full z-[1] blur-lg"
style:background-color="rgb(0, 0, 0)"
style:background-image=move || format!("url({})", view_bg_url().unwrap_or_default())
></div>
</div>
<VideoDetailsOverlay post = pd/>
<VideoPlayer node_ref=video_node_ref view_bg_url view_video_url/>
</div>
})
})
}
}
</Suspense>
}
}
3 changes: 2 additions & 1 deletion src/utils/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,9 @@ impl CursoredDataProvider for PostsProvider {
end: usize,
) -> Result<PageEntry<PostDetails>, AgentError> {
let user = self.canisters.individual_user(self.user);
let limit = end - start;
let posts = user
.get_posts_of_this_user_profile_with_pagination_cursor(start as u64, end as u64)
.get_posts_of_this_user_profile_with_pagination_cursor(start as u64, limit as u64)
.await?;
let posts = match posts {
Result5::Ok(v) => v,
Expand Down

0 comments on commit ce56bb0

Please sign in to comment.