commit 1566cd5cf4d3b3d69d06e9b518bed3e037984acf
parent bdcd31cda00f942b4472748365280ec63c247a65
Author: kernelkind <kernelkind@gmail.com>
Date: Thu, 7 Aug 2025 17:35:29 -0400
integrate onboarding
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
6 files changed, 190 insertions(+), 83 deletions(-)
diff --git a/crates/notedeck_columns/src/accounts/mod.rs b/crates/notedeck_columns/src/accounts/mod.rs
@@ -1,15 +1,19 @@
use enostr::{FullKeypair, Pubkey};
use nostrdb::{Ndb, Transaction};
-use notedeck::{Accounts, AppContext, Localization, SingleUnkIdAction, UnknownIds};
+use notedeck::{Accounts, AppContext, JobsCache, Localization, SingleUnkIdAction, UnknownIds};
+use notedeck_ui::nip51_set::Nip51SetUiCache;
+pub use crate::accounts::route::AccountsResponse;
use crate::app::get_active_columns_mut;
use crate::decks::DecksCache;
+use crate::onboarding::Onboarding;
use crate::profile::send_new_contact_list;
+use crate::subscriptions::Subscriptions;
+use crate::ui::onboarding::{FollowPackOnboardingView, FollowPacksResponse, OnboardingResponse};
use crate::{
login_manager::AcquireKeyState,
route::Route,
- timeline::TimelineCache,
ui::{
account_login_view::{AccountLoginResponse, AccountLoginView},
accounts::{AccountsView, AccountsViewResponse},
@@ -37,6 +41,7 @@ pub struct SwitchAccountAction {
/// The account to switch to
pub switch_to: Pubkey,
+ pub switching_to_new: bool,
}
impl SwitchAccountAction {
@@ -44,8 +49,14 @@ impl SwitchAccountAction {
SwitchAccountAction {
source_column,
switch_to,
+ switching_to_new: false,
}
}
+
+ pub fn switching_to_new(mut self) -> Self {
+ self.switching_to_new = true;
+ self
+ }
}
#[derive(Debug)]
@@ -65,13 +76,13 @@ pub struct AddAccountAction {
pub fn render_accounts_route(
ui: &mut egui::Ui,
app_ctx: &mut AppContext,
- col: usize,
- decks: &mut DecksCache,
- timeline_cache: &mut TimelineCache,
+ jobs: &mut JobsCache,
login_state: &mut AcquireKeyState,
+ onboarding: &Onboarding,
+ follow_packs_ui: &mut Nip51SetUiCache,
route: AccountsRoute,
-) -> AddAccountAction {
- let resp = match route {
+) -> Option<AccountsResponse> {
+ match route {
AccountsRoute::Accounts => AccountsView::new(
app_ctx.ndb,
app_ctx.accounts,
@@ -80,47 +91,33 @@ pub fn render_accounts_route(
)
.ui(ui)
.inner
- .map(AccountsRouteResponse::Accounts),
-
+ .map(AccountsRouteResponse::Accounts)
+ .map(AccountsResponse::Account),
AccountsRoute::AddAccount => {
AccountLoginView::new(login_state, app_ctx.clipboard, app_ctx.i18n)
.ui(ui)
.inner
.map(AccountsRouteResponse::AddAccount)
+ .map(AccountsResponse::Account)
}
- };
-
- if let Some(resp) = resp {
- match resp {
- AccountsRouteResponse::Accounts(response) => {
- let action = process_accounts_view_response(
- app_ctx.i18n,
- app_ctx.accounts,
- decks,
- col,
- response,
- );
- AddAccountAction {
- accounts_action: action,
- unk_id_action: SingleUnkIdAction::no_action(),
- }
- }
- AccountsRouteResponse::AddAccount(response) => {
- let action =
- process_login_view_response(app_ctx, timeline_cache, decks, col, response);
- *login_state = Default::default();
- let router = get_active_columns_mut(app_ctx.i18n, app_ctx.accounts, decks)
- .column_mut(col)
- .router_mut();
- router.go_back();
- action
+ AccountsRoute::Onboarding => FollowPackOnboardingView::new(
+ onboarding,
+ follow_packs_ui,
+ app_ctx.ndb,
+ app_ctx.img_cache,
+ app_ctx.i18n,
+ app_ctx.job_pool,
+ jobs,
+ )
+ .ui(ui)
+ .map(|r| match r {
+ OnboardingResponse::FollowPacks(follow_packs_response) => {
+ AccountsResponse::Account(AccountsRouteResponse::AddAccount(
+ AccountLoginResponse::Onboarding(follow_packs_response),
+ ))
}
- }
- } else {
- AddAccountAction {
- accounts_action: None,
- unk_id_action: SingleUnkIdAction::no_action(),
- }
+ OnboardingResponse::ViewProfile(pubkey) => AccountsResponse::ViewProfile(pubkey),
+ }),
}
}
@@ -155,31 +152,53 @@ pub fn process_accounts_view_response(
pub fn process_login_view_response(
app_ctx: &mut AppContext,
- timeline_cache: &mut TimelineCache,
decks: &mut DecksCache,
+ subs: &mut Subscriptions,
+ onboarding: &mut Onboarding,
col: usize,
response: AccountLoginResponse,
) -> AddAccountAction {
- let (r, pubkey) = match response {
- AccountLoginResponse::CreateNew => {
- let kp = FullKeypair::generate();
- let pubkey = kp.pubkey;
- send_new_contact_list(kp.to_filled(), app_ctx.ndb, app_ctx.pool);
- (app_ctx.accounts.add_account(kp.to_keypair()), pubkey)
- }
+ let cur_router = get_active_columns_mut(app_ctx.i18n, app_ctx.accounts, decks)
+ .column_mut(col)
+ .router_mut();
+
+ let r = match response {
AccountLoginResponse::LoginWith(keypair) => {
- let pubkey = keypair.pubkey;
- (app_ctx.accounts.add_account(keypair), pubkey)
+ cur_router.go_back();
+ app_ctx.accounts.add_account(keypair)
}
- };
+ AccountLoginResponse::CreatingNew => {
+ cur_router.route_to(Route::Accounts(AccountsRoute::Onboarding));
- decks.add_deck_default(app_ctx, timeline_cache, pubkey);
+ onboarding.process(app_ctx.pool, app_ctx.ndb, subs, app_ctx.unknown_ids);
+
+ None
+ }
+ AccountLoginResponse::Onboarding(onboarding_response) => match onboarding_response {
+ FollowPacksResponse::NoFollowPacks => {
+ onboarding.process(app_ctx.pool, app_ctx.ndb, subs, app_ctx.unknown_ids);
+ None
+ }
+ FollowPacksResponse::UserSelectedPacks(nip51_sets_ui_state) => {
+ let pks_to_follow = nip51_sets_ui_state.get_all_selected();
+
+ let kp = FullKeypair::generate();
+
+ send_new_contact_list(kp.to_filled(), app_ctx.ndb, app_ctx.pool, pks_to_follow);
+ cur_router.go_back();
+ onboarding.end_onboarding(app_ctx.pool, app_ctx.ndb);
+
+ app_ctx.accounts.add_account(kp.to_keypair())
+ }
+ },
+ };
if let Some(action) = r {
AddAccountAction {
accounts_action: Some(AccountsAction::Switch(SwitchAccountAction {
source_column: col,
switch_to: action.switch_to,
+ switching_to_new: true,
})),
unk_id_action: action.unk_id_action,
}
@@ -190,3 +209,41 @@ pub fn process_login_view_response(
}
}
}
+
+impl AccountsRouteResponse {
+ pub fn process(
+ self,
+ app_ctx: &mut AppContext,
+ app: &mut crate::Damus,
+ col: usize,
+ ) -> AddAccountAction {
+ match self {
+ AccountsRouteResponse::Accounts(response) => {
+ let action = process_accounts_view_response(
+ app_ctx.i18n,
+ app_ctx.accounts,
+ &mut app.decks_cache,
+ col,
+ response,
+ );
+ AddAccountAction {
+ accounts_action: action,
+ unk_id_action: notedeck::SingleUnkIdAction::no_action(),
+ }
+ }
+ AccountsRouteResponse::AddAccount(response) => {
+ let action = process_login_view_response(
+ app_ctx,
+ &mut app.decks_cache,
+ &mut app.subscriptions,
+ &mut app.onboarding,
+ col,
+ response,
+ );
+ app.view_state.login = Default::default();
+
+ action
+ }
+ }
+ }
+}
diff --git a/crates/notedeck_columns/src/accounts/route.rs b/crates/notedeck_columns/src/accounts/route.rs
@@ -7,10 +7,16 @@ pub enum AccountsRouteResponse {
AddAccount(AccountLoginResponse),
}
+pub enum AccountsResponse {
+ ViewProfile(enostr::Pubkey),
+ Account(AccountsRouteResponse),
+}
+
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum AccountsRoute {
Accounts,
AddAccount,
+ Onboarding,
}
impl AccountsRoute {
@@ -19,6 +25,7 @@ impl AccountsRoute {
match self {
Self::Accounts => &["accounts", "show"],
Self::AddAccount => &["accounts", "new"],
+ Self::Onboarding => &["accounts", "onboarding"],
}
}
}
diff --git a/crates/notedeck_columns/src/nav.rs b/crates/notedeck_columns/src/nav.rs
@@ -1,5 +1,5 @@
use crate::{
- accounts::{render_accounts_route, AccountsAction},
+ accounts::{render_accounts_route, AccountsAction, AccountsResponse},
app::{get_active_columns_mut, get_decks_mut},
column::ColumnsAction,
deck_state::DeckState,
@@ -19,6 +19,7 @@ use crate::{
configure_deck::ConfigureDeckView,
edit_deck::{EditDeckResponse, EditDeckView},
note::{custom_zap::CustomZapView, NewPostAction, PostAction, PostType, QuoteRepostView},
+ onboarding::FollowPackOnboardingView,
profile::EditProfileView,
search::{FocusState, SearchView},
settings::SettingsAction,
@@ -85,14 +86,21 @@ impl SwitchingAction {
match &self {
SwitchingAction::Accounts(account_action) => match account_action {
AccountsAction::Switch(switch_action) => {
- let txn = Transaction::new(ctx.ndb).expect("txn");
- ctx.accounts.select_account(
- &switch_action.switch_to,
- ctx.ndb,
- &txn,
- ctx.pool,
- ui_ctx,
- );
+ {
+ let txn = Transaction::new(ctx.ndb).expect("txn");
+ ctx.accounts.select_account(
+ &switch_action.switch_to,
+ ctx.ndb,
+ &txn,
+ ctx.pool,
+ ui_ctx,
+ );
+ }
+
+ if switch_action.switching_to_new {
+ decks_cache.add_deck_default(ctx, timeline_cache, switch_action.switch_to);
+ }
+
// pop nav after switch
get_active_columns_mut(ctx.i18n, ctx.accounts, decks_cache)
.column_mut(switch_action.source_column)
@@ -564,21 +572,33 @@ fn render_nav_body(
&mut note_context,
&mut app.jobs,
),
- Route::Accounts(amr) => {
- let mut action = render_accounts_route(
+ Route::Accounts(amr) => 's: {
+ let Some(action) = render_accounts_route(
ui,
ctx,
- col,
- &mut app.decks_cache,
- &mut app.timeline_cache,
+ &mut app.jobs,
&mut app.view_state.login,
+ &app.onboarding,
+ &mut app.view_state.follow_packs,
*amr,
- );
- let txn = Transaction::new(ctx.ndb).expect("txn");
- action.process_action(ctx.unknown_ids, ctx.ndb, &txn);
- action
- .accounts_action
- .map(|f| RenderNavAction::SwitchingAction(SwitchingAction::Accounts(f)))
+ ) else {
+ break 's None;
+ };
+
+ match action {
+ AccountsResponse::ViewProfile(pubkey) => {
+ Some(RenderNavAction::NoteAction(NoteAction::Profile(pubkey)))
+ }
+ AccountsResponse::Account(accounts_route_response) => {
+ let mut action = accounts_route_response.process(ctx, app, col);
+
+ let txn = Transaction::new(ctx.ndb).expect("txn");
+ action.process_action(ctx.unknown_ids, ctx.ndb, &txn);
+ action
+ .accounts_action
+ .map(|f| RenderNavAction::SwitchingAction(SwitchingAction::Accounts(f)))
+ }
+ }
}
Route::Relays => RelayView::new(ctx.pool, &mut app.view_state.id_string_map, ctx.i18n)
.ui(ui)
@@ -1061,6 +1081,9 @@ fn get_scroll_id(
Route::Accounts(accounts_route) => match accounts_route {
crate::accounts::AccountsRoute::Accounts => Some(AccountsView::scroll_id()),
crate::accounts::AccountsRoute::AddAccount => None,
+ crate::accounts::AccountsRoute::Onboarding => {
+ Some(FollowPackOnboardingView::scroll_id())
+ }
},
Route::Reply(note_id) => Some(PostReplyView::scroll_id(col, note_id.bytes())),
Route::Quote(note_id) => Some(QuoteRepostView::scroll_id(col, note_id.bytes())),
@@ -1085,6 +1108,7 @@ fn route_uses_frame(route: &Route) -> bool {
Route::Accounts(accounts_route) => match accounts_route {
crate::accounts::AccountsRoute::Accounts => true,
crate::accounts::AccountsRoute::AddAccount => false,
+ crate::accounts::AccountsRoute::Onboarding => false,
},
Route::Relays => true,
Route::Timeline(_) => false,
diff --git a/crates/notedeck_columns/src/profile.rs b/crates/notedeck_columns/src/profile.rs
@@ -218,18 +218,30 @@ fn send_note_builder(builder: NoteBuilder, ndb: &Ndb, pool: &mut RelayPool, kp:
pool.send(event);
}
-pub fn send_new_contact_list(kp: FilledKeypair, ndb: &Ndb, pool: &mut RelayPool) {
- let builder = construct_new_contact_list(kp.pubkey);
+pub fn send_new_contact_list(
+ kp: FilledKeypair,
+ ndb: &Ndb,
+ pool: &mut RelayPool,
+ mut pks_to_follow: Vec<Pubkey>,
+) {
+ if !pks_to_follow.contains(kp.pubkey) {
+ pks_to_follow.push(*kp.pubkey);
+ }
+
+ let builder = construct_new_contact_list(pks_to_follow);
send_note_builder(builder, ndb, pool, kp);
}
-fn construct_new_contact_list<'a>(pk: &'a Pubkey) -> NoteBuilder<'a> {
- NoteBuilder::new()
+fn construct_new_contact_list<'a>(pks: Vec<Pubkey>) -> NoteBuilder<'a> {
+ let mut builder = NoteBuilder::new()
.content("")
.kind(3)
- .options(NoteBuildOptions::default())
- .start_tag()
- .tag_str("p")
- .tag_str(&pk.hex())
+ .options(NoteBuildOptions::default());
+
+ for pk in pks {
+ builder = builder.start_tag().tag_str("p").tag_str(&pk.hex());
+ }
+
+ builder
}
diff --git a/crates/notedeck_columns/src/route.rs b/crates/notedeck_columns/src/route.rs
@@ -278,6 +278,11 @@ impl Route {
"Add Account",
"Column title for adding new account"
)),
+ AccountsRoute::Onboarding => ColumnTitle::formatted(tr!(
+ i18n,
+ "Onboarding",
+ "Column title for finding users to follow"
+ )),
},
Route::ComposeNote => ColumnTitle::formatted(tr!(
i18n,
diff --git a/crates/notedeck_columns/src/ui/account_login_view.rs b/crates/notedeck_columns/src/ui/account_login_view.rs
@@ -1,4 +1,5 @@
use crate::login_manager::AcquireKeyState;
+use crate::ui::onboarding::FollowPacksResponse;
use crate::ui::{Preview, PreviewConfig};
use egui::{
Align, Button, Color32, Frame, InnerResponse, Layout, Margin, RichText, TextEdit, Vec2,
@@ -18,7 +19,8 @@ pub struct AccountLoginView<'a> {
}
pub enum AccountLoginResponse {
- CreateNew,
+ CreatingNew,
+ Onboarding(FollowPacksResponse),
LoginWith(Keypair),
}
@@ -96,7 +98,7 @@ impl<'a> AccountLoginView<'a> {
});
if self.manager.check_for_create_new() {
- return Some(AccountLoginResponse::CreateNew);
+ return Some(AccountLoginResponse::CreatingNew);
}
if let Some(keypair) = self.manager.get_login_keypair() {