Skip to content

Commit

Permalink
ConversationListItem + UserPreferences
Browse files Browse the repository at this point in the history
  • Loading branch information
insipx committed Feb 21, 2025
1 parent c178fe0 commit 5a51c79
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 54 deletions.
11 changes: 9 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ version: 2
updates:
# Maintain dependencies for cargo
- package-ecosystem: "cargo"
directory: "/"
directories:
- "/"
- "/xmtp_*"
- "/bindings_*"
- "/mls_validation_service"
- "/examples/cli"
- "/xtask"
- "/common"
schedule:
interval: "weekly"
groups:
Expand All @@ -13,7 +20,7 @@ updates:
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
# Maintain dependencies for GitHub Actions
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
# Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.)
directory: "/"
Expand Down
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions bindings_node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ crate-type = ["cdylib"]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
futures.workspace = true
hex.workspace = true
napi = { version = "2.12.2", default-features = false, features = [
napi = { version = "2.16.16", default-features = false, features = [
"napi4",
"napi6",
"async",
"serde-json",
] }
napi-derive = "2.12.2"
napi-derive = "2.16.6"
prost.workspace = true
tokio = { workspace = true, features = ["sync"] }
tracing.workspace = true
Expand All @@ -32,6 +33,7 @@ xmtp_cryptography.workspace = true
xmtp_id.workspace = true
xmtp_mls.workspace = true
xmtp_proto = { workspace = true, features = ["proto_full"] }
serde.workspace = true

[build-dependencies]
napi-build = "2.0.1"
Expand Down
3 changes: 3 additions & 0 deletions bindings_node/src/consent_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use xmtp_mls::storage::consent_record::{
use crate::{client::Client, ErrorWrapper};

#[napi]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ConsentState {
Unknown,
Allowed,
Expand Down Expand Up @@ -34,6 +35,7 @@ impl From<ConsentState> for XmtpConsentState {
}

#[napi]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ConsentEntityType {
GroupId,
InboxId,
Expand Down Expand Up @@ -61,6 +63,7 @@ impl From<XmtpConsentType> for ConsentEntityType {
}

#[napi(object)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Consent {
pub entity_type: ConsentEntityType,
pub state: ConsentState,
Expand Down
1 change: 1 addition & 0 deletions bindings_node/src/conversation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub struct GroupMember {
}

#[napi]
#[derive(Clone)]
pub struct Conversation {
inner_client: Arc<RustXmtpClient>,
group_id: Vec<u8>,
Expand Down
139 changes: 91 additions & 48 deletions bindings_node/src/conversations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use std::sync::Arc;
use std::vec;

use napi::bindgen_prelude::{BigInt, Error, Result, Uint8Array};
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode};
use napi::threadsafe_function::{
ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
};
use napi::JsFunction;
use napi_derive::napi;
// use xmtp_mls::groups::device_sync::preference_sync::UserPreferenceUpdate as XmtpUserPreferenceUpdate;
use xmtp_mls::groups::device_sync::preference_sync::UserPreferenceUpdate as XmtpUserPreferenceUpdate;
use xmtp_mls::groups::{
DMMetadataOptions, GroupMetadataOptions, HmacKey as XmtpHmacKey, PreconfiguredPolicies,
};
Expand All @@ -21,6 +23,7 @@ use crate::message::Message;
use crate::permissions::{GroupPermissionsOptions, PermissionPolicySet};
use crate::ErrorWrapper;
use crate::{client::RustXmtpClient, conversation::Conversation, streams::StreamCloser};
use serde::{Deserialize, Serialize};
use xmtp_mls::groups::group_mutable_metadata::MessageDisappearingSettings as XmtpMessageDisappearingSettings;

#[napi]
Expand Down Expand Up @@ -146,26 +149,54 @@ impl From<XmtpHmacKey> for HmacKey {
}
}

// #[napi(object)]
// pub struct UserPreferenceUpdate {
// pub hmac_key_update: Vec<u8>,
// }

// impl TryFrom<XmtpUserPreferenceUpdate> for UserPreferenceUpdate {
// fn try_from(value: XmtpUserPreferenceUpdate) -> Result<Self, Error> {
// match value {
// XmtpUserPreferenceUpdate::HmacKeyUpdate { key } => Ok(Self {
// hmac_key_update: key,
// }),
// _ => Error::from_reason("Only HmacKeyUpdate is supported"),
// }
// }
// }
// TODO: Napi-rs 3.0.0 will support structured enums
// alpha release: https://github.com/napi-rs/napi-rs/releases/tag/napi%403.0.0-alpha.9
// PR: https://github.com/napi-rs/napi-rs/pull/2222
// Issue: https://github.com/napi-rs/napi-rs/issues/507
#[derive(Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Tag<T> {
V(T),
}

#[napi(object)]
#[derive(Serialize, Deserialize)]
pub enum UserPreferenceUpdate {
ConsentUpdate { consent: Consent },
HmacKeyUpdate { key: Vec<u8> },
}

impl From<XmtpUserPreferenceUpdate> for Tag<UserPreferenceUpdate> {
fn from(value: XmtpUserPreferenceUpdate) -> Self {
match value {
XmtpUserPreferenceUpdate::HmacKeyUpdate { key } => {
Tag::V(UserPreferenceUpdate::HmacKeyUpdate { key })
}
XmtpUserPreferenceUpdate::ConsentUpdate(consent) => {
Tag::V(UserPreferenceUpdate::ConsentUpdate {
consent: Consent::from(consent),
})
}
}
}
}

#[napi]
pub struct ConversationListItem {
pub conversation: Conversation,
pub last_message: Option<Message>,
conversation: Conversation,
last_message: Option<Message>,
}

#[napi]
impl ConversationListItem {
#[napi(getter)]
pub fn conversation(&self) -> Conversation {
self.conversation.clone()
}

#[napi(getter)]
pub fn last_message(&self) -> Option<Message> {
self.last_message.clone()
}
}

#[napi(object)]
Expand Down Expand Up @@ -647,32 +678,44 @@ impl Conversations {
Ok(StreamCloser::new(stream_closer))
}

// #[napi(ts_args_type = "callback: (err: null | Error, result: Consent[] | undefined) => void")]
// pub fn stream_preferences(&self, callback: JsFunction) -> Result<StreamCloser> {
// tracing::trace!(inbox_id = self.inner_client.inbox_id(),);
// let tsfn: ThreadsafeFunction<Vec<UserPreferenceUpdate>, ErrorStrategy::CalleeHandled> =
// callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;
// let inbox_id = self.inner_client.inbox_id().to_string();
// let stream_closer =
// RustXmtpClient::stream_preferences_with_callback(self.inner_client.clone(), move |message| {
// tracing::trace!(inbox_id, "[received] calling tsfn callback");
// match message {
// Ok(message) => {
// let msg: Vec<UserPreferenceUpdate> = message
// .into_iter()
// .filter_map(|v| v.try_into().ok())
// .collect();
// tsfn.call(Ok(msg), ThreadsafeFunctionCallMode::Blocking);
// }
// Err(e) => {
// tsfn.call(
// Err(Error::from(ErrorWrapper::from(e))),
// ThreadsafeFunctionCallMode::Blocking,
// );
// }
// }
// });

// Ok(StreamCloser::new(stream_closer))
// }
#[napi(ts_args_type = "callback: (err: null | Error, result: Consent[] | undefined) => void")]
pub fn stream_preferences(&self, callback: JsFunction) -> Result<StreamCloser> {
tracing::trace!(inbox_id = self.inner_client.inbox_id(),);
let tsfn: ThreadsafeFunction<Vec<Tag<UserPreferenceUpdate>>, ErrorStrategy::CalleeHandled> =
callback.create_threadsafe_function(
0,
|ctx: ThreadSafeCallContext<Vec<Tag<UserPreferenceUpdate>>>| {
let env = ctx.env;
Ok(
ctx
.value
.into_iter()
.map(|v| env.to_js_value(&v))
.collect::<Result<Vec<napi::JsUnknown>, _>>()?,
)
},
)?;
let inbox_id = self.inner_client.inbox_id().to_string();
let stream_closer =
RustXmtpClient::stream_preferences_with_callback(self.inner_client.clone(), move |message| {
tracing::trace!(inbox_id, "[received] calling tsfn callback");
match message {
Ok(message) => {
let msg: Vec<Tag<UserPreferenceUpdate>> = message
.into_iter()
.map(|p| Tag::<UserPreferenceUpdate>::from(p))
.collect();
tsfn.call(Ok(msg), ThreadsafeFunctionCallMode::Blocking);
}
Err(e) => {
tsfn.call(
Err(Error::from(ErrorWrapper::from(e))),
ThreadsafeFunctionCallMode::Blocking,
);
}
}
});

Ok(StreamCloser::new(stream_closer))
}
}
1 change: 1 addition & 0 deletions bindings_node/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ impl From<ListMessagesOptions> for MsgQueryArgs {
}

#[napi(object)]
#[derive(Clone)]
pub struct Message {
pub id: String,
pub sent_at_ns: i64,
Expand Down

0 comments on commit 5a51c79

Please sign in to comment.