1- use js_sys:: Function ;
21use seed:: { prelude:: * , * } ;
3- use wasm_bindgen:: JsCast ;
4- use web_sys:: { MessageEvent , WebSocket } ;
52
6- mod json ;
3+ mod shared ;
74
85const WS_URL : & str = "ws://127.0.0.1:9000/ws" ;
96
@@ -12,105 +9,74 @@ const WS_URL: &str = "ws://127.0.0.1:9000/ws";
129// ------ ------
1310
1411struct Model {
15- data : Data ,
16- services : Services ,
17- }
18-
19- #[ derive( Default ) ]
20- struct Data {
21- connected : bool ,
22- msg_rx_cnt : usize ,
23- msg_tx_cnt : usize ,
24- input_text : String ,
12+ sent_messages_count : usize ,
2513 messages : Vec < String > ,
26- }
27-
28- struct Services {
29- ws : WebSocket ,
14+ input_text : String ,
15+ web_socket : WebSocket ,
3016}
3117
3218// ------ ------
33- // After Mount
19+ // Init
3420// ------ ------
3521
36- fn after_mount ( _: Url , orders : & mut impl Orders < Msg > ) -> AfterMount < Model > {
37- let ws = WebSocket :: new ( WS_URL ) . unwrap ( ) ;
38-
39- register_ws_handler ( WebSocket :: set_onopen, Msg :: Connected , & ws, orders) ;
40- register_ws_handler ( WebSocket :: set_onclose, Msg :: Closed , & ws, orders) ;
41- register_ws_handler ( WebSocket :: set_onmessage, Msg :: ServerMessage , & ws, orders) ;
42- register_ws_handler ( WebSocket :: set_onerror, Msg :: Error , & ws, orders) ;
43-
44- AfterMount :: new ( Model {
45- data : Data :: default ( ) ,
46- services : Services { ws } ,
47- } )
48- }
49-
50- fn register_ws_handler < T , F > (
51- ws_cb_setter : fn ( & WebSocket , Option < & Function > ) ,
52- msg : F ,
53- ws : & WebSocket ,
54- orders : & mut impl Orders < Msg > ,
55- ) where
56- T : wasm_bindgen:: convert:: FromWasmAbi + ' static ,
57- F : Fn ( T ) -> Msg + ' static ,
58- {
59- let ( app, msg_mapper) = ( orders. clone_app ( ) , orders. msg_mapper ( ) ) ;
60-
61- let closure = Closure :: new ( move |data| {
62- app. update ( msg_mapper ( msg ( data) ) ) ;
63- } ) ;
64-
65- ws_cb_setter ( ws, Some ( closure. as_ref ( ) . unchecked_ref ( ) ) ) ;
66- closure. forget ( ) ;
22+ fn init ( _: Url , orders : & mut impl Orders < Msg > ) -> Model {
23+ let web_socket = WebSocket :: builder ( WS_URL , orders)
24+ . on_open ( || log ! ( "WebSocket connection is open now" ) )
25+ . on_message ( Msg :: MessageReceived )
26+ . on_close ( Msg :: WebSocketClosed )
27+ . on_error ( || log ! ( "Error" ) )
28+ . build_and_open ( )
29+ . unwrap ( ) ;
30+
31+ Model {
32+ sent_messages_count : 0 ,
33+ messages : Vec :: new ( ) ,
34+ input_text : String :: new ( ) ,
35+ web_socket,
36+ }
6737}
6838
6939// ------ ------
7040// Update
7141// ------ ------
7242
7343enum Msg {
74- Connected ( JsValue ) ,
75- ServerMessage ( MessageEvent ) ,
76- Send ( json:: ClientMessage ) ,
77- Sent ,
78- EditChange ( String ) ,
79- Closed ( JsValue ) ,
80- Error ( JsValue ) ,
44+ MessageReceived ( WebSocketMessage ) ,
45+ CloseWebSocket ,
46+ WebSocketClosed ( CloseEvent ) ,
47+ InputTextChanged ( String ) ,
48+ SendMessage ( shared:: ClientMessage ) ,
8149}
8250
83- fn update ( msg : Msg , mut model : & mut Model , orders : & mut impl Orders < Msg > ) {
51+ fn update ( msg : Msg , mut model : & mut Model , _ : & mut impl Orders < Msg > ) {
8452 match msg {
85- Msg :: Connected ( _) => {
86- log ! ( "WebSocket connection is open now" ) ;
87- model. data . connected = true ;
88- }
89- Msg :: ServerMessage ( msg_event) => {
53+ Msg :: MessageReceived ( message) => {
9054 log ! ( "Client received a message" ) ;
91- let txt = msg_event. data ( ) . as_string ( ) . unwrap ( ) ;
92- let json: json:: ServerMessage = serde_json:: from_str ( & txt) . unwrap ( ) ;
93-
94- model. data . msg_rx_cnt += 1 ;
95- model. data . messages . push ( json. text ) ;
96- }
97- Msg :: EditChange ( input_text) => {
98- model. data . input_text = input_text;
55+ model
56+ . messages
57+ . push ( message. json :: < shared:: ServerMessage > ( ) . unwrap ( ) . text ) ;
9958 }
100- Msg :: Send ( msg) => {
101- let s = serde_json:: to_string ( & msg) . unwrap ( ) ;
102- model. services . ws . send_with_str ( & s) . unwrap ( ) ;
103- orders. send_msg ( Msg :: Sent ) ;
59+ Msg :: CloseWebSocket => {
60+ model
61+ . web_socket
62+ . close ( None , Some ( "user clicked Close button" ) )
63+ . unwrap ( ) ;
10464 }
105- Msg :: Sent => {
106- model. data . input_text = "" . into ( ) ;
107- model. data . msg_tx_cnt += 1 ;
65+ Msg :: WebSocketClosed ( close_event) => {
66+ log ! ( "==================" ) ;
67+ log ! ( "WebSocket connection was closed:" ) ;
68+ log ! ( "Clean:" , close_event. was_clean( ) ) ;
69+ log ! ( "Code:" , close_event. code( ) ) ;
70+ log ! ( "Reason:" , close_event. reason( ) ) ;
71+ log ! ( "==================" ) ;
10872 }
109- Msg :: Closed ( _ ) => {
110- log ! ( "WebSocket connection was closed" ) ;
73+ Msg :: InputTextChanged ( input_text ) => {
74+ model . input_text = input_text ;
11175 }
112- Msg :: Error ( _) => {
113- log ! ( "Error" ) ;
76+ Msg :: SendMessage ( msg) => {
77+ model. web_socket . send_json ( & msg) . unwrap ( ) ;
78+ model. input_text . clear ( ) ;
79+ model. sent_messages_count += 1 ;
11480 }
11581 }
11682}
@@ -119,43 +85,35 @@ fn update(msg: Msg, mut model: &mut Model, orders: &mut impl Orders<Msg>) {
11985// View
12086// ------ ------
12187
122- fn view ( model : & Model ) -> impl IntoNodes < Msg > {
123- let data = & model. data ;
124-
88+ fn view ( model : & Model ) -> Vec < Node < Msg > > {
12589 vec ! [
126- h1![ "seed websocket example" ] ,
127- if data. connected {
90+ h1![ "WebSocket example" ] ,
91+ div![ model. messages. iter( ) . map( |message| p![ message] ) ] ,
92+ if model. web_socket. state( ) == web_socket:: State :: Open {
12893 div![
12994 input![
13095 id!( "text" ) ,
13196 attrs! {
13297 At :: Type => "text" ,
133- At :: Value => data . input_text;
98+ At :: Value => model . input_text;
13499 } ,
135- input_ev( Ev :: Input , Msg :: EditChange )
100+ input_ev( Ev :: Input , Msg :: InputTextChanged )
136101 ] ,
137102 button![
138- id!( "send" ) ,
139- attrs! { At :: Type => "button" } ,
140103 ev( Ev :: Click , {
141- let message_text = data . input_text. to_owned( ) ;
142- move |_| Msg :: Send ( json :: ClientMessage { text: message_text } )
104+ let message_text = model . input_text. to_owned( ) ;
105+ move |_| Msg :: SendMessage ( shared :: ClientMessage { text: message_text } )
143106 } ) ,
144107 "Send"
145- ]
108+ ] ,
109+ button![ ev( Ev :: Click , |_| Msg :: CloseWebSocket ) , "Close" ] ,
146110 ]
147111 } else {
148- div![ p![ em![ "Connecting... " ] ] ]
112+ div![ p![ em![ "Connecting or closed " ] ] ]
149113 } ,
150- div![ data. messages. iter( ) . map( |message| p![ message] ) ] ,
151114 footer![
152- if data. connected {
153- p![ "Connected" ]
154- } else {
155- p![ "Disconnected" ]
156- } ,
157- p![ format!( "{} messages received" , data. msg_rx_cnt) ] ,
158- p![ format!( "{} messages sent" , data. msg_tx_cnt) ]
115+ p![ format!( "{} messages" , model. messages. len( ) ) ] ,
116+ p![ format!( "{} messages sent" , model. sent_messages_count) ]
159117 ] ,
160118 ]
161119}
@@ -166,7 +124,5 @@ fn view(model: &Model) -> impl IntoNodes<Msg> {
166124
167125#[ wasm_bindgen( start) ]
168126pub fn start ( ) {
169- App :: builder ( update, view)
170- . after_mount ( after_mount)
171- . build_and_start ( ) ;
127+ App :: start ( "app" , init, update, view) ;
172128}
0 commit comments