musli_web/
yew021.rs

1//! Integration with yew `0.21.x`.
2//!
3//! # Examples
4//!
5//! This example uses [`yew021`]:
6//!
7//! [`yew021`]: crate::yew021
8//!
9//! ```
10//! # extern crate yew021 as yew;
11//! # extern crate web_sys03 as web_sys;
12//! use web_sys::HtmlInputElement;
13//! use yew::prelude::*;
14//! use musli_web::web03::prelude::*;
15//!
16//! mod api {
17//!     use musli::{Decode, Encode};
18//!     use musli_web::api;
19//!
20//!     #[derive(Encode, Decode)]
21//!     pub struct HelloRequest<'de> {
22//!         pub message: &'de str,
23//!     }
24//!
25//!     #[derive(Encode, Decode)]
26//!     pub struct HelloResponse<'de> {
27//!         pub message: &'de str,
28//!     }
29//!
30//!     #[derive(Encode, Decode)]
31//!     pub struct TickEvent<'de> {
32//!         pub message: &'de str,
33//!         pub tick: u32,
34//!     }
35//!
36//!     api::define! {
37//!         pub type Hello;
38//!
39//!         impl Endpoint for Hello {
40//!             impl<'de> Request for HelloRequest<'de>;
41//!             type Response<'de> = HelloResponse<'de>;
42//!         }
43//!
44//!         pub type Tick;
45//!
46//!         impl Broadcast for Tick {
47//!             impl<'de> Event for TickEvent<'de>;
48//!         }
49//!     }
50//! }
51//!
52//! enum Msg {
53//!     Error(ws::Error),
54//!     Change(String),
55//!     Send,
56//!     HelloResponse(Result<ws::Packet<api::Hello>, ws::Error>),
57//!     Tick(Result<ws::Packet<api::Tick>, ws::Error>),
58//! }
59//!
60//! struct App {
61//!     service: ws::Service,
62//!     _listen: ws::Listener,
63//!     request: ws::Request,
64//!     text: String,
65//!     tick: u32,
66//!     responses: Vec<String>,
67//! }
68//!
69//! impl Component for App {
70//!     type Message = Msg;
71//!     type Properties = ();
72//!
73//!     fn create(ctx: &Context<Self>) -> Self {
74//!         let service = ws::connect(ws::Connect::location("ws"))
75//!             .on_error(ctx.link().callback(Msg::Error))
76//!             .build();
77//!
78//!         service.connect();
79//!
80//!         let listen = service.handle().on_broadcast(ctx.link().callback(Msg::Tick));
81//!
82//!         Self {
83//!             service,
84//!             _listen: listen,
85//!             request: ws::Request::new(),
86//!             text: String::new(),
87//!             tick: 0,
88//!             responses: Vec::new(),
89//!         }
90//!     }
91//!
92//!     fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
93//!         match msg {
94//!             Msg::Error(error) => {
95//!                 tracing::error!("WebSocket error: {:?}", error);
96//!                 false
97//!             }
98//!             Msg::Change(text) => {
99//!                 self.text = text;
100//!                 true
101//!             }
102//!             Msg::Send => {
103//!                 self.request = self
104//!                     .service
105//!                     .handle()
106//!                     .request()
107//!                     .body(api::HelloRequest {
108//!                         message: self.text.as_str(),
109//!                     })
110//!                     .on_packet(ctx.link().callback(Msg::HelloResponse))
111//!                     .send();
112//!
113//!                 self.text.clear();
114//!                 true
115//!             }
116//!             Msg::HelloResponse(Err(error)) => {
117//!                 tracing::error!("Request error: {:?}", error);
118//!                 false
119//!             }
120//!             Msg::HelloResponse(Ok(packet)) => {
121//!                 tracing::debug!("Got response");
122//!
123//!                 while !packet.is_empty() {
124//!                     let Ok(response) = packet.decode() else {
125//!                         break;
126//!                     };
127//!
128//!                     self.responses.push(response.message.to_owned());
129//!                 }
130//!
131//!                 true
132//!             }
133//!             Msg::Tick(Err(error)) => {
134//!                 tracing::error!("Tick error: {error}");
135//!                 false
136//!             }
137//!             Msg::Tick(Ok(packet)) => {
138//!                 tracing::debug!("Got tick");
139//!
140//!                 if let Ok(tick) = packet.decode_event() {
141//!                     self.tick = tick.tick;
142//!                 }
143//!
144//!                 true
145//!             }
146//!         }
147//!     }
148//!
149//!     fn view(&self, ctx: &Context<Self>) -> Html {
150//!         let oninput = ctx.link().callback(|e: InputEvent| {
151//!             let input = e.target_unchecked_into::<HtmlInputElement>();
152//!             Msg::Change(input.value())
153//!         });
154//!
155//!         let onclick = ctx.link().callback(|_: MouseEvent| {
156//!             Msg::Send
157//!         });
158//!
159//!         html! {
160//!             <div class="container">
161//!                 <input type="text" {oninput} value={self.text.clone()} />
162//!                 <button {onclick}>{"Send Message"}</button>
163//!                 {for self.responses.iter().enumerate().map(|(index, response)| html!(<div>{format!("Response #{index}: {response}")}</div>))}
164//!                 <div>{format!("Global tick: {}", self.tick)}</div>
165//!             </div>
166//!         }
167//!     }
168//! }
169//! ```
170
171use yew021::Callback;
172use yew021::html::ImplicitClone;
173
174use crate::web::{Handle, WebImpl};
175
176impl<H> ImplicitClone for Handle<H>
177where
178    H: WebImpl,
179{
180    #[inline]
181    fn implicit_clone(&self) -> Self {
182        self.clone()
183    }
184}
185
186impl<I> crate::web::Callback<I> for Callback<I>
187where
188    I: 'static,
189{
190    #[inline]
191    fn call(&self, result: I) {
192        self.emit(result);
193    }
194}