yew_oauth2/components/redirect/
mod.rs1pub mod location;
4#[cfg(feature = "yew-nested-router")]
5pub mod router;
6
7use super::missing_context;
8use crate::agent::{Client, OAuth2Operations};
9use crate::components::context::Agent;
10use crate::context::{OAuth2Context, Reason};
11use yew::{context::ContextHandle, prelude::*};
12
13pub trait Redirector: 'static {
14 type Properties: RedirectorProperties;
15
16 fn new<COMP: Component>(ctx: &Context<COMP>) -> Self;
17
18 fn logout(&self, props: &Self::Properties);
19}
20
21pub trait RedirectorProperties: yew::Properties {
22 fn children(&self) -> &Html;
23}
24
25#[derive(Debug, Clone)]
26pub enum Msg<C: Client> {
27 Context(OAuth2Context),
28 Agent(Agent<C>),
29}
30
31pub struct Redirect<C, R>
33where
34 C: Client,
35 R: Redirector,
36{
37 auth: Option<OAuth2Context>,
38 agent: Option<Agent<C>>,
39
40 _auth_handler: Option<ContextHandle<OAuth2Context>>,
41 _agent_handler: Option<ContextHandle<Agent<C>>>,
42
43 redirector: R,
44}
45
46impl<C, R> Component for Redirect<C, R>
47where
48 C: Client,
49 R: Redirector,
50{
51 type Message = Msg<C>;
52 type Properties = R::Properties;
53
54 fn create(ctx: &Context<Self>) -> Self {
55 let (auth, auth_handler) = match ctx
56 .link()
57 .context::<OAuth2Context>(ctx.link().callback(Msg::Context))
58 {
59 Some((auth, handler)) => (Some(auth), Some(handler)),
60 None => (None, None),
61 };
62 let (agent, agent_handler) = match ctx
63 .link()
64 .context::<Agent<C>>(ctx.link().callback(Msg::Agent))
65 {
66 Some((agent, handler)) => (Some(agent), Some(handler)),
67 None => (None, None),
68 };
69
70 log::debug!("Initial state: {auth:?}");
71
72 let mut result = Self {
73 auth: None,
74 agent,
75 _auth_handler: auth_handler,
76 _agent_handler: agent_handler,
77 redirector: R::new(ctx),
78 };
79
80 if let Some(auth) = auth {
81 result.apply_state(ctx, auth);
82 }
83
84 result
85 }
86
87 fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
88 log::debug!("update: {msg:?}");
89
90 match msg {
91 Self::Message::Context(auth) => {
92 let changed = self.auth.as_ref() != Some(&auth);
93 self.apply_state(ctx, auth);
94 changed
95 }
96 Self::Message::Agent(agent) => {
97 self.agent = Some(agent);
98 false
100 }
101 }
102 }
103
104 fn view(&self, ctx: &Context<Self>) -> Html {
105 match self.auth {
106 None => missing_context(),
107 Some(OAuth2Context::Authenticated(..)) => ctx.props().children().clone(),
108 _ => html!(),
109 }
110 }
111}
112
113impl<C, R> Redirect<C, R>
114where
115 C: Client,
116 R: Redirector,
117{
118 fn apply_state(&mut self, ctx: &Context<Self>, auth: OAuth2Context) {
119 if self.auth.as_ref() == Some(&auth) {
120 return;
121 }
122
123 log::debug!("Current state: {:?}, new state: {:?}", self.auth, auth);
124
125 match &auth {
126 OAuth2Context::NotInitialized
127 | OAuth2Context::Failed(..)
128 | OAuth2Context::Authenticated { .. } => {
129 }
131 OAuth2Context::NotAuthenticated { reason } => match reason {
132 Reason::NewSession => {
133 if let Some(agent) = &mut self.agent {
135 let _ = agent.start_login();
136 }
137 }
138 Reason::Expired | Reason::Logout => {
139 match self.auth {
140 None | Some(OAuth2Context::NotInitialized) => {
141 if let Some(agent) = &mut self.agent {
142 let _ = agent.start_login();
143 }
144 }
145 _ => {
146 self.logout(ctx.props());
148 }
149 }
150 }
151 },
152 }
153
154 self.auth = Some(auth);
155 }
156
157 fn logout(&self, props: &R::Properties) {
158 self.redirector.logout(props);
159 }
160}