diff --git a/.github/workflows/build-check.yml b/.github/workflows/build-check.yml index f9d85a29..9fc00dd7 100644 --- a/.github/workflows/build-check.yml +++ b/.github/workflows/build-check.yml @@ -9,6 +9,10 @@ on: type: boolean workflow_dispatch: +concurrency: + group: ${{ github.ref }}:${{ github.workflow }} + cancel-in-progress: true + jobs: build_check: runs-on: ubuntu-latest diff --git a/ssr/public/img/bg.png b/ssr/public/img/bg.png new file mode 100644 index 00000000..7ba9ed4a Binary files /dev/null and b/ssr/public/img/bg.png differ diff --git a/ssr/public/img/cloud.webp b/ssr/public/img/cloud.webp new file mode 100644 index 00000000..e0de2d67 Binary files /dev/null and b/ssr/public/img/cloud.webp differ diff --git a/ssr/public/img/parachute.webp b/ssr/public/img/parachute.webp new file mode 100644 index 00000000..3cfb8dde Binary files /dev/null and b/ssr/public/img/parachute.webp differ diff --git a/ssr/src/component/buttons.rs b/ssr/src/component/buttons.rs new file mode 100644 index 00000000..0798db76 --- /dev/null +++ b/ssr/src/component/buttons.rs @@ -0,0 +1,133 @@ +use leptos::*; + +#[component] +pub fn Button( + children: Children, + on_click: impl Fn() + 'static, + #[prop(optional)] classes: String, + #[prop(optional)] alt_style: Signal, + #[prop(optional)] disabled: Signal, +) -> impl IntoView { + let on_click = move |_| on_click(); + view! { + + } +} + +#[component] +pub fn LinkButton( + children: Children, + href: String, + #[prop(optional)] classes: String, + #[prop(optional)] alt_style: Signal, + #[prop(optional)] disabled: Signal, +) -> impl IntoView { + view! { + + {children()} + + } +} + +#[component] +pub fn SecondaryLinkButton( + children: Children, + href: String, + #[prop(optional)] classes: String, + #[prop(optional)] alt_style: Signal, +) -> impl IntoView { + view! { + + {children()} + + } +} + +#[component] +pub fn SecondaryButton( + children: Children, + disabled: Signal, + alt_style: Signal, + classes: String, + on_click: impl Fn() + 'static, +) -> impl IntoView { + let on_click = move |_| on_click(); + view! { + + } +} diff --git a/ssr/src/component/icons/airdrop_icon.rs b/ssr/src/component/icons/airdrop_icon.rs new file mode 100644 index 00000000..2512702b --- /dev/null +++ b/ssr/src/component/icons/airdrop_icon.rs @@ -0,0 +1,46 @@ +use leptos::*; + +#[component] +pub fn AirdropIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + + + + + + + + + + } +} diff --git a/ssr/src/component/icons/arrow_down_icon.rs b/ssr/src/component/icons/arrow_down_icon.rs new file mode 100644 index 00000000..d66d611f --- /dev/null +++ b/ssr/src/component/icons/arrow_down_icon.rs @@ -0,0 +1,32 @@ +use leptos::*; + +#[component] +pub fn ArrowDownIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + } +} diff --git a/ssr/src/component/icons/arrow_left_icon.rs b/ssr/src/component/icons/arrow_left_icon.rs new file mode 100644 index 00000000..984b1f86 --- /dev/null +++ b/ssr/src/component/icons/arrow_left_icon.rs @@ -0,0 +1,33 @@ +use leptos::*; + +#[component] +pub fn ArrowLeftIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + + + + + } +} diff --git a/ssr/src/component/icons/arrow_left_right_icon.rs b/ssr/src/component/icons/arrow_left_right_icon.rs new file mode 100644 index 00000000..afdc932e --- /dev/null +++ b/ssr/src/component/icons/arrow_left_right_icon.rs @@ -0,0 +1,33 @@ +use leptos::*; + +#[component] +pub fn ArrowLeftRightIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + + + + + } +} diff --git a/ssr/src/component/icons/arrow_right_long_icon.rs b/ssr/src/component/icons/arrow_right_long_icon.rs new file mode 100644 index 00000000..f2be9be8 --- /dev/null +++ b/ssr/src/component/icons/arrow_right_long_icon.rs @@ -0,0 +1,32 @@ +use leptos::*; + +#[component] +pub fn ArrowRightLongIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + } +} diff --git a/ssr/src/component/icons/arrow_up_icon.rs b/ssr/src/component/icons/arrow_up_icon.rs new file mode 100644 index 00000000..16768c22 --- /dev/null +++ b/ssr/src/component/icons/arrow_up_icon.rs @@ -0,0 +1,22 @@ +use leptos::*; + +#[component] +pub fn ArrowUpIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + } +} diff --git a/ssr/src/component/icons/chevron_left_icon.rs b/ssr/src/component/icons/chevron_left_icon.rs new file mode 100644 index 00000000..7e9c4f95 --- /dev/null +++ b/ssr/src/component/icons/chevron_left_icon.rs @@ -0,0 +1,22 @@ +use leptos::*; + +#[component] +pub fn ChevronLeftIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + } +} diff --git a/ssr/src/component/icons/chevron_right_icon.rs b/ssr/src/component/icons/chevron_right_icon.rs new file mode 100644 index 00000000..e045dc76 --- /dev/null +++ b/ssr/src/component/icons/chevron_right_icon.rs @@ -0,0 +1,20 @@ +use leptos::*; + +#[component] +pub fn ChevronRightIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + } +} diff --git a/ssr/src/component/icons/close_icon.rs b/ssr/src/component/icons/close_icon.rs new file mode 100644 index 00000000..6ac6fae7 --- /dev/null +++ b/ssr/src/component/icons/close_icon.rs @@ -0,0 +1,20 @@ +use leptos::*; + +#[component] +pub fn CloseIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + } +} diff --git a/ssr/src/component/icons/eye_hide_icon.rs b/ssr/src/component/icons/eye_hide_icon.rs new file mode 100644 index 00000000..8b772477 --- /dev/null +++ b/ssr/src/component/icons/eye_hide_icon.rs @@ -0,0 +1,24 @@ +use leptos::*; + +#[component] +pub fn EyeHiddenIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + } +} diff --git a/ssr/src/component/icons/mod.rs b/ssr/src/component/icons/mod.rs new file mode 100644 index 00000000..20cb838f --- /dev/null +++ b/ssr/src/component/icons/mod.rs @@ -0,0 +1,16 @@ +pub mod airdrop_icon; +pub mod arrow_down_icon; +pub mod arrow_left_icon; +pub mod arrow_left_right_icon; +pub mod arrow_right_long_icon; +pub mod arrow_up_icon; +pub mod chevron_left_icon; +pub mod chevron_right_icon; +pub mod close_icon; +pub mod eye_hide_icon; +pub mod more_icon; +pub mod notification_icon; +pub mod pump_ai_logo; +pub mod send_icon; +pub mod share_icon; +pub mod wallet_icon; diff --git a/ssr/src/component/icons/more_icon.rs b/ssr/src/component/icons/more_icon.rs new file mode 100644 index 00000000..bc5cffe4 --- /dev/null +++ b/ssr/src/component/icons/more_icon.rs @@ -0,0 +1,20 @@ +use leptos::*; + +#[component] +pub fn MoreIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + + } +} diff --git a/ssr/src/component/icons/notification_icon.rs b/ssr/src/component/icons/notification_icon.rs new file mode 100644 index 00000000..4138d670 --- /dev/null +++ b/ssr/src/component/icons/notification_icon.rs @@ -0,0 +1,51 @@ +use leptos::*; + +#[component] +pub fn NotificationIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, + #[prop(optional)] show_dot: bool, +) -> impl IntoView { + if show_dot { + view! { + + + + + } + } else { + view! { + + + + } + } +} diff --git a/ssr/src/component/icons/pump_ai_logo.rs b/ssr/src/component/icons/pump_ai_logo.rs new file mode 100644 index 00000000..6208eecb --- /dev/null +++ b/ssr/src/component/icons/pump_ai_logo.rs @@ -0,0 +1,76 @@ +use leptos::*; + +#[component] +pub fn PumpAILogo( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, + #[prop(optional)] animate: bool, +) -> impl IntoView { + view! { + + + + + + + + + + + + + + + + + } +} diff --git a/ssr/src/component/icons/send_icon.rs b/ssr/src/component/icons/send_icon.rs new file mode 100644 index 00000000..fbed9d7c --- /dev/null +++ b/ssr/src/component/icons/send_icon.rs @@ -0,0 +1,36 @@ +use leptos::*; + +#[component] +pub fn SendIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, + #[prop(optional)] filled: bool, +) -> impl IntoView { + view! { + + + + + + + + + } +} diff --git a/ssr/src/component/icons/share_icon.rs b/ssr/src/component/icons/share_icon.rs new file mode 100644 index 00000000..fda5a53e --- /dev/null +++ b/ssr/src/component/icons/share_icon.rs @@ -0,0 +1,46 @@ +use leptos::*; + +#[component] +pub fn ShareIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + + + + + + + + + + + } +} diff --git a/ssr/src/component/icons/wallet_icon.rs b/ssr/src/component/icons/wallet_icon.rs new file mode 100644 index 00000000..68762a6d --- /dev/null +++ b/ssr/src/component/icons/wallet_icon.rs @@ -0,0 +1,23 @@ +use leptos::*; + +#[component] +pub fn WalletIcon( + #[prop(optional, default = "w-full h-full".to_string())] classes: String, +) -> impl IntoView { + view! { + + + + } +} diff --git a/ssr/src/component/mod.rs b/ssr/src/component/mod.rs index 38bd66c1..e93b521a 100644 --- a/ssr/src/component/mod.rs +++ b/ssr/src/component/mod.rs @@ -3,6 +3,7 @@ pub mod auth_providers; pub mod back_btn; pub mod base_route; pub mod bullet_loader; +pub mod buttons; pub mod canisters_prov; pub mod coming_soon; pub mod connect; @@ -11,6 +12,7 @@ pub mod dashbox; pub mod feed_popup; pub mod hn_icons; pub mod ic_symbol; +pub mod icons; pub mod infinite_scroller; pub mod loading; pub mod login_modal; @@ -20,9 +22,11 @@ pub mod nav_icons; pub mod onboarding_flow; pub mod option; pub mod overlay; +pub mod page_selector; pub mod profile_placeholders; pub mod scrolling_post_view; pub mod share_popup; +pub mod shimmer; pub mod social; pub mod spinner; pub mod title; diff --git a/ssr/src/component/page_selector.rs b/ssr/src/component/page_selector.rs new file mode 100644 index 00000000..f42b0266 --- /dev/null +++ b/ssr/src/component/page_selector.rs @@ -0,0 +1,51 @@ +use leptos::*; + +use crate::component::icons::chevron_right_icon::ChevronRightIcon; + +#[component] +pub fn PageSelector( + current_page: u8, + total_pages: u8, + previous_href: String, + next_href: String, + href: impl Fn(u8) -> String, +) -> impl IntoView { + let pages = 0..total_pages; + view! { +
+ + + + {pages + .into_iter() + .map(|i| { + let page_number = i + 1; + view! { + + {page_number} + + } + }) + .collect_view()} + + + +
+ } +} diff --git a/ssr/src/component/shimmer.rs b/ssr/src/component/shimmer.rs new file mode 100644 index 00000000..980b58b1 --- /dev/null +++ b/ssr/src/component/shimmer.rs @@ -0,0 +1,11 @@ +use leptos::*; + +#[component] +pub fn Shimmer() -> impl IntoView { + view! { +
+ } +} diff --git a/ssr/src/page/icpump/ai.rs b/ssr/src/page/icpump/ai.rs index 28be212d..302cd1e1 100644 --- a/ssr/src/page/icpump/ai.rs +++ b/ssr/src/page/icpump/ai.rs @@ -1,5 +1,9 @@ use std::collections::VecDeque; +use crate::component::icons::chevron_left_icon::ChevronLeftIcon; +use crate::component::icons::pump_ai_logo::PumpAILogo; +use crate::component::icons::send_icon::SendIcon; +use crate::component::shimmer::Shimmer; use leptos::*; use leptos_icons::*; use pulldown_cmark::{Options, Parser}; @@ -45,20 +49,125 @@ pub struct ICPumpAiChat { } #[component] -pub fn MarkdownRenderer(text: String) -> impl IntoView { - let parsed_markdown = create_memo(move |_| { - let mut options = Options::empty(); - options.insert(Options::ENABLE_STRIKETHROUGH); - let parser = Parser::new_ext(&text, options); +pub fn ICPumpAi() -> impl IntoView { + let page_no = create_rw_signal(1); + let query = create_rw_signal("".to_string()); + let chat = create_rw_signal(ICPumpAiChat { + items: VecDeque::new(), + rag_data: "".to_string(), + interactions: vec![], + }); - let mut html_output = String::new(); - pulldown_cmark::html::push_html(&mut html_output, parser); - html_output + let search_action = create_action(move |()| async move { + page_no.set(3); + let q = query.get(); + + chat.update(|c| { + c.items + .push_front(ICPumpAiChatItem::UserItem { query: q.clone() }) + }); + + if chat.with(|c| c.interactions.is_empty()) { + let results = get_pumpai_results(q.clone()).await; + let results = try_or_redirect!(results); + + chat.update(|c| { + c.rag_data = results.rag_data; + c.items.push_front(ICPumpAiChatItem::ResponseItem { + response: results.text.clone(), + tokens: results.items, + }); + + let interaction = ICPumpChatInteraction { + query: q.clone(), + response: results.text, + }; + c.interactions.push(interaction); + }); + } else { + let results = get_pumpai_results_contextual( + q.clone(), + chat.get().interactions.clone(), + chat.get().rag_data.clone(), + ) + .await; + let results = try_or_redirect!(results); + + chat.update(|c| { + c.items.push_front(ICPumpAiChatItem::ResponseItem { + response: results.text.clone(), + tokens: vec![], + }); + + let interaction = ICPumpChatInteraction { + query: q.clone(), + response: results.text, + }; + c.interactions.push(interaction); + }); + } + + query.set("".to_string()); + }); + + let reset_state = create_action(move |()| async move { + query.set("".to_string()); + chat.set(ICPumpAiChat { + items: VecDeque::new(), + rag_data: "".to_string(), + interactions: vec![], + }); }); view! { -
-
+
+
+ + {move || { + match page_no.get() { + 1 => { + view! { + + } + .into_view() + } + 2 => { + view! { + + } + .into_view() + } + 3 => { + view! { + + } + .into_view() + } + _ => view! { <> }.into_view(), + } + }} + +
} } @@ -71,47 +180,42 @@ pub fn ICPumpAiPage1( ) -> impl IntoView { view! {
- -
Welcome to
Pump AI
-
- +
+ Welcome to
Pump AI +
+ - -
+ on_focus=move || { + page_no.set(2); + } + query=query + />
-
Try these:
- Try these:
+ + class="border-[#1B1D22] transition-colors hover:bg-zinc-800 active:bg-zinc-900 text-sm text-left font-medium py-2 px-4 border w-full" + > {token} } } + /> - />
} } @@ -142,61 +246,163 @@ pub fn ICPumpAiPage2( }); view! { -
- -
Pump AI
- -
-
- - -
-
- + + class="border-[#1B1D22] transition-colors hover:bg-zinc-800 active:bg-zinc-900 text-sm text-left font-medium py-2 px-4 border w-full" + > {token} } } + /> +
+ } +} + +#[component] +pub fn ICPumpAiPage3( + query: RwSignal, + chat: RwSignal, + page_no: RwSignal, + search_action: Action<(), ()>, + reset_state: Action<(), ()>, +) -> impl IntoView { + view! { +
+
+ {move || { + if search_action.pending().get() { + return view! { +
+ +
+ }; + } + view! { +
+ +
+ } +} + +#[component] +pub fn SearchInput( + #[prop(optional, default = "".to_string())] classes: String, + on_submit: impl Fn(String) + 'static, + on_focus: impl Fn() + 'static, + query: RwSignal, +) -> impl IntoView { + view! { +
+ + + +
+ } +} + +#[component] +pub fn Header(on_back: impl Fn() + 'static) -> impl IntoView { + view! { +
+
+ +
Pump AI
+ +
} } @@ -206,39 +412,33 @@ pub fn ICPumpAiToken(details: TokenListItem) -> impl IntoView { view! { -
- +
+ - -
- + /> + +
+ +
+
+
+
+
+ {details.token_name} + {details.token_symbol}
- -
-
-
- {details.token_name} - {details.token_symbol} + {details.description}
- - {details.description} - -
-
+ } } @@ -267,9 +467,7 @@ pub fn ICPumpAiTokenListing(tokens: Vec) -> impl IntoView { each=move || tokens_final.clone() key=|t| t.token_symbol.clone() children=move |token: TokenListItem| { - view! { - - } + view! { } } />
@@ -286,15 +484,17 @@ pub fn ICPumpAiTokenListing(tokens: Vec) -> impl IntoView { class="flex items-center gap-2 rounded-xs border border-[#202125] w-fit p-2" on:click=move |_| view_more.update(|v| *v = !*v) > - {move || if view_more.get() { - view! { - View less - "↑" - } - } else { - view! { - View more - "↓" + {move || { + if view_more.get() { + view! { + View less + "↑" + } + } else { + view! { + View more + "↓" + } } }} @@ -302,220 +502,28 @@ pub fn ICPumpAiTokenListing(tokens: Vec) -> impl IntoView { } .into_view() } else { - view! {<>}.into_view() + view! { <> }.into_view() } }); - view! { -
- {tokens_view} -
- } + view! {
{tokens_view}
} } #[component] -pub fn ICPumpAiPage3( - query: RwSignal, - chat: RwSignal, - page_no: RwSignal, - search_action: Action<(), ()>, - reset_state: Action<(), ()>, -) -> impl IntoView { - view! { -
- -
Pump AI
- -
-
- { - move || { - if search_action.pending().get() { - return view! { - <> -
-
-
Thinking
-
- - }; - } - view! { - <>
- } - } - } - - { - view! { -
-
- {query} -
-
- } - } - ICPumpAiChatItem::ResponseItem{response, tokens} => { - view! { -
-
- -
- -
- } - } - } - } - /> - - -
-
- - -
- } -} - -#[component] -pub fn ICPumpAi() -> impl IntoView { - let page_no = create_rw_signal(1); - let query = create_rw_signal("".to_string()); - let chat = create_rw_signal(ICPumpAiChat { - items: VecDeque::new(), - rag_data: "".to_string(), - interactions: vec![], - }); - - let search_action = create_action(move |()| async move { - page_no.set(3); - let q = query.get(); - - chat.update(|c| { - c.items - .push_front(ICPumpAiChatItem::UserItem { query: q.clone() }) - }); - - if chat.with(|c| c.interactions.is_empty()) { - let results = get_pumpai_results(q.clone()).await; - let results = try_or_redirect!(results); - - chat.update(|c| { - c.rag_data = results.rag_data; - c.items.push_front(ICPumpAiChatItem::ResponseItem { - response: results.text.clone(), - tokens: results.items, - }); - - let interaction = ICPumpChatInteraction { - query: q.clone(), - response: results.text, - }; - c.interactions.push(interaction); - }); - } else { - let results = get_pumpai_results_contextual( - q.clone(), - chat.get().interactions.clone(), - chat.get().rag_data.clone(), - ) - .await; - let results = try_or_redirect!(results); - - chat.update(|c| { - c.items.push_front(ICPumpAiChatItem::ResponseItem { - response: results.text.clone(), - tokens: vec![], - }); - - let interaction = ICPumpChatInteraction { - query: q.clone(), - response: results.text, - }; - c.interactions.push(interaction); - }); - } - - query.set("".to_string()); - }); +pub fn MarkdownRenderer(text: String) -> impl IntoView { + let parsed_markdown = create_memo(move |_| { + let mut options = Options::empty(); + options.insert(Options::ENABLE_STRIKETHROUGH); + let parser = Parser::new_ext(&text, options); - let reset_state = create_action(move |()| async move { - query.set("".to_string()); - chat.set(ICPumpAiChat { - items: VecDeque::new(), - rag_data: "".to_string(), - interactions: vec![], - }); + let mut html_output = String::new(); + pulldown_cmark::html::push_html(&mut html_output, parser); + html_output }); view! { -
-
- - { - move || { - match page_no.get() { - 1 => { - view! { - - }.into_view() - } - 2 => { - view! { - - }.into_view() - } - 3 => { - view! { - - }.into_view() - } - _ => { - view! { - <> - }.into_view() - } - } - } - } - -
+
+
} } diff --git a/ssr/src/page/icpump/mod.rs b/ssr/src/page/icpump/mod.rs index 22f3d0dd..5b1c7e62 100644 --- a/ssr/src/page/icpump/mod.rs +++ b/ssr/src/page/icpump/mod.rs @@ -3,8 +3,14 @@ use std::collections::VecDeque; use futures::StreamExt; use leptos::*; -use leptos_icons::*; +use crate::component::buttons::LinkButton; +use crate::component::icons::airdrop_icon::AirdropIcon; +use crate::component::icons::arrow_left_right_icon::ArrowLeftRightIcon; +use crate::component::icons::chevron_right_icon::ChevronRightIcon; +use crate::component::icons::eye_hide_icon::EyeHiddenIcon; +use crate::component::icons::send_icon::SendIcon; +use crate::component::icons::share_icon::ShareIcon; use crate::component::spinner::FullScreenSpinner; use crate::consts::ICPUMP_LISTING_PAGE_SIZE; use crate::utils::token::firestore::init_firebase; @@ -14,67 +20,6 @@ use crate::utils::token::icpump::TokenListItem; pub mod ai; -#[component] -pub fn TokenListing( - details: TokenListItem, - #[prop(optional, default = false)] is_new_token: bool, -) -> impl IntoView { - view! { - -
- details.token_name.clone() - -
- -
-
-
-
-
- - {details.token_name} - - - "$" - {details.token_symbol} - -
-
- {details.description} -
-
- "Created by: "{details.user_id} - -
- - {details.formatted_created_at} - -
-
- } -} - #[component] pub fn ICPumpListing() -> impl IntoView { let page = create_rw_signal(1); @@ -110,98 +55,265 @@ pub fn ICPumpListing() -> impl IntoView { }); view! { -
- - {move || { - let _ = act - .get() - .map(|res| { - if res.len() < ICPUMP_LISTING_PAGE_SIZE { - end_of_list.set(true); - } - update!( - move |token_list, cache| { + + {move || { + let _ = act + .get() + .map(|res| { + if res.len() < ICPUMP_LISTING_PAGE_SIZE { + end_of_list.set(true); + } + update!( + move |token_list, cache| { *token_list = res.clone(); cache.insert(page.get_untracked(), res.clone()); } - ); - }); - view! { -
- } - } - /> - } - } - /> -
- -
- - {page} - -
- } - }} -
-
+ ); + }); + view! { +
+ } + } + /> + } + } + /> +
+
+ +
+ } + }} + } } #[component] pub fn ICPumpLanding() -> impl IntoView { view! { -
-
- - -
- - [telegram] - +
+
+
+
Follow us:
+
+ + + +
+ + "Create a new coin" +
-
- + } +} + +#[component] +pub fn TokenCard( + details: TokenListItem, + #[prop(optional, default = false)] is_new_token: bool, +) -> impl IntoView { + let show_nsfw = create_rw_signal(false); + + view! { +
+
+
+ + + + details.token_name.clone() +
+
+
+ {details.name} + {details.token_symbol} +
+ + {details.description} + +
+ "Created by" {details.user_id} + {details.formatted_created_at} +
-
- +
+ + + + + + + + + + + + + + +
} } + +#[component] +pub fn PageSelector(page: RwSignal, end_of_list: RwSignal) -> impl IntoView { + view! { +
+ +
{page}
+ +
+ } +} + +#[component] +pub fn ActionButton(href: String, label: String, children: Children) -> impl IntoView { + view! { + +
{children()}
+ +
{label}
+
+ } +} + +#[component] +pub fn TelegramIcon(href: String, classes: String) -> impl IntoView { + view! { + + + + + + + + } +} + +#[component] +pub fn XIcon(href: String, classes: String) -> impl IntoView { + view! { + + + + + + + + + } +} + +#[component] +pub fn InstagramIcon(href: String, classes: String) -> impl IntoView { + view! { + + + + + + + + + } +} diff --git a/ssr/src/page/notifications/mod.rs b/ssr/src/page/notifications/mod.rs new file mode 100644 index 00000000..d9f1aad7 --- /dev/null +++ b/ssr/src/page/notifications/mod.rs @@ -0,0 +1,145 @@ +use leptos::*; + +use crate::component::buttons::SecondaryButton; +use crate::component::icons::chevron_right_icon::ChevronRightIcon; +use crate::component::icons::chevron_left_icon::ChevronLeftIcon; + +#[component] +fn NotificationPage() -> impl IntoView { + + let all_notifications = create_signal(vec![]); + + view! { + {if (show_popup.get()) { + view! { } + }} + +
+ +
+
0 { "pt-20" } else { "" }, + )> + {if (all_notifications.get().len() > 0) { + view! { + + + + + } + } else { + view! { +
"No notifications"
+ + "Go Back" + + } + }} +
+
+
+ } +} + +#[component] +pub fn NotificationItem(status: String, status_expired_at: String, notification_text: String) -> impl IntoView { + let request = create_signal(None); + let show_popup = create_signal(false); + let accept_request = create_action(move |_| {}); + let reject_request = create_action(move |_| {}); + let error = create_signal("".to_string()); + + view! { +
+
+
+ {if (request.get().is_some()) { + view! { + "Swap request for" + + {request.details.amount} {request.toToken.symbol} + + to + + {request.details.amount * 0.854} {request.fromToken.symbol} + + by + {request.details.fromAddress} + } + } else { + view! { {notificationText.get()} } + }} +
+ +
+
+ {if (request.get().is_some()) { + view! { +
+ "Accept" + + "Reject" + +
+ } + } else if status { + view! { +
+ +
+ } + }} +
+ {if (error.get().is_some()) { + view! {
* {error.get()}
} + }} +
+ } +} + +#[component] +pub fn NotificationStatus() -> impl IntoView { + let status = create_signal(None); + let expired_at = create_signal(None); + + view! { +
+ {if (expired_at.get().is_some()) { + view! { +

Request expired at {new Date(expired_at.get().unwrap()).toDateString()}

+ } + } else { + view! {

Status: {status.get()}

} + }} + +
+ } +} + +#[component] +pub fn Header() -> impl IntoView { + view! { +
+
+
"Notifications"
+ + + +
+
+ } +} \ No newline at end of file diff --git a/ssr/src/page/token/info.rs b/ssr/src/page/token/info.rs index 50f0b7a1..cb3fb0ea 100644 --- a/ssr/src/page/token/info.rs +++ b/ssr/src/page/token/info.rs @@ -1,5 +1,6 @@ use crate::page::token::RootType; use crate::page::token::TokenInfoParams; +use crate::page::wallet::airdrop::AirdropPage; use crate::state::canisters::authenticated_canisters; use crate::utils::token::icpump::IcpumpTokenInfo; @@ -8,11 +9,13 @@ use crate::{ page::wallet::transactions::Transactions, utils::web::copy_to_clipboard, }; +use candid::Nat; use candid::Principal; use leptos::*; use leptos_icons::*; use leptos_router::*; use serde::{Deserialize, Serialize}; +use yral_canisters_client::individual_user_template::Result21; use yral_canisters_common::cursored_data::transaction::IndexOrLedger; use yral_canisters_common::utils::token::TokenMetadata; use yral_canisters_common::Canisters; @@ -199,7 +202,12 @@ fn TokenInfoInner( #[derive(Params, PartialEq, Clone, Serialize, Deserialize)] pub struct TokenKeyParam { - key_principal: Principal, + pub key_principal: Principal, +} + +#[derive(Params, PartialEq, Clone, Serialize, Deserialize)] +pub struct AirdropAmount { + pub airdrop_amt: u64, } #[component] @@ -207,9 +215,11 @@ pub fn TokenInfo() -> impl IntoView { let params = use_params::(); let key_principal = use_params::(); let key_principal = move || key_principal.with(|p| p.as_ref().map(|p| p.key_principal).ok()); + let airdrop_param = use_query::(); + let token_metadata_fetch = authenticated_canisters().derive( - move || (params(), key_principal()), - move |cans_wire, (params, key_principal)| async move { + move || (params(), key_principal(), airdrop_param()), + move |cans_wire, (params, key_principal, airdrop_param)| async move { let Ok(params) = params else { return Ok::<_, ServerFnError>(None); }; @@ -224,12 +234,52 @@ pub fn TokenInfo() -> impl IntoView { .await .ok() .flatten(); + + let mut airdrop_res: Option = None; + if let Some(key_principal) = key_principal { + if let Ok(airdrop_amt) = airdrop_param { + if let RootType::Other(root) = params.token_root { + let user = cans + .get_individual_canister_by_user_principal(key_principal) + .await?; + let user = cans.individual_user(user.unwrap()).await; + + let res = user + .request_airdrop( + root, + None, + Into::::into(airdrop_amt.airdrop_amt) + * 10u64.pow( + meta.as_ref() + .ok_or(ServerFnError::new( + "Failed to get metadata to extract decimals", + ))? + .decimals + .into(), + ), + cans.user_canister(), + ) + .await?; + + airdrop_res = match res { + Result21::Ok => { + let user = cans.individual_user(cans.user_canister()).await; + user.add_token(root).await?; + Some(airdrop_amt.airdrop_amt) + } + _ => None, + }; + } + } + } + Ok(meta.map(|m| { ( m, params.token_root, key_principal, Some(cans.user_principal()) == key_principal, + airdrop_res, ) })) }, @@ -242,14 +292,23 @@ pub fn TokenInfo() -> impl IntoView { .and_then(|info| info.ok()) .map(|info| { match info { - Some((metadata, root, key_principal, is_user_principal)) => { - view! { - + Some((metadata, root, key_principal, is_user_principal, res)) => { + match res{ + Some(amt) => { + view! { + + } + }, + None => { + view! { + + } + } } } None => view! { }, diff --git a/ssr/src/page/token/mod.rs b/ssr/src/page/token/mod.rs index 158c0e75..1de9f94b 100644 --- a/ssr/src/page/token/mod.rs +++ b/ssr/src/page/token/mod.rs @@ -16,6 +16,6 @@ struct TokenParams { } #[derive(Params, PartialEq, Clone)] -struct TokenInfoParams { - token_root: RootType, +pub struct TokenInfoParams { + pub token_root: RootType, } diff --git a/ssr/src/page/wallet/airdrop.rs b/ssr/src/page/wallet/airdrop.rs new file mode 100644 index 00000000..0c6a7380 --- /dev/null +++ b/ssr/src/page/wallet/airdrop.rs @@ -0,0 +1,196 @@ +use crate::component::buttons::Button; +use leptos::*; + +#[component] +pub fn AirdropPage( + coin_image: String, + airdrop_amount: u64, + airdrop_from_token: String, +) -> impl IntoView { + let (bg_img_loaded, set_bg_img_loaded) = create_signal(true); + let (claimed, set_claimed) = create_signal(false); + let (coin_image_loaded, set_coin_image_loaded) = create_signal(true); + + let bg_image = "/img/bg.png"; + let cloud_image = "/img/cloud.webp"; + let parachute_image = "/img/parachute.webp"; + + let handle_claim = move || { + if !claimed.get() { + set_claimed(true); + } else { + // goto wallet page + } + }; + + view! { +
+ bg + + {move || { + if bg_img_loaded.get() && !claimed.get() { + view! { +
+
+ Parachute + +
+ Airdrop +
+
+ Cloud + Cloud +
+ } + } else if claimed.get() { + view! { +
+
+ +
+ Airdrop +
+
+
+ } + } else { + view! {