@@ -9,8 +9,8 @@ use std::{
99use crate :: event:: { AppEvent , Event , EventHandler } ;
1010use crate :: {
1111 chat:: {
12- ChatDeletionSettings , ChatFeatures , ChatRef , ChatSummary , Message , Profile , SimplexEvent ,
13- User ,
12+ ChatDeletionSettings , ChatFeatures , ChatRef , ChatSummary , Message , Profile , ServerEntry ,
13+ ServerProtocol , SimplexEvent , User ,
1414 } ,
1515 preferences:: Preferences ,
1616 simplex:: SimplexApi ,
@@ -41,6 +41,7 @@ pub enum InputMode {
4141 None ,
4242 CreateProfile ,
4343 ConfirmDeleteProfile ,
44+ AddServer ,
4445}
4546
4647#[ derive( Clone , Debug ) ]
@@ -94,7 +95,9 @@ pub struct App {
9495 pub preferences : Preferences ,
9596 pub auto_delete_seconds : i64 ,
9697 pub auto_delete_pending : Option < i64 > ,
97- pub smp_servers : Vec < String > ,
98+ pub servers : Vec < ServerEntry > ,
99+ pub server_protocol : ServerProtocol ,
100+ pub selected_server : usize ,
98101 pub chat_features : ChatFeatures ,
99102 pub invitation_link : Option < String > ,
100103 pub invitation_loading : bool ,
@@ -150,7 +153,9 @@ impl Default for App {
150153 preferences : Preferences :: default ( ) ,
151154 auto_delete_seconds : 0 ,
152155 auto_delete_pending : None ,
153- smp_servers : Vec :: new ( ) ,
156+ servers : Vec :: new ( ) ,
157+ server_protocol : ServerProtocol :: Smp ,
158+ selected_server : 0 ,
154159 chat_features : ChatFeatures :: default ( ) ,
155160 invitation_link : None ,
156161 invitation_loading : false ,
@@ -189,7 +194,7 @@ impl App {
189194 "Privacy & Security" ,
190195 "Appearance" ,
191196 "Chat Features" ,
192- "SMP Servers" ,
197+ "Servers" ,
193198 "About" ,
194199 ] ;
195200
@@ -287,6 +292,23 @@ impl App {
287292 }
288293 return Ok ( ( ) ) ;
289294 }
295+ if self . input_mode == InputMode :: AddServer {
296+ match key. code {
297+ KeyCode :: Esc => {
298+ self . input_mode = InputMode :: None ;
299+ self . input . clear ( ) ;
300+ }
301+ KeyCode :: Enter if !self . input . trim ( ) . is_empty ( ) => self . add_server ( ) ,
302+ KeyCode :: Backspace => {
303+ self . input . pop ( ) ;
304+ }
305+ KeyCode :: Char ( character) if !key. modifiers . contains ( KeyModifiers :: CONTROL ) => {
306+ self . input . push ( character)
307+ }
308+ _ => { }
309+ }
310+ return Ok ( ( ) ) ;
311+ }
290312 if self . chat_deletion_dialog . is_some ( ) {
291313 match key. code {
292314 KeyCode :: Esc => self . chat_deletion_dialog = None ,
@@ -352,7 +374,39 @@ impl App {
352374 self . input_mode = InputMode :: ConfirmDeleteProfile ;
353375 }
354376 KeyCode :: Enter | KeyCode :: Char ( ' ' ) if self . section == Section :: Settings => {
355- self . activate_setting ( )
377+ if self . selected_setting == 5 {
378+ self . toggle_selected_server ( ) ;
379+ } else {
380+ self . activate_setting ( )
381+ }
382+ }
383+ KeyCode :: Char ( 'a' )
384+ if self . section == Section :: Settings && self . selected_setting == 5 =>
385+ {
386+ self . input_mode = InputMode :: AddServer ;
387+ self . input . clear ( ) ;
388+ }
389+ KeyCode :: Char ( 'p' )
390+ if self . section == Section :: Settings && self . selected_setting == 5 =>
391+ {
392+ self . server_protocol = match self . server_protocol {
393+ ServerProtocol :: Smp => ServerProtocol :: Xftp ,
394+ ServerProtocol :: Xftp => ServerProtocol :: Smp ,
395+ } ;
396+ self . selected_server = 0 ;
397+ }
398+ KeyCode :: Char ( 'k' )
399+ if self . section == Section :: Settings && self . selected_setting == 5 =>
400+ {
401+ self . selected_server = self . selected_server . saturating_sub ( 1 ) ;
402+ }
403+ KeyCode :: Char ( 'j' )
404+ if self . section == Section :: Settings && self . selected_setting == 5 =>
405+ {
406+ self . selected_server = self
407+ . selected_server
408+ . saturating_add ( 1 )
409+ . min ( self . visible_servers ( ) . len ( ) . saturating_sub ( 1 ) ) ;
356410 }
357411 KeyCode :: Char ( 'c' )
358412 if self . section == Section :: Settings && self . selected_setting == 3 =>
@@ -764,7 +818,15 @@ impl App {
764818 replace_message ( messages, message) ;
765819 }
766820 }
767- SimplexEvent :: ServersLoaded ( servers) => self . smp_servers = servers,
821+ SimplexEvent :: ServersLoaded ( servers) => {
822+ self . servers = servers;
823+ self . selected_server = self
824+ . selected_server
825+ . min ( self . visible_servers ( ) . len ( ) . saturating_sub ( 1 ) ) ;
826+ }
827+ SimplexEvent :: ServersUpdateFailed ( error) => {
828+ self . notice = Some ( format ! ( "Could not update servers: {error}" ) ) ;
829+ }
768830 SimplexEvent :: ChatFeaturesLoaded ( features) => self . chat_features = features,
769831 SimplexEvent :: InvitationCreated ( link) => {
770832 self . invitation_link = Some ( link) ;
@@ -1042,6 +1104,52 @@ impl App {
10421104 } ) ;
10431105 }
10441106
1107+ pub fn visible_servers ( & self ) -> Vec < & ServerEntry > {
1108+ self . servers
1109+ . iter ( )
1110+ . filter ( |server| server. protocol == self . server_protocol )
1111+ . collect ( )
1112+ }
1113+
1114+ fn toggle_selected_server ( & mut self ) {
1115+ let Some ( user_id) = self . active_user ( ) . map ( |user| user. id ) else {
1116+ self . notice = Some ( "Create a profile first" . into ( ) ) ;
1117+ return ;
1118+ } ;
1119+ let Some ( server) = self . visible_servers ( ) . get ( self . selected_server ) . copied ( ) else {
1120+ self . notice = Some ( "Add a server first" . into ( ) ) ;
1121+ return ;
1122+ } ;
1123+ let protocol = server. protocol ;
1124+ let address = server. address . clone ( ) ;
1125+ let enabled = !server. enabled ;
1126+ self . notice = Some ( "Updating server configuration…" . into ( ) ) ;
1127+ let _ = self
1128+ . simplex_commands
1129+ . send ( SimplexCommand :: SetServerEnabled {
1130+ user_id,
1131+ protocol,
1132+ address,
1133+ enabled,
1134+ } ) ;
1135+ }
1136+
1137+ fn add_server ( & mut self ) {
1138+ let Some ( user_id) = self . active_user ( ) . map ( |user| user. id ) else {
1139+ self . notice = Some ( "Create a profile first" . into ( ) ) ;
1140+ return ;
1141+ } ;
1142+ let address = self . input . trim ( ) . to_owned ( ) ;
1143+ self . input_mode = InputMode :: None ;
1144+ self . input . clear ( ) ;
1145+ self . notice = Some ( format ! ( "Adding {} server…" , self . server_protocol. label( ) ) ) ;
1146+ let _ = self . simplex_commands . send ( SimplexCommand :: AddServer {
1147+ user_id,
1148+ protocol : self . server_protocol ,
1149+ address,
1150+ } ) ;
1151+ }
1152+
10451153 fn activate_profile ( & mut self ) {
10461154 let Some ( profile) = self . profiles . get ( self . selected_profile ) else {
10471155 self . input_mode = InputMode :: CreateProfile ;
@@ -1787,4 +1895,62 @@ mod tests {
17871895 } ;
17881896 assert_eq ! ( file_id, 41 ) ;
17891897 }
1898+
1899+ #[ tokio:: test]
1900+ async fn server_settings_toggle_and_add_both_protocols ( ) {
1901+ let ( commands, receiver) = mpsc:: channel ( ) ;
1902+ let mut app = App {
1903+ section : Section :: Settings ,
1904+ selected_setting : 5 ,
1905+ startup : StartupState :: Ready ( User {
1906+ id : 3 ,
1907+ display_name : "alice" . into ( ) ,
1908+ notifications : true ,
1909+ active : true ,
1910+ } ) ,
1911+ servers : vec ! [ ServerEntry {
1912+ protocol: ServerProtocol :: Smp ,
1913+ address: "smp://key@smp.example" . into( ) ,
1914+ enabled: true ,
1915+ preset: true ,
1916+ } ] ,
1917+ simplex_commands : commands,
1918+ ..App :: default ( )
1919+ } ;
1920+
1921+ app. handle_key_events ( KeyEvent :: new ( KeyCode :: Enter , KeyModifiers :: NONE ) )
1922+ . unwrap ( ) ;
1923+ let SimplexCommand :: SetServerEnabled {
1924+ user_id,
1925+ protocol,
1926+ address,
1927+ enabled,
1928+ } = receiver. try_recv ( ) . unwrap ( )
1929+ else {
1930+ panic ! ( "expected server-toggle command" )
1931+ } ;
1932+ assert_eq ! ( user_id, 3 ) ;
1933+ assert_eq ! ( protocol, ServerProtocol :: Smp ) ;
1934+ assert_eq ! ( address, "smp://key@smp.example" ) ;
1935+ assert ! ( !enabled) ;
1936+
1937+ app. handle_key_events ( KeyEvent :: new ( KeyCode :: Char ( 'p' ) , KeyModifiers :: NONE ) )
1938+ . unwrap ( ) ;
1939+ assert_eq ! ( app. server_protocol, ServerProtocol :: Xftp ) ;
1940+ app. handle_key_events ( KeyEvent :: new ( KeyCode :: Char ( 'a' ) , KeyModifiers :: NONE ) )
1941+ . unwrap ( ) ;
1942+ app. input = "xftp://key@files.example" . into ( ) ;
1943+ app. handle_key_events ( KeyEvent :: new ( KeyCode :: Enter , KeyModifiers :: NONE ) )
1944+ . unwrap ( ) ;
1945+ let SimplexCommand :: AddServer {
1946+ user_id,
1947+ protocol,
1948+ address,
1949+ } = receiver. try_recv ( ) . unwrap ( )
1950+ else {
1951+ panic ! ( "expected add-server command" )
1952+ } ;
1953+ assert_eq ! ( ( user_id, protocol) , ( 3 , ServerProtocol :: Xftp ) ) ;
1954+ assert_eq ! ( address, "xftp://key@files.example" ) ;
1955+ }
17901956}
0 commit comments