From ceba687df3ec5a7730d1f2605f4c319741ce35c6 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Thu, 8 Jan 2026 17:06:03 +0100 Subject: [PATCH] feat: Config option to skip seen synchronization (#7694) At urgent request from @hpk42, this adds a config option `team_profile`. This option is only settable via SQLite (not exposed in the UI), and the only thing it does is disabling synchronization of seen status. I tested manually on my Android phone that it works. Not straigthforward to write an automatic test, because we want to test that something does _not_ happen (i.e. that the seen status is _not_ synchronized), and it's not clear how long to wait before we check. Probably it's fine to just not add a test. This is what I tried: ```python @pytest.mark.parametrize("team_profile", [True, False]) def test_markseen_basic(team_profile, acfactory): """ Test that seen status is synchronized iff `team_profile` isn't set. """ alice, bob = acfactory.get_online_accounts(2) # Bob sets up a second device. bob2 = bob.clone() bob2.start_io() alice_chat_bob = alice.create_chat(bob) bob.create_chat(alice) bob2.create_chat(alice) alice_chat_bob.send_text("Hello Bob!") message = bob.wait_for_incoming_msg() message2 = bob2.wait_for_incoming_msg() assert message2.get_snapshot().state == MessageState.IN_FRESH message.mark_seen() # PROBLEM: We're not waiting 'long enough', # so, the 'state == MessageState.IN_SEEN' assertion below fails bob.create_chat(bob).send_text("Self-sent message") self_sent = bob2.wait_for_msg(EventType.MSGS_CHANGED) assert self_sent.get_snapshot().text == "Self-sent message" if team_profile: assert message2.get_snapshot().state == MessageState.IN_FRESH else: assert message2.get_snapshot().state == MessageState.IN_SEEN ``` --- src/config.rs | 4 ++++ src/context.rs | 4 ++++ src/imap.rs | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/config.rs b/src/config.rs index e719ac389..2350f75f0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -452,6 +452,10 @@ pub enum Config { /// The options are from the `WhoCanCallMe` enum. #[strum(props(default = "1"))] WhoCanCallMe, + + /// Experimental option denoting that the current profile is shared between multiple team members. + /// For now, the only effect of this option is that seen flags are not synchronized. + TeamProfile, } impl Config { diff --git a/src/context.rs b/src/context.rs index 12c3f645d..d6f72f491 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1106,6 +1106,10 @@ impl Context { .await? .unwrap_or_default(), ); + res.insert( + "team_profile", + self.get_config_bool(Config::TeamProfile).await?.to_string(), + ); let elapsed = time_elapsed(&self.creation_time); res.insert("uptime", duration_to_str(elapsed)); diff --git a/src/imap.rs b/src/imap.rs index 0806c14a9..84c91ba63 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1112,6 +1112,11 @@ impl Session { /// Stores pending `\Seen` flags for messages in `imap_markseen` table. pub(crate) async fn store_seen_flags_on_imap(&mut self, context: &Context) -> Result<()> { + if context.get_config_bool(Config::TeamProfile).await? { + info!(context, "Team profile, skipping seen flag synchronization."); + return Ok(()); + } + let rows = context .sql .query_map_vec( @@ -1180,6 +1185,11 @@ impl Session { return Ok(()); } + if context.get_config_bool(Config::TeamProfile).await? { + info!(context, "Team profile, skipping seen flag synchronization."); + return Ok(()); + } + let create = false; let folder_exists = self .select_with_uidvalidity(context, folder, create)