Skip to content

Commit 047831f

Browse files
add add to list option to the actions menu for a user, add Accelerator alt+A for actions button and bind it to aplication key in the view followers/following dialogs
1 parent 7ad588e commit 047831f

7 files changed

Lines changed: 78 additions & 11 deletions

File tree

src/commands.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ pub enum UiCommand {
133133
ManageListsDialogClosed,
134134
ManageListMembersDialogClosed,
135135
OpenList,
136+
AddUserToList(String),
136137
ContinueThread(Box<Status>),
137138
Find(String),
138139
FindNext,
@@ -1095,6 +1096,7 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
10951096
account,
10961097
state.current_user_id.as_deref(),
10971098
net_tx,
1099+
ui_tx.clone(),
10981100
move || {
10991101
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
11001102
},
@@ -1188,6 +1190,7 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
11881190
account,
11891191
state.current_user_id.as_deref(),
11901192
net_tx,
1193+
ui_tx.clone(),
11911194
move || {
11921195
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
11931196
},
@@ -1407,6 +1410,7 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
14071410
account,
14081411
state.current_user_id.as_deref(),
14091412
net_tx,
1413+
ui_tx.clone(),
14101414
move || {
14111415
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
14121416
},
@@ -1727,6 +1731,7 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
17271731
account.clone(),
17281732
state.current_user_id.as_deref(),
17291733
net_tx,
1734+
ui_tx.clone(),
17301735
move || {
17311736
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
17321737
},
@@ -2076,6 +2081,14 @@ pub fn handle_ui_command(cmd: UiCommand, ctx: &mut UiCommandContext<'_>) {
20762081
live_region.announce("Network not available");
20772082
}
20782083
}
2084+
UiCommand::AddUserToList(account_id) => {
2085+
state.pending_add_to_list_user = Some(account_id);
2086+
if let Some(handle) = &state.network_handle {
2087+
handle.send(NetworkCommand::FetchLists);
2088+
} else {
2089+
live_region.announce("Network not available");
2090+
}
2091+
}
20792092
UiCommand::Find(query) => {
20802093
if let Some(active) = state.timeline_manager.active_mut() {
20812094
active.find_query = Some(query);

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pub(crate) struct AppState {
112112
pub(crate) pending_thread_continuation: bool,
113113
pub(crate) pending_restore_post_id: Option<(crate::timeline::TimelineType, String)>,
114114
pub(crate) pending_post: Option<PendingPost>,
115+
pub(crate) pending_add_to_list_user: Option<String>,
115116
}
116117

117118
impl AppState {
@@ -145,6 +146,7 @@ impl AppState {
145146
pending_thread_continuation: false,
146147
pending_restore_post_id: None,
147148
pending_post: None,
149+
pending_add_to_list_user: None,
148150
}
149151
}
150152

src/responses.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
507507
account.clone(),
508508
state.current_user_id.as_deref(),
509509
net_tx,
510+
ui_tx.clone(),
510511
move || {
511512
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
512513
},
@@ -798,6 +799,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
798799
account.clone(),
799800
state.current_user_id.as_deref(),
800801
net_tx,
802+
ui_tx.clone(),
801803
move || {
802804
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
803805
},
@@ -844,6 +846,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
844846
account.clone(),
845847
state.current_user_id.as_deref(),
846848
net_tx,
849+
ui_tx.clone(),
847850
move || {
848851
let _ = ui_tx_timeline.send(UiCommand::OpenTimeline(timeline_type.clone()));
849852
},
@@ -881,6 +884,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
881884
};
882885
let ui_tx_timeline = ui_tx.clone();
883886
let ui_tx_close = ui_tx.clone();
887+
let ui_tx_dlg = ui_tx.clone();
884888
let account_id_opt = next_max_id.as_ref().map(|_| account_id.clone());
885889
let profile_dlg_handle = state.profile_dialog.as_ref().map(|pd| pd.dialog_handle());
886890
let followers_parent: &dyn wxdragon::window::WxWidget =
@@ -893,6 +897,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
893897
total_count,
894898
account_id_opt,
895899
net_tx_dlg,
900+
ui_tx_dlg,
896901
move |account| {
897902
let timeline_type = TimelineType::User {
898903
id: account.id.clone(),
@@ -961,6 +966,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
961966
};
962967
let ui_tx_timeline = ui_tx.clone();
963968
let ui_tx_close = ui_tx.clone();
969+
let ui_tx_dlg = ui_tx.clone();
964970
let account_id_opt = next_max_id.as_ref().map(|_| account_id.clone());
965971
let profile_dlg_handle = state.profile_dialog.as_ref().map(|pd| pd.dialog_handle());
966972
let following_parent: &dyn wxdragon::window::WxWidget =
@@ -973,6 +979,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
973979
total_count,
974980
account_id_opt,
975981
net_tx_dlg,
982+
ui_tx_dlg,
976983
move |account| {
977984
let timeline_type = TimelineType::User {
978985
id: account.id.clone(),
@@ -1253,11 +1260,20 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
12531260
live_region.announce(&format!("Search for '{query}' failed: {}", summarize_api_error(err)));
12541261
}
12551262
NetworkResponse::ListsFetched { result: Ok(lists) } => {
1256-
if let Some(dlg) = &state.manage_lists_dialog {
1263+
if let Some(account_id) = state.pending_add_to_list_user.take() {
1264+
if lists.is_empty() {
1265+
live_region.announce("No lists found to add user to");
1266+
} else if let Some(list) = dialogs::show_list_selection_dialog(frame, &lists, "Add to List", "Add")
1267+
{
1268+
if let Some(handle) = &state.network_handle {
1269+
handle.send(NetworkCommand::AddListAccount { list_id: list.id, account_id });
1270+
}
1271+
}
1272+
} else if let Some(dlg) = &state.manage_lists_dialog {
12571273
dlg.update_lists(lists);
12581274
} else if lists.is_empty() {
12591275
live_region.announce("No lists found");
1260-
} else if let Some(list) = dialogs::show_list_selection_dialog(frame, &lists) {
1276+
} else if let Some(list) = dialogs::show_list_selection_dialog(frame, &lists, "Open List", "Open") {
12611277
let timeline_type = TimelineType::List { id: list.id, title: list.title };
12621278
dispatch_ui_command!(UiCommand::OpenTimeline(timeline_type));
12631279
}
@@ -1277,6 +1293,7 @@ pub fn process_network_responses(ctx: &mut NetworkResponseContext<'_>) {
12771293
| NetworkResponse::ListAccountsFetched { result: Err(err), .. }
12781294
| NetworkResponse::ListAccountAdded { result: Err(err), .. }
12791295
| NetworkResponse::ListAccountRemoved { result: Err(err), .. } => {
1296+
state.pending_add_to_list_user = None;
12801297
let parent: &dyn WxWidget = if let Some(dlg) = &state.manage_list_members_dialog {
12811298
dlg.get_dialog()
12821299
} else if let Some(dlg) = &state.manage_lists_dialog {

src/ui/dialogs/follow_list.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::mpsc::Sender};
22

3-
use wxdragon::prelude::*;
3+
use wxdragon::{event::MenuEvents, prelude::*};
44

55
use super::user_actions;
66
use crate::{
@@ -29,6 +29,7 @@ impl FollowListDialog {
2929
total_count: u64,
3030
account_id: Option<String>,
3131
net_tx: Sender<NetworkCommand>,
32+
ui_tx: crate::ui_wake::UiCommandSender,
3233
on_view_timeline: F,
3334
on_close: C,
3435
) -> Self
@@ -57,7 +58,7 @@ impl FollowListDialog {
5758
}
5859

5960
let button_sizer = BoxSizer::builder(Orientation::Horizontal).build();
60-
let actions_button = Button::builder(&panel).with_label("Actions...").build();
61+
let actions_button = Button::builder(&panel).with_label("&Actions...").build();
6162
let timeline_button = Button::builder(&panel).with_id(ID_VIEW_TIMELINE).with_label("View &Timeline").build();
6263
let close_button = Button::builder(&panel).with_id(ID_CANCEL).with_label("Close").build();
6364
button_sizer.add(&actions_button, 0, SizerFlag::Right, 8);
@@ -100,7 +101,9 @@ impl FollowListDialog {
100101

101102
let relationships_click = relationships_rc.clone();
102103
let current_account_click = current_account_rc.clone();
103-
actions_button.on_click(move |_| {
104+
let panel_clone = panel.clone();
105+
106+
let show_menu = Rc::new(move || {
104107
let current = current_account_click.borrow();
105108
let Some(account) = current.as_ref() else { return };
106109
let rel = relationships_click.borrow().get(&account.id).cloned();
@@ -148,7 +151,19 @@ impl FollowListDialog {
148151
menu.append_separator();
149152
menu.append(user_actions::ID_ACTION_VIEW_FOLLOWERS, "View Followers", "", ItemKind::Normal);
150153
menu.append(user_actions::ID_ACTION_VIEW_FOLLOWING, "View Following", "", ItemKind::Normal);
151-
panel.popup_menu(&mut menu, None);
154+
menu.append_separator();
155+
menu.append(user_actions::ID_ACTION_ADD_TO_LIST, "Add to List...", "", ItemKind::Normal);
156+
panel_clone.popup_menu(&mut menu, None);
157+
});
158+
159+
let show_menu_btn = show_menu.clone();
160+
actions_button.on_click(move |_| {
161+
show_menu_btn();
162+
});
163+
164+
let show_menu_ctx = show_menu.clone();
165+
panel.on_context_menu(move |_| {
166+
show_menu_ctx();
152167
});
153168

154169
let relationships_handler = relationships_rc.clone();
@@ -176,6 +191,10 @@ impl FollowListDialog {
176191
let _ = net_tx.send(NetworkCommand::FetchFollowing { account_id, acct, total_count });
177192
return;
178193
}
194+
if id == user_actions::ID_ACTION_ADD_TO_LIST {
195+
let _ = ui_tx.send(crate::commands::UiCommand::AddUserToList(account_id));
196+
return;
197+
}
179198
let rel = relationships_handler.borrow().get(&account_id).cloned();
180199
let cmd = match id {
181200
user_actions::ID_ACTION_FOLLOW => NetworkCommand::FollowAccount {

src/ui/dialogs/list_selection.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use wxdragon::prelude::*;
22

33
use crate::mastodon::List;
44

5-
pub fn show_list_selection_dialog(frame: &Frame, lists: &[List]) -> Option<List> {
6-
let dialog = Dialog::builder(frame, "Open List").with_size(300, 400).build();
5+
pub fn show_list_selection_dialog(frame: &Frame, lists: &[List], title: &str, button_label: &str) -> Option<List> {
6+
let dialog = Dialog::builder(frame, title).with_size(300, 400).build();
77
let panel = Panel::builder(&dialog).build();
88
let main_sizer = BoxSizer::builder(Orientation::Vertical).build();
99
let list_label = StaticText::builder(&panel).with_label("Select a list").build();
@@ -15,7 +15,7 @@ pub fn show_list_selection_dialog(frame: &Frame, lists: &[List]) -> Option<List>
1515
list_box.set_selection(0, true);
1616
}
1717
let button_sizer = BoxSizer::builder(Orientation::Horizontal).build();
18-
let ok_button = Button::builder(&panel).with_id(ID_OK).with_label("Open").build();
18+
let ok_button = Button::builder(&panel).with_id(ID_OK).with_label(button_label).build();
1919
ok_button.set_default();
2020
let cancel_button = Button::builder(&panel).with_id(ID_CANCEL).with_label("Cancel").build();
2121
button_sizer.add_stretch_spacer(1);

src/ui/dialogs/profile.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ impl ProfileDialog {
2323
account: MastodonAccount,
2424
current_user_id: Option<&str>,
2525
net_tx: std::sync::mpsc::Sender<NetworkCommand>,
26+
ui_tx: crate::ui_wake::UiCommandSender,
2627
on_view_timeline: F,
2728
on_close: C,
2829
) -> Self
@@ -40,7 +41,7 @@ impl ProfileDialog {
4041
.build();
4142
profile_text.set_value(&account.profile_display());
4243
let button_sizer = BoxSizer::builder(Orientation::Horizontal).build();
43-
let actions_button = Button::builder(&panel).with_label("Actions...").build();
44+
let actions_button = Button::builder(&panel).with_label("&Actions...").build();
4445
let timeline_button = Button::builder(&panel).with_id(ID_OK).with_label("View &Timeline").build();
4546
let close_button = Button::builder(&panel).with_id(ID_CANCEL).with_label("&Close").build();
4647
close_button.set_default();
@@ -64,7 +65,14 @@ impl ProfileDialog {
6465
let relationship: Rc<RefCell<Option<crate::mastodon::Relationship>>> = Rc::new(RefCell::new(None));
6566
let account_rc = Rc::new(RefCell::new(account));
6667

67-
user_actions::setup_actions_button(panel, actions_button, account_rc.clone(), relationship.clone(), net_tx);
68+
user_actions::setup_actions_button(
69+
panel,
70+
actions_button,
71+
account_rc.clone(),
72+
relationship.clone(),
73+
net_tx,
74+
ui_tx,
75+
);
6876
let dlg_timeline = dialog;
6977
let on_view_timeline = on_view_timeline;
7078
timeline_button.on_click(move |_| {

src/ui/dialogs/user_actions.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub(crate) const ID_ACTION_VIEW_FOLLOWERS: i32 = 6010;
2020
pub(crate) const ID_ACTION_VIEW_FOLLOWING: i32 = 6011;
2121
pub(crate) const ID_ACTION_ACCEPT_FOLLOW_REQUEST: i32 = 6012;
2222
pub(crate) const ID_ACTION_REJECT_FOLLOW_REQUEST: i32 = 6013;
23+
pub(crate) const ID_ACTION_ADD_TO_LIST: i32 = 6014;
2324

2425
pub(crate) fn append_relationship_text(text: &mut String, relationship: &Relationship, is_own_account: bool) {
2526
text.push_str("\r\n\r\nRelationship:\r\n");
@@ -62,6 +63,7 @@ pub(crate) fn setup_actions_button(
6263
account: Rc<RefCell<Account>>,
6364
relationship: Rc<RefCell<Option<Relationship>>>,
6465
net_tx: Sender<NetworkCommand>,
66+
ui_tx: crate::ui_wake::UiCommandSender,
6567
) {
6668
let relationship_click = relationship.clone();
6769
button.on_click(move |_| {
@@ -102,6 +104,8 @@ pub(crate) fn setup_actions_button(
102104
menu.append_separator();
103105
menu.append(ID_ACTION_VIEW_FOLLOWERS, "View Followers", "", ItemKind::Normal);
104106
menu.append(ID_ACTION_VIEW_FOLLOWING, "View Following", "", ItemKind::Normal);
107+
menu.append_separator();
108+
menu.append(ID_ACTION_ADD_TO_LIST, "Add to List...", "", ItemKind::Normal);
105109
panel.popup_menu(&mut menu, None);
106110
});
107111

@@ -126,6 +130,10 @@ pub(crate) fn setup_actions_button(
126130
let _ = net_tx.send(NetworkCommand::FetchFollowing { account_id, acct, total_count });
127131
return;
128132
}
133+
if id == ID_ACTION_ADD_TO_LIST {
134+
let _ = ui_tx.send(crate::commands::UiCommand::AddUserToList(account_id));
135+
return;
136+
}
129137
let cmd = match id {
130138
ID_ACTION_FOLLOW => NetworkCommand::FollowAccount {
131139
account_id,

0 commit comments

Comments
 (0)