mirror of
https://github.com/chatmail/core.git
synced 2026-04-26 01:46:34 +03:00
@@ -1577,6 +1577,22 @@ int dc_set_chat_name (dc_context_t* context, uint32_t ch
|
|||||||
int dc_set_chat_profile_image (dc_context_t* context, uint32_t chat_id, const char* image);
|
int dc_set_chat_profile_image (dc_context_t* context, uint32_t chat_id, const char* image);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set mute duration of a chat.
|
||||||
|
*
|
||||||
|
* This value can be checked by the ui upon receiving a new message to decide whether it should trigger an notification.
|
||||||
|
*
|
||||||
|
* Sends out #DC_EVENT_CHAT_MODIFIED.
|
||||||
|
*
|
||||||
|
* @memberof dc_context_t
|
||||||
|
* @param chat_id The chat ID to set the mute duration.
|
||||||
|
* @param duration The duration (0 for no mute, -1 for forever mute, everything else is is the relative mute duration from now in seconds)
|
||||||
|
* @param context The context as created by dc_context_new().
|
||||||
|
* @return 1=success, 0=error
|
||||||
|
*/
|
||||||
|
int dc_set_chat_mute_duration (dc_context_t* context, uint32_t chat_id, int64_t duration);
|
||||||
|
|
||||||
// handle messages
|
// handle messages
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2919,6 +2935,26 @@ int dc_chat_is_verified (const dc_chat_t* chat);
|
|||||||
int dc_chat_is_sending_locations (const dc_chat_t* chat);
|
int dc_chat_is_sending_locations (const dc_chat_t* chat);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the chat is currently muted
|
||||||
|
*
|
||||||
|
* @memberof dc_chat_t
|
||||||
|
* @param chat The chat object.
|
||||||
|
* @return 1=muted, 0=not muted
|
||||||
|
*/
|
||||||
|
int dc_chat_is_muted (const dc_chat_t* chat);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the exact state of the mute of a chat
|
||||||
|
*
|
||||||
|
* @memberof dc_chat_t
|
||||||
|
* @param chat The chat object.
|
||||||
|
* @return 0=not muted, -1=forever muted, (x>0)=remaining seconds until the mute is lifted
|
||||||
|
*/
|
||||||
|
int64_t dc_chat_get_remaining_mute_duration (const dc_chat_t* chat);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class dc_msg_t
|
* @class dc_msg_t
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ use std::fmt::Write;
|
|||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use libc::uintptr_t;
|
use libc::uintptr_t;
|
||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
|
|
||||||
use deltachat::chat::ChatId;
|
use deltachat::chat::ChatId;
|
||||||
|
use deltachat::chat::MuteDuration;
|
||||||
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
use deltachat::constants::DC_MSG_ID_LAST_SPECIAL;
|
||||||
use deltachat::contact::Contact;
|
use deltachat::contact::Contact;
|
||||||
use deltachat::context::Context;
|
use deltachat::context::Context;
|
||||||
@@ -1407,6 +1409,37 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_set_chat_mute_duration(
|
||||||
|
context: *mut dc_context_t,
|
||||||
|
chat_id: u32,
|
||||||
|
duration: i64,
|
||||||
|
) -> libc::c_int {
|
||||||
|
if context.is_null() {
|
||||||
|
eprintln!("ignoring careless call to dc_set_chat_mute_duration()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ffi_context = &*context;
|
||||||
|
let muteDuration = match duration {
|
||||||
|
0 => MuteDuration::NotMuted,
|
||||||
|
-1 => MuteDuration::Forever,
|
||||||
|
n if n > 0 => MuteDuration::Until(SystemTime::now() + Duration::from_secs(duration as u64)),
|
||||||
|
_ => {
|
||||||
|
ffi_context.warning(
|
||||||
|
"dc_chat_set_mute_duration(): Can not use negative duration other than -1",
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ffi_context
|
||||||
|
.with_inner(|ctx| {
|
||||||
|
chat::set_muted(ctx, ChatId::new(chat_id), muteDuration)
|
||||||
|
.map(|_| 1)
|
||||||
|
.unwrap_or_log_default(ctx, "Failed to set mute duration")
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_get_msg_info(
|
pub unsafe extern "C" fn dc_get_msg_info(
|
||||||
context: *mut dc_context_t,
|
context: *mut dc_context_t,
|
||||||
@@ -2481,6 +2514,37 @@ pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> l
|
|||||||
ffi_chat.chat.is_sending_locations() as libc::c_int
|
ffi_chat.chat.is_sending_locations() as libc::c_int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_chat_is_muted(chat: *mut dc_chat_t) -> libc::c_int {
|
||||||
|
if chat.is_null() {
|
||||||
|
eprintln!("ignoring careless call to dc_chat_is_muted()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ffi_chat = &*chat;
|
||||||
|
ffi_chat.chat.is_muted() as libc::c_int
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn dc_chat_get_remaining_mute_duration(chat: *mut dc_chat_t) -> i64 {
|
||||||
|
if chat.is_null() {
|
||||||
|
eprintln!("ignoring careless call to dc_chat_get_remaining_mute_duration()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let ffi_chat = &*chat;
|
||||||
|
if !ffi_chat.chat.is_muted() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// If the chat was muted to before the epoch, it is not muted.
|
||||||
|
match ffi_chat.chat.mute_duration {
|
||||||
|
MuteDuration::NotMuted => 0,
|
||||||
|
MuteDuration::Forever => -1,
|
||||||
|
MuteDuration::Until(when) => when
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.map(|d| d.as_secs() as i64)
|
||||||
|
.unwrap_or(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dc_chat_get_info_json(
|
pub unsafe extern "C" fn dc_chat_get_info_json(
|
||||||
context: *mut dc_context_t,
|
context: *mut dc_context_t,
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ class Chat(object):
|
|||||||
"""
|
"""
|
||||||
return self.id == const.DC_CHAT_ID_DEADDROP
|
return self.id == const.DC_CHAT_ID_DEADDROP
|
||||||
|
|
||||||
|
def is_muted(self):
|
||||||
|
""" return true if this chat is muted.
|
||||||
|
|
||||||
|
:returns: True if chat is muted, False otherwise.
|
||||||
|
"""
|
||||||
|
return lib.dc_chat_is_muted(self._dc_chat)
|
||||||
|
|
||||||
def is_promoted(self):
|
def is_promoted(self):
|
||||||
""" return True if this chat is promoted, i.e.
|
""" return True if this chat is promoted, i.e.
|
||||||
the member contacts are aware of their membership,
|
the member contacts are aware of their membership,
|
||||||
@@ -84,12 +91,43 @@ class Chat(object):
|
|||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
""" set name of this chat.
|
""" set name of this chat.
|
||||||
|
|
||||||
:param: name as a unicode string.
|
:param name: as a unicode string.
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
name = as_dc_charpointer(name)
|
name = as_dc_charpointer(name)
|
||||||
return lib.dc_set_chat_name(self._dc_context, self.id, name)
|
return lib.dc_set_chat_name(self._dc_context, self.id, name)
|
||||||
|
|
||||||
|
def mute(self, duration=None):
|
||||||
|
""" mutes the chat
|
||||||
|
|
||||||
|
:param duration: Number of seconds to mute the chat for. None to mute until unmuted again.
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
if duration is None:
|
||||||
|
mute_duration = -1
|
||||||
|
else:
|
||||||
|
mute_duration = duration
|
||||||
|
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, mute_duration)
|
||||||
|
if not bool(ret):
|
||||||
|
raise ValueError("Call to dc_set_chat_mute_duration failed")
|
||||||
|
|
||||||
|
def unmute(self):
|
||||||
|
""" unmutes the chat
|
||||||
|
|
||||||
|
:returns: None
|
||||||
|
"""
|
||||||
|
ret = lib.dc_set_chat_mute_duration(self._dc_context, self.id, 0)
|
||||||
|
if not bool(ret):
|
||||||
|
raise ValueError("Failed to unmute chat")
|
||||||
|
|
||||||
|
def get_mute_duration(self):
|
||||||
|
""" Returns the number of seconds until the mute of this chat is lifted.
|
||||||
|
|
||||||
|
:param duration:
|
||||||
|
:returns: Returns the number of seconds the chat is still muted for. (0 for not muted, -1 forever muted)
|
||||||
|
"""
|
||||||
|
return bool(lib.dc_chat_get_remaining_mute_duration(self.id))
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
""" return type of this chat.
|
""" return type of this chat.
|
||||||
|
|
||||||
|
|||||||
@@ -219,6 +219,18 @@ class TestOfflineChat:
|
|||||||
chat.remove_profile_image()
|
chat.remove_profile_image()
|
||||||
assert chat.get_profile_image() is None
|
assert chat.get_profile_image() is None
|
||||||
|
|
||||||
|
def test_mute(self, ac1):
|
||||||
|
chat = ac1.create_group_chat(name="title1")
|
||||||
|
assert not chat.is_muted()
|
||||||
|
chat.mute()
|
||||||
|
assert chat.is_muted()
|
||||||
|
chat.unmute()
|
||||||
|
assert not chat.is_muted()
|
||||||
|
chat.mute(50)
|
||||||
|
assert chat.is_muted()
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
chat.mute(-51)
|
||||||
|
|
||||||
def test_delete_and_send_fails(self, ac1, chat1):
|
def test_delete_and_send_fails(self, ac1, chat1):
|
||||||
chat1.delete()
|
chat1.delete()
|
||||||
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
|
|||||||
132
src/chat.rs
132
src/chat.rs
@@ -1,6 +1,8 @@
|
|||||||
//! # Chat module
|
//! # Chat module
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
@@ -422,6 +424,7 @@ pub struct Chat {
|
|||||||
blocked: Blocked,
|
blocked: Blocked,
|
||||||
pub param: Params,
|
pub param: Params,
|
||||||
is_sending_locations: bool,
|
is_sending_locations: bool,
|
||||||
|
pub mute_duration: MuteDuration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Chat {
|
impl Chat {
|
||||||
@@ -429,7 +432,7 @@ impl Chat {
|
|||||||
pub fn load_from_db(context: &Context, chat_id: ChatId) -> Result<Self, Error> {
|
pub fn load_from_db(context: &Context, chat_id: ChatId) -> Result<Self, Error> {
|
||||||
let res = context.sql.query_row(
|
let res = context.sql.query_row(
|
||||||
"SELECT c.type, c.name, c.grpid, c.param, c.archived,
|
"SELECT c.type, c.name, c.grpid, c.param, c.archived,
|
||||||
c.blocked, c.locations_send_until
|
c.blocked, c.locations_send_until, c.muted_until
|
||||||
FROM chats c
|
FROM chats c
|
||||||
WHERE c.id=?;",
|
WHERE c.id=?;",
|
||||||
params![chat_id],
|
params![chat_id],
|
||||||
@@ -443,6 +446,7 @@ impl Chat {
|
|||||||
archived: row.get(4)?,
|
archived: row.get(4)?,
|
||||||
blocked: row.get::<_, Option<_>>(5)?.unwrap_or_default(),
|
blocked: row.get::<_, Option<_>>(5)?.unwrap_or_default(),
|
||||||
is_sending_locations: row.get(6)?,
|
is_sending_locations: row.get(6)?,
|
||||||
|
mute_duration: row.get(7)?,
|
||||||
};
|
};
|
||||||
Ok(c)
|
Ok(c)
|
||||||
},
|
},
|
||||||
@@ -658,6 +662,7 @@ impl Chat {
|
|||||||
profile_image: self.get_profile_image(context).unwrap_or_else(PathBuf::new),
|
profile_image: self.get_profile_image(context).unwrap_or_else(PathBuf::new),
|
||||||
subtitle: self.get_subtitle(context),
|
subtitle: self.get_subtitle(context),
|
||||||
draft,
|
draft,
|
||||||
|
is_muted: self.is_muted(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,6 +689,14 @@ impl Chat {
|
|||||||
self.is_sending_locations
|
self.is_sending_locations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_muted(&self) -> bool {
|
||||||
|
match self.mute_duration {
|
||||||
|
MuteDuration::NotMuted => false,
|
||||||
|
MuteDuration::Forever => true,
|
||||||
|
MuteDuration::Until(when) => when > SystemTime::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_msg_raw(
|
fn prepare_msg_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
@@ -968,6 +981,11 @@ pub struct ChatInfo {
|
|||||||
/// which contain non-text parts. Perhaps it should be a
|
/// which contain non-text parts. Perhaps it should be a
|
||||||
/// simple `has_draft` bool instead.
|
/// simple `has_draft` bool instead.
|
||||||
pub draft: String,
|
pub draft: String,
|
||||||
|
|
||||||
|
/// Whether the chat is muted
|
||||||
|
///
|
||||||
|
/// The exact time its muted can be found out via the `chat.mute_duration` property
|
||||||
|
pub is_muted: bool,
|
||||||
// ToDo:
|
// ToDo:
|
||||||
// - [ ] deaddrop,
|
// - [ ] deaddrop,
|
||||||
// - [ ] summary,
|
// - [ ] summary,
|
||||||
@@ -1901,6 +1919,66 @@ pub fn shall_attach_selfavatar(context: &Context, chat_id: ChatId) -> Result<boo
|
|||||||
Ok(needs_attach)
|
Ok(needs_attach)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum MuteDuration {
|
||||||
|
NotMuted,
|
||||||
|
Forever,
|
||||||
|
Until(SystemTime),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl rusqlite::types::ToSql for MuteDuration {
|
||||||
|
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
|
||||||
|
let duration: i64 = match &self {
|
||||||
|
MuteDuration::NotMuted => 0,
|
||||||
|
MuteDuration::Forever => -1,
|
||||||
|
MuteDuration::Until(when) => {
|
||||||
|
let duration = when
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?;
|
||||||
|
i64::try_from(duration.as_secs())
|
||||||
|
.map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let val = rusqlite::types::Value::Integer(duration);
|
||||||
|
let out = rusqlite::types::ToSqlOutput::Owned(val);
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl rusqlite::types::FromSql for MuteDuration {
|
||||||
|
fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||||
|
// Negative values other than -1 should not be in the
|
||||||
|
// database. If found they'll be NotMuted.
|
||||||
|
match i64::column_result(value)? {
|
||||||
|
0 => Ok(MuteDuration::NotMuted),
|
||||||
|
-1 => Ok(MuteDuration::Forever),
|
||||||
|
n if n > 0 => match SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(n as u64)) {
|
||||||
|
Some(t) => Ok(MuteDuration::Until(t)),
|
||||||
|
None => Err(rusqlite::types::FromSqlError::OutOfRange(n)),
|
||||||
|
},
|
||||||
|
_ => Ok(MuteDuration::NotMuted),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_muted(context: &Context, chat_id: ChatId, duration: MuteDuration) -> Result<(), Error> {
|
||||||
|
ensure!(!chat_id.is_special(), "Invalid chat ID");
|
||||||
|
if real_group_exists(context, chat_id)
|
||||||
|
&& sql::execute(
|
||||||
|
context,
|
||||||
|
&context.sql,
|
||||||
|
"UPDATE chats SET muted_until=? WHERE id=?;",
|
||||||
|
params![duration, chat_id],
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
context.call_cb(Event::ChatModified(chat_id));
|
||||||
|
} else {
|
||||||
|
bail!("Failed to set name");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remove_contact_from_chat(
|
pub fn remove_contact_from_chat(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
@@ -2398,7 +2476,7 @@ mod tests {
|
|||||||
let chat = Chat::load_from_db(&t.ctx, chat_id).unwrap();
|
let chat = Chat::load_from_db(&t.ctx, chat_id).unwrap();
|
||||||
let info = chat.get_info(&t.ctx).unwrap();
|
let info = chat.get_info(&t.ctx).unwrap();
|
||||||
|
|
||||||
// Ensure we can serialise this.
|
// Ensure we can serialize this.
|
||||||
println!("{}", serde_json::to_string_pretty(&info).unwrap());
|
println!("{}", serde_json::to_string_pretty(&info).unwrap());
|
||||||
|
|
||||||
let expected = r#"
|
let expected = r#"
|
||||||
@@ -2413,11 +2491,12 @@ mod tests {
|
|||||||
"color": 15895624,
|
"color": 15895624,
|
||||||
"profile_image": "",
|
"profile_image": "",
|
||||||
"subtitle": "bob@example.com",
|
"subtitle": "bob@example.com",
|
||||||
"draft": ""
|
"draft": "",
|
||||||
|
"is_muted": false
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
// Ensure we can deserialise this.
|
// Ensure we can deserialize this.
|
||||||
let loaded: ChatInfo = serde_json::from_str(expected).unwrap();
|
let loaded: ChatInfo = serde_json::from_str(expected).unwrap();
|
||||||
assert_eq!(info, loaded);
|
assert_eq!(info, loaded);
|
||||||
}
|
}
|
||||||
@@ -2777,4 +2856,49 @@ mod tests {
|
|||||||
assert!(chat_id.set_selfavatar_timestamp(&t.ctx, time()).is_ok());
|
assert!(chat_id.set_selfavatar_timestamp(&t.ctx, time()).is_ok());
|
||||||
assert!(!shall_attach_selfavatar(&t.ctx, chat_id).unwrap());
|
assert!(!shall_attach_selfavatar(&t.ctx, chat_id).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_mute_duration() {
|
||||||
|
let t = dummy_context();
|
||||||
|
let chat_id = create_group_chat(&t.ctx, VerifiedStatus::Unverified, "foo").unwrap();
|
||||||
|
// Initial
|
||||||
|
assert_eq!(
|
||||||
|
Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Forever
|
||||||
|
set_muted(&t.ctx, chat_id, MuteDuration::Forever).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// unMute
|
||||||
|
set_muted(&t.ctx, chat_id, MuteDuration::NotMuted).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Timed in the future
|
||||||
|
set_muted(
|
||||||
|
&t.ctx,
|
||||||
|
chat_id,
|
||||||
|
MuteDuration::Until(SystemTime::now() + Duration::from_secs(3600)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// Time in the past
|
||||||
|
set_muted(
|
||||||
|
&t.ctx,
|
||||||
|
chat_id,
|
||||||
|
MuteDuration::Until(SystemTime::now() - Duration::from_secs(3600)),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Chat::load_from_db(&t.ctx, chat_id).unwrap().is_muted(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -885,6 +885,14 @@ fn open(
|
|||||||
update_icons = true;
|
update_icons = true;
|
||||||
sql.set_raw_config_int(context, "dbversion", 61)?;
|
sql.set_raw_config_int(context, "dbversion", 61)?;
|
||||||
}
|
}
|
||||||
|
if dbversion < 62 {
|
||||||
|
info!(context, "[migration] v62");
|
||||||
|
sql.execute(
|
||||||
|
"ALTER TABLE chats ADD COLUMN muted_until INTEGER DEFAULT 0;",
|
||||||
|
NO_PARAMS,
|
||||||
|
)?;
|
||||||
|
sql.set_raw_config_int(context, "dbversion", 62)?;
|
||||||
|
}
|
||||||
|
|
||||||
// (2) updates that require high-level objects
|
// (2) updates that require high-level objects
|
||||||
// (the structure is complete now and all objects are usable)
|
// (the structure is complete now and all objects are usable)
|
||||||
|
|||||||
Reference in New Issue
Block a user