@@ -23,6 +23,7 @@ use matrix_sdk::authentication::oauth::{
2323};
2424use matrix_sdk_base::crypto::types::qr_login::{self, QrCodeIntent};
2525use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm, stream::StreamExt};
26+ use tokio::sync::Notify;
2627
2728use crate::{
2829 authentication::OAuthConfiguration, runtime::get_runtime_handle, task_handle::TaskHandle,
@@ -33,11 +34,14 @@ use crate::{
3334pub struct LoginWithQrCodeHandler {
3435 oauth: OAuth,
3536 oauth_configuration: OAuthConfiguration,
37+ /// Request a the handler to abort cooperatively. This will make the handler
38+ /// tear down its running task and then emit the `Cancelled` update.
39+ cancel: Notify,
3640}
3741
3842impl LoginWithQrCodeHandler {
3943 pub(crate) fn new(oauth: OAuth, oauth_configuration: OAuthConfiguration) -> Self {
40- Self { oauth, oauth_configuration }
44+ Self { oauth, oauth_configuration, cancel: Notify::new() }
4145 }
4246}
4347
@@ -82,15 +86,25 @@ impl LoginWithQrCodeHandler {
8286
8387 // We create this task, which will get cancelled once it's dropped, just in case
8488 // the progress stream doesn't end.
85- let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
89+ let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
8690 while let Some(state) = progress.next().await {
8791 progress_listener.on_update(state.into());
8892 }
8993 }));
9094
91- login.await?;
92-
93- Ok(())
95+ tokio::select! {
96+ biased;
97+ result = login => {
98+ result?;
99+ Ok(())
100+ }
101+ _ = self.cancel.notified() => {
102+ // Stop forwarding progress to the foreign callback before tearing
103+ // down the handler.
104+ drop(progress_task);
105+ Err(HumanQrLoginError::Cancelled)
106+ }
107+ }
94108 }
95109
96110 /// This method allows you to log in by generating a QR code.
@@ -126,27 +140,44 @@ impl LoginWithQrCodeHandler {
126140
127141 // We create this task, which will get cancelled once it's dropped, just in case
128142 // the progress stream doesn't end.
129- let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
143+ let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
130144 while let Some(state) = progress.next().await {
131145 progress_listener.on_update(state.into());
132146 }
133147 }));
134148
135- login.await?;
149+ tokio::select! {
150+ biased;
151+ result = login => {
152+ result?;
153+ Ok(())
154+ }
155+ _ = self.cancel.notified() => {
156+ // Stop forwarding progress to the foreign callback before tearing
157+ // down the handler.
158+ drop(progress_task);
159+ Err(HumanQrLoginError::Cancelled)
160+ }
161+ }
162+ }
136163
137- Ok(())
164+ /// Request a the handler to abort cooperatively. This will make the handler
165+ /// tear down its running task and then return the `Cancelled` error.
166+ pub fn abort(&self) {
167+ self.cancel.notify_waiters();
138168 }
139169}
140170
141171/// Handler for granting login in with a QR code.
142172#[derive(uniffi::Object)]
143173pub struct GrantLoginWithQrCodeHandler {
144174 oauth: OAuth,
175+ cancel: Notify,
145176}
146177
147178impl GrantLoginWithQrCodeHandler {
148179 pub(crate) fn new(oauth: OAuth) -> Self {
149- Self { oauth }
180+ Self { oauth, cancel: Notify::new() }
150181 }
151182}
152183
@@ -182,15 +213,25 @@ impl GrantLoginWithQrCodeHandler {
182213
183214 // We create this task, which will get cancelled once it's dropped, just in case
184215 // the progress stream doesn't end.
185- let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
216+ let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
186217 while let Some(state) = progress.next().await {
187218 progress_listener.on_update(state.into());
188219 }
189220 }));
190221
191- grant.await?;
192-
193- Ok(())
222+ tokio::select! {
223+ biased;
224+ result = grant => {
225+ result?;
226+ Ok(())
227+ }
228+ _ = self.cancel.notified() => {
229+ // Stop forwarding progress to the foreign callback before tearing
230+ // down the handler.
231+ drop(progress_task);
232+ Err(HumanQrGrantLoginError::Cancelled)
233+ }
234+ }
194235 }
195236
196237 /// This method allows you to grant login by generating a QR code.
@@ -220,15 +261,31 @@ impl GrantLoginWithQrCodeHandler {
220261
221262 // We create this task, which will get cancelled once it's dropped, just in case
222263 // the progress stream doesn't end.
223- let _progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
264+ let progress_task = TaskHandle::new(get_runtime_handle().spawn(async move {
224265 while let Some(state) = progress.next().await {
225266 progress_listener.on_update(state.into());
226267 }
227268 }));
228269
229- grant.await?;
270+ tokio::select! {
271+ biased;
272+ result = grant => {
273+ result?;
274+ Ok(())
275+ }
276+ _ = self.cancel.notified() => {
277+ // Stop forwarding progress to the foreign callback before tearing
278+ // down the handler.
279+ drop(progress_task);
280+ Err(HumanQrGrantLoginError::Cancelled)
281+ }
282+ }
283+ }
230284
231- Ok(())
285+ /// Request a the handler to abort cooperatively. This will make the handler
286+ /// tear down its running task and then return the `Cancelled` error.
287+ pub fn abort(&self) {
288+ self.cancel.notify_waiters();
232289 }
233290}
234291
0 commit comments