Skip to content

Commit

Permalink
added content upload modal (#247)
Browse files Browse the repository at this point in the history
  • Loading branch information
ravi-sawlani-yral authored Jul 25, 2024
1 parent 1f734aa commit c9dde55
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ tonic = { version = "0.11.0", features = [
], optional = true }
prost = { version = "0.12.4", optional = true }
hmac = { version = "0.12.1", optional = true }
wasm-bindgen-futures = { version = "0.4.42", optional = true }
testcontainers = { version = "0.20.0", optional = true }
yral-testcontainers = { git = "https://github.com/go-bazzinga/yral-testcontainers", rev = "f9d2c01c498d58fca0595a48bdc3f9400e57ec2f", optional = true }

Expand All @@ -110,6 +111,8 @@ hydrate = [
"ic-agent/wasm-bindgen",
"dep:web-sys",
"reqwest/native-tls",
"dep:rand_chacha",
"dep:wasm-bindgen-futures"
]
ssr = [
"dep:axum",
Expand Down Expand Up @@ -244,7 +247,7 @@ env = "DEV"
# The features to use when compiling the bin target
#
# Optional. Can be over-ridden with the command line parameter --bin-features
bin-features = ["ssr"]
bin-features = ["ssr","local-auth"]

# If the --no-default-features flag should be used when compiling the bin target
#
Expand All @@ -254,7 +257,7 @@ bin-default-features = false
# The features to use when compiling the lib target
#
# Optional. Can be over-ridden with the command line parameter --lib-features
lib-features = ["hydrate"]
lib-features = ["hydrate local-auth"]

# If the --no-default-features flag should be used when compiling the lib target
#
Expand Down
31 changes: 20 additions & 11 deletions public/app.webmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@
"short_name": "Yral",
"name": "Yral",
"icons": [
{
"src": "/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
{
"src": "/img/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/img/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"start_url": "/",
"orientation": "portrait",
"background_color": "#000",
"display": "standalone",
"scope": "/",
"theme_color": "#E20479",
"share_target": {
"action": "/menu",
"method": "GET",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
},
"shortcuts": [
{
"name": "All Videos",
Expand Down Expand Up @@ -65,4 +74,4 @@
"sizes": "780x1688"
}
]
}
}
9 changes: 5 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ use crate::{
account_transfer::AccountTransfer,
err::ServerErrorPage,
leaderboard::Leaderboard,
menu::Menu,
menu::{AuthorizedUserToSeedContent, Menu},
post_view::{PostView, PostViewCtx},
privacy::PrivacyPolicy,
profile::ProfilePostsContext,
profile::{profile_post::ProfilePost, ProfileView},
profile::{profile_post::ProfilePost, ProfilePostsContext, ProfileView},
refer_earn::ReferEarn,
root::RootPage,
terms::TermsOfService,
upload::UploadPostPage,
wallet::{transactions::Transactions, Wallet},
},
state::{canisters::Canisters, history::HistoryCtx},
state::{canisters::Canisters, content_seed_client::ContentSeedClient, history::HistoryCtx},
utils::event_streaming::EventHistory,
};
use leptos::*;
Expand Down Expand Up @@ -63,8 +62,10 @@ pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
provide_context(Canisters::default());
provide_context(ContentSeedClient::default());
provide_context(PostViewCtx::default());
provide_context(ProfilePostsContext::default());
provide_context(AuthorizedUserToSeedContent::default());

// History Tracking
let history_ctx = HistoryCtx::default();
Expand Down
37 changes: 36 additions & 1 deletion src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
pub mod server_impl;

use candid::Principal;
use ic_agent::identity::{DelegatedIdentity, Secp256k1Identity, SignedDelegation};
use ic_agent::{
identity::{DelegatedIdentity, Delegation, Secp256k1Identity, SignedDelegation},
Identity,
};
use k256::elliptic_curve::JwkEcKey;
use leptos::{server, ServerFnError};
use rand_chacha::rand_core::OsRng;
use serde::{Deserialize, Serialize};
use web_time::Duration;

use crate::utils::current_epoch;

/// Delegated identity that can be serialized over the wire
#[derive(Serialize, Deserialize, Clone)]
Expand All @@ -20,6 +27,34 @@ pub struct DelegatedIdentityWire {
delegation_chain: Vec<SignedDelegation>,
}

impl DelegatedIdentityWire {
pub fn delegate_short_lived_identity(from: &impl Identity) -> Self {
let to_secret = k256::SecretKey::random(&mut OsRng);
let to_identity = Secp256k1Identity::from_private_key(to_secret.clone());
let expiry = current_epoch() + Duration::from_secs(24 * 60 * 60); //1 day
let expiry_ns = expiry.as_nanos() as u64;
let delegation = Delegation {
pubkey: to_identity.public_key().unwrap(),
expiration: expiry_ns,
targets: None,
};
let sig = from.sign_delegation(&delegation).unwrap();
let signed_delegation = SignedDelegation {
delegation,
signature: sig.signature.unwrap(),
};

let mut delegation_chain = from.delegation_chain();
delegation_chain.push(signed_delegation);

Self {
from_key: sig.public_key.unwrap(),
to_secret: to_secret.to_jwk(),
delegation_chain,
}
}
}

impl std::fmt::Debug for DelegatedIdentityWire {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DelegatedIdentityWire").finish()
Expand Down
79 changes: 79 additions & 0 deletions src/component/content_upload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::{
auth::DelegatedIdentityWire,
state::{canisters::Canisters, content_seed_client::ContentSeedClient},
};
use leptos::*;

#[component]
pub fn YoutubeUpload(
canisters: Canisters<true>,
#[prop(optional)] url: Option<String>,
) -> impl IntoView {
let response = RwSignal::new(String::new());
let url_value = RwSignal::new(String::new());

if let Some(val) = url {
url_value.set_untracked(val);
}

let create_short_lived_delegated_identity = |canisters: &Canisters<true>| {
let id = canisters.identity();
DelegatedIdentityWire::delegate_short_lived_identity(id)
};

let on_submit = create_action(move |_| {
let canisters_copy = canisters.clone();
async move {
let delegated_identity = create_short_lived_delegated_identity(&canisters_copy);
let content_seed_client: ContentSeedClient = expect_context();
let res = content_seed_client
.upload_content(url_value(), delegated_identity)
.await;
match res {
Err(e) => response.set(e.to_string()),
_ => response.set("Submitted!".to_string()),
};
}
});

view! {
{move || {
view! {
<div data-hk="1-0-0-3" class="flex h-full items-center justify-around p-4">
<div data-hk="1-0-0-4" class="flex flex-col items-center justify-center">
<div class="flex h-full flex-col justify-around gap-6">
<div class="flex basis-9/12 flex-col items-center justify-center">
<h1 data-hk="1-0-0-5" class="text-2xl md:text-3xl text-white">
VIDEO IMPORTER
</h1>
</div>
<div class="flex basis-3/12 flex-col justify-around items-center gap-4">
<input
type="text"
value=move || url_value.get()
on:input=move |ev| {
let val = event_target_value(&ev);
url_value.set(val);
}

placeholder=" Paste your link here"
class="p-1 md:text-xl"
/>
<button
type="submit"
class="border border-solid px-4 text-xl md:text-2xl w-fit text-white hover:bg-white hover:text-black"
on:click=move |_| on_submit.dispatch(())
>

Submit
</button>
<p class="text-base md:text-lg text-white">{response()}</p>

</div>
</div>
</div>
</div>
}
}}
}
}
1 change: 1 addition & 0 deletions src/component/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod bullet_loader;
pub mod canisters_prov;
pub mod coming_soon;
pub mod connect;
pub mod content_upload;
pub mod dashbox;
pub mod feed_popup;
pub mod ic_symbol;
Expand Down
2 changes: 2 additions & 0 deletions src/consts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub static OFF_CHAIN_AGENT_GRPC_URL: Lazy<Url> =
Lazy::new(|| Url::parse("https://icp-off-chain-agent.fly.dev:443").unwrap());
// G-6W5Q2MRX0E to test locally | G-PLNNETMSLM
pub static GTAG_MEASUREMENT_ID: Lazy<&str> = Lazy::new(|| "G-PLNNETMSLM");
pub static DOWNLOAD_UPLOAD_SERVICE: Lazy<Url> =
Lazy::new(|| Url::parse("https://download-upload-service.fly.dev").unwrap());

pub mod social {
pub const TELEGRAM: &str = "https://t.me/+c-LTX0Cp-ENmMzI1";
Expand Down
89 changes: 89 additions & 0 deletions src/page/menu.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
use crate::component::back_btn::BackButton;
use crate::component::canisters_prov::AuthCansProvider;
use crate::component::content_upload::YoutubeUpload;
use crate::component::modal::Modal;
use crate::component::spinner::Spinner;
use crate::component::title::Title;
use crate::component::{connect::ConnectLogin, social::*, toggle::Toggle};
use crate::consts::{social, NSFW_TOGGLE_STORE};
use crate::state::auth::account_connected_reader;
use crate::state::canisters::authenticated_canisters;
use crate::state::content_seed_client::ContentSeedClient;
use crate::utils::profile::ProfileDetails;
use candid::Principal;
use leptos::html::Input;
use leptos::*;
use leptos_icons::*;
use leptos_router::use_query_map;
use leptos_use::use_event_listener;
use leptos_use::{storage::use_local_storage, utils::FromToStringCodec};

#[derive(Clone, Default)]
pub struct AuthorizedUserToSeedContent(RwSignal<Option<(Principal, bool)>>);

#[component]
fn MenuItem(
#[prop(into)] text: String,
Expand Down Expand Up @@ -138,8 +148,73 @@ fn NsfwToggle() -> impl IntoView {
#[component]
pub fn Menu() -> impl IntoView {
let (is_connected, _) = account_connected_reader();
let query_map = use_query_map();
let show_content_modal = create_rw_signal(false);
let is_authorized_to_seed_content: AuthorizedUserToSeedContent = expect_context();

let can_res = authenticated_canisters();
let check_authorized_action = create_action(move |user_principal: &Principal| {
let user_principal = *user_principal;
async move {
let content_seed_client: ContentSeedClient = expect_context();
let res = content_seed_client
.check_if_authorized(user_principal)
.await
.ok()?;
is_authorized_to_seed_content
.0
.set(Some((user_principal, res)));
Some(())
}
});

create_effect(move |_| {
let canisters = can_res.get()?.ok()?;
let authorized_user_to_seed_content = is_authorized_to_seed_content.0.get_untracked();
match authorized_user_to_seed_content {
Some((user_principal, _)) if user_principal != canisters.user_principal() => {
check_authorized_action.dispatch(canisters.user_principal())
}
None => check_authorized_action.dispatch(canisters.user_principal()),
_ => {}
}
Some(())
});

create_effect(move |_| {
//check whether query param is right if right set the show_modal_content as true.
let query_params = query_map.get();
let url = query_params.0.get("text")?;
if !url.is_empty() && is_connected.get() {
show_content_modal.set(true);
}
Some(())
});

view! {
<Modal show=show_content_modal>
<Suspense fallback=|| {
view! { <Spinner/> }
}>
{move || {
let authenticated_canister = can_res.get()?.ok()?;
let authorized = is_authorized_to_seed_content.0.get()?.1;
if !authorized {
show_content_modal.set(false);
return None;
}
Some(
view! {
<YoutubeUpload
canisters=authenticated_canister.clone()
url=query_map.get().0.get("text").cloned().unwrap_or_default()
/>
},
)
}}

</Suspense>
</Modal>
<div class="min-h-screen w-full flex flex-col text-white pt-2 pb-12 bg-black items-center divide-y divide-white/10">
<div class="flex flex-col items-center w-full gap-20 pb-16">
<Title justify_center=false>
Expand All @@ -161,6 +236,20 @@ pub fn Menu() -> impl IntoView {
{r#"Your Yral account has been setup. Login with Google to not lose progress."#}
</div>
</Show>
<Show when=move || {
is_authorized_to_seed_content.0.get().map(|auth| auth.1).unwrap_or(false)
&& is_connected.get()
}>
<div class="w-full px-8 md:w-4/12 xl:w-2/12">
<button
class="font-bold rounded-full bg-primary-600 py-2 md:py-3 w-full text-center text-lg md:text-xl text-white"
on:click=move |_| show_content_modal.set(true)
>
Upload Content
</button>
</div>
</Show>

</div>
</div>
<div class="flex flex-col py-12 px-8 gap-8 w-full text-lg">
Expand Down
Loading

0 comments on commit c9dde55

Please sign in to comment.