deltachat/
update_helper.rs

1//! # Functions to update timestamps.
2
3use anyhow::Result;
4
5use crate::chat::ChatId;
6use crate::contact::ContactId;
7use crate::context::Context;
8use crate::param::{Param, Params};
9
10impl Context {
11    /// Updates a contact's timestamp, if reasonable.
12    /// Returns true if the caller shall update the settings belonging to the scope.
13    /// (if we have a ContactId type at some point, the function should go there)
14    pub(crate) async fn update_contacts_timestamp(
15        &self,
16        contact_id: ContactId,
17        scope: Param,
18        new_timestamp: i64,
19    ) -> Result<bool> {
20        self.sql
21            .transaction(|transaction| {
22                let mut param: Params = transaction.query_row(
23                    "SELECT param FROM contacts WHERE id=?",
24                    [contact_id],
25                    |row| {
26                        let param: String = row.get(0)?;
27                        Ok(param.parse().unwrap_or_default())
28                    },
29                )?;
30                let update = param.update_timestamp(scope, new_timestamp)?;
31                if update {
32                    transaction.execute(
33                        "UPDATE contacts SET param=? WHERE id=?",
34                        (param.to_string(), contact_id),
35                    )?;
36                }
37                Ok(update)
38            })
39            .await
40    }
41}
42
43impl ChatId {
44    /// Updates a chat id's timestamp on disk, if reasonable.
45    /// Returns true if the caller shall update the settings belonging to the scope.
46    pub(crate) async fn update_timestamp(
47        &self,
48        context: &Context,
49        scope: Param,
50        new_timestamp: i64,
51    ) -> Result<bool> {
52        context
53            .sql
54            .transaction(|transaction| {
55                let mut param: Params =
56                    transaction.query_row("SELECT param FROM chats WHERE id=?", [self], |row| {
57                        let param: String = row.get(0)?;
58                        Ok(param.parse().unwrap_or_default())
59                    })?;
60                let update = param.update_timestamp(scope, new_timestamp)?;
61                if update {
62                    transaction.execute(
63                        "UPDATE chats SET param=? WHERE id=?",
64                        (param.to_string(), self),
65                    )?;
66                }
67                Ok(update)
68            })
69            .await
70    }
71}
72
73impl Params {
74    /// Updates a param's timestamp in memory, if reasonable.
75    /// Returns true if the caller shall update the settings belonging to the scope.
76    pub(crate) fn update_timestamp(&mut self, scope: Param, new_timestamp: i64) -> Result<bool> {
77        let old_timestamp = self.get_i64(scope).unwrap_or_default();
78        if new_timestamp >= old_timestamp {
79            self.set_i64(scope, new_timestamp);
80            return Ok(true);
81        }
82        Ok(false)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::chat::Chat;
90    use crate::receive_imf::receive_imf;
91    use crate::test_utils::TestContext;
92    use crate::tools::time;
93
94    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
95    async fn test_params_update_timestamp() -> Result<()> {
96        let mut params = Params::new();
97        let ts = time();
98
99        assert!(params.update_timestamp(Param::LastSubject, ts)?);
100        assert!(params.update_timestamp(Param::LastSubject, ts)?); // same timestamp -> update
101        assert!(params.update_timestamp(Param::LastSubject, ts + 10)?);
102        assert!(!params.update_timestamp(Param::LastSubject, ts)?); // `ts` is now too old
103        assert!(!params.update_timestamp(Param::LastSubject, 0)?);
104        assert_eq!(params.get_i64(Param::LastSubject).unwrap(), ts + 10);
105
106        assert!(params.update_timestamp(Param::GroupNameTimestamp, 0)?); // stay unset -> update ...
107        assert!(params.update_timestamp(Param::GroupNameTimestamp, 0)?); // ... also on multiple calls
108        assert_eq!(params.get_i64(Param::GroupNameTimestamp).unwrap(), 0);
109
110        assert!(!params.update_timestamp(Param::AvatarTimestamp, -1)?);
111        assert_eq!(params.get_i64(Param::AvatarTimestamp), None);
112
113        Ok(())
114    }
115
116    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
117    async fn test_out_of_order_subject() -> Result<()> {
118        let t = TestContext::new_alice().await;
119
120        receive_imf(
121            &t,
122            b"From: Bob Authname <bob@example.org>\n\
123                 To: alice@example.org\n\
124                 Subject: updated subject\n\
125                 Message-ID: <msg2@example.org>\n\
126                 Chat-Version: 1.0\n\
127                 Date: Sun, 22 Mar 2021 23:37:57 +0000\n\
128                 \n\
129                 second message\n",
130            false,
131        )
132        .await?;
133        receive_imf(
134            &t,
135            b"From: Bob Authname <bob@example.org>\n\
136                 To: alice@example.org\n\
137                 Subject: original subject\n\
138                 Message-ID: <msg1@example.org>\n\
139                 Chat-Version: 1.0\n\
140                 Date: Sun, 22 Mar 2021 22:37:57 +0000\n\
141                 \n\
142                 first message\n",
143            false,
144        )
145        .await?;
146
147        let msg = t.get_last_msg().await;
148        let chat = Chat::load_from_db(&t, msg.chat_id).await?;
149        assert_eq!(
150            chat.param.get(Param::LastSubject).unwrap(),
151            "updated subject"
152        );
153
154        Ok(())
155    }
156}