mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
cffi:jsonrpc: send events (#3662)
* jsonrpc in cffi also sends events now * add pr id to changelog * jsonrpc: new format for events and better typescript autocompletion (#3663) * jsonrpc: new format for events and better typescript autocompletion * adjust doc comments
This commit is contained in:
@@ -41,6 +41,8 @@
|
|||||||
|
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
- jsonrpc in cffi also sends events now #3662
|
||||||
|
- jsonrpc: new format for events and better typescript autocompletion
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- share stock string translations across accounts created by the same account manager #3640
|
- share stock string translations across accounts created by the same account manager #3640
|
||||||
|
|||||||
@@ -4452,11 +4452,13 @@ pub unsafe extern "C" fn dc_accounts_get_event_emitter(
|
|||||||
mod jsonrpc {
|
mod jsonrpc {
|
||||||
use super::*;
|
use super::*;
|
||||||
use deltachat_jsonrpc::api::CommandApi;
|
use deltachat_jsonrpc::api::CommandApi;
|
||||||
|
use deltachat_jsonrpc::events::event_to_json_rpc_notification;
|
||||||
use deltachat_jsonrpc::yerpc::{OutReceiver, RpcClient, RpcSession};
|
use deltachat_jsonrpc::yerpc::{OutReceiver, RpcClient, RpcSession};
|
||||||
|
|
||||||
pub struct dc_jsonrpc_instance_t {
|
pub struct dc_jsonrpc_instance_t {
|
||||||
receiver: OutReceiver,
|
receiver: OutReceiver,
|
||||||
handle: RpcSession<CommandApi>,
|
handle: RpcSession<CommandApi>,
|
||||||
|
event_thread: JoinHandle<Result<(), anyhow::Error>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@@ -4472,9 +4474,36 @@ mod jsonrpc {
|
|||||||
deltachat_jsonrpc::api::CommandApi::from_arc((*account_manager).inner.clone());
|
deltachat_jsonrpc::api::CommandApi::from_arc((*account_manager).inner.clone());
|
||||||
|
|
||||||
let (request_handle, receiver) = RpcClient::new();
|
let (request_handle, receiver) = RpcClient::new();
|
||||||
|
let request_handle2 = request_handle.clone();
|
||||||
let handle = RpcSession::new(request_handle, cmd_api);
|
let handle = RpcSession::new(request_handle, cmd_api);
|
||||||
|
|
||||||
let instance = dc_jsonrpc_instance_t { receiver, handle };
|
let events = block_on({
|
||||||
|
async {
|
||||||
|
let am = (*account_manager).inner.clone();
|
||||||
|
let ev = am.read().await.get_event_emitter();
|
||||||
|
drop(am);
|
||||||
|
ev
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_thread = spawn({
|
||||||
|
async move {
|
||||||
|
while let Some(event) = events.recv().await {
|
||||||
|
let event = event_to_json_rpc_notification(event);
|
||||||
|
request_handle2
|
||||||
|
.send_notification("event", Some(event))
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
let res: Result<(), anyhow::Error> = Ok(());
|
||||||
|
res
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let instance = dc_jsonrpc_instance_t {
|
||||||
|
receiver,
|
||||||
|
handle,
|
||||||
|
event_thread,
|
||||||
|
};
|
||||||
|
|
||||||
Box::into_raw(Box::new(instance))
|
Box::into_raw(Box::new(instance))
|
||||||
}
|
}
|
||||||
@@ -4485,7 +4514,7 @@ mod jsonrpc {
|
|||||||
eprintln!("ignoring careless call to dc_jsonrpc_unref()");
|
eprintln!("ignoring careless call to dc_jsonrpc_unref()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
(*jsonrpc_instance).event_thread.abort();
|
||||||
Box::from_raw(jsonrpc_instance);
|
Box::from_raw(jsonrpc_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,142 +4,353 @@ use serde_json::{json, Value};
|
|||||||
use typescript_type_def::TypeDef;
|
use typescript_type_def::TypeDef;
|
||||||
|
|
||||||
pub fn event_to_json_rpc_notification(event: Event) -> Value {
|
pub fn event_to_json_rpc_notification(event: Event) -> Value {
|
||||||
let (field1, field2): (Value, Value) = match &event.typ {
|
let id: JSONRPCEventType = event.typ.into();
|
||||||
// events with a single string in field1
|
|
||||||
EventType::Info(txt)
|
|
||||||
| EventType::SmtpConnected(txt)
|
|
||||||
| EventType::ImapConnected(txt)
|
|
||||||
| EventType::SmtpMessageSent(txt)
|
|
||||||
| EventType::ImapMessageDeleted(txt)
|
|
||||||
| EventType::ImapMessageMoved(txt)
|
|
||||||
| EventType::NewBlobFile(txt)
|
|
||||||
| EventType::DeletedBlobFile(txt)
|
|
||||||
| EventType::Warning(txt)
|
|
||||||
| EventType::Error(txt)
|
|
||||||
| EventType::ErrorSelfNotInGroup(txt) => (json!(txt), Value::Null),
|
|
||||||
EventType::ImexFileWritten(path) => (json!(path.to_str()), Value::Null),
|
|
||||||
// single number
|
|
||||||
EventType::MsgsNoticed(chat_id) | EventType::ChatModified(chat_id) => {
|
|
||||||
(json!(chat_id), Value::Null)
|
|
||||||
}
|
|
||||||
EventType::ImexProgress(progress) => (json!(progress), Value::Null),
|
|
||||||
// both fields contain numbers
|
|
||||||
EventType::MsgsChanged { chat_id, msg_id }
|
|
||||||
| EventType::IncomingMsg { chat_id, msg_id }
|
|
||||||
| EventType::MsgDelivered { chat_id, msg_id }
|
|
||||||
| EventType::MsgFailed { chat_id, msg_id }
|
|
||||||
| EventType::MsgRead { chat_id, msg_id } => (json!(chat_id), json!(msg_id)),
|
|
||||||
EventType::ChatEphemeralTimerModified { chat_id, timer } => (json!(chat_id), json!(timer)),
|
|
||||||
EventType::SecurejoinInviterProgress {
|
|
||||||
contact_id,
|
|
||||||
progress,
|
|
||||||
}
|
|
||||||
| EventType::SecurejoinJoinerProgress {
|
|
||||||
contact_id,
|
|
||||||
progress,
|
|
||||||
} => (json!(contact_id), json!(progress)),
|
|
||||||
// field 1 number or null
|
|
||||||
EventType::ContactsChanged(maybe_number) | EventType::LocationChanged(maybe_number) => (
|
|
||||||
match maybe_number {
|
|
||||||
Some(number) => json!(number),
|
|
||||||
None => Value::Null,
|
|
||||||
},
|
|
||||||
Value::Null,
|
|
||||||
),
|
|
||||||
// number and maybe string
|
|
||||||
EventType::ConfigureProgress { progress, comment } => (
|
|
||||||
json!(progress),
|
|
||||||
match comment {
|
|
||||||
Some(content) => json!(content),
|
|
||||||
None => Value::Null,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
EventType::ConnectivityChanged => (Value::Null, Value::Null),
|
|
||||||
EventType::SelfavatarChanged => (Value::Null, Value::Null),
|
|
||||||
EventType::WebxdcStatusUpdate {
|
|
||||||
msg_id,
|
|
||||||
status_update_serial,
|
|
||||||
} => (json!(msg_id), json!(status_update_serial)),
|
|
||||||
EventType::WebxdcInstanceDeleted { msg_id } => (json!(msg_id), Value::Null),
|
|
||||||
};
|
|
||||||
|
|
||||||
let id: EventTypeName = event.typ.into();
|
|
||||||
json!({
|
json!({
|
||||||
"id": id,
|
"event": id,
|
||||||
"contextId": event.id,
|
"contextId": event.id,
|
||||||
"field1": field1,
|
|
||||||
"field2": field2
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, TypeDef)]
|
#[derive(Serialize, TypeDef)]
|
||||||
pub enum EventTypeName {
|
#[serde(tag = "type", rename = "Event")]
|
||||||
Info,
|
pub enum JSONRPCEventType {
|
||||||
SmtpConnected,
|
/// The library-user may write an informational string to the log.
|
||||||
ImapConnected,
|
///
|
||||||
SmtpMessageSent,
|
/// This event should *not* be reported to the end-user using a popup or something like
|
||||||
ImapMessageDeleted,
|
/// that.
|
||||||
ImapMessageMoved,
|
Info {
|
||||||
NewBlobFile,
|
msg: String,
|
||||||
DeletedBlobFile,
|
},
|
||||||
Warning,
|
|
||||||
Error,
|
/// Emitted when SMTP connection is established and login was successful.
|
||||||
ErrorSelfNotInGroup,
|
SmtpConnected {
|
||||||
MsgsChanged,
|
msg: String,
|
||||||
IncomingMsg,
|
},
|
||||||
MsgsNoticed,
|
|
||||||
MsgDelivered,
|
/// Emitted when IMAP connection is established and login was successful.
|
||||||
MsgFailed,
|
ImapConnected {
|
||||||
MsgRead,
|
msg: String,
|
||||||
ChatModified,
|
},
|
||||||
ChatEphemeralTimerModified,
|
|
||||||
ContactsChanged,
|
/// Emitted when a message was successfully sent to the SMTP server.
|
||||||
LocationChanged,
|
SmtpMessageSent {
|
||||||
ConfigureProgress,
|
msg: String,
|
||||||
ImexProgress,
|
},
|
||||||
ImexFileWritten,
|
|
||||||
SecurejoinInviterProgress,
|
/// Emitted when an IMAP message has been marked as deleted
|
||||||
SecurejoinJoinerProgress,
|
ImapMessageDeleted {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Emitted when an IMAP message has been moved
|
||||||
|
ImapMessageMoved {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Emitted when an new file in the $BLOBDIR was created
|
||||||
|
NewBlobFile {
|
||||||
|
file: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Emitted when an file in the $BLOBDIR was deleted
|
||||||
|
DeletedBlobFile {
|
||||||
|
file: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The library-user should write a warning string to the log.
|
||||||
|
///
|
||||||
|
/// This event should *not* be reported to the end-user using a popup or something like
|
||||||
|
/// that.
|
||||||
|
Warning {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The library-user should report an error to the end-user.
|
||||||
|
///
|
||||||
|
/// As most things are asynchronous, things may go wrong at any time and the user
|
||||||
|
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||||
|
///
|
||||||
|
/// However, for ongoing processes (eg. configure())
|
||||||
|
/// or for functions that are expected to fail (eg. autocryptContinueKeyTransfer())
|
||||||
|
/// it might be better to delay showing these events until the function has really
|
||||||
|
/// failed (returned false). It should be sufficient to report only the *last* error
|
||||||
|
/// in a messasge box then.
|
||||||
|
Error {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// An action cannot be performed because the user is not in the group.
|
||||||
|
/// Reported eg. after a call to
|
||||||
|
/// setChatName(), setChatProfileImage(),
|
||||||
|
/// addContactToChat(), removeContactFromChat(),
|
||||||
|
/// and messages sending functions.
|
||||||
|
ErrorSelfNotInGroup {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Messages or chats changed. One or more messages or chats changed for various
|
||||||
|
/// reasons in the database:
|
||||||
|
/// - Messages sent, received or removed
|
||||||
|
/// - Chats created, deleted or archived
|
||||||
|
/// - A draft has been set
|
||||||
|
///
|
||||||
|
/// `chatId` is set if only a single chat is affected by the changes, otherwise 0.
|
||||||
|
/// `msgId` is set if only a single message is affected by the changes, otherwise 0.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
MsgsChanged {
|
||||||
|
chat_id: u32,
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// There is a fresh message. Typically, the user will show an notification
|
||||||
|
/// when receiving this message.
|
||||||
|
///
|
||||||
|
/// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
IncomingMsg {
|
||||||
|
chat_id: u32,
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Messages were seen or noticed.
|
||||||
|
/// chat id is always set.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
MsgsNoticed {
|
||||||
|
chat_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||||
|
/// DC_STATE_OUT_DELIVERED, see `Message.state`.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
MsgDelivered {
|
||||||
|
chat_id: u32,
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||||
|
/// DC_STATE_OUT_FAILED, see `Message.state`.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
MsgFailed {
|
||||||
|
chat_id: u32,
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||||
|
/// DC_STATE_OUT_MDN_RCVD, see `Message.state`.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
MsgRead {
|
||||||
|
chat_id: u32,
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||||
|
/// Or the verify state of a chat has changed.
|
||||||
|
/// See setChatName(), setChatProfileImage(), addContactToChat()
|
||||||
|
/// and removeContactFromChat().
|
||||||
|
///
|
||||||
|
/// This event does not include ephemeral timer modification, which
|
||||||
|
/// is a separate event.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ChatModified {
|
||||||
|
chat_id: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Chat ephemeral timer changed.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ChatEphemeralTimerModified {
|
||||||
|
chat_id: u32,
|
||||||
|
timer: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Contact(s) created, renamed, blocked or deleted.
|
||||||
|
///
|
||||||
|
/// @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ContactsChanged {
|
||||||
|
contact_id: Option<u32>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Location of one or more contact has changed.
|
||||||
|
///
|
||||||
|
/// @param data1 (u32) contact_id of the contact for which the location has changed.
|
||||||
|
/// If the locations of several contacts have been changed,
|
||||||
|
/// this parameter is set to `None`.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
LocationChanged {
|
||||||
|
contact_id: Option<u32>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Inform about the configuration progress started by configure().
|
||||||
|
ConfigureProgress {
|
||||||
|
/// Progress.
|
||||||
|
///
|
||||||
|
/// 0=error, 1-999=progress in permille, 1000=success and done
|
||||||
|
progress: usize,
|
||||||
|
|
||||||
|
/// Progress comment or error, something to display to the user.
|
||||||
|
comment: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Inform about the import/export progress started by imex().
|
||||||
|
///
|
||||||
|
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||||
|
/// @param data2 0
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ImexProgress {
|
||||||
|
progress: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A file has been exported. A file has been written by imex().
|
||||||
|
/// This event may be sent multiple times by a single call to imex().
|
||||||
|
///
|
||||||
|
/// A typical purpose for a handler of this event may be to make the file public to some system
|
||||||
|
/// services.
|
||||||
|
///
|
||||||
|
/// @param data2 0
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
ImexFileWritten {
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Progress information of a secure-join handshake from the view of the inviter
|
||||||
|
/// (Alice, the person who shows the QR code).
|
||||||
|
///
|
||||||
|
/// These events are typically sent after a joiner has scanned the QR code
|
||||||
|
/// generated by getChatSecurejoinQrCodeSvg().
|
||||||
|
///
|
||||||
|
/// @param data1 (int) ID of the contact that wants to join.
|
||||||
|
/// @param data2 (int) Progress as:
|
||||||
|
/// 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||||
|
/// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
|
||||||
|
/// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
||||||
|
/// 1000=Protocol finished for this contact.
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
SecurejoinInviterProgress {
|
||||||
|
contact_id: u32,
|
||||||
|
progress: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Progress information of a secure-join handshake from the view of the joiner
|
||||||
|
/// (Bob, the person who scans the QR code).
|
||||||
|
/// The events are typically sent while secureJoin(), which
|
||||||
|
/// may take some time, is executed.
|
||||||
|
/// @param data1 (int) ID of the inviting contact.
|
||||||
|
/// @param data2 (int) Progress as:
|
||||||
|
/// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
||||||
|
/// (Bob has verified alice and waits until Alice does the same for him)
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
SecurejoinJoinerProgress {
|
||||||
|
contact_id: u32,
|
||||||
|
progress: usize,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// The connectivity to the server changed.
|
||||||
|
/// This means that you should refresh the connectivity view
|
||||||
|
/// and possibly the connectivtiy HTML; see getConnectivity() and
|
||||||
|
/// getConnectivityHtml() for details.
|
||||||
ConnectivityChanged,
|
ConnectivityChanged,
|
||||||
|
|
||||||
SelfavatarChanged,
|
SelfavatarChanged,
|
||||||
WebxdcStatusUpdate,
|
|
||||||
WebXdInstanceDeleted,
|
#[serde(rename_all = "camelCase")]
|
||||||
|
WebxdcStatusUpdate {
|
||||||
|
msg_id: u32,
|
||||||
|
status_update_serial: u32,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Inform that a message containing a webxdc instance has been deleted
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
WebxdcInstanceDeleted {
|
||||||
|
msg_id: u32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<EventType> for EventTypeName {
|
impl From<EventType> for JSONRPCEventType {
|
||||||
fn from(event: EventType) -> Self {
|
fn from(event: EventType) -> Self {
|
||||||
use EventTypeName::*;
|
use JSONRPCEventType::*;
|
||||||
match event {
|
match event {
|
||||||
EventType::Info(_) => Info,
|
EventType::Info(msg) => Info { msg },
|
||||||
EventType::SmtpConnected(_) => SmtpConnected,
|
EventType::SmtpConnected(msg) => SmtpConnected { msg },
|
||||||
EventType::ImapConnected(_) => ImapConnected,
|
EventType::ImapConnected(msg) => ImapConnected { msg },
|
||||||
EventType::SmtpMessageSent(_) => SmtpMessageSent,
|
EventType::SmtpMessageSent(msg) => SmtpMessageSent { msg },
|
||||||
EventType::ImapMessageDeleted(_) => ImapMessageDeleted,
|
EventType::ImapMessageDeleted(msg) => ImapMessageDeleted { msg },
|
||||||
EventType::ImapMessageMoved(_) => ImapMessageMoved,
|
EventType::ImapMessageMoved(msg) => ImapMessageMoved { msg },
|
||||||
EventType::NewBlobFile(_) => NewBlobFile,
|
EventType::NewBlobFile(file) => NewBlobFile { file },
|
||||||
EventType::DeletedBlobFile(_) => DeletedBlobFile,
|
EventType::DeletedBlobFile(file) => DeletedBlobFile { file },
|
||||||
EventType::Warning(_) => Warning,
|
EventType::Warning(msg) => Warning { msg },
|
||||||
EventType::Error(_) => Error,
|
EventType::Error(msg) => Error { msg },
|
||||||
EventType::ErrorSelfNotInGroup(_) => ErrorSelfNotInGroup,
|
EventType::ErrorSelfNotInGroup(msg) => ErrorSelfNotInGroup { msg },
|
||||||
EventType::MsgsChanged { .. } => MsgsChanged,
|
EventType::MsgsChanged { chat_id, msg_id } => MsgsChanged {
|
||||||
EventType::IncomingMsg { .. } => IncomingMsg,
|
chat_id: chat_id.to_u32(),
|
||||||
EventType::MsgsNoticed(_) => MsgsNoticed,
|
msg_id: msg_id.to_u32(),
|
||||||
EventType::MsgDelivered { .. } => MsgDelivered,
|
},
|
||||||
EventType::MsgFailed { .. } => MsgFailed,
|
EventType::IncomingMsg { chat_id, msg_id } => IncomingMsg {
|
||||||
EventType::MsgRead { .. } => MsgRead,
|
chat_id: chat_id.to_u32(),
|
||||||
EventType::ChatModified(_) => ChatModified,
|
msg_id: msg_id.to_u32(),
|
||||||
EventType::ChatEphemeralTimerModified { .. } => ChatEphemeralTimerModified,
|
},
|
||||||
EventType::ContactsChanged(_) => ContactsChanged,
|
EventType::MsgsNoticed(chat_id) => MsgsNoticed {
|
||||||
EventType::LocationChanged(_) => LocationChanged,
|
chat_id: chat_id.to_u32(),
|
||||||
EventType::ConfigureProgress { .. } => ConfigureProgress,
|
},
|
||||||
EventType::ImexProgress(_) => ImexProgress,
|
EventType::MsgDelivered { chat_id, msg_id } => MsgDelivered {
|
||||||
EventType::ImexFileWritten(_) => ImexFileWritten,
|
chat_id: chat_id.to_u32(),
|
||||||
EventType::SecurejoinInviterProgress { .. } => SecurejoinInviterProgress,
|
msg_id: msg_id.to_u32(),
|
||||||
EventType::SecurejoinJoinerProgress { .. } => SecurejoinJoinerProgress,
|
},
|
||||||
|
EventType::MsgFailed { chat_id, msg_id } => MsgFailed {
|
||||||
|
chat_id: chat_id.to_u32(),
|
||||||
|
msg_id: msg_id.to_u32(),
|
||||||
|
},
|
||||||
|
EventType::MsgRead { chat_id, msg_id } => MsgRead {
|
||||||
|
chat_id: chat_id.to_u32(),
|
||||||
|
msg_id: msg_id.to_u32(),
|
||||||
|
},
|
||||||
|
EventType::ChatModified(chat_id) => ChatModified {
|
||||||
|
chat_id: chat_id.to_u32(),
|
||||||
|
},
|
||||||
|
EventType::ChatEphemeralTimerModified { chat_id, timer } => {
|
||||||
|
ChatEphemeralTimerModified {
|
||||||
|
chat_id: chat_id.to_u32(),
|
||||||
|
timer: timer.to_u32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EventType::ContactsChanged(contact) => ContactsChanged {
|
||||||
|
contact_id: contact.map(|c| c.to_u32()),
|
||||||
|
},
|
||||||
|
EventType::LocationChanged(contact) => LocationChanged {
|
||||||
|
contact_id: contact.map(|c| c.to_u32()),
|
||||||
|
},
|
||||||
|
EventType::ConfigureProgress { progress, comment } => {
|
||||||
|
ConfigureProgress { progress, comment }
|
||||||
|
}
|
||||||
|
EventType::ImexProgress(progress) => ImexProgress { progress },
|
||||||
|
EventType::ImexFileWritten(path) => ImexFileWritten {
|
||||||
|
path: path.to_str().unwrap_or_default().to_owned(),
|
||||||
|
},
|
||||||
|
EventType::SecurejoinInviterProgress {
|
||||||
|
contact_id,
|
||||||
|
progress,
|
||||||
|
} => SecurejoinInviterProgress {
|
||||||
|
contact_id: contact_id.to_u32(),
|
||||||
|
progress,
|
||||||
|
},
|
||||||
|
EventType::SecurejoinJoinerProgress {
|
||||||
|
contact_id,
|
||||||
|
progress,
|
||||||
|
} => SecurejoinJoinerProgress {
|
||||||
|
contact_id: contact_id.to_u32(),
|
||||||
|
progress,
|
||||||
|
},
|
||||||
EventType::ConnectivityChanged => ConnectivityChanged,
|
EventType::ConnectivityChanged => ConnectivityChanged,
|
||||||
EventType::SelfavatarChanged => SelfavatarChanged,
|
EventType::SelfavatarChanged => SelfavatarChanged,
|
||||||
EventType::WebxdcStatusUpdate { .. } => WebxdcStatusUpdate,
|
EventType::WebxdcStatusUpdate {
|
||||||
EventType::WebxdcInstanceDeleted { .. } => WebXdInstanceDeleted,
|
msg_id,
|
||||||
|
status_update_serial,
|
||||||
|
} => WebxdcStatusUpdate {
|
||||||
|
msg_id: msg_id.to_u32(),
|
||||||
|
status_update_serial: status_update_serial.to_u32(),
|
||||||
|
},
|
||||||
|
EventType::WebxdcInstanceDeleted { msg_id } => WebxdcInstanceDeleted {
|
||||||
|
msg_id: msg_id.to_u32(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +364,8 @@ fn generate_events_ts_types_definition() {
|
|||||||
root_namespace: None,
|
root_namespace: None,
|
||||||
..typescript_type_def::DefinitionFileOptions::default()
|
..typescript_type_def::DefinitionFileOptions::default()
|
||||||
};
|
};
|
||||||
typescript_type_def::write_definition_file::<_, EventTypeName>(&mut buf, options).unwrap();
|
typescript_type_def::write_definition_file::<_, JSONRPCEventType>(&mut buf, options)
|
||||||
|
.unwrap();
|
||||||
String::from_utf8(buf).unwrap()
|
String::from_utf8(buf).unwrap()
|
||||||
};
|
};
|
||||||
std::fs::write("typescript/generated/events.ts", events).unwrap();
|
std::fs::write("typescript/generated/events.ts", events).unwrap();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DeltaChat, DeltaChatEvent } from "../deltachat.js";
|
import { DcEvent, DeltaChat } from "../deltachat.js";
|
||||||
|
|
||||||
var SELECTED_ACCOUNT = 0;
|
var SELECTED_ACCOUNT = 0;
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ window.addEventListener("DOMContentLoaded", (_event) => {
|
|||||||
SELECTED_ACCOUNT = Number(id);
|
SELECTED_ACCOUNT = Number(id);
|
||||||
window.dispatchEvent(new Event("account-changed"));
|
window.dispatchEvent(new Event("account-changed"));
|
||||||
};
|
};
|
||||||
console.log('launch run script...')
|
console.log("launch run script...");
|
||||||
run().catch((err) => console.error("run failed", err));
|
run().catch((err) => console.error("run failed", err));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -16,13 +16,13 @@ async function run() {
|
|||||||
const $side = document.getElementById("side")!;
|
const $side = document.getElementById("side")!;
|
||||||
const $head = document.getElementById("header")!;
|
const $head = document.getElementById("header")!;
|
||||||
|
|
||||||
const client = new DeltaChat('ws://localhost:20808/ws')
|
const client = new DeltaChat("ws://localhost:20808/ws");
|
||||||
|
|
||||||
;(window as any).client = client.rpc;
|
(window as any).client = client.rpc;
|
||||||
|
|
||||||
client.on("ALL", event => {
|
client.on("ALL", (accountId, event) => {
|
||||||
onIncomingEvent(event)
|
onIncomingEvent(accountId, event);
|
||||||
})
|
});
|
||||||
|
|
||||||
window.addEventListener("account-changed", async (_event: Event) => {
|
window.addEventListener("account-changed", async (_event: Event) => {
|
||||||
listChatsForSelectedAccount();
|
listChatsForSelectedAccount();
|
||||||
@@ -31,9 +31,9 @@ async function run() {
|
|||||||
await Promise.all([loadAccountsInHeader(), listChatsForSelectedAccount()]);
|
await Promise.all([loadAccountsInHeader(), listChatsForSelectedAccount()]);
|
||||||
|
|
||||||
async function loadAccountsInHeader() {
|
async function loadAccountsInHeader() {
|
||||||
console.log('load accounts')
|
console.log("load accounts");
|
||||||
const accounts = await client.rpc.getAllAccounts();
|
const accounts = await client.rpc.getAllAccounts();
|
||||||
console.log('accounts loaded', accounts)
|
console.log("accounts loaded", accounts);
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
if (account.type === "Configured") {
|
if (account.type === "Configured") {
|
||||||
write(
|
write(
|
||||||
@@ -48,14 +48,14 @@ async function run() {
|
|||||||
`<a href="#">
|
`<a href="#">
|
||||||
${account.id}: (unconfigured)
|
${account.id}: (unconfigured)
|
||||||
</a> `
|
</a> `
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listChatsForSelectedAccount() {
|
async function listChatsForSelectedAccount() {
|
||||||
clear($main);
|
clear($main);
|
||||||
const selectedAccount = SELECTED_ACCOUNT
|
const selectedAccount = SELECTED_ACCOUNT;
|
||||||
const info = await client.rpc.getAccountInfo(selectedAccount);
|
const info = await client.rpc.getAccountInfo(selectedAccount);
|
||||||
if (info.type !== "Configured") {
|
if (info.type !== "Configured") {
|
||||||
return write($main, "Account is not configured");
|
return write($main, "Account is not configured");
|
||||||
@@ -88,14 +88,15 @@ async function run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIncomingEvent(event: DeltaChatEvent) {
|
function onIncomingEvent(accountId: number, event: DcEvent) {
|
||||||
write(
|
write(
|
||||||
$side,
|
$side,
|
||||||
`
|
`
|
||||||
<p class="message">
|
<p class="message">
|
||||||
[<strong>${event.id}</strong> on account ${event.contextId}]<br>
|
[<strong>${event.type}</strong> on account ${accountId}]<br>
|
||||||
<em>f1:</em> ${JSON.stringify(event.field1)}<br>
|
<em>f1:</em> ${JSON.stringify(
|
||||||
<em>f2:</em> ${JSON.stringify(event.field2)}
|
Object.assign({}, event, { type: undefined })
|
||||||
|
)}
|
||||||
</p>`
|
</p>`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,202 @@
|
|||||||
// AUTO-GENERATED by typescript-type-def
|
// AUTO-GENERATED by typescript-type-def
|
||||||
|
|
||||||
export type EventTypeName=("Info"|"SmtpConnected"|"ImapConnected"|"SmtpMessageSent"|"ImapMessageDeleted"|"ImapMessageMoved"|"NewBlobFile"|"DeletedBlobFile"|"Warning"|"Error"|"ErrorSelfNotInGroup"|"MsgsChanged"|"IncomingMsg"|"MsgsNoticed"|"MsgDelivered"|"MsgFailed"|"MsgRead"|"ChatModified"|"ChatEphemeralTimerModified"|"ContactsChanged"|"LocationChanged"|"ConfigureProgress"|"ImexProgress"|"ImexFileWritten"|"SecurejoinInviterProgress"|"SecurejoinJoinerProgress"|"ConnectivityChanged"|"SelfavatarChanged"|"WebxdcStatusUpdate"|"WebXdInstanceDeleted");
|
export type U32=number;
|
||||||
|
export type Usize=number;
|
||||||
|
export type Event=(({
|
||||||
|
/**
|
||||||
|
* The library-user may write an informational string to the log.
|
||||||
|
*
|
||||||
|
* This event should *not* be reported to the end-user using a popup or something like
|
||||||
|
* that.
|
||||||
|
*/
|
||||||
|
"type":"Info";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when SMTP connection is established and login was successful.
|
||||||
|
*/
|
||||||
|
"type":"SmtpConnected";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when IMAP connection is established and login was successful.
|
||||||
|
*/
|
||||||
|
"type":"ImapConnected";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when a message was successfully sent to the SMTP server.
|
||||||
|
*/
|
||||||
|
"type":"SmtpMessageSent";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when an IMAP message has been marked as deleted
|
||||||
|
*/
|
||||||
|
"type":"ImapMessageDeleted";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when an IMAP message has been moved
|
||||||
|
*/
|
||||||
|
"type":"ImapMessageMoved";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when an new file in the $BLOBDIR was created
|
||||||
|
*/
|
||||||
|
"type":"NewBlobFile";}&{"file":string;})|({
|
||||||
|
/**
|
||||||
|
* Emitted when an file in the $BLOBDIR was deleted
|
||||||
|
*/
|
||||||
|
"type":"DeletedBlobFile";}&{"file":string;})|({
|
||||||
|
/**
|
||||||
|
* The library-user should write a warning string to the log.
|
||||||
|
*
|
||||||
|
* This event should *not* be reported to the end-user using a popup or something like
|
||||||
|
* that.
|
||||||
|
*/
|
||||||
|
"type":"Warning";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* The library-user should report an error to the end-user.
|
||||||
|
*
|
||||||
|
* As most things are asynchronous, things may go wrong at any time and the user
|
||||||
|
* should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||||
|
*
|
||||||
|
* However, for ongoing processes (eg. configure())
|
||||||
|
* or for functions that are expected to fail (eg. dc_continue_key_transfer())
|
||||||
|
* it might be better to delay showing these events until the function has really
|
||||||
|
* failed (returned false). It should be sufficient to report only the *last* error
|
||||||
|
* in a messasge box then.
|
||||||
|
*/
|
||||||
|
"type":"Error";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* An action cannot be performed because the user is not in the group.
|
||||||
|
* Reported eg. after a call to
|
||||||
|
* dc_set_chat_name(), dc_set_chat_profile_image(),
|
||||||
|
* dc_add_contact_to_chat(), dc_remove_contact_from_chat(),
|
||||||
|
* dc_send_text_msg() or another sending function.
|
||||||
|
*/
|
||||||
|
"type":"ErrorSelfNotInGroup";}&{"msg":string;})|({
|
||||||
|
/**
|
||||||
|
* Messages or chats changed. One or more messages or chats changed for various
|
||||||
|
* reasons in the database:
|
||||||
|
* - Messages sent, received or removed
|
||||||
|
* - Chats created, deleted or archived
|
||||||
|
* - A draft has been set
|
||||||
|
*
|
||||||
|
* `chat_id` is set if only a single chat is affected by the changes, otherwise 0.
|
||||||
|
* `msg_id` is set if only a single message is affected by the changes, otherwise 0.
|
||||||
|
*/
|
||||||
|
"type":"MsgsChanged";}&{"chatId":U32;"msgId":U32;})|({
|
||||||
|
/**
|
||||||
|
* There is a fresh message. Typically, the user will show an notification
|
||||||
|
* when receiving this message.
|
||||||
|
*
|
||||||
|
* There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event.
|
||||||
|
*/
|
||||||
|
"type":"IncomingMsg";}&{"chatId":U32;"msgId":U32;})|({
|
||||||
|
/**
|
||||||
|
* Messages were seen or noticed.
|
||||||
|
* chat id is always set.
|
||||||
|
*/
|
||||||
|
"type":"MsgsNoticed";}&{"chatId":U32;})|({
|
||||||
|
/**
|
||||||
|
* A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to
|
||||||
|
* DC_STATE_OUT_DELIVERED, see dc_msg_get_state().
|
||||||
|
*/
|
||||||
|
"type":"MsgDelivered";}&{"chatId":U32;"msgId":U32;})|({
|
||||||
|
/**
|
||||||
|
* A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to
|
||||||
|
* DC_STATE_OUT_FAILED, see dc_msg_get_state().
|
||||||
|
*/
|
||||||
|
"type":"MsgFailed";}&{"chatId":U32;"msgId":U32;})|({
|
||||||
|
/**
|
||||||
|
* A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to
|
||||||
|
* DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state().
|
||||||
|
*/
|
||||||
|
"type":"MsgRead";}&{"chatId":U32;"msgId":U32;})|({
|
||||||
|
/**
|
||||||
|
* Chat changed. The name or the image of a chat group was changed or members were added or removed.
|
||||||
|
* Or the verify state of a chat has changed.
|
||||||
|
* See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat()
|
||||||
|
* and dc_remove_contact_from_chat().
|
||||||
|
*
|
||||||
|
* This event does not include ephemeral timer modification, which
|
||||||
|
* is a separate event.
|
||||||
|
*/
|
||||||
|
"type":"ChatModified";}&{"chatId":U32;})|({
|
||||||
|
/**
|
||||||
|
* Chat ephemeral timer changed.
|
||||||
|
*/
|
||||||
|
"type":"ChatEphemeralTimerModified";}&{"chatId":U32;"timer":U32;})|({
|
||||||
|
/**
|
||||||
|
* Contact(s) created, renamed, blocked or deleted.
|
||||||
|
*
|
||||||
|
* @param data1 (int) If set, this is the contact_id of an added contact that should be selected.
|
||||||
|
*/
|
||||||
|
"type":"ContactsChanged";}&{"contactId":(U32|null);})|({
|
||||||
|
/**
|
||||||
|
* Location of one or more contact has changed.
|
||||||
|
*
|
||||||
|
* @param data1 (u32) contact_id of the contact for which the location has changed.
|
||||||
|
* If the locations of several contacts have been changed,
|
||||||
|
* eg. after calling dc_delete_all_locations(), this parameter is set to `None`.
|
||||||
|
*/
|
||||||
|
"type":"LocationChanged";}&{"contactId":(U32|null);})|({
|
||||||
|
/**
|
||||||
|
* Inform about the configuration progress started by configure().
|
||||||
|
*/
|
||||||
|
"type":"ConfigureProgress";}&{
|
||||||
|
/**
|
||||||
|
* Progress.
|
||||||
|
*
|
||||||
|
* 0=error, 1-999=progress in permille, 1000=success and done
|
||||||
|
*/
|
||||||
|
"progress":Usize;
|
||||||
|
/**
|
||||||
|
* Progress comment or error, something to display to the user.
|
||||||
|
*/
|
||||||
|
"comment":(string|null);})|({
|
||||||
|
/**
|
||||||
|
* Inform about the import/export progress started by imex().
|
||||||
|
*
|
||||||
|
* @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||||
|
* @param data2 0
|
||||||
|
*/
|
||||||
|
"type":"ImexProgress";}&{"progress":Usize;})|({
|
||||||
|
/**
|
||||||
|
* A file has been exported. A file has been written by imex().
|
||||||
|
* This event may be sent multiple times by a single call to imex().
|
||||||
|
*
|
||||||
|
* A typical purpose for a handler of this event may be to make the file public to some system
|
||||||
|
* services.
|
||||||
|
*
|
||||||
|
* @param data2 0
|
||||||
|
*/
|
||||||
|
"type":"ImexFileWritten";}&{"path":string;})|({
|
||||||
|
/**
|
||||||
|
* Progress information of a secure-join handshake from the view of the inviter
|
||||||
|
* (Alice, the person who shows the QR code).
|
||||||
|
*
|
||||||
|
* These events are typically sent after a joiner has scanned the QR code
|
||||||
|
* generated by dc_get_securejoin_qr().
|
||||||
|
*
|
||||||
|
* @param data1 (int) ID of the contact that wants to join.
|
||||||
|
* @param data2 (int) Progress as:
|
||||||
|
* 300=vg-/vc-request received, typically shown as "bob@addr joins".
|
||||||
|
* 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified".
|
||||||
|
* 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol.
|
||||||
|
* 1000=Protocol finished for this contact.
|
||||||
|
*/
|
||||||
|
"type":"SecurejoinInviterProgress";}&{"contactId":U32;"progress":Usize;})|({
|
||||||
|
/**
|
||||||
|
* Progress information of a secure-join handshake from the view of the joiner
|
||||||
|
* (Bob, the person who scans the QR code).
|
||||||
|
* The events are typically sent while dc_join_securejoin(), which
|
||||||
|
* may take some time, is executed.
|
||||||
|
* @param data1 (int) ID of the inviting contact.
|
||||||
|
* @param data2 (int) Progress as:
|
||||||
|
* 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself."
|
||||||
|
* (Bob has verified alice and waits until Alice does the same for him)
|
||||||
|
*/
|
||||||
|
"type":"SecurejoinJoinerProgress";}&{"contactId":U32;"progress":Usize;})|{
|
||||||
|
/**
|
||||||
|
* The connectivity to the server changed.
|
||||||
|
* This means that you should refresh the connectivity view
|
||||||
|
* and possibly the connectivtiy HTML; see dc_get_connectivity() and
|
||||||
|
* dc_get_connectivity_html() for details.
|
||||||
|
*/
|
||||||
|
"type":"ConnectivityChanged";}|{"type":"SelfavatarChanged";}|({"type":"WebxdcStatusUpdate";}&{"msgId":U32;"statusUpdateSerial":U32;})|({
|
||||||
|
/**
|
||||||
|
* Inform that a message containing a webxdc instance has been deleted
|
||||||
|
*/
|
||||||
|
"type":"WebxdcInstanceDeleted";}&{"msgId":U32;}));
|
||||||
|
|||||||
@@ -1,40 +1,56 @@
|
|||||||
import * as T from "../generated/types.js";
|
import * as T from "../generated/types.js";
|
||||||
import * as RPC from "../generated/jsonrpc.js";
|
import * as RPC from "../generated/jsonrpc.js";
|
||||||
import { RawClient } from "../generated/client.js";
|
import { RawClient } from "../generated/client.js";
|
||||||
import { EventTypeName } from "../generated/events.js";
|
import { Event } from "../generated/events.js";
|
||||||
import { WebsocketTransport, BaseTransport, Request } from "yerpc";
|
import { WebsocketTransport, BaseTransport, Request } from "yerpc";
|
||||||
import { TinyEmitter } from "tiny-emitter";
|
import { TinyEmitter } from "tiny-emitter";
|
||||||
|
|
||||||
export type DeltaChatEvent = {
|
type DCWireEvent<T extends Event> = {
|
||||||
id: EventTypeName;
|
event: T;
|
||||||
contextId: number;
|
contextId: number;
|
||||||
field1: any;
|
|
||||||
field2: any;
|
|
||||||
};
|
};
|
||||||
export type Events = Record<
|
// export type Events = Record<
|
||||||
EventTypeName | "ALL",
|
// Event["type"] | "ALL",
|
||||||
(event: DeltaChatEvent) => void
|
// (event: DeltaChatEvent<Event>) => void
|
||||||
>;
|
// >;
|
||||||
|
|
||||||
|
type Events = { ALL: (accountId: number, event: Event) => void } & {
|
||||||
|
[Property in Event["type"]]: (
|
||||||
|
accountId: number,
|
||||||
|
event: Extract<Event, { type: Property }>
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContextEvents = { ALL: (event: Event) => void } & {
|
||||||
|
[Property in Event["type"]]: (
|
||||||
|
event: Extract<Event, { type: Property }>
|
||||||
|
) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DcEvent = Event;
|
||||||
|
|
||||||
export class BaseDeltaChat<
|
export class BaseDeltaChat<
|
||||||
Transport extends BaseTransport<any>
|
Transport extends BaseTransport<any>
|
||||||
> extends TinyEmitter<Events> {
|
> extends TinyEmitter<Events> {
|
||||||
rpc: RawClient;
|
rpc: RawClient;
|
||||||
account?: T.Account;
|
account?: T.Account;
|
||||||
private contextEmitters: TinyEmitter<Events>[] = [];
|
private contextEmitters: TinyEmitter<ContextEvents>[] = [];
|
||||||
constructor(public transport: Transport) {
|
constructor(public transport: Transport) {
|
||||||
super();
|
super();
|
||||||
this.rpc = new RawClient(this.transport);
|
this.rpc = new RawClient(this.transport);
|
||||||
this.transport.on("request", (request: Request) => {
|
this.transport.on("request", (request: Request) => {
|
||||||
const method = request.method;
|
const method = request.method;
|
||||||
if (method === "event") {
|
if (method === "event") {
|
||||||
const event = request.params! as DeltaChatEvent;
|
const event = request.params! as DCWireEvent<Event>;
|
||||||
this.emit(event.id, event);
|
this.emit(event.event.type, event.contextId, event.event as any);
|
||||||
this.emit("ALL", event);
|
this.emit("ALL", event.contextId, event.event as any);
|
||||||
|
|
||||||
if (this.contextEmitters[event.contextId]) {
|
if (this.contextEmitters[event.contextId]) {
|
||||||
this.contextEmitters[event.contextId].emit(event.id, event);
|
this.contextEmitters[event.contextId].emit(
|
||||||
this.contextEmitters[event.contextId].emit("ALL", event);
|
event.event.type,
|
||||||
|
event.event as any
|
||||||
|
);
|
||||||
|
this.contextEmitters[event.contextId].emit("ALL", event.event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -70,7 +86,7 @@ export class DeltaChat extends BaseDeltaChat<WebsocketTransport> {
|
|||||||
if (typeof opts === "string") opts = { url: opts };
|
if (typeof opts === "string") opts = { url: opts };
|
||||||
if (opts) opts = { ...DEFAULT_OPTS, ...opts };
|
if (opts) opts = { ...DEFAULT_OPTS, ...opts };
|
||||||
else opts = { ...DEFAULT_OPTS };
|
else opts = { ...DEFAULT_OPTS };
|
||||||
const transport = new WebsocketTransport(opts.url)
|
const transport = new WebsocketTransport(opts.url);
|
||||||
super(transport);
|
super(transport);
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { assert, expect } from "chai";
|
import { assert, expect } from "chai";
|
||||||
import { DeltaChat, DeltaChatEvent, EventTypeName } from "../deltachat.js";
|
import { DeltaChat, DcEvent } from "../deltachat.js";
|
||||||
import {
|
import { RpcServerHandle, createTempUser, startServer } from "./test_base.js";
|
||||||
RpcServerHandle,
|
|
||||||
createTempUser,
|
|
||||||
startServer,
|
|
||||||
} from "./test_base.js";
|
|
||||||
|
|
||||||
const EVENT_TIMEOUT = 20000
|
const EVENT_TIMEOUT = 20000;
|
||||||
|
|
||||||
describe("online tests", function () {
|
describe("online tests", function () {
|
||||||
let serverHandle: RpcServerHandle;
|
let serverHandle: RpcServerHandle;
|
||||||
@@ -16,7 +12,7 @@ describe("online tests", function () {
|
|||||||
let accountId1: number, accountId2: number;
|
let accountId1: number, accountId2: number;
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(12000)
|
this.timeout(12000);
|
||||||
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
if (!process.env.DCC_NEW_TMP_EMAIL) {
|
||||||
if (process.env.COVERAGE && !process.env.COVERAGE_OFFLINE) {
|
if (process.env.COVERAGE && !process.env.COVERAGE_OFFLINE) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -31,10 +27,10 @@ describe("online tests", function () {
|
|||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
serverHandle = await startServer();
|
serverHandle = await startServer();
|
||||||
dc = new DeltaChat(serverHandle.url)
|
dc = new DeltaChat(serverHandle.url);
|
||||||
|
|
||||||
dc.on("ALL", ({ id, contextId }) => {
|
dc.on("ALL", (contextId, { type }) => {
|
||||||
if (id !== "Info") console.log(contextId, id);
|
if (type !== "Info") console.log(contextId, type);
|
||||||
});
|
});
|
||||||
|
|
||||||
account1 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
account1 = await createTempUser(process.env.DCC_NEW_TMP_EMAIL);
|
||||||
@@ -74,7 +70,7 @@ describe("online tests", function () {
|
|||||||
addr: account2.email,
|
addr: account2.email,
|
||||||
mail_pw: account2.password,
|
mail_pw: account2.password,
|
||||||
});
|
});
|
||||||
await dc.rpc.configure(accountId2)
|
await dc.rpc.configure(accountId2);
|
||||||
accountsConfigured = true;
|
accountsConfigured = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,14 +85,17 @@ describe("online tests", function () {
|
|||||||
account2.email,
|
account2.email,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const chatId = await dc.rpc.contactsCreateChatByContactId(accountId1, contactId);
|
const chatId = await dc.rpc.contactsCreateChatByContactId(
|
||||||
|
accountId1,
|
||||||
|
contactId
|
||||||
|
);
|
||||||
const eventPromise = Promise.race([
|
const eventPromise = Promise.race([
|
||||||
waitForEvent(dc, "MsgsChanged", accountId2),
|
waitForEvent(dc, "MsgsChanged", accountId2),
|
||||||
waitForEvent(dc, "IncomingMsg", accountId2),
|
waitForEvent(dc, "IncomingMsg", accountId2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await dc.rpc.miscSendTextMessage(accountId1, "Hello", chatId);
|
await dc.rpc.miscSendTextMessage(accountId1, "Hello", chatId);
|
||||||
const { field1: chatIdOnAccountB } = await eventPromise;
|
const { chatId: chatIdOnAccountB } = await eventPromise;
|
||||||
await dc.rpc.acceptChat(accountId2, chatIdOnAccountB);
|
await dc.rpc.acceptChat(accountId2, chatIdOnAccountB);
|
||||||
const messageList = await dc.rpc.getMessageIds(
|
const messageList = await dc.rpc.getMessageIds(
|
||||||
accountId2,
|
accountId2,
|
||||||
@@ -121,7 +120,10 @@ describe("online tests", function () {
|
|||||||
account2.email,
|
account2.email,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const chatId = await dc.rpc.contactsCreateChatByContactId(accountId1, contactId);
|
const chatId = await dc.rpc.contactsCreateChatByContactId(
|
||||||
|
accountId1,
|
||||||
|
contactId
|
||||||
|
);
|
||||||
const eventPromise = Promise.race([
|
const eventPromise = Promise.race([
|
||||||
waitForEvent(dc, "MsgsChanged", accountId2),
|
waitForEvent(dc, "MsgsChanged", accountId2),
|
||||||
waitForEvent(dc, "IncomingMsg", accountId2),
|
waitForEvent(dc, "IncomingMsg", accountId2),
|
||||||
@@ -131,7 +133,7 @@ describe("online tests", function () {
|
|||||||
console.log("wait for message from A");
|
console.log("wait for message from A");
|
||||||
|
|
||||||
const event = await eventPromise;
|
const event = await eventPromise;
|
||||||
const { field1: chatIdOnAccountB } = event;
|
const { chatId: chatIdOnAccountB } = event;
|
||||||
|
|
||||||
await dc.rpc.acceptChat(accountId2, chatIdOnAccountB);
|
await dc.rpc.acceptChat(accountId2, chatIdOnAccountB);
|
||||||
const messageList = await dc.rpc.getMessageIds(
|
const messageList = await dc.rpc.getMessageIds(
|
||||||
@@ -179,22 +181,22 @@ describe("online tests", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function waitForEvent(
|
async function waitForEvent<T extends DcEvent["type"]>(
|
||||||
dc: DeltaChat,
|
dc: DeltaChat,
|
||||||
eventType: EventTypeName,
|
eventType: T,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
timeout: number = EVENT_TIMEOUT
|
timeout: number = EVENT_TIMEOUT
|
||||||
): Promise<DeltaChatEvent> {
|
): Promise<Extract<DcEvent, { type: T }>> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const rejectTimeout = setTimeout(
|
const rejectTimeout = setTimeout(
|
||||||
() => reject(new Error('Timeout reached before event came in')),
|
() => reject(new Error("Timeout reached before event came in")),
|
||||||
timeout
|
timeout
|
||||||
)
|
);
|
||||||
const callback = (event: DeltaChatEvent) => {
|
const callback = (contextId: number, event: DcEvent) => {
|
||||||
if (event.contextId == accountId) {
|
if (contextId == accountId) {
|
||||||
dc.off(eventType, callback);
|
dc.off(eventType, callback);
|
||||||
clearTimeout(rejectTimeout)
|
clearTimeout(rejectTimeout);
|
||||||
resolve(event);
|
resolve(event as any);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
dc.on(eventType, callback);
|
dc.on(eventType, callback);
|
||||||
|
|||||||
Reference in New Issue
Block a user