mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
remove getAllUpdates() and add a typical replicatio API for the update call (#3081)
* (r10s, adb, hpk) remove getAllUpdates() and add a typical replica-API that works with increasing serials. Streamline docs a bit. * adapt ffi to new api * documentation: updates serials may have gaps * get_webxdc_status_updates() return updates larger than a given serial * remove status_update_id from status-update-event; it is not needed (ui should update from the last known serial) and easily gets confused with last_serial * unify wording to 'StatusUpdateSerial' * remove legacy payload format, all known webxdc should be adapted * add serial and max_serial to status updates * avoid races when getting max_serial by avoiding two SQL calls * update changelog Co-authored-by: B. Petersen <r10s@b44t.com>
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
### API changes
|
||||
- change semantics of `dc_get_webxdc_status_updates()` second parameter
|
||||
and remove update-id from `DC_EVENT_WEBXDC_STATUS_UPDATE` #3081
|
||||
|
||||
### Changes
|
||||
- add more SMTP logging #3093
|
||||
- place common headers like `From:` before the large `Autocrypt:` header #3079
|
||||
|
||||
@@ -1037,14 +1037,18 @@ int dc_send_webxdc_status_update (dc_context_t* context, uint32_t msg_id, const
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object
|
||||
* @param msg_id id of the message with the webxdc instance
|
||||
* @param status_update_id Can be used to filter out only a concrete status update.
|
||||
* When set to 0, all known status updates are returned.
|
||||
* @return JSON-array containing the requested updates,
|
||||
* each element was created by dc_send_webxdc_status_update()
|
||||
* on this or other devices.
|
||||
* If there are no updates, an empty JSON-array is returned.
|
||||
* @param serial The last known serial. Pass 0 if there are no known serials to receive all updates.
|
||||
* @return JSON-array containing the requested updates.
|
||||
* Each `update` comes with the following properties:
|
||||
* - `update.payload`: equals the payload given to dc_send_webxdc_status_update()
|
||||
* - `update.serial`: the serial number of this update. Serials are larger `0` and newer serials have higher numbers.
|
||||
* - `update.max_serial`: the maximum serial currently known.
|
||||
* If `max_serial` equals `serial` this update is the last update (until new network messages arrive).
|
||||
* - `update.info`: optional, short, informational message.
|
||||
* - `update.summary`: optional, short text, shown beside app icon.
|
||||
* If there are no updates, an empty JSON-array is returned.
|
||||
*/
|
||||
char* dc_get_webxdc_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t status_update_id);
|
||||
char* dc_get_webxdc_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t serial);
|
||||
|
||||
/**
|
||||
* Save a draft for a chat in the database.
|
||||
@@ -5603,15 +5607,12 @@ void dc_event_unref(dc_event_t* event);
|
||||
|
||||
/**
|
||||
* webxdc status update received.
|
||||
* To get the received status update, use dc_get_webxdc_status_updates().
|
||||
* To get the received status update, use dc_get_webxdc_status_updates() with
|
||||
* `serial` set to the last known update.
|
||||
* To send status updates, use dc_send_webxdc_status_update().
|
||||
*
|
||||
* Note, that you do not get events that arrive when the app is not running;
|
||||
* instead, you can use dc_get_webxdc_status_updates() to get all status updates
|
||||
* and catch up that way.
|
||||
*
|
||||
* @param data1 (int) msg_id
|
||||
* @param data2 (int) status_update_id
|
||||
* @param data2 (int) 0
|
||||
*/
|
||||
#define DC_EVENT_WEBXDC_STATUS_UPDATE 2120
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ use deltachat::ephemeral::Timer as EphemeralTimer;
|
||||
use deltachat::key::DcKey;
|
||||
use deltachat::message::MsgId;
|
||||
use deltachat::stock_str::StockMessage;
|
||||
use deltachat::webxdc::StatusUpdateId;
|
||||
use deltachat::webxdc::StatusUpdateSerial;
|
||||
use deltachat::*;
|
||||
use deltachat::{accounts::Accounts, log::LogExt};
|
||||
|
||||
@@ -501,7 +501,7 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
|
||||
EventType::ImexFileWritten(_) => 0,
|
||||
EventType::SecurejoinInviterProgress { contact_id, .. }
|
||||
| EventType::SecurejoinJoinerProgress { contact_id, .. } => *contact_id as libc::c_int,
|
||||
EventType::WebxdcStatusUpdate { msg_id, .. } => msg_id.to_u32() as libc::c_int,
|
||||
EventType::WebxdcStatusUpdate(msg_id) => msg_id.to_u32() as libc::c_int,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,6 +534,7 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
| EventType::MsgsNoticed(_)
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::WebxdcStatusUpdate(_)
|
||||
| EventType::ChatModified(_) => 0,
|
||||
EventType::MsgsChanged { msg_id, .. }
|
||||
| EventType::IncomingMsg { msg_id, .. }
|
||||
@@ -543,9 +544,6 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
|
||||
EventType::SecurejoinInviterProgress { progress, .. }
|
||||
| EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int,
|
||||
EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int,
|
||||
EventType::WebxdcStatusUpdate {
|
||||
status_update_id, ..
|
||||
} => status_update_id.to_u32() as libc::c_int,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +585,7 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
|
||||
| EventType::SecurejoinJoinerProgress { .. }
|
||||
| EventType::ConnectivityChanged
|
||||
| EventType::SelfavatarChanged
|
||||
| EventType::WebxdcStatusUpdate { .. }
|
||||
| EventType::WebxdcStatusUpdate(_)
|
||||
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
|
||||
EventType::ConfigureProgress { comment, .. } => {
|
||||
if let Some(comment) = comment {
|
||||
@@ -903,7 +901,7 @@ pub unsafe extern "C" fn dc_send_webxdc_status_update(
|
||||
pub unsafe extern "C" fn dc_get_webxdc_status_updates(
|
||||
context: *mut dc_context_t,
|
||||
msg_id: u32,
|
||||
status_update_id: u32,
|
||||
last_known_serial: u32,
|
||||
) -> *mut libc::c_char {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_get_webxdc_status_updates()");
|
||||
@@ -913,11 +911,7 @@ pub unsafe extern "C" fn dc_get_webxdc_status_updates(
|
||||
|
||||
block_on(ctx.get_webxdc_status_updates(
|
||||
MsgId::new(msg_id),
|
||||
if status_update_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(StatusUpdateId::new(status_update_id))
|
||||
},
|
||||
StatusUpdateSerial::new(last_known_serial),
|
||||
))
|
||||
.unwrap_or_else(|_| "".to_string())
|
||||
.strdup()
|
||||
|
||||
@@ -29,7 +29,7 @@ window.webxdc.sendUpdate(update, descr);
|
||||
Webxdc apps are usually shared in a chat and run independently on each peer.
|
||||
To get a shared state, the peers use `sendUpdate()` to send updates to each other.
|
||||
|
||||
- `update`: an object with the following fields:
|
||||
- `update`: an object with the following properties:
|
||||
- `update.payload`: any javascript primitive, array or object.
|
||||
- `update.info`: optional, short, informational message that will be added to the chat,
|
||||
eg. "Alice voted" or "Bob scored 123 in MyGame";
|
||||
@@ -45,48 +45,37 @@ All peers, including the sending one,
|
||||
will receive the update by the callback given to `setUpdateListener()`.
|
||||
|
||||
There are situations where the user cannot send messages to a chat,
|
||||
eg. contact requests or if the user has left a group.
|
||||
eg. if the webxdc instance comes as a contact request or if the user has left a group.
|
||||
In these cases, you can still call `sendUpdate()`,
|
||||
however, the update won't be sent to other peers
|
||||
and you won't get the update by `setUpdateListener()` nor by `getAllUpdates()`.
|
||||
and you won't get the update by `setUpdateListener()`.
|
||||
|
||||
|
||||
### setUpdateListener()
|
||||
|
||||
```js
|
||||
window.webxdc.setUpdateListener((update) => {});
|
||||
window.webxdc.setUpdateListener((update) => {}, serial);
|
||||
```
|
||||
|
||||
With `setUpdateListener()` you define a callback that receives the updates
|
||||
sent by `sendUpdate()`.
|
||||
sent by `sendUpdate()`. The callback is called for updates sent by you or other peers.
|
||||
The `serial` specifies the last serial that you know about (defaults to 0).
|
||||
|
||||
- `update`: passed to the callback on updates with the following fields:
|
||||
`update.payload`: equals the payload given to `sendUpdate()`
|
||||
Each `update` which is passed to the callback comes with the following properties:
|
||||
|
||||
The callback is called for updates sent by you or other peers.
|
||||
- `update.payload`: equals the payload given to `sendUpdate()`
|
||||
|
||||
- `update.serial`: the serial number of this update.
|
||||
Serials are larger `0` and newer serials have higher numbers.
|
||||
There may be gaps in the serials
|
||||
and it is not guaranteed that the next serial is exactly incremented by one.
|
||||
|
||||
### getAllUpdates()
|
||||
- `update.max_serial`: the maximum serial currently known.
|
||||
If `max_serial` equals `serial` this update is the last update (until new network messages arrive).
|
||||
|
||||
```js
|
||||
updates = await window.webxdc.getAllUpdates();
|
||||
```
|
||||
- `update.info`: optional, short, informational message (see `send_update`)
|
||||
|
||||
In case your Webxdc was just started,
|
||||
you may want to reconstruct the state from the last run -
|
||||
and also incorporate updates that may have arrived while the app was not running.
|
||||
|
||||
- `updates`: All previous updates in an array,
|
||||
eg. `[{payload: "foo"},{payload: "bar"}]`
|
||||
if `webxdc.sendUpdate({payload: "foo"}); webxdc.sendUpdate({payload: "bar"};` was called on the last run.
|
||||
|
||||
The updates are wrapped into a Promise that you can `await` for.
|
||||
If you are not in an async function and cannot use `await` therefore,
|
||||
you can get the updates with `then()`:
|
||||
|
||||
```js
|
||||
window.webxdc.getAllUpdates().then(updates => {});
|
||||
```
|
||||
- `update.summary`: optional, short text, shown beside app icon (see `send_update`)
|
||||
|
||||
|
||||
### selfAddr
|
||||
@@ -162,9 +151,7 @@ The following example shows an input field and every input is show on all peers
|
||||
document.getElementById('output').innerHTML += update.payload + "<br>";
|
||||
}
|
||||
|
||||
window.webxdc.setUpdateListener(receiveUpdate);
|
||||
window.webxdc.getAllUpdates().then(updates => updates.forEach(receiveUpdate));
|
||||
|
||||
window.webxdc.setUpdateListener(receiveUpdate, 0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -191,4 +178,4 @@ just clone and start adapting things to your need.
|
||||
you may want to transpile your code down to an older js version eg. with https://babeljs.io
|
||||
- there are tons of ideas for enhancements of the API and the file format,
|
||||
eg. in the future, we will may define icon- and manifest-files,
|
||||
allow to aggregate the state or add metadata.
|
||||
allow to aggregate the state or add metadata.
|
||||
|
||||
@@ -9,7 +9,6 @@ use strum::EnumProperty;
|
||||
use crate::chat::ChatId;
|
||||
use crate::ephemeral::Timer as EphemeralTimer;
|
||||
use crate::message::MsgId;
|
||||
use crate::webxdc::StatusUpdateId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Events {
|
||||
@@ -329,8 +328,5 @@ pub enum EventType {
|
||||
SelfavatarChanged,
|
||||
|
||||
#[strum(props(id = "2120"))]
|
||||
WebxdcStatusUpdate {
|
||||
msg_id: MsgId,
|
||||
status_update_id: StatusUpdateId,
|
||||
},
|
||||
WebxdcStatusUpdate(MsgId),
|
||||
}
|
||||
|
||||
294
src/webxdc.rs
294
src/webxdc.rs
@@ -10,6 +10,7 @@ use crate::param::Param;
|
||||
use crate::{chat, EventType};
|
||||
use anyhow::{bail, ensure, format_err, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use deltachat_derive::FromSql;
|
||||
use lettre_email::mime::{self};
|
||||
use lettre_email::PartBuilder;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -56,14 +57,26 @@ pub struct WebxdcInfo {
|
||||
|
||||
/// Status Update ID.
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Default,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
FromSql,
|
||||
FromPrimitive,
|
||||
)]
|
||||
pub struct StatusUpdateId(u32);
|
||||
pub struct StatusUpdateSerial(u32);
|
||||
|
||||
impl StatusUpdateId {
|
||||
impl StatusUpdateSerial {
|
||||
/// Create a new [MsgId].
|
||||
pub fn new(id: u32) -> StatusUpdateId {
|
||||
StatusUpdateId(id)
|
||||
pub fn new(id: u32) -> StatusUpdateSerial {
|
||||
StatusUpdateSerial(id)
|
||||
}
|
||||
|
||||
/// Gets StatusUpdateId as untyped integer.
|
||||
@@ -73,7 +86,7 @@ impl StatusUpdateId {
|
||||
}
|
||||
}
|
||||
|
||||
impl rusqlite::types::ToSql for StatusUpdateId {
|
||||
impl rusqlite::types::ToSql for StatusUpdateSerial {
|
||||
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
|
||||
let val = rusqlite::types::Value::Integer(i64::from(self.0));
|
||||
let out = rusqlite::types::ToSqlOutput::Owned(val);
|
||||
@@ -99,6 +112,16 @@ pub(crate) struct StatusUpdateItem {
|
||||
summary: Option<String>,
|
||||
}
|
||||
|
||||
/// Update items as passed to the UIs.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct StatusUpdateItemAndSerial {
|
||||
#[serde(flatten)]
|
||||
item: StatusUpdateItem,
|
||||
|
||||
serial: StatusUpdateSerial,
|
||||
max_serial: StatusUpdateSerial,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// check if a file is an acceptable webxdc for sending or receiving.
|
||||
pub(crate) async fn is_webxdc_file(&self, filename: &str, buf: &[u8]) -> Result<bool> {
|
||||
@@ -157,7 +180,7 @@ impl Context {
|
||||
instance: &mut Message,
|
||||
update_str: &str,
|
||||
timestamp: i64,
|
||||
) -> Result<StatusUpdateId> {
|
||||
) -> Result<StatusUpdateSerial> {
|
||||
let update_str = update_str.trim();
|
||||
if update_str.is_empty() {
|
||||
bail!("create_status_update_record: empty update.");
|
||||
@@ -176,13 +199,7 @@ impl Context {
|
||||
_ => item,
|
||||
}
|
||||
} else {
|
||||
// TODO: this fallback (legacy `PAYLOAD`) should be deleted soon, together with the test below
|
||||
let payload: Value = serde_json::from_str(update_str)?; // checks if input data are valid json
|
||||
StatusUpdateItem {
|
||||
payload,
|
||||
info: None,
|
||||
summary: None,
|
||||
}
|
||||
bail!("create_status_update_record: no valid update item.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -219,14 +236,10 @@ impl Context {
|
||||
paramsv![instance.id, serde_json::to_string(&status_update_item)?],
|
||||
)
|
||||
.await?;
|
||||
let status_update_id = StatusUpdateId(u32::try_from(rowid)?);
|
||||
|
||||
self.emit_event(EventType::WebxdcStatusUpdate {
|
||||
msg_id: instance.id,
|
||||
status_update_id,
|
||||
});
|
||||
self.emit_event(EventType::WebxdcStatusUpdate(instance.id));
|
||||
|
||||
Ok(status_update_id)
|
||||
Ok(StatusUpdateSerial(u32::try_from(rowid)?))
|
||||
}
|
||||
|
||||
/// Sends a status update for an webxdc instance.
|
||||
@@ -250,7 +263,7 @@ impl Context {
|
||||
let chat = Chat::load_from_db(self, instance.chat_id).await?;
|
||||
ensure!(chat.can_send(self).await?, "cannot send to {}", chat.id);
|
||||
|
||||
let status_update_id = self
|
||||
let status_update_serial = self
|
||||
.create_status_update_record(
|
||||
&mut instance,
|
||||
update_str,
|
||||
@@ -279,7 +292,7 @@ impl Context {
|
||||
Param::Arg,
|
||||
self.render_webxdc_status_update_object(
|
||||
instance_msg_id,
|
||||
Some(status_update_id),
|
||||
Some(status_update_serial),
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| format_err!("Status object expected."))?,
|
||||
@@ -338,24 +351,79 @@ impl Context {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns status updates as an JSON-array.
|
||||
/// Returns status updates as an JSON-array, ready to be consumed by a webxdc.
|
||||
///
|
||||
/// Example: `[{"payload":"any update data"},{"payload":"another update data"}]`
|
||||
/// The updates may be filtered by a given status_update_id;
|
||||
/// if no updates are available, an empty JSON-array is returned.
|
||||
/// Example: `[{"serial":1, "max_serial":3, "payload":"any update data"},
|
||||
/// {"serial":3, "max_serial":3, "payload":"another update data"}]`
|
||||
/// Updates with serials larger than `last_known_serial` are returned.
|
||||
/// If no last serial is known, set `last_known_serial` to 0.
|
||||
/// If no updates are available, an empty JSON-array is returned.
|
||||
pub async fn get_webxdc_status_updates(
|
||||
&self,
|
||||
instance_msg_id: MsgId,
|
||||
status_update_id: Option<StatusUpdateId>,
|
||||
last_known_serial: StatusUpdateSerial,
|
||||
) -> Result<String> {
|
||||
let json = self
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT update_item FROM msgs_status_updates WHERE msg_id=? AND (1=? OR id=?)",
|
||||
"SELECT update_item, id FROM msgs_status_updates WHERE msg_id=? AND id>? ORDER BY id",
|
||||
paramsv![instance_msg_id, last_known_serial],
|
||||
|row| {
|
||||
let update_item_str = row.get::<_, String>(0)?;
|
||||
let serial = row.get::<_, StatusUpdateSerial>(1)?;
|
||||
Ok((update_item_str, serial))
|
||||
},
|
||||
|rows| {
|
||||
let mut rows_copy : Vec<(String, StatusUpdateSerial)> = Vec::new(); // `rows_copy` needed as `rows` cannot be iterated twice.
|
||||
let mut max_serial = StatusUpdateSerial(0);
|
||||
for row in rows {
|
||||
let row = row?;
|
||||
if row.1 > max_serial {
|
||||
max_serial = row.1;
|
||||
}
|
||||
rows_copy.push(row);
|
||||
}
|
||||
|
||||
let mut json = String::default();
|
||||
for row in rows_copy {
|
||||
let (update_item_str, serial) = row;
|
||||
let update_item = StatusUpdateItemAndSerial
|
||||
{
|
||||
item: serde_json::from_str(&*update_item_str)?,
|
||||
serial,
|
||||
max_serial,
|
||||
};
|
||||
|
||||
if !json.is_empty() {
|
||||
json.push_str(",\n");
|
||||
}
|
||||
json.push_str(&*serde_json::to_string(&update_item)?);
|
||||
}
|
||||
Ok(json)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(format!("[{}]", json))
|
||||
}
|
||||
|
||||
/// Renders JSON-object for status updates as used on the wire.
|
||||
///
|
||||
/// Example: `{"updates": [{"payload":"any update data"},
|
||||
/// {"payload":"another update data"}]}`
|
||||
/// If `status_update_serial` is set, exactly that update is rendered, otherwise all updates are rendered.
|
||||
pub(crate) async fn render_webxdc_status_update_object(
|
||||
&self,
|
||||
instance_msg_id: MsgId,
|
||||
status_update_serial: Option<StatusUpdateSerial>,
|
||||
) -> Result<Option<String>> {
|
||||
let json = self
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT update_item FROM msgs_status_updates WHERE msg_id=? AND (1=? OR id=?) ORDER BY id",
|
||||
paramsv![
|
||||
instance_msg_id,
|
||||
if status_update_id.is_some() { 0 } else { 1 },
|
||||
status_update_id.unwrap_or(StatusUpdateId(0))
|
||||
if status_update_serial.is_some() { 0 } else { 1 },
|
||||
status_update_serial.unwrap_or(StatusUpdateSerial(0))
|
||||
],
|
||||
|row| row.get::<_, String>(0),
|
||||
|rows| {
|
||||
@@ -371,22 +439,10 @@ impl Context {
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(format!("[{}]", json))
|
||||
}
|
||||
|
||||
/// Render JSON-object for status updates as used on the wire.
|
||||
pub(crate) async fn render_webxdc_status_update_object(
|
||||
&self,
|
||||
instance_msg_id: MsgId,
|
||||
status_update_id: Option<StatusUpdateId>,
|
||||
) -> Result<Option<String>> {
|
||||
let updates_array = self
|
||||
.get_webxdc_status_updates(instance_msg_id, status_update_id)
|
||||
.await?;
|
||||
if updates_array == "[]" {
|
||||
if json.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(format!(r#"{{"updates":{}}}"#, updates_array)))
|
||||
Ok(Some(format!(r#"{{"updates":[{}]}}"#, json)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -635,8 +691,9 @@ mod tests {
|
||||
.await?;
|
||||
assert!(!instance.is_forwarded());
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":42,"info":"foo","summary":"bar"}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":42,"info":"foo","summary":"bar","serial":1,"max_serial":1}]"#
|
||||
);
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 2); // instance and info
|
||||
let info = Message::load_from_db(&t, instance.id)
|
||||
@@ -649,7 +706,11 @@ mod tests {
|
||||
forward_msgs(&t, &[instance.get_id()], chat_id).await?;
|
||||
let instance2 = t.get_last_msg_in(chat_id).await;
|
||||
assert!(instance2.is_forwarded());
|
||||
assert_eq!(t.get_webxdc_status_updates(instance2.id, None).await?, "[]");
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance2.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
"[]"
|
||||
);
|
||||
assert_eq!(chat_id.get_msg_cnt(&t).await?, 3); // two instances, only one info
|
||||
let info = Message::load_from_db(&t, instance2.id)
|
||||
.await?
|
||||
@@ -712,7 +773,8 @@ mod tests {
|
||||
.await
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, None).await?,
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
"[]"
|
||||
);
|
||||
|
||||
@@ -723,8 +785,9 @@ mod tests {
|
||||
.await
|
||||
.is_ok());
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, None).await?,
|
||||
r#"[{"payload":42}]"#
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":42,"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -746,14 +809,16 @@ mod tests {
|
||||
t.send_webxdc_status_update(instance.id, r#"{"payload": 42}"#, "descr")
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":42}]"#.to_string()
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":42,"serial":1,"max_serial":1}]"#.to_string()
|
||||
);
|
||||
|
||||
// set_draft(None) deletes the message without the need to simulate network
|
||||
chat_id.set_draft(&t, None).await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
"[]".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -772,9 +837,13 @@ mod tests {
|
||||
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||
let mut instance = send_webxdc_instance(&t, chat_id).await?;
|
||||
|
||||
assert_eq!(t.get_webxdc_status_updates(instance.id, None).await?, "[]");
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
"[]"
|
||||
);
|
||||
|
||||
let id = t
|
||||
let update_id1 = t
|
||||
.create_status_update_record(
|
||||
&mut instance,
|
||||
"\n\n{\"payload\": {\"foo\":\"bar\"}}\n",
|
||||
@@ -782,8 +851,9 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, Some(id)).await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
assert!(t
|
||||
@@ -795,15 +865,12 @@ mod tests {
|
||||
.await
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, Some(id)).await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
);
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
let id = t
|
||||
let update_id2 = t
|
||||
.create_status_update_record(
|
||||
&mut instance,
|
||||
r#"{"payload" : { "foo2":"bar2"}}"#,
|
||||
@@ -811,19 +878,20 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, Some(id)).await?,
|
||||
r#"[{"payload":{"foo2":"bar2"}}]"#
|
||||
t.get_webxdc_status_updates(instance.id, update_id1).await?,
|
||||
r#"[{"payload":{"foo2":"bar2"},"serial":2,"max_serial":2}]"#
|
||||
);
|
||||
t.create_status_update_record(&mut instance, r#"{"payload":true}"#, 1640178619)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}},
|
||||
{"payload":{"foo2":"bar2"}},
|
||||
{"payload":true}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":3},
|
||||
{"payload":{"foo2":"bar2"},"serial":2,"max_serial":3},
|
||||
{"payload":true,"serial":3,"max_serial":3}]"#
|
||||
);
|
||||
|
||||
let id = t
|
||||
let _update_id3 = t
|
||||
.create_status_update_record(
|
||||
&mut instance,
|
||||
r#"{"payload" : 1, "sender": "that is not used"}"#,
|
||||
@@ -831,17 +899,9 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, Some(id)).await?,
|
||||
r#"[{"payload":1}]"#
|
||||
);
|
||||
|
||||
// TODO: legacy `PAYLOAD` support should be deleted soon
|
||||
let id = t
|
||||
.create_status_update_record(&mut instance, r#"{"foo" : 1}"#, 1640178619)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, Some(id)).await?,
|
||||
r#"[{"payload":{"foo":1}}]"#
|
||||
t.get_webxdc_status_updates(instance.id, update_id2).await?,
|
||||
r#"[{"payload":true,"serial":3,"max_serial":4},
|
||||
{"payload":1,"serial":4,"max_serial":4}]"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -873,8 +933,9 @@ mod tests {
|
||||
t.receive_status_update(instance.id, r#"{"updates":[{"payload":{"foo":"bar"}}]}"#)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
t.receive_status_update(
|
||||
@@ -883,10 +944,11 @@ mod tests {
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}},
|
||||
{"payload":42},
|
||||
{"payload":23}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":3},
|
||||
{"payload":42,"serial":2,"max_serial":3},
|
||||
{"payload":23,"serial":3,"max_serial":3}]"#
|
||||
);
|
||||
|
||||
t.receive_status_update(
|
||||
@@ -895,11 +957,12 @@ mod tests {
|
||||
)
|
||||
.await?; // ignore members that may be added in the future
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}},
|
||||
{"payload":42},
|
||||
{"payload":23},
|
||||
{"payload":"ok"}]"#
|
||||
t.get_webxdc_status_updates(instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":4},
|
||||
{"payload":42,"serial":2,"max_serial":4},
|
||||
{"payload":23,"serial":3,"max_serial":4},
|
||||
{"payload":"ok","serial":4,"max_serial":4}]"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -911,15 +974,7 @@ mod tests {
|
||||
.get_matching(|evt| matches!(evt, EventType::WebxdcStatusUpdate { .. }))
|
||||
.await;
|
||||
match event {
|
||||
EventType::WebxdcStatusUpdate {
|
||||
msg_id,
|
||||
status_update_id,
|
||||
} => {
|
||||
assert_eq!(
|
||||
t.get_webxdc_status_updates(msg_id, Some(status_update_id))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
);
|
||||
EventType::WebxdcStatusUpdate(msg_id) => {
|
||||
assert_eq!(msg_id, instance_id);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
@@ -964,9 +1019,9 @@ mod tests {
|
||||
assert!(sent2.payload().contains("descr text"));
|
||||
assert_eq!(
|
||||
alice
|
||||
.get_webxdc_status_updates(alice_instance.id, None)
|
||||
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
alice
|
||||
@@ -979,10 +1034,10 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
alice
|
||||
.get_webxdc_status_updates(alice_instance.id, None)
|
||||
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"}},
|
||||
{"payload":{"snipp":"snapp"}}]"#
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":2},
|
||||
{"payload":{"snipp":"snapp"},"serial":2,"max_serial":2}]"#
|
||||
);
|
||||
|
||||
// Bob receives all messages
|
||||
@@ -998,8 +1053,9 @@ mod tests {
|
||||
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
|
||||
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}}]"#
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
// Alice has a second device and also receives messages there
|
||||
@@ -1091,9 +1147,10 @@ mod tests {
|
||||
assert!(sent1.payload().contains("status-update.json"));
|
||||
assert!(sent1.payload().contains(r#""payload":{"foo":"bar"}"#));
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, None).await?,
|
||||
r#"[{"payload":{"foo":"bar"}},
|
||||
{"payload":42}]"# // 'info: "i"' ignored as sent in draft mode
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":{"foo":"bar"},"serial":1,"max_serial":2},
|
||||
{"payload":42,"serial":2,"max_serial":2}]"# // 'info: "i"' ignored as sent in draft mode
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@@ -1411,9 +1468,9 @@ sth_for_the = "future""#
|
||||
assert!(info_msg.quoted_message(&alice).await?.is_none());
|
||||
assert_eq!(
|
||||
alice
|
||||
.get_webxdc_status_updates(alice_instance.id, None)
|
||||
.get_webxdc_status_updates(alice_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat"}]"#
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat","serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
// Bob receives all messages
|
||||
@@ -1431,8 +1488,9 @@ sth_for_the = "future""#
|
||||
assert_eq!(info_msg.parent(&bob).await?.unwrap().id, bob_instance.id);
|
||||
assert!(info_msg.quoted_message(&bob).await?.is_none());
|
||||
assert_eq!(
|
||||
bob.get_webxdc_status_updates(bob_instance.id, None).await?,
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat"}]"#
|
||||
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat","serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
// Alice has a second device and also receives the info message there
|
||||
@@ -1455,9 +1513,9 @@ sth_for_the = "future""#
|
||||
assert!(info_msg.quoted_message(&alice2).await?.is_none());
|
||||
assert_eq!(
|
||||
alice2
|
||||
.get_webxdc_status_updates(alice2_instance.id, None)
|
||||
.get_webxdc_status_updates(alice2_instance.id, StatusUpdateSerial(0))
|
||||
.await?,
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat"}]"#
|
||||
r#"[{"payload":"sth. else","info":"this appears in-chat","serial":1,"max_serial":1}]"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user