Initial support for multiple account profiles
This commit is contained in:
parent
cd0fd30e69
commit
29e661ec5e
|
@ -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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,7 +128,7 @@ impl SimpleComponent for InstancesPage {
|
||||||
current_account.instance_url = url[0..url.len() - 1].to_string();
|
current_account.instance_url = url[0..url.len() - 1].to_string();
|
||||||
current_account.jwt = None;
|
current_account.jwt = None;
|
||||||
settings::update_current_account(current_account);
|
settings::update_current_account(current_account);
|
||||||
crate::AppMsg::ShowLogin
|
crate::AppMsg::UpdateState(crate::AppState::Login)
|
||||||
}
|
}
|
||||||
Err(err) => crate::AppMsg::ShowMessage(err.to_string()),
|
Err(err) => crate::AppMsg::ShowMessage(err.to_string()),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod account_row;
|
||||||
|
pub mod accounts_page;
|
||||||
pub mod comment_row;
|
pub mod comment_row;
|
||||||
pub mod communities_page;
|
pub mod communities_page;
|
||||||
pub mod community_page;
|
pub mod community_page;
|
||||||
|
|
41
src/main.rs
41
src/main.rs
|
@ -7,6 +7,7 @@ pub mod util;
|
||||||
|
|
||||||
use api::{community::default_community, post::default_post, user::default_person};
|
use api::{community::default_community, post::default_post, user::default_person};
|
||||||
use components::{
|
use components::{
|
||||||
|
accounts_page::AccountsPage,
|
||||||
communities_page::{CommunitiesPage, CommunitiesPageInput},
|
communities_page::{CommunitiesPage, CommunitiesPageInput},
|
||||||
community_page::{self, CommunityPage},
|
community_page::{self, CommunityPage},
|
||||||
inbox_page::{InboxInput, InboxPage},
|
inbox_page::{InboxInput, InboxPage},
|
||||||
|
@ -45,6 +46,7 @@ pub enum AppState {
|
||||||
Login,
|
Login,
|
||||||
Message,
|
Message,
|
||||||
Inbox,
|
Inbox,
|
||||||
|
AccountsPage,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
|
@ -59,6 +61,7 @@ struct App {
|
||||||
post_page: Controller<PostPage>,
|
post_page: Controller<PostPage>,
|
||||||
inbox_page: Controller<InboxPage>,
|
inbox_page: Controller<InboxPage>,
|
||||||
login_page: Controller<LoginPage>,
|
login_page: Controller<LoginPage>,
|
||||||
|
accounts_page: Controller<AccountsPage>,
|
||||||
logged_in: bool,
|
logged_in: bool,
|
||||||
about_dialog: Controller<AboutDialog>,
|
about_dialog: Controller<AboutDialog>,
|
||||||
}
|
}
|
||||||
|
@ -66,7 +69,6 @@ struct App {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AppMsg {
|
pub enum AppMsg {
|
||||||
ChooseInstance,
|
ChooseInstance,
|
||||||
ShowLogin,
|
|
||||||
LoggedIn,
|
LoggedIn,
|
||||||
Logout,
|
Logout,
|
||||||
ShowMessage(String),
|
ShowMessage(String),
|
||||||
|
@ -194,16 +196,22 @@ impl SimpleComponent for App {
|
||||||
inbox_page -> gtk::Box {}
|
inbox_page -> gtk::Box {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AppState::AccountsPage => {
|
||||||
|
gtk::Box {
|
||||||
|
#[local_ref]
|
||||||
|
accounts_page -> gtk::Box {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
menu! {
|
menu! {
|
||||||
menu_model: {
|
menu_model: {
|
||||||
"Choose Instance" => ChangeInstanceAction,
|
"Change Instance" => ChangeInstanceAction,
|
||||||
"Profile" => ProfileAction,
|
"Accounts" => AccountsAction,
|
||||||
"Login" => LoginAction,
|
"Login" => LoginAction,
|
||||||
"Logout" => LogoutAction,
|
"Profile" => ProfileAction,
|
||||||
"About" => AboutAction
|
"About" => AboutAction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,6 +258,9 @@ impl SimpleComponent for App {
|
||||||
let login_page = LoginPage::builder()
|
let login_page = LoginPage::builder()
|
||||||
.launch(())
|
.launch(())
|
||||||
.forward(sender.input_sender(), |msg| msg);
|
.forward(sender.input_sender(), |msg| msg);
|
||||||
|
let accounts_page = AccountsPage::builder()
|
||||||
|
.launch(())
|
||||||
|
.forward(sender.input_sender(), |msg| msg);
|
||||||
|
|
||||||
let model = App {
|
let model = App {
|
||||||
state,
|
state,
|
||||||
|
@ -263,6 +274,7 @@ impl SimpleComponent for App {
|
||||||
inbox_page,
|
inbox_page,
|
||||||
communities_page,
|
communities_page,
|
||||||
login_page,
|
login_page,
|
||||||
|
accounts_page,
|
||||||
message: None,
|
message: None,
|
||||||
about_dialog,
|
about_dialog,
|
||||||
};
|
};
|
||||||
|
@ -281,6 +293,7 @@ impl SimpleComponent for App {
|
||||||
let inbox_page = model.inbox_page.widget();
|
let inbox_page = model.inbox_page.widget();
|
||||||
let communities_page = model.communities_page.widget();
|
let communities_page = model.communities_page.widget();
|
||||||
let login_page = model.login_page.widget();
|
let login_page = model.login_page.widget();
|
||||||
|
let accounts_page = model.accounts_page.widget();
|
||||||
|
|
||||||
let widgets = view_output!();
|
let widgets = view_output!();
|
||||||
|
|
||||||
|
@ -290,6 +303,10 @@ impl SimpleComponent for App {
|
||||||
RelmAction::new_stateless(move |_| {
|
RelmAction::new_stateless(move |_| {
|
||||||
instance_sender.input(AppMsg::ChooseInstance);
|
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_sender = sender.clone();
|
||||||
let profile_action: RelmAction<ProfileAction> = RelmAction::new_stateless(move |_| {
|
let profile_action: RelmAction<ProfileAction> = RelmAction::new_stateless(move |_| {
|
||||||
let person = settings::get_current_account();
|
let person = settings::get_current_account();
|
||||||
|
@ -299,10 +316,7 @@ impl SimpleComponent for App {
|
||||||
});
|
});
|
||||||
let login_sender = sender.clone();
|
let login_sender = sender.clone();
|
||||||
let login_action: RelmAction<LoginAction> = RelmAction::new_stateless(move |_| {
|
let login_action: RelmAction<LoginAction> = RelmAction::new_stateless(move |_| {
|
||||||
login_sender.input(AppMsg::ShowLogin);
|
login_sender.input(AppMsg::UpdateState(AppState::Login));
|
||||||
});
|
|
||||||
let logout_action: RelmAction<LogoutAction> = RelmAction::new_stateless(move |_| {
|
|
||||||
sender.input(AppMsg::Logout);
|
|
||||||
});
|
});
|
||||||
let about_action = {
|
let about_action = {
|
||||||
let sender = model.about_dialog.sender().clone();
|
let sender = model.about_dialog.sender().clone();
|
||||||
|
@ -313,9 +327,9 @@ impl SimpleComponent for App {
|
||||||
|
|
||||||
let mut group = RelmActionGroup::<WindowActionGroup>::new();
|
let mut group = RelmActionGroup::<WindowActionGroup>::new();
|
||||||
group.add_action(instance_action);
|
group.add_action(instance_action);
|
||||||
|
group.add_action(accounts_action);
|
||||||
group.add_action(profile_action);
|
group.add_action(profile_action);
|
||||||
group.add_action(login_action);
|
group.add_action(login_action);
|
||||||
group.add_action(logout_action);
|
|
||||||
group.add_action(about_action);
|
group.add_action(about_action);
|
||||||
group.register_for_widget(&widgets.main_window);
|
group.register_for_widget(&widgets.main_window);
|
||||||
|
|
||||||
|
@ -390,12 +404,11 @@ impl SimpleComponent for App {
|
||||||
.emit(post_page::PostPageInput::UpdatePost(post));
|
.emit(post_page::PostPageInput::UpdatePost(post));
|
||||||
self.state = AppState::Post;
|
self.state = AppState::Post;
|
||||||
}
|
}
|
||||||
AppMsg::ShowLogin => {
|
|
||||||
self.state = AppState::Login;
|
|
||||||
}
|
|
||||||
AppMsg::Logout => {
|
AppMsg::Logout => {
|
||||||
let mut account = settings::get_current_account();
|
let mut account = settings::get_current_account();
|
||||||
account.jwt = None;
|
account.jwt = None;
|
||||||
|
account.name = "".to_string();
|
||||||
|
account.id = 0;
|
||||||
settings::update_current_account(account);
|
settings::update_current_account(account);
|
||||||
self.logged_in = false;
|
self.logged_in = false;
|
||||||
}
|
}
|
||||||
|
@ -434,9 +447,9 @@ impl SimpleComponent for App {
|
||||||
|
|
||||||
relm4::new_action_group!(WindowActionGroup, "win");
|
relm4::new_action_group!(WindowActionGroup, "win");
|
||||||
relm4::new_stateless_action!(ChangeInstanceAction, WindowActionGroup, "instance");
|
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!(LoginAction, WindowActionGroup, "login");
|
||||||
relm4::new_stateless_action!(LogoutAction, WindowActionGroup, "logout");
|
relm4::new_stateless_action!(ProfileAction, WindowActionGroup, "profile");
|
||||||
relm4::new_stateless_action!(AboutAction, WindowActionGroup, "about");
|
relm4::new_stateless_action!(AboutAction, WindowActionGroup, "about");
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -45,14 +45,40 @@ pub fn get_prefs() -> Preferences {
|
||||||
pub fn get_current_account() -> Account {
|
pub fn get_current_account() -> Account {
|
||||||
let mut prefs = get_prefs();
|
let mut prefs = get_prefs();
|
||||||
if prefs.accounts.len() == 0 {
|
if prefs.accounts.len() == 0 {
|
||||||
prefs.accounts.push(Account::default());
|
prefs = create_account(true);
|
||||||
save_prefs(&prefs);
|
|
||||||
}
|
}
|
||||||
prefs.accounts[prefs.current_account_index as usize].clone()
|
prefs.accounts[prefs.current_account_index as usize].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_current_account(account: Account) {
|
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();
|
let mut settings = get_prefs();
|
||||||
settings.accounts[settings.current_account_index as usize] = account;
|
settings.accounts[index] = account;
|
||||||
save_prefs(&settings);
|
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);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue