integrate onboarding

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-08-07 17:35:29 -04:00
parent bdcd31cda0
commit 1566cd5cf4
6 changed files with 192 additions and 85 deletions

View File

@@ -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(),
}
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),
))
}
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
}
}
} 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)
}
AccountLoginResponse::LoginWith(keypair) => {
let pubkey = keypair.pubkey;
(app_ctx.accounts.add_account(keypair), pubkey)
}
};
let cur_router = get_active_columns_mut(app_ctx.i18n, app_ctx.accounts, decks)
.column_mut(col)
.router_mut();
decks.add_deck_default(app_ctx, timeline_cache, pubkey);
let r = match response {
AccountLoginResponse::LoginWith(keypair) => {
cur_router.go_back();
app_ctx.accounts.add_account(keypair)
}
AccountLoginResponse::CreatingNew => {
cur_router.route_to(Route::Accounts(AccountsRoute::Onboarding));
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
}
}
}
}

View File

@@ -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"],
}
}
}

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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,

View File

@@ -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() {