ffi: add bindings for App

This commit is contained in:
Benjamin Bouvier
2023-07-07 15:15:48 +02:00
parent dd7d4ddf7c
commit dbc8f0136b
7 changed files with 134 additions and 28 deletions

View File

@@ -29,7 +29,7 @@ eyeball-im = { workspace = true }
extension-trait = "1.0.1"
futures-core = { workspace = true }
futures-util = { workspace = true }
matrix-sdk-ui = { path = "../../crates/matrix-sdk-ui", default-features = false, features = ["e2e-encryption", "experimental-room-list", "experimental-encryption-sync", "experimental-notification-client"] }
matrix-sdk-ui = { path = "../../crates/matrix-sdk-ui", default-features = false, features = ["e2e-encryption", "experimental-room-list", "experimental-encryption-sync", "experimental-notification-client", "experimental-app"] }
mime = "0.3.16"
# FIXME: we currently can't feature flag anything in the api.udl, therefore we must enforce experimental-sliding-sync being exposed here..
# see https://github.com/matrix-org/matrix-rust-sdk/issues/1014

View File

@@ -0,0 +1,110 @@
// 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 that specific language governing permissions and
// limitations under the License.
use std::{fmt::Debug, sync::Arc};
use futures_util::{pin_mut, StreamExt as _};
use matrix_sdk::Client;
use matrix_sdk_ui::app::{
App as MatrixApp, AppBuilder as MatrixAppBuilder, AppState as MatrixAppState,
};
use crate::{
error::ClientError, helpers::unwrap_or_clone_arc, room_list::RoomListService,
task_handle::TaskHandle, RUNTIME,
};
#[derive(uniffi::Enum)]
pub enum AppState {
Running,
Terminated,
Error,
}
impl From<MatrixAppState> for AppState {
fn from(value: MatrixAppState) -> Self {
match value {
MatrixAppState::Running => Self::Running,
MatrixAppState::Terminated => Self::Terminated,
MatrixAppState::Error => Self::Error,
}
}
}
#[uniffi::export(callback_interface)]
pub trait AppStateObserver: Send + Sync + Debug {
fn on_update(&self, state: AppState);
}
#[derive(uniffi::Object)]
pub struct App {
inner: MatrixApp,
}
#[uniffi::export]
impl App {
pub fn room_list_service(&self) -> Arc<RoomListService> {
Arc::new(RoomListService { inner: self.inner.room_list_service() })
}
fn state(&self, listener: Box<dyn AppStateObserver>) -> Arc<TaskHandle> {
let state_stream = self.inner.observe_state();
Arc::new(TaskHandle::new(RUNTIME.spawn(async move {
pin_mut!(state_stream);
while let Some(state) = state_stream.next().await {
listener.on_update(state.into());
}
})))
}
pub fn start(&self) -> Result<(), ClientError> {
let start = self.inner.start();
RUNTIME.block_on(async { Ok(start.await?) })
}
pub fn pause(&self) -> Result<(), ClientError> {
Ok(self.inner.pause()?)
}
}
#[derive(Clone, uniffi::Object)]
pub struct AppBuilder {
builder: MatrixAppBuilder,
}
impl AppBuilder {
pub(crate) fn new(client: Client) -> Arc<Self> {
Arc::new(Self { builder: MatrixApp::builder(client) })
}
}
#[uniffi::export]
impl AppBuilder {
pub fn with_encryption_sync(
self: Arc<Self>,
with_cross_process_lock: bool,
app_identifier: Option<String>,
) -> Arc<Self> {
let this = unwrap_or_clone_arc(self);
let builder = this.builder.with_encryption_sync(with_cross_process_lock, app_identifier);
Arc::new(Self { builder })
}
pub fn finish(self: Arc<Self>) -> Result<Arc<App>, ClientError> {
let this = unwrap_or_clone_arc(self);
RUNTIME.block_on(async move { Ok(Arc::new(App { inner: this.builder.build().await? })) })
}
}

View File

@@ -32,8 +32,8 @@ use url::Url;
use super::{room::Room, session_verification::SessionVerificationController, RUNTIME};
use crate::{
client, notification::NotificationClientBuilder, notification_settings::NotificationSettings,
ClientError,
app::AppBuilder, client, notification::NotificationClientBuilder,
notification_settings::NotificationSettings, ClientError,
};
#[derive(Clone, uniffi::Record)]
@@ -588,6 +588,10 @@ impl Client {
NotificationClientBuilder::new(self.inner.clone())
}
pub fn app(&self) -> Arc<AppBuilder> {
AppBuilder::new(self.inner.clone())
}
pub fn get_notification_settings(&self) -> Arc<NotificationSettings> {
RUNTIME.block_on(async move {
Arc::new(NotificationSettings::new(

View File

@@ -4,7 +4,7 @@ use matrix_sdk::{
self, encryption::CryptoStoreError, HttpError, IdParseError,
NotificationSettingsError as SdkNotificationSettingsError, StoreError,
};
use matrix_sdk_ui::{encryption_sync, notification_client, timeline};
use matrix_sdk_ui::{app, encryption_sync, notification_client, timeline};
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
@@ -90,6 +90,12 @@ impl From<notification_client::Error> for ClientError {
}
}
impl From<app::Error> for ClientError {
fn from(e: app::Error) -> Self {
Self::new(e)
}
}
#[derive(Debug, thiserror::Error, uniffi::Error)]
#[uniffi(flat_error)]
pub enum RoomError {

View File

@@ -20,6 +20,7 @@ macro_rules! unwrap_or_clone_arc_into_variant {
};
}
mod app;
mod authentication_service;
mod client;
mod client_builder;

View File

@@ -16,6 +16,7 @@ use tokio::sync::RwLock;
use crate::{client::Client, room::Room, timeline::EventTimelineItem, TaskHandle, RUNTIME};
// TODO(bnjbvr) remove
#[uniffi::export]
impl Client {
/// Get a new `RoomListService` instance.
@@ -102,28 +103,11 @@ impl From<RoomListInput> for matrix_sdk_ui::room_list_service::Input {
#[derive(uniffi::Object)]
pub struct RoomListService {
inner: Arc<matrix_sdk_ui::RoomListService>,
pub(crate) inner: Arc<matrix_sdk_ui::RoomListService>,
}
#[uniffi::export]
impl RoomListService {
fn sync(&self) {
let this = self.inner.clone();
RUNTIME.spawn(async move {
let sync_stream = this.sync();
pin_mut!(sync_stream);
while sync_stream.next().await.is_some() {
// keep going!
}
});
}
fn stop_sync(&self) -> Result<(), RoomListError> {
self.inner.stop_sync().map_err(Into::into)
}
fn is_syncing(&self) -> bool {
use matrix_sdk_ui::room_list_service::State;

View File

@@ -53,7 +53,7 @@ pub enum AppState {
}
pub struct App {
room_list: Arc<RoomListService>,
room_list_service: Arc<RoomListService>,
encryption_sync: Option<Arc<EncryptionSync>>,
task_handle: Arc<Mutex<Option<JoinHandle<()>>>>,
state_observer: SharedObservable<AppState>,
@@ -67,8 +67,8 @@ impl App {
/// Get the underlying `RoomListService` instance for easier access to its
/// methods.
pub fn room_list_service(&self) -> &RoomListService {
&*self.room_list
pub fn room_list_service(&self) -> Arc<RoomListService> {
self.room_list_service.clone()
}
/// Observe the current state of the application.
@@ -84,7 +84,7 @@ impl App {
/// spawned to run the syncs, then it will be properly aborted and
/// restarted.
pub async fn start(&self) -> Result<(), Error> {
let room_list = self.room_list.clone();
let room_list = self.room_list_service.clone();
let encryption_sync = self.encryption_sync.clone();
let state_observer = self.state_observer.clone();
@@ -162,7 +162,7 @@ impl App {
/// to call this API when the application exits, although not strictly
/// necessary.
pub fn pause(&self) -> Result<(), Error> {
self.room_list.stop_sync()?;
self.room_list_service.stop_sync()?;
if let Some(ref encryption_sync) = self.encryption_sync {
encryption_sync.stop()?;
}
@@ -170,6 +170,7 @@ impl App {
}
}
#[derive(Clone)]
pub struct AppBuilder {
/// SDK client.
client: Client,
@@ -243,7 +244,7 @@ impl AppBuilder {
};
let app = App {
room_list: Arc::new(room_list),
room_list_service: Arc::new(room_list),
encryption_sync,
state_observer: SharedObservable::new(AppState::Running),
task_handle: Default::default(),