notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }