Initial support for multiple account profiles

This commit is contained in:
Bnyro 2023-07-06 17:41:12 +02:00
parent cd0fd30e69
commit 29e661ec5e
6 changed files with 257 additions and 18 deletions

View File

@ -0,0 +1,100 @@
use gtk::prelude::*;
use relm4::prelude::*;
use crate::settings::{self, Account};
use super::accounts_page::AccountsPageInput;
pub struct AccountRow {
account: Account,
index: usize,
}
#[derive(Debug)]
pub enum AccountRowInput {
Select,
Logout,
Delete,
}
#[relm4::factory(pub)]
impl FactoryComponent for AccountRow {
type Init = Account;
type Input = AccountRowInput;
type Output = AccountsPageInput;
type ParentInput = AccountsPageInput;
type ParentWidget = gtk::Box;
type CommandOutput = ();
view! {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
gtk::Box {
set_orientation: gtk::Orientation::Horizontal,
gtk::Label {
#[watch]
set_label: &format!("{} ({})", self.account.name, self.account.instance_url),
add_controller = gtk::GestureClick {
connect_pressed[sender] => move |_, _, _, _| {
sender.input(AccountRowInput::Select);
}
},
},
gtk::Box {
set_hexpand: true,
},
gtk::Button {
set_icon_name: "system-log-out-symbolic",
connect_clicked => AccountRowInput::Logout,
},
gtk::Button {
set_icon_name: "edit-delete",
connect_clicked => AccountRowInput::Delete,
set_margin_start: 5,
},
},
gtk::Separator {
set_margin_all: 10,
}
}
}
fn init_model(init: Self::Init, index: &Self::Index, _sender: FactorySender<Self>) -> Self {
Self {
account: init,
index: index.current_index(),
}
}
fn forward_to_parent(output: Self::Output) -> Option<Self::ParentInput> {
Some(output)
}
fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
match message {
AccountRowInput::Select => {
settings::update_account_index(self.index);
let message = if self.account.instance_url.is_empty() {
crate::AppMsg::ChooseInstance
} else {
crate::AppMsg::OpenPosts
};
sender
.output_sender()
.emit(AccountsPageInput::Forward(message))
}
AccountRowInput::Logout => {
self.account.name = "".to_string();
self.account.id = 0;
self.account.jwt = None;
settings::update_account(self.account.clone(), self.index);
}
AccountRowInput::Delete => sender
.output_sender()
.emit(AccountsPageInput::Remove(self.index)),
}
}
}

View File

@ -0,0 +1,98 @@
use gtk::prelude::*;
use relm4::{factory::FactoryVecDeque, prelude::*};
use crate::settings::{self, get_prefs, Account};
use super::account_row::AccountRow;
pub struct AccountsPage {
accounts: FactoryVecDeque<AccountRow>,
}
#[derive(Debug)]
pub enum AccountsPageInput {
Update,
CreateNew,
Remove(usize),
Forward(crate::AppMsg),
}
#[relm4::component(pub)]
impl SimpleComponent for AccountsPage {
type Init = ();
type Input = AccountsPageInput;
type Output = crate::AppMsg;
view! {
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
gtk::Label {
set_label: "Accounts",
add_css_class: "font-very-bold",
set_margin_top: 10,
},
gtk::ScrolledWindow {
set_vexpand: true,
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_vexpand: true,
set_margin_all: 10,
#[local_ref]
accounts -> gtk::Box {
set_orientation: gtk::Orientation::Vertical,
},
gtk::Button {
set_label: "New",
connect_clicked => AccountsPageInput::CreateNew,
set_margin_top: 10,
}
}
}
}
}
fn init(
_init: Self::Init,
root: &Self::Root,
sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
let accounts = FactoryVecDeque::new(gtk::Box::builder().build(), sender.input_sender());
sender.input(AccountsPageInput::Update);
let model = Self { accounts };
let accounts = model.accounts.widget();
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
match message {
AccountsPageInput::Update => {
self.accounts.guard().clear();
for account in get_prefs().accounts {
self.accounts.guard().push_back(account);
}
}
AccountsPageInput::CreateNew => {
self.accounts.guard().push_back(Account::default());
settings::create_account(false);
}
AccountsPageInput::Remove(index) => {
if index as u32 != get_prefs().current_account_index {
self.accounts.guard().remove(index);
settings::remove_account(index);
// for now: update all to fix broken indexes
sender.input(AccountsPageInput::Update);
}
}
AccountsPageInput::Forward(msg) => {
sender.output_sender().emit(msg);
}
}
}
}

View File

@ -128,7 +128,7 @@ impl SimpleComponent for InstancesPage {
current_account.instance_url = url[0..url.len() - 1].to_string();
current_account.jwt = None;
settings::update_current_account(current_account);
crate::AppMsg::ShowLogin
crate::AppMsg::UpdateState(crate::AppState::Login)
}
Err(err) => crate::AppMsg::ShowMessage(err.to_string()),
};

View File

@ -1,3 +1,5 @@
pub mod account_row;
pub mod accounts_page;
pub mod comment_row;
pub mod communities_page;
pub mod community_page;

View File

@ -7,6 +7,7 @@ pub mod util;
use api::{community::default_community, post::default_post, user::default_person};
use components::{
accounts_page::AccountsPage,
communities_page::{CommunitiesPage, CommunitiesPageInput},
community_page::{self, CommunityPage},
inbox_page::{InboxInput, InboxPage},
@ -45,6 +46,7 @@ pub enum AppState {
Login,
Message,
Inbox,
AccountsPage,
}
struct App {
@ -59,6 +61,7 @@ struct App {
post_page: Controller<PostPage>,
inbox_page: Controller<InboxPage>,
login_page: Controller<LoginPage>,
accounts_page: Controller<AccountsPage>,
logged_in: bool,
about_dialog: Controller<AboutDialog>,
}
@ -66,7 +69,6 @@ struct App {
#[derive(Debug, Clone)]
pub enum AppMsg {
ChooseInstance,
ShowLogin,
LoggedIn,
Logout,
ShowMessage(String),
@ -194,16 +196,22 @@ impl SimpleComponent for App {
inbox_page -> gtk::Box {}
}
}
AppState::AccountsPage => {
gtk::Box {
#[local_ref]
accounts_page -> gtk::Box {}
}
}
}
}
}
menu! {
menu_model: {
"Choose Instance" => ChangeInstanceAction,
"Profile" => ProfileAction,
"Change Instance" => ChangeInstanceAction,
"Accounts" => AccountsAction,
"Login" => LoginAction,
"Logout" => LogoutAction,
"Profile" => ProfileAction,
"About" => AboutAction
}
}
@ -250,6 +258,9 @@ impl SimpleComponent for App {
let login_page = LoginPage::builder()
.launch(())
.forward(sender.input_sender(), |msg| msg);
let accounts_page = AccountsPage::builder()
.launch(())
.forward(sender.input_sender(), |msg| msg);
let model = App {
state,
@ -263,6 +274,7 @@ impl SimpleComponent for App {
inbox_page,
communities_page,
login_page,
accounts_page,
message: None,
about_dialog,
};
@ -281,6 +293,7 @@ impl SimpleComponent for App {
let inbox_page = model.inbox_page.widget();
let communities_page = model.communities_page.widget();
let login_page = model.login_page.widget();
let accounts_page = model.accounts_page.widget();
let widgets = view_output!();
@ -290,6 +303,10 @@ impl SimpleComponent for App {
RelmAction::new_stateless(move |_| {
instance_sender.input(AppMsg::ChooseInstance);
});
let accounts_sender = sender.clone();
let accounts_action: RelmAction<AccountsAction> = RelmAction::new_stateless(move |_| {
accounts_sender.input(AppMsg::UpdateState(AppState::AccountsPage));
});
let profile_sender = sender.clone();
let profile_action: RelmAction<ProfileAction> = RelmAction::new_stateless(move |_| {
let person = settings::get_current_account();
@ -299,10 +316,7 @@ impl SimpleComponent for App {
});
let login_sender = sender.clone();
let login_action: RelmAction<LoginAction> = RelmAction::new_stateless(move |_| {
login_sender.input(AppMsg::ShowLogin);
});
let logout_action: RelmAction<LogoutAction> = RelmAction::new_stateless(move |_| {
sender.input(AppMsg::Logout);
login_sender.input(AppMsg::UpdateState(AppState::Login));
});
let about_action = {
let sender = model.about_dialog.sender().clone();
@ -313,9 +327,9 @@ impl SimpleComponent for App {
let mut group = RelmActionGroup::<WindowActionGroup>::new();
group.add_action(instance_action);
group.add_action(accounts_action);
group.add_action(profile_action);
group.add_action(login_action);
group.add_action(logout_action);
group.add_action(about_action);
group.register_for_widget(&widgets.main_window);
@ -390,12 +404,11 @@ impl SimpleComponent for App {
.emit(post_page::PostPageInput::UpdatePost(post));
self.state = AppState::Post;
}
AppMsg::ShowLogin => {
self.state = AppState::Login;
}
AppMsg::Logout => {
let mut account = settings::get_current_account();
account.jwt = None;
account.name = "".to_string();
account.id = 0;
settings::update_current_account(account);
self.logged_in = false;
}
@ -434,9 +447,9 @@ impl SimpleComponent for App {
relm4::new_action_group!(WindowActionGroup, "win");
relm4::new_stateless_action!(ChangeInstanceAction, WindowActionGroup, "instance");
relm4::new_stateless_action!(ProfileAction, WindowActionGroup, "profile");
relm4::new_stateless_action!(AccountsAction, WindowActionGroup, "accounts");
relm4::new_stateless_action!(LoginAction, WindowActionGroup, "login");
relm4::new_stateless_action!(LogoutAction, WindowActionGroup, "logout");
relm4::new_stateless_action!(ProfileAction, WindowActionGroup, "profile");
relm4::new_stateless_action!(AboutAction, WindowActionGroup, "about");
fn main() {

View File

@ -45,14 +45,40 @@ pub fn get_prefs() -> Preferences {
pub fn get_current_account() -> Account {
let mut prefs = get_prefs();
if prefs.accounts.len() == 0 {
prefs.accounts.push(Account::default());
save_prefs(&prefs);
prefs = create_account(true);
}
prefs.accounts[prefs.current_account_index as usize].clone()
}
pub fn update_current_account(account: Account) {
let settings = get_prefs();
update_account(account, settings.current_account_index as usize)
}
pub fn update_account(account: Account, index: usize) {
let mut settings = get_prefs();
settings.accounts[settings.current_account_index as usize] = account;
settings.accounts[index] = account;
save_prefs(&settings);
}
pub fn remove_account(index: usize) {
let mut settings = get_prefs();
settings.accounts.remove(index);
save_prefs(&settings);
}
pub fn create_account(reset_index: bool) -> Preferences {
let mut prefs = get_prefs();
prefs.accounts.push(Account::default());
if reset_index {
prefs.current_account_index = 0;
}
save_prefs(&prefs);
prefs
}
pub fn update_account_index(index: usize) {
let mut prefs = get_prefs();
prefs.current_account_index = index as u32;
save_prefs(&prefs);
}