mod.rs (8934B)
1 use enostr::{FullKeypair, Pubkey}; 2 use nostrdb::{Ndb, Transaction}; 3 4 use notedeck::{Accounts, AppContext, DragResponse, Localization, SingleUnkIdAction, UnknownIds}; 5 use notedeck_ui::nip51_set::Nip51SetUiCache; 6 7 pub use crate::accounts::route::AccountsResponse; 8 use crate::app::get_active_columns_mut; 9 use crate::decks::DecksCache; 10 use crate::onboarding::{Onboarding, OnboardingEffect}; 11 use crate::profile::{send_default_dms_relay_list, send_new_contact_list}; 12 use crate::scoped_sub_owner_keys::onboarding_owner_key; 13 use crate::ui::onboarding::{FollowPackOnboardingView, FollowPacksResponse, OnboardingResponse}; 14 use crate::{ 15 login_manager::AcquireKeyState, 16 route::Route, 17 ui::{ 18 account_login_view::{AccountLoginResponse, AccountLoginView}, 19 accounts::{AccountsView, AccountsViewResponse}, 20 }, 21 }; 22 use tracing::info; 23 24 mod route; 25 26 pub use route::{AccountsRoute, AccountsRouteResponse}; 27 28 impl AddAccountAction { 29 // Simple wrapper around processing the unknown action to expose too 30 // much internal logic. This allows us to have a must_use on our 31 // LoginAction type, otherwise the SingleUnkIdAction's must_use will 32 // be lost when returned in the login action 33 pub fn process_action(&mut self, ids: &mut UnknownIds, ndb: &Ndb, txn: &Transaction) { 34 self.unk_id_action.process_action(ids, ndb, txn); 35 } 36 } 37 38 #[derive(Debug, Clone)] 39 pub struct SwitchAccountAction { 40 pub source_column: usize, 41 42 /// The account to switch to 43 pub switch_to: Pubkey, 44 pub switching_to_new: bool, 45 } 46 47 impl SwitchAccountAction { 48 pub fn new(source_column: usize, switch_to: Pubkey) -> Self { 49 SwitchAccountAction { 50 source_column, 51 switch_to, 52 switching_to_new: false, 53 } 54 } 55 56 pub fn switching_to_new(mut self) -> Self { 57 self.switching_to_new = true; 58 self 59 } 60 } 61 62 #[derive(Debug)] 63 pub enum AccountsAction { 64 Switch(SwitchAccountAction), 65 Remove(Pubkey), 66 } 67 68 #[must_use = "You must call process_login_action on this to handle unknown ids"] 69 pub struct AddAccountAction { 70 pub accounts_action: Option<AccountsAction>, 71 pub unk_id_action: SingleUnkIdAction, 72 } 73 74 /// Render account management views from a route 75 #[allow(clippy::too_many_arguments)] 76 pub fn render_accounts_route( 77 ui: &mut egui::Ui, 78 app_ctx: &mut AppContext, 79 login_state: &mut AcquireKeyState, 80 onboarding: &mut Onboarding, 81 follow_packs_ui: &mut Nip51SetUiCache, 82 route: AccountsRoute, 83 ) -> DragResponse<AccountsResponse> { 84 match route { 85 AccountsRoute::Accounts => AccountsView::new( 86 app_ctx.ndb, 87 app_ctx.accounts, 88 app_ctx.media_jobs.sender(), 89 app_ctx.img_cache, 90 app_ctx.i18n, 91 ) 92 .ui(ui) 93 .map_output(AccountsRouteResponse::Accounts) 94 .map_output(AccountsResponse::Account), 95 AccountsRoute::AddAccount => { 96 let action = AccountLoginView::new(login_state, app_ctx.clipboard, app_ctx.i18n) 97 .ui(ui) 98 .inner 99 .map(AccountsRouteResponse::AddAccount) 100 .map(AccountsResponse::Account); 101 DragResponse::output(action) 102 } 103 AccountsRoute::Onboarding => FollowPackOnboardingView::new( 104 onboarding, 105 follow_packs_ui, 106 app_ctx.ndb, 107 app_ctx.img_cache, 108 app_ctx.i18n, 109 app_ctx.media_jobs.sender(), 110 ) 111 .ui(ui) 112 .map_output(|r| match r { 113 OnboardingResponse::FollowPacks(follow_packs_response) => { 114 AccountsResponse::Account(AccountsRouteResponse::AddAccount( 115 AccountLoginResponse::Onboarding(follow_packs_response), 116 )) 117 } 118 OnboardingResponse::ViewProfile(pubkey) => AccountsResponse::ViewProfile(pubkey), 119 }), 120 } 121 } 122 123 pub fn process_accounts_view_response( 124 i18n: &mut Localization, 125 accounts: &mut Accounts, 126 decks: &mut DecksCache, 127 col: usize, 128 response: AccountsViewResponse, 129 ) -> Option<AccountsAction> { 130 let router = get_active_columns_mut(i18n, accounts, decks) 131 .column_mut(col) 132 .router_mut(); 133 let mut action = None; 134 match response { 135 AccountsViewResponse::RemoveAccount(pk_to_remove) => { 136 let cur_action = AccountsAction::Remove(pk_to_remove); 137 info!("account selection: {:?}", action); 138 action = Some(cur_action); 139 } 140 AccountsViewResponse::SelectAccount(new_pk) => { 141 let acc_sel = AccountsAction::Switch(SwitchAccountAction::new(col, new_pk)); 142 info!("account selection: {:?}", acc_sel); 143 action = Some(acc_sel); 144 } 145 AccountsViewResponse::RouteToLogin => { 146 router.route_to(Route::add_account()); 147 } 148 } 149 action 150 } 151 152 pub fn process_login_view_response( 153 app_ctx: &mut AppContext, 154 decks: &mut DecksCache, 155 onboarding: &mut Onboarding, 156 col: usize, 157 response: AccountLoginResponse, 158 ) -> AddAccountAction { 159 let cur_router = get_active_columns_mut(app_ctx.i18n, app_ctx.accounts, decks) 160 .column_mut(col) 161 .router_mut(); 162 163 let r = match response { 164 AccountLoginResponse::LoginWith(keypair) => { 165 cur_router.go_back(); 166 app_ctx.accounts.add_account(keypair) 167 } 168 AccountLoginResponse::CreatingNew => { 169 cur_router.route_to(Route::Accounts(AccountsRoute::Onboarding)); 170 process_onboarding_step(app_ctx, onboarding, col); 171 172 None 173 } 174 AccountLoginResponse::Onboarding(onboarding_response) => match onboarding_response { 175 FollowPacksResponse::NoFollowPacks => { 176 process_onboarding_step(app_ctx, onboarding, col); 177 None 178 } 179 FollowPacksResponse::UserSelectedPacks(nip51_sets_ui_state) => { 180 let pks_to_follow = nip51_sets_ui_state.get_all_selected(); 181 182 let kp = FullKeypair::generate(); 183 184 { 185 let mut publisher = app_ctx.remote.publisher(app_ctx.accounts); 186 send_new_contact_list( 187 kp.to_filled(), 188 app_ctx.ndb, 189 &mut publisher, 190 pks_to_follow, 191 ); 192 send_default_dms_relay_list(kp.to_filled(), app_ctx.ndb, &mut publisher); 193 } 194 cur_router.go_back(); 195 onboarding.end_onboarding(app_ctx.ndb); 196 let mut scoped_subs = app_ctx.remote.scoped_subs(app_ctx.accounts); 197 let _ = scoped_subs.drop_owner(onboarding_owner_key(col)); 198 199 app_ctx.accounts.add_account(kp.to_keypair()) 200 } 201 }, 202 }; 203 204 if let Some(action) = r { 205 AddAccountAction { 206 accounts_action: Some(AccountsAction::Switch(SwitchAccountAction { 207 source_column: col, 208 switch_to: action.switch_to, 209 switching_to_new: true, 210 })), 211 unk_id_action: action.unk_id_action, 212 } 213 } else { 214 AddAccountAction { 215 accounts_action: None, 216 unk_id_action: SingleUnkIdAction::NoAction, 217 } 218 } 219 } 220 221 fn process_onboarding_step(app_ctx: &mut AppContext, onboarding: &mut Onboarding, col: usize) { 222 let owner = onboarding_owner_key(col); 223 let effect = { 224 let mut scoped_subs = app_ctx.remote.scoped_subs(app_ctx.accounts); 225 onboarding.process(&mut scoped_subs, owner, app_ctx.ndb, app_ctx.unknown_ids) 226 }; 227 228 if let Some(OnboardingEffect::Oneshot(filters)) = effect { 229 let mut oneshot = app_ctx.remote.oneshot(app_ctx.accounts); 230 oneshot.oneshot(filters); 231 } 232 } 233 234 impl AccountsRouteResponse { 235 pub fn process( 236 self, 237 app_ctx: &mut AppContext, 238 app: &mut crate::Damus, 239 col: usize, 240 ) -> AddAccountAction { 241 match self { 242 AccountsRouteResponse::Accounts(response) => { 243 let action = process_accounts_view_response( 244 app_ctx.i18n, 245 app_ctx.accounts, 246 &mut app.decks_cache, 247 col, 248 response, 249 ); 250 AddAccountAction { 251 accounts_action: action, 252 unk_id_action: notedeck::SingleUnkIdAction::no_action(), 253 } 254 } 255 AccountsRouteResponse::AddAccount(response) => { 256 let action = process_login_view_response( 257 app_ctx, 258 &mut app.decks_cache, 259 &mut app.onboarding, 260 col, 261 response, 262 ); 263 app.view_state.login = Default::default(); 264 265 action 266 } 267 } 268 } 269 }