mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-04-25 09:39:34 -04:00
widget: Add toWidget & MatrixDriver response handling skeleton
This commit is contained in:
committed by
Jonas Platte
parent
f75b2cd1d0
commit
e038ced2c6
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3070,6 +3070,7 @@ dependencies = [
|
||||
"hyper",
|
||||
"image 0.24.7",
|
||||
"imbl",
|
||||
"indexmap 2.0.2",
|
||||
"language-tags",
|
||||
"mas-oidc-client",
|
||||
"matrix-sdk-base",
|
||||
|
||||
@@ -81,6 +81,7 @@ futures-util = { workspace = true }
|
||||
http = { workspace = true }
|
||||
hyper = { version = "0.14.20", features = ["http1", "http2", "server"], optional = true }
|
||||
imbl = { version = "2.0.0", features = ["serde"] }
|
||||
indexmap = "2.0.2"
|
||||
language-tags = { version = "0.3.2", optional = true }
|
||||
matrix-sdk-base = { version = "0.6.0", path = "../matrix-sdk-base", default_features = false }
|
||||
matrix-sdk-common = { version = "0.6.0", path = "../matrix-sdk-common" }
|
||||
|
||||
@@ -12,36 +12,45 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{borrow::Cow, error::Error, ops::Deref};
|
||||
use std::error::Error;
|
||||
|
||||
use ruma::events::{MessageLikeEventType, StateEventType, TimelineEventType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value as JsonValue;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::widget::{Permissions, StateKeySelector};
|
||||
use super::driver_req::AcquirePermissions;
|
||||
use crate::widget::StateKeySelector;
|
||||
|
||||
/// Action (a command) that client (driver) must perform.
|
||||
#[allow(dead_code)] // TODO: Remove once all actions are implemented.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Action {
|
||||
/// Send a raw message to the widget.
|
||||
SendToWidget(String),
|
||||
/// Acquire permissions from the user given the set of desired permissions.
|
||||
/// Must eventually be answered with `Event::PermissionsAcquired`.
|
||||
AcquirePermissions(Command<Permissions>),
|
||||
/// Get OpenId token for a given request ID.
|
||||
GetOpenId(Command<()>),
|
||||
/// Read message event(s).
|
||||
ReadMessageLikeEvent(Command<ReadMessageLikeEventCommand>),
|
||||
/// Read state event(s).
|
||||
ReadStateEvent(Command<ReadStateEventCommand>),
|
||||
/// Send matrix event that corresponds to the given description.
|
||||
SendMatrixEvent(Command<SendEventCommand>),
|
||||
|
||||
/// Command that is sent from the client widget API state machine to the
|
||||
/// client (driver) that must be performed. Once the command is executed,
|
||||
/// the client will typically generate an `Event` with the result of it.
|
||||
MatrixDriverRequest {
|
||||
/// Certain commands are typically answered with certain event once the
|
||||
/// command is performed. The api state machine will "tag" each command
|
||||
/// with some "cookie" (in this case just an ID), so that once the
|
||||
/// result of the execution of this command is received, it could be
|
||||
/// matched.
|
||||
request_id: Uuid,
|
||||
|
||||
/// Data associated with this command.
|
||||
data: MatrixDriverRequestData,
|
||||
},
|
||||
|
||||
/// Subscribe to the events in the *current* room, i.e. a room which this
|
||||
/// widget is instantiated with. The client is aware of the room.
|
||||
#[allow(dead_code)]
|
||||
Subscribe,
|
||||
|
||||
/// Unsuscribe from the events in the *current* room. Symmetrical to
|
||||
/// `Subscribe`.
|
||||
#[allow(dead_code)]
|
||||
Unsubscribe,
|
||||
}
|
||||
|
||||
@@ -78,38 +87,24 @@ pub(crate) struct SendEventCommand {
|
||||
pub(crate) content: JsonValue,
|
||||
}
|
||||
|
||||
/// Command that is sent from the client widget API state machine to the
|
||||
/// client (driver) that must be performed. Once the command is executed,
|
||||
/// the client will typically generate an `Event` with the result of it.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Command<T> {
|
||||
/// Certain commands are typically answered with certain event once the
|
||||
/// command is performed. The api state machine will "tag" each command
|
||||
/// with some "cookie" (in this case just an ID), so that once the
|
||||
/// result of the execution of this command is received, it could be
|
||||
/// matched.
|
||||
id: String,
|
||||
// Data associated with this command.
|
||||
data: T,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum MatrixDriverRequestData {
|
||||
/// Acquire permissions from the user given the set of desired permissions.
|
||||
/// Must eventually be answered with `Event::PermissionsAcquired`.
|
||||
AcquirePermissions(AcquirePermissions),
|
||||
|
||||
impl<T> Command<T> {
|
||||
/// Consumes the command and produces a command result with given data.
|
||||
pub(crate) fn result<U, E: Error>(self, result: Result<U, E>) -> CommandResult<U> {
|
||||
CommandResult { id: self.id, result: result.map_err(|e| e.to_string().into()) }
|
||||
}
|
||||
/// Get OpenId token for a given request ID.
|
||||
GetOpenId,
|
||||
|
||||
pub(crate) fn ok<U>(self, value: U) -> CommandResult<U> {
|
||||
CommandResult { id: self.id, result: Ok(value) }
|
||||
}
|
||||
}
|
||||
/// Read message event(s).
|
||||
ReadMessageLikeEvent(ReadMessageLikeEventCommand),
|
||||
|
||||
impl<T> Deref for Command<T> {
|
||||
type Target = T;
|
||||
/// Read state event(s).
|
||||
ReadStateEvent(ReadStateEventCommand),
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
/// Send matrix event that corresponds to the given description.
|
||||
SendMatrixEvent(SendEventCommand),
|
||||
}
|
||||
|
||||
/// The result of the execution of a command. Note that this type can only be
|
||||
@@ -118,9 +113,19 @@ impl<T> Deref for Command<T> {
|
||||
/// client (driver) won't be able to send "invalid" commands, because they could
|
||||
/// only be generated from a `Command` instance.
|
||||
#[allow(dead_code)] // TODO: Remove once results are used.
|
||||
pub(crate) struct CommandResult<T> {
|
||||
pub(crate) struct MatrixDriverResponse<T> {
|
||||
/// ID of the command that was executed. See `Command::id` for more details.
|
||||
id: String,
|
||||
request_id: Uuid,
|
||||
/// Result of the execution of the command.
|
||||
result: Result<T, Cow<'static, str>>,
|
||||
result: Result<T, String>,
|
||||
}
|
||||
|
||||
impl<T> MatrixDriverResponse<T> {
|
||||
pub(crate) fn new<E: Error>(request_id: Uuid, result: Result<T, E>) -> Self {
|
||||
Self { request_id, result: result.map_err(|e| e.to_string()) }
|
||||
}
|
||||
|
||||
pub(crate) fn ok(request_id: Uuid, value: T) -> Self {
|
||||
Self { request_id, result: Ok(value) }
|
||||
}
|
||||
}
|
||||
|
||||
134
crates/matrix-sdk/src/widget/machine/driver_req.rs
Normal file
134
crates/matrix-sdk/src/widget/machine/driver_req.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! A high-level API for requests that we send to the matrix driver.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
//use ruma::{
|
||||
// api::client::account::request_openid_token::v3::Response as
|
||||
// RumaOpenIdResponse, events::AnyTimelineEvent, serde::Raw, OwnedEventId,
|
||||
//};
|
||||
use tracing::error;
|
||||
|
||||
use super::{
|
||||
actions::MatrixDriverRequestData,
|
||||
//actions::{ReadMessageLikeEventCommand, SendEventCommand},
|
||||
Event,
|
||||
MatrixDriverRequestMeta,
|
||||
WidgetMachine,
|
||||
};
|
||||
use crate::widget::Permissions;
|
||||
|
||||
/// A handle to a pending `toWidget` request.
|
||||
pub(crate) struct MatrixDriverRequestHandle<'m, T> {
|
||||
request_meta: Option<&'m mut MatrixDriverRequestMeta>,
|
||||
_phantom: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<'m, T> MatrixDriverRequestHandle<'m, T>
|
||||
where
|
||||
T: MatrixDriverResponse,
|
||||
{
|
||||
pub(crate) fn new(request_meta: &'m mut MatrixDriverRequestMeta) -> Self {
|
||||
Self { request_meta: Some(request_meta), _phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn null() -> Self {
|
||||
Self { request_meta: None, _phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn then(
|
||||
self,
|
||||
response_handler: impl FnOnce(T, &mut WidgetMachine) + Send + 'static,
|
||||
) {
|
||||
if let Some(request_meta) = self.request_meta {
|
||||
request_meta.response_fn = Some(Box::new(move |event, machine| {
|
||||
if let Some(response_data) = T::from_event(event) {
|
||||
response_handler(response_data, machine)
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a request that the widget API state machine can send.
|
||||
pub(crate) trait MatrixDriverRequest: Into<MatrixDriverRequestData> {
|
||||
type Response: MatrixDriverResponse;
|
||||
}
|
||||
|
||||
pub(crate) trait MatrixDriverResponse: Sized {
|
||||
fn from_event(_: Event) -> Option<Self>;
|
||||
}
|
||||
|
||||
/// Ask the client (permission provider) to acquire given permissions
|
||||
/// from the user. The client must eventually respond with granted permissions.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AcquirePermissions {
|
||||
pub(crate) desired_permissions: Permissions,
|
||||
}
|
||||
|
||||
pub(crate) struct AcquirePermissionsResponse {
|
||||
pub(crate) granted_permissions: Permissions,
|
||||
}
|
||||
|
||||
impl From<AcquirePermissions> for MatrixDriverRequestData {
|
||||
fn from(value: AcquirePermissions) -> Self {
|
||||
MatrixDriverRequestData::AcquirePermissions(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl MatrixDriverRequest for AcquirePermissions {
|
||||
type Response = AcquirePermissionsResponse;
|
||||
}
|
||||
|
||||
impl MatrixDriverResponse for AcquirePermissionsResponse {
|
||||
fn from_event(ev: Event) -> Option<Self> {
|
||||
match ev {
|
||||
Event::PermissionsAcquired(_) => todo!(),
|
||||
Event::MessageFromWidget(_) | Event::MatrixEventReceived(_) => {
|
||||
error!("this should be unreachable, no ID to match");
|
||||
None
|
||||
}
|
||||
Event::OpenIdReceived(_) | Event::MatrixEventSent(_) | Event::MatrixEventRead(_) => {
|
||||
error!("bug in MatrixDriver, received wrong event response");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// Request open ID from the Matrix client.
|
||||
pub(crate) struct RequestOpenId;
|
||||
impl MatrixDriverRequest for RequestOpenId {
|
||||
type Response = RumaOpenIdResponse;
|
||||
}
|
||||
|
||||
/// Ask the client to read matrix event(s) that corresponds to the given
|
||||
/// description and return a list of events as a response.
|
||||
pub(crate) struct ReadMatrixEvent(pub(crate) ReadMessageLikeEventCommand);
|
||||
impl MatrixDriverRequest for ReadMatrixEvent {
|
||||
type Response = Vec<Raw<AnyTimelineEvent>>;
|
||||
}
|
||||
|
||||
/// Ask the client to send matrix event that corresponds to the given
|
||||
/// description and return an event ID as a response.
|
||||
pub(crate) struct SendMatrixEvent(pub(crate) SendEventCommand);
|
||||
impl MatrixDriverRequest for SendMatrixEvent {
|
||||
type Response = OwnedEventId;
|
||||
}
|
||||
*/
|
||||
@@ -17,7 +17,7 @@ use ruma::{
|
||||
events::AnyTimelineEvent, serde::Raw, OwnedEventId,
|
||||
};
|
||||
|
||||
use super::actions::CommandResult;
|
||||
use super::actions::MatrixDriverResponse;
|
||||
use crate::widget::Permissions;
|
||||
|
||||
/// Incoming event that the client API must process.
|
||||
@@ -29,14 +29,14 @@ pub(crate) enum Event {
|
||||
MatrixEventReceived(Raw<AnyTimelineEvent>),
|
||||
/// Client acquired permissions from the user.
|
||||
/// A response to an `Action::AcquirePermissions` command.
|
||||
PermissionsAcquired(CommandResult<Permissions>),
|
||||
PermissionsAcquired(MatrixDriverResponse<Permissions>),
|
||||
/// Client got OpenId token for a given request ID.
|
||||
/// A response to an `Action::GetOpenId` command.
|
||||
OpenIdReceived(CommandResult<RumaOpenIdResponse>),
|
||||
OpenIdReceived(MatrixDriverResponse<RumaOpenIdResponse>),
|
||||
/// Client read some matrix event(s).
|
||||
/// A response to an `Action::ReadMatrixEvent` commands.
|
||||
MatrixEventRead(CommandResult<Vec<Raw<AnyTimelineEvent>>>),
|
||||
MatrixEventRead(MatrixDriverResponse<Vec<Raw<AnyTimelineEvent>>>),
|
||||
/// Client sent some matrix event. The response contains the event ID.
|
||||
/// A response to an `Action::SendMatrixEvent` command.
|
||||
MatrixEventSent(CommandResult<OwnedEventId>),
|
||||
MatrixEventSent(MatrixDriverResponse<OwnedEventId>),
|
||||
}
|
||||
|
||||
@@ -16,23 +16,28 @@
|
||||
|
||||
#![warn(unreachable_pub)]
|
||||
|
||||
use indexmap::{map::Entry, IndexMap};
|
||||
use serde::Serialize;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use tracing::{error, instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
use self::to_widget::{RequestPermissions, ToWidgetRequest};
|
||||
use self::{
|
||||
driver_req::{AcquirePermissions, MatrixDriverRequest, MatrixDriverRequestHandle},
|
||||
to_widget::{RequestPermissions, ToWidgetRequest, ToWidgetRequestHandle},
|
||||
};
|
||||
|
||||
mod actions;
|
||||
mod driver_req;
|
||||
mod events;
|
||||
mod openid;
|
||||
mod outgoing;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod to_widget;
|
||||
|
||||
pub(crate) use self::{
|
||||
actions::{Action, SendEventCommand},
|
||||
actions::{Action, MatrixDriverRequestData, MatrixDriverResponse, SendEventCommand},
|
||||
events::Event,
|
||||
};
|
||||
#[cfg(doc)]
|
||||
@@ -44,6 +49,8 @@ use super::WidgetDriver;
|
||||
pub(crate) struct WidgetMachine {
|
||||
widget_id: String,
|
||||
actions_sender: UnboundedSender<Action>,
|
||||
pending_to_widget_requests: IndexMap<Uuid, ToWidgetRequestMeta>,
|
||||
pending_matrix_driver_requests: IndexMap<Uuid, MatrixDriverRequestMeta>,
|
||||
}
|
||||
|
||||
impl WidgetMachine {
|
||||
@@ -55,17 +62,31 @@ impl WidgetMachine {
|
||||
init_on_content_load: bool,
|
||||
) -> (Self, UnboundedReceiver<Action>) {
|
||||
let (actions_sender, actions_receiver) = unbounded_channel();
|
||||
let this = Self { widget_id, actions_sender };
|
||||
let mut machine = Self {
|
||||
widget_id,
|
||||
actions_sender,
|
||||
pending_to_widget_requests: IndexMap::new(),
|
||||
pending_matrix_driver_requests: IndexMap::new(),
|
||||
};
|
||||
|
||||
if !init_on_content_load {
|
||||
this.send_to_widget(RequestPermissions {});
|
||||
machine
|
||||
.send_to_widget_request(RequestPermissions {})
|
||||
// rustfmt please
|
||||
.then(|desired_permissions, machine| {
|
||||
machine.send_matrix_driver_request(AcquirePermissions { desired_permissions });
|
||||
// TODO: use the result! (chain another `.then`)
|
||||
});
|
||||
}
|
||||
|
||||
(this, actions_receiver)
|
||||
(machine, actions_receiver)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(action = T::ACTION))]
|
||||
fn send_to_widget<T: ToWidgetRequest>(&self, to_widget_request: T) {
|
||||
fn send_to_widget_request<T: ToWidgetRequest>(
|
||||
&mut self,
|
||||
to_widget_request: T,
|
||||
) -> ToWidgetRequestHandle<'_, T::ResponseData> {
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "api", rename = "toWidget", rename_all = "camelCase")]
|
||||
struct ToWidgetRequestSerHelper<'a, T> {
|
||||
@@ -75,23 +96,57 @@ impl WidgetMachine {
|
||||
data: T,
|
||||
}
|
||||
|
||||
let request_id = Uuid::new_v4();
|
||||
let full_request = ToWidgetRequestSerHelper {
|
||||
widget_id: &self.widget_id,
|
||||
request_id: Uuid::new_v4(),
|
||||
request_id,
|
||||
action: T::ACTION,
|
||||
data: to_widget_request,
|
||||
};
|
||||
|
||||
let serialized = match serde_json::to_string(&full_request) {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
error!("Failed to serialize outgoing message: {e}");
|
||||
return;
|
||||
return ToWidgetRequestHandle::null();
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = self.actions_sender.send(Action::SendToWidget(serialized)) {
|
||||
error!("Failed to send action: {e}");
|
||||
return ToWidgetRequestHandle::null();
|
||||
}
|
||||
|
||||
let request_meta = ToWidgetRequestMeta::new(T::ACTION);
|
||||
let Entry::Vacant(entry) = self.pending_to_widget_requests.entry(request_id) else {
|
||||
panic!("uuid collision");
|
||||
};
|
||||
let meta = entry.insert(request_meta);
|
||||
|
||||
ToWidgetRequestHandle::new(meta)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
fn send_matrix_driver_request<T: MatrixDriverRequest>(
|
||||
&mut self,
|
||||
matrix_driver_request: T,
|
||||
) -> MatrixDriverRequestHandle<'_, T::Response> {
|
||||
let request_id = Uuid::new_v4();
|
||||
if let Err(e) = self
|
||||
.actions_sender
|
||||
.send(Action::MatrixDriverRequest { request_id, data: matrix_driver_request.into() })
|
||||
{
|
||||
error!("Failed to send action: {e}");
|
||||
return MatrixDriverRequestHandle::null();
|
||||
}
|
||||
|
||||
let request_meta = MatrixDriverRequestMeta::new();
|
||||
let Entry::Vacant(entry) = self.pending_matrix_driver_requests.entry(request_id) else {
|
||||
panic!("uuid collision");
|
||||
};
|
||||
let meta = entry.insert(request_meta);
|
||||
|
||||
MatrixDriverRequestHandle::new(meta)
|
||||
}
|
||||
|
||||
/// Processes an incoming event (an incoming raw message from a widget,
|
||||
@@ -101,3 +156,29 @@ impl WidgetMachine {
|
||||
// TODO: Process the event.
|
||||
}
|
||||
}
|
||||
|
||||
type ToWidgetResponseFn = Box<dyn FnOnce(Box<RawJsonValue>, &mut WidgetMachine) + Send>;
|
||||
|
||||
pub(crate) struct ToWidgetRequestMeta {
|
||||
#[allow(dead_code)]
|
||||
action: &'static str,
|
||||
response_fn: Option<ToWidgetResponseFn>,
|
||||
}
|
||||
|
||||
impl ToWidgetRequestMeta {
|
||||
fn new(action: &'static str) -> Self {
|
||||
Self { action, response_fn: None }
|
||||
}
|
||||
}
|
||||
|
||||
type MatrixDriverResponseFn = Box<dyn FnOnce(Event, &mut WidgetMachine) + Send>;
|
||||
|
||||
pub(crate) struct MatrixDriverRequestMeta {
|
||||
response_fn: Option<MatrixDriverResponseFn>,
|
||||
}
|
||||
|
||||
impl MatrixDriverRequestMeta {
|
||||
fn new() -> Self {
|
||||
Self { response_fn: None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! A high-level API to generate commands (requests) that we send either
|
||||
//! directly to the widget, or to the matrix driver/client.
|
||||
|
||||
use ruma::{
|
||||
api::client::account::request_openid_token::v3::Response as RumaOpenIdResponse,
|
||||
events::AnyTimelineEvent, serde::Raw, OwnedEventId,
|
||||
};
|
||||
|
||||
use super::{
|
||||
actions::{ReadMessageLikeEventCommand, SendEventCommand},
|
||||
openid::OpenIdResponse,
|
||||
};
|
||||
use crate::widget::Permissions;
|
||||
|
||||
/// Represents a request that the widget API state machine can send.
|
||||
pub(crate) trait Request {
|
||||
type Response;
|
||||
}
|
||||
|
||||
// `toWidget` requests
|
||||
|
||||
/// Send a request to a widget asking it to respond with the list of
|
||||
/// permissinos (capabilities) that the widget wants to have.
|
||||
pub(crate) struct RequestPermissions;
|
||||
impl Request for RequestPermissions {
|
||||
type Response = Permissions;
|
||||
}
|
||||
|
||||
/// Send a request to the widget asking it to update its permissions.
|
||||
pub(crate) struct UpdatePermissions(pub(crate) Permissions);
|
||||
impl Request for UpdatePermissions {
|
||||
type Response = ();
|
||||
}
|
||||
|
||||
/// Send a request to the widget asking it to update its open ID state.
|
||||
pub(crate) struct UpdateOpenId(pub(crate) OpenIdResponse);
|
||||
impl Request for UpdateOpenId {
|
||||
type Response = ();
|
||||
}
|
||||
|
||||
// requests to the `MatrixDriver`
|
||||
|
||||
/// Ask the client (permission provider) to acquire given permissions
|
||||
/// from the user. The client must eventually respond with granted permissions.
|
||||
pub(crate) struct AcquirePermissions(pub(crate) Permissions);
|
||||
impl Request for AcquirePermissions {
|
||||
type Response = Permissions;
|
||||
}
|
||||
|
||||
/// Request open ID from the Matrix client.
|
||||
pub(crate) struct RequestOpenId;
|
||||
impl Request for RequestOpenId {
|
||||
type Response = RumaOpenIdResponse;
|
||||
}
|
||||
|
||||
/// Ask the client to read matrix event(s) that corresponds to the given
|
||||
/// description and return a list of events as a response.
|
||||
pub(crate) struct ReadMatrixEvent(pub(crate) ReadMessageLikeEventCommand);
|
||||
impl Request for ReadMatrixEvent {
|
||||
type Response = Vec<Raw<AnyTimelineEvent>>;
|
||||
}
|
||||
|
||||
/// Ask the client to send matrix event that corresponds to the given
|
||||
/// description and return an event ID as a response.
|
||||
pub(crate) struct SendMatrixEvent(pub(crate) SendEventCommand);
|
||||
impl Request for SendMatrixEvent {
|
||||
type Response = OwnedEventId;
|
||||
}
|
||||
@@ -12,13 +12,53 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::Serialize;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use tracing::error;
|
||||
|
||||
use super::{ToWidgetRequestMeta, WidgetMachine};
|
||||
use crate::widget::Permissions;
|
||||
|
||||
/// A handle to a pending `toWidget` request.
|
||||
pub(crate) struct ToWidgetRequestHandle<'m, T> {
|
||||
request_meta: Option<&'m mut ToWidgetRequestMeta>,
|
||||
_phantom: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<'m, T> ToWidgetRequestHandle<'m, T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
pub(crate) fn new(request_meta: &'m mut ToWidgetRequestMeta) -> Self {
|
||||
Self { request_meta: Some(request_meta), _phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn null() -> Self {
|
||||
Self { request_meta: None, _phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn then(
|
||||
self,
|
||||
response_handler: impl FnOnce(T, &mut WidgetMachine) + Send + 'static,
|
||||
) {
|
||||
if let Some(request_meta) = self.request_meta {
|
||||
request_meta.response_fn = Some(Box::new(move |raw_response_data, machine| {
|
||||
match serde_json::from_str(raw_response_data.get()) {
|
||||
Ok(response_data) => response_handler(response_data, machine),
|
||||
Err(e) => error!("Failed to deserialize toWidget response: {e}"),
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A request that the driver can send to the widget.
|
||||
///
|
||||
/// In postmessage interface terms: an `"api": "toWidget"` message.
|
||||
pub(crate) trait ToWidgetRequest: Serialize {
|
||||
const ACTION: &'static str;
|
||||
type ResponseData: DeserializeOwned;
|
||||
}
|
||||
|
||||
/// Request the widget to send the list of capabilities that it wants to have.
|
||||
@@ -27,4 +67,5 @@ pub(crate) struct RequestPermissions {}
|
||||
|
||||
impl ToWidgetRequest for RequestPermissions {
|
||||
const ACTION: &'static str = "capabilities";
|
||||
type ResponseData = Permissions;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@ use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio_util::sync::{CancellationToken, DropGuard};
|
||||
|
||||
use self::{
|
||||
machine::{Action, Event, SendEventCommand, WidgetMachine},
|
||||
machine::{
|
||||
Action, Event, MatrixDriverRequestData, MatrixDriverResponse, SendEventCommand,
|
||||
WidgetMachine,
|
||||
},
|
||||
matrix::MatrixDriver,
|
||||
};
|
||||
use crate::{room::Room, Result};
|
||||
@@ -148,31 +151,40 @@ impl WidgetDriver {
|
||||
while let Some(action) = actions.recv().await {
|
||||
match action {
|
||||
Action::SendToWidget(msg) => self.to_widget_tx.send(msg).await.map_err(|_| ())?,
|
||||
Action::AcquirePermissions(cmd) => {
|
||||
let obtained = permissions_provider.acquire_permissions(cmd.clone()).await;
|
||||
let event = Event::PermissionsAcquired(cmd.ok(obtained));
|
||||
events_tx.send(event).map_err(|_| ())?;
|
||||
}
|
||||
Action::GetOpenId(cmd) => {
|
||||
let result = cmd.result(matrix_driver.get_open_id().await);
|
||||
events_tx.send(Event::OpenIdReceived(result)).map_err(|_| ())?;
|
||||
}
|
||||
Action::ReadMessageLikeEvent(cmd) => {
|
||||
let events = matrix_driver
|
||||
.read_message_like_events(cmd.event_type.clone(), cmd.limit)
|
||||
.await;
|
||||
events_tx.send(Event::MatrixEventRead(cmd.result(events))).map_err(|_| ())?;
|
||||
}
|
||||
Action::ReadStateEvent(cmd) => {
|
||||
let events = matrix_driver
|
||||
.read_state_events(cmd.event_type.clone(), &cmd.state_key)
|
||||
.await;
|
||||
events_tx.send(Event::MatrixEventRead(cmd.result(events))).map_err(|_| ())?;
|
||||
}
|
||||
Action::SendMatrixEvent(cmd) => {
|
||||
let SendEventCommand { event_type, state_key, content } = cmd.clone();
|
||||
let matrix_event_id = matrix_driver.send(event_type, state_key, content).await;
|
||||
let event = Event::MatrixEventSent(cmd.result(matrix_event_id));
|
||||
Action::MatrixDriverRequest { request_id: id, data } => {
|
||||
let event = match data {
|
||||
MatrixDriverRequestData::AcquirePermissions(cmd) => {
|
||||
let obtained = permissions_provider
|
||||
.acquire_permissions(cmd.desired_permissions.clone())
|
||||
.await;
|
||||
|
||||
Event::PermissionsAcquired(MatrixDriverResponse::ok(id, obtained))
|
||||
}
|
||||
MatrixDriverRequestData::GetOpenId => {
|
||||
let result =
|
||||
MatrixDriverResponse::new(id, matrix_driver.get_open_id().await);
|
||||
Event::OpenIdReceived(result)
|
||||
}
|
||||
MatrixDriverRequestData::ReadMessageLikeEvent(cmd) => {
|
||||
let events = matrix_driver
|
||||
.read_message_like_events(cmd.event_type.clone(), cmd.limit)
|
||||
.await;
|
||||
Event::MatrixEventRead(MatrixDriverResponse::new(id, events))
|
||||
}
|
||||
MatrixDriverRequestData::ReadStateEvent(cmd) => {
|
||||
let events = matrix_driver
|
||||
.read_state_events(cmd.event_type.clone(), &cmd.state_key)
|
||||
.await;
|
||||
Event::MatrixEventRead(MatrixDriverResponse::new(id, events))
|
||||
}
|
||||
MatrixDriverRequestData::SendMatrixEvent(cmd) => {
|
||||
let SendEventCommand { event_type, state_key, content } = cmd.clone();
|
||||
let matrix_event_id =
|
||||
matrix_driver.send(event_type, state_key, content).await;
|
||||
Event::MatrixEventSent(MatrixDriverResponse::new(id, matrix_event_id))
|
||||
}
|
||||
};
|
||||
|
||||
events_tx.send(event).map_err(|_| ())?;
|
||||
}
|
||||
Action::Subscribe => {
|
||||
|
||||
Reference in New Issue
Block a user