Skip to content

Commit

Permalink
Merge pull request #97 from go-bazzinga/rupansh/wallet
Browse files Browse the repository at this point in the history
Initial Wallet Implementation
  • Loading branch information
rupansh-sekar-yral authored Mar 6, 2024
2 parents 1083464 + d027acc commit 5dbce20
Show file tree
Hide file tree
Showing 10 changed files with 531 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
nix-shell --run "cargo fmt --check"
nix-shell --run "cargo clippy --no-deps --all-features --release -- -Dwarnings"
- name: Build the Leptos project to `musl` output
run: nix-shell --run 'cargo leptos build --release'
run: nix-shell --run 'cargo leptos build --release --lib-features release-lib --bin-features release-bin'
env:
LEPTOS_BIN_TARGET_TRIPLE: x86_64-unknown-linux-musl
- run: touch .empty
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ ssr = [
]
# Fetch mock referral history instead of history via canister
mock-referral-history = ["rand_chacha", "k256/arithmetic"]
# Fetch mock wallet transactions instead of history via canister
mock-wallet-history = ["rand_chacha"]
mock-history = ["mock-referral-history", "mock-wallet-history"]
cloudflare = []
release-bin = ["ssr", "cloudflare"]
release-lib = ["hydrate", "cloudflare"]
Expand Down
3 changes: 3 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::{
root::RootPage,
terms::TermsOfService,
upload::UploadPostPage,
wallet::{transactions::Transactions, Wallet},
},
state::{
auth::{AuthClient, AuthState},
Expand Down Expand Up @@ -62,6 +63,8 @@ pub fn App() -> impl IntoView {
<Route path="/faq" view=Faq/>
<Route path="/terms-of-service" view=TermsOfService/>
<Route path="/privacy-policy" view=PrivacyPolicy/>
<Route path="/wallet" view=Wallet/>
<Route path="/transactions" view=Transactions/>
<Route path="" view=RootPage/>
</Route>
</Routes>
Expand Down
14 changes: 9 additions & 5 deletions src/component/nav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,30 @@ pub fn NavBar() -> impl IntoView {
let path = cur_location.pathname.get();
match path.as_str() {
"/" => 0,
"/upload" => 1,
"/menu" => 2,
"/upload" => 2,
"/wallet" | "/transactions" => 3,
"/menu" => 4,
s if s.starts_with("/hot-or-not") => {
home_path.set(path);
0
}
_ => 2,
_ => 4,
}
});

view! {
<div class="flex flex-row justify-between px-4 py-5 w-full bg-transparent fixed left-0 bottom-0 z-50">
<NavIcon idx=0 href=home_path icon=icondata::TbHome cur_selected=cur_selected/>
// TODO: achievements page
<NavIcon idx=1 href="/#" icon=icondata::AiTrophyOutlined cur_selected/>
<NavIcon
idx=1
idx=2
href="/upload"
icon=icondata::AiPlusCircleFilled
cur_selected=cur_selected
/>
<NavIcon idx=2 href="/menu" icon=icondata::AiMenuOutlined cur_selected=cur_selected/>
<NavIcon idx=3 href="/wallet" icon=icondata::BiWalletRegular cur_selected=cur_selected/>
<NavIcon idx=4 href="/menu" icon=icondata::AiMenuOutlined cur_selected=cur_selected/>
</div>
}
}
1 change: 1 addition & 0 deletions src/page/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod refer_earn;
pub mod root;
pub mod terms;
pub mod upload;
pub mod wallet;
48 changes: 27 additions & 21 deletions src/page/refer_earn/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,18 @@ mod history_provider {
}

pub trait HistoryProvider {
async fn get_history(&self, from: u64, end: u64)
-> Result<Vec<HistoryDetails>, AgentError>;
async fn get_history(
&self,
from: u64,
end: u64,
) -> Result<(Vec<HistoryDetails>, bool), AgentError>;
}

pub async fn get_history(
prov: &impl HistoryProvider,
from: u64,
) -> Result<HistoryRes, AgentError> {
let details = prov.get_history(from, from + 10).await?;
let list_end = details.len() < 10;
let (details, list_end) = prov.get_history(from, from + 10).await?;
Ok(HistoryRes {
details,
cursor: from + 10,
Expand All @@ -158,7 +160,7 @@ mod history_provider {
&self,
from: u64,
end: u64,
) -> Result<Vec<HistoryDetails>, AgentError> {
) -> Result<(Vec<HistoryDetails>, bool), AgentError> {
use crate::canister::individual_user_template::{MintEvent, Result5, TokenEvent};
use crate::utils::route::failure_redirect;
let individual = self.authenticated_user();
Expand All @@ -169,9 +171,10 @@ mod history_provider {
Result5::Ok(history) => history,
Result5::Err(_) => {
failure_redirect("failed to get posts");
return Ok(vec![]);
return Ok((vec![], true));
}
};
let list_end = history.len() < (end - from) as usize;
let details = history
.into_iter()
.filter_map(|(_, ev)| {
Expand All @@ -194,7 +197,7 @@ mod history_provider {
})
})
.collect();
Ok(details)
Ok((details, list_end))
}
}

Expand All @@ -218,21 +221,24 @@ mod history_provider {
&self,
from: u64,
end: u64,
) -> Result<Vec<HistoryDetails>, AgentError> {
) -> Result<(Vec<HistoryDetails>, bool), AgentError> {
let mut rand_gen = ChaCha8Rng::seed_from_u64(current_epoch().as_nanos() as u64);
Ok((from..end)
.map(|_| {
let sk = SecretKey::random(&mut rand_gen);
let epoch_secs = rand_gen.next_u32() as u64;
let identity = Secp256k1Identity::from_private_key(sk);
let amount = rand_gen.next_u64() % 500;
HistoryDetails {
epoch_secs,
referee: identity.sender().unwrap(),
amount,
}
})
.collect())
Ok((
(from..end)
.map(|_| {
let sk = SecretKey::random(&mut rand_gen);
let epoch_secs = rand_gen.next_u32() as u64;
let identity = Secp256k1Identity::from_private_key(sk);
let amount = rand_gen.next_u64() % 500;
HistoryDetails {
epoch_secs,
referee: identity.sender().unwrap(),
amount,
}
})
.collect(),
false,
))
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/page/refer_earn/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ fn ReferCode() -> impl IntoView {

// Is refer id supposed to be individual canister id or user id?

// Is refer id supposed to be individual canister id or user id?

<ReferLoading/>
}
})
Expand Down
135 changes: 135 additions & 0 deletions src/page/wallet/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
pub mod transactions;
mod txn;
use leptos::*;

use crate::{
component::bullet_loader::BulletLoader,
state::canisters::authenticated_canisters,
try_or_redirect_opt,
utils::{profile::ProfileDetails, MockPartialEq},
};
use txn::{
provider::{get_history_provider, HistoryProvider},
TxnView,
};

#[component]
fn FallbackGreeter() -> impl IntoView {
view! {
<div class="flex flex-col">
<span class="text-white/50 text-md">Welcome!</span>
<div class="w-3/4 rounded-full py-2 bg-white/40 animate-pulse"></div>
</div>
<div class="w-16 aspect-square overflow-clip rounded-full justify-self-end bg-white/40 animate-pulse"></div>
}
}

#[component]
fn ProfileGreeter(details: ProfileDetails) -> impl IntoView {
view! {
<div class="flex flex-col">
<span class="text-white/50 text-md">Welcome!</span>
<span class="text-white text-lg md:text-xl truncate">
{details.display_name_or_fallback()}
</span>
</div>
<div class="w-16 aspect-square overflow-clip justify-self-end rounded-full">
<img class="h-full w-full object-cover" src=details.profile_pic_or_random()/>
</div>
}
}

const RECENT_TXN_CNT: u64 = 10;

#[component]
fn BalanceFallback() -> impl IntoView {
view! { <div class="w-1/4 rounded-full py-3 mt-1 bg-white/30 animate-pulse"></div> }
}

#[component]
pub fn Wallet() -> impl IntoView {
let canisters = authenticated_canisters();
let canisters_reader = move || MockPartialEq(canisters.get().and_then(|c| c.transpose()));
let profile_details = create_resource(canisters_reader, move |canisters| async move {
let canisters = try_or_redirect_opt!(canisters.0?);
let user = canisters.authenticated_user();
let user_details = user.get_profile_details().await.ok()?;
Some(ProfileDetails::from(user_details))
});
let balance_resource = create_resource(canisters_reader, move |canisters| async move {
let canisters = try_or_redirect_opt!(canisters.0?);
let user = canisters.authenticated_user();
let balance = user
.get_utility_token_balance()
.await
.map(|b| b.to_string())
.unwrap_or("Error".to_string());
Some(balance)
});
let history_resource = create_resource(canisters_reader, move |canisters| async move {
let canisters = try_or_redirect_opt!(canisters.0?);
let history_prov = get_history_provider(canisters);
let (history, _) = history_prov.get_history(0, RECENT_TXN_CNT).await.ok()?;

Some(history)
});

view! {
<div class="flex flex-col w-dvw min-h-dvh bg-black gap-12 px-4 py-4">
<div class="grid grid-cols-2 grid-rows-1 items-center w-full">
<Suspense fallback=FallbackGreeter>
{move || {
profile_details
.get()
.flatten()
.map(|details| view! { <ProfileGreeter details/> })
.unwrap_or_else(|| view! { <FallbackGreeter/> })
}}

</Suspense>
</div>
<div class="flex flex-col w-full items-center mt-12 text-white">
<span class="text-md lg:text-lg uppercase">Your Coin Balance</span>
<Suspense fallback=BalanceFallback>
{move || {
balance_resource
.get()
.flatten()
.map(|bal| view! { <span class="text-xl lg:text-2xl">{bal}</span> })
.unwrap_or_else(|| {
view! {
<span class="flex justify-center w-full">
<BalanceFallback/>
</span>
}
})
}}
</Suspense>
</div>
<div class="flex flex-col w-full gap-2">
<div class="flex flex-row w-full items-end justify-between">
<span class="text-white text-sm md:text-md">Recent Transactions</span>
<a href="/transactions" class="text-white/50 text-md md:text-lg">
See All
</a>
</div>
<div class="flex flex-col divide-y divide-white/10">
<Suspense fallback=BulletLoader>
{move || {
history_resource
.get()
.flatten()
.map(|history| {
history
.into_iter()
.map(|info| view! { <TxnView info/> })
.collect::<Vec<_>>()
})
.unwrap_or_else(|| vec![view! { <BulletLoader/> }])
}}
</Suspense>
</div>
</div>
</div>
}
}
Loading

0 comments on commit 5dbce20

Please sign in to comment.