mirror of
https://github.com/chatmail/core.git
synced 2026-05-17 05:46:30 +03:00
globally search for media (#3528)
* get_chat_media() from any chat similar to search_msgs() * do not return hidden media this fixes the issue that drafts and other hidden messages pop up in the gallery. * add a test for get_chat_media() * use None instead of ChatId::new(0) * clarify scope of 'any' in get_chat_media() * adapt json rpc to changed get_chat_media() * jsonrpc: chat_get_media turn chat_id into option and also still allow `0` for dev convenience (though I'm not totally sure if thats the right decision) * cargo fmt Co-authored-by: Simon Laux <mobile.info@simonlaux.de>
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
### Changes
|
### Changes
|
||||||
- refactorings #3545 #3551
|
- refactorings #3545 #3551
|
||||||
- use [pathlib](https://docs.python.org/3/library/pathlib.html) in provider update script #3543
|
- use [pathlib](https://docs.python.org/3/library/pathlib.html) in provider update script #3543
|
||||||
|
- `dc_get_chat_media()` can return media globally #3528
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- improved error handling for account setup from qrcode #3474
|
- improved error handling for account setup from qrcode #3474
|
||||||
|
|||||||
@@ -1263,7 +1263,7 @@ void dc_marknoticed_chat (dc_context_t* context, uint32_t ch
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all message IDs of the given types in a chat.
|
* Returns all message IDs of the given types in a given chat or any chat.
|
||||||
* Typically used to show a gallery.
|
* Typically used to show a gallery.
|
||||||
* The result must be dc_array_unref()'d
|
* The result must be dc_array_unref()'d
|
||||||
*
|
*
|
||||||
@@ -1273,7 +1273,8 @@ void dc_marknoticed_chat (dc_context_t* context, uint32_t ch
|
|||||||
*
|
*
|
||||||
* @memberof dc_context_t
|
* @memberof dc_context_t
|
||||||
* @param context The context object as returned from dc_context_new().
|
* @param context The context object as returned from dc_context_new().
|
||||||
* @param chat_id The chat ID to get all messages with media from.
|
* @param chat_id >0: get messages with media from this chat ID.
|
||||||
|
* 0: get messages with media from any chat of the currently used account.
|
||||||
* @param msg_type Specify a message type to query here, one of the @ref DC_MSG constants.
|
* @param msg_type Specify a message type to query here, one of the @ref DC_MSG constants.
|
||||||
* @param msg_type2 Alternative message type to search for. 0 to skip.
|
* @param msg_type2 Alternative message type to search for. 0 to skip.
|
||||||
* @param msg_type3 Alternative message type to search for. 0 to skip.
|
* @param msg_type3 Alternative message type to search for. 0 to skip.
|
||||||
@@ -1284,7 +1285,6 @@ dc_array_t* dc_get_chat_media (dc_context_t* context, uint32_t ch
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Search next/previous message based on a given message and a list of types.
|
* Search next/previous message based on a given message and a list of types.
|
||||||
* The
|
|
||||||
* Typically used to implement the "next" and "previous" buttons
|
* Typically used to implement the "next" and "previous" buttons
|
||||||
* in a gallery or in a media player.
|
* in a gallery or in a media player.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1208,6 +1208,11 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
|||||||
return ptr::null_mut();
|
return ptr::null_mut();
|
||||||
}
|
}
|
||||||
let ctx = &*context;
|
let ctx = &*context;
|
||||||
|
let chat_id = if chat_id == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ChatId::new(chat_id))
|
||||||
|
};
|
||||||
let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {}", msg_type));
|
let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {}", msg_type));
|
||||||
let or_msg_type2 =
|
let or_msg_type2 =
|
||||||
from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {}", or_msg_type2));
|
from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {}", or_msg_type2));
|
||||||
@@ -1216,16 +1221,10 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
|||||||
|
|
||||||
block_on(async move {
|
block_on(async move {
|
||||||
Box::into_raw(Box::new(
|
Box::into_raw(Box::new(
|
||||||
chat::get_chat_media(
|
chat::get_chat_media(ctx, chat_id, msg_type, or_msg_type2, or_msg_type3)
|
||||||
ctx,
|
.await
|
||||||
ChatId::new(chat_id),
|
.unwrap_or_log_default(ctx, "Failed get_chat_media")
|
||||||
msg_type,
|
.into(),
|
||||||
or_msg_type2,
|
|
||||||
or_msg_type3,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap_or_log_default(ctx, "Failed get_chat_media")
|
|
||||||
.into(),
|
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,28 +577,28 @@ impl CommandApi {
|
|||||||
/// The list is already sorted and starts with the oldest message.
|
/// The list is already sorted and starts with the oldest message.
|
||||||
/// Clients should not try to re-sort the list as this would be an expensive action
|
/// Clients should not try to re-sort the list as this would be an expensive action
|
||||||
/// and would result in inconsistencies between clients.
|
/// and would result in inconsistencies between clients.
|
||||||
|
///
|
||||||
|
/// Setting `chat_id` to `None` (`null` in typescript) means get messages with media
|
||||||
|
/// from any chat of the currently used account.
|
||||||
async fn chat_get_media(
|
async fn chat_get_media(
|
||||||
&self,
|
&self,
|
||||||
account_id: u32,
|
account_id: u32,
|
||||||
chat_id: u32,
|
chat_id: Option<u32>,
|
||||||
message_type: MessageViewtype,
|
message_type: MessageViewtype,
|
||||||
or_message_type2: Option<MessageViewtype>,
|
or_message_type2: Option<MessageViewtype>,
|
||||||
or_message_type3: Option<MessageViewtype>,
|
or_message_type3: Option<MessageViewtype>,
|
||||||
) -> Result<Vec<u32>> {
|
) -> Result<Vec<u32>> {
|
||||||
let ctx = self.get_context(account_id).await?;
|
let ctx = self.get_context(account_id).await?;
|
||||||
|
|
||||||
|
let chat_id = match chat_id {
|
||||||
|
None | Some(0) => None,
|
||||||
|
Some(id) => Some(ChatId::new(id)),
|
||||||
|
};
|
||||||
let msg_type = message_type.into();
|
let msg_type = message_type.into();
|
||||||
let or_msg_type2 = or_message_type2.map_or(Viewtype::Unknown, |v| v.into());
|
let or_msg_type2 = or_message_type2.map_or(Viewtype::Unknown, |v| v.into());
|
||||||
let or_msg_type3 = or_message_type3.map_or(Viewtype::Unknown, |v| v.into());
|
let or_msg_type3 = or_message_type3.map_or(Viewtype::Unknown, |v| v.into());
|
||||||
|
|
||||||
let media = get_chat_media(
|
let media = get_chat_media(&ctx, chat_id, msg_type, or_msg_type2, or_msg_type3).await?;
|
||||||
&ctx,
|
|
||||||
ChatId::new(chat_id),
|
|
||||||
msg_type,
|
|
||||||
or_msg_type2,
|
|
||||||
or_msg_type3,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(media.iter().map(|msg_id| msg_id.to_u32()).collect())
|
Ok(media.iter().map(|msg_id| msg_id.to_u32()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -299,8 +299,11 @@ export class RawClient {
|
|||||||
* The list is already sorted and starts with the oldest message.
|
* The list is already sorted and starts with the oldest message.
|
||||||
* Clients should not try to re-sort the list as this would be an expensive action
|
* Clients should not try to re-sort the list as this would be an expensive action
|
||||||
* and would result in inconsistencies between clients.
|
* and would result in inconsistencies between clients.
|
||||||
|
*
|
||||||
|
* Setting `chat_id` to `None` (`null` in typescript) means get messages with media
|
||||||
|
* from any chat of the currently used account.
|
||||||
*/
|
*/
|
||||||
public chatGetMedia(accountId: T.U32, chatId: T.U32, messageType: T.Viewtype, orMessageType2: (T.Viewtype|null), orMessageType3: (T.Viewtype|null)): Promise<(T.U32)[]> {
|
public chatGetMedia(accountId: T.U32, chatId: (T.U32|null), messageType: T.Viewtype, orMessageType2: (T.Viewtype|null), orMessageType3: (T.Viewtype|null)): Promise<(T.U32)[]> {
|
||||||
return (this._transport.request('chat_get_media', [accountId, chatId, messageType, orMessageType2, orMessageType3] as RPC.Params)) as Promise<(T.U32)[]>;
|
return (this._transport.request('chat_get_media', [accountId, chatId, messageType, orMessageType2, orMessageType3] as RPC.Params)) as Promise<(T.U32)[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,4 +95,4 @@ export type WebxdcMessageInfo={
|
|||||||
* Implementations may offer an menu or a button to open this URL.
|
* Implementations may offer an menu or a button to open this URL.
|
||||||
*/
|
*/
|
||||||
"sourceCodeUrl":(string|null);};
|
"sourceCodeUrl":(string|null);};
|
||||||
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record<U32,Message>,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,U32,Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32];
|
export type __AllTyps=[string,boolean,Record<string,string>,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record<string,string>,U32,string,(string|null),null,U32,Record<string,(string|null)>,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record<string,(string|null)>,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record<U32,ChatListItemFetchResult>,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record<U32,Message>,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record<U32,Contact>,U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32];
|
||||||
|
|||||||
@@ -983,11 +983,9 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
|||||||
chat::add_device_msg(&context, None, Some(&mut msg)).await?;
|
chat::add_device_msg(&context, None, Some(&mut msg)).await?;
|
||||||
}
|
}
|
||||||
"listmedia" => {
|
"listmedia" => {
|
||||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
|
||||||
|
|
||||||
let images = chat::get_chat_media(
|
let images = chat::get_chat_media(
|
||||||
&context,
|
&context,
|
||||||
sel_chat.as_ref().unwrap().get_id(),
|
sel_chat.map(|c| c.id),
|
||||||
Viewtype::Image,
|
Viewtype::Image,
|
||||||
Viewtype::Gif,
|
Viewtype::Gif,
|
||||||
Viewtype::Video,
|
Viewtype::Video,
|
||||||
|
|||||||
164
src/chat.rs
164
src/chat.rs
@@ -2423,7 +2423,7 @@ pub(crate) async fn mark_old_messages_as_noticed(
|
|||||||
|
|
||||||
pub async fn get_chat_media(
|
pub async fn get_chat_media(
|
||||||
context: &Context,
|
context: &Context,
|
||||||
chat_id: ChatId,
|
chat_id: Option<ChatId>,
|
||||||
msg_type: Viewtype,
|
msg_type: Viewtype,
|
||||||
msg_type2: Viewtype,
|
msg_type2: Viewtype,
|
||||||
msg_type3: Viewtype,
|
msg_type3: Viewtype,
|
||||||
@@ -2434,11 +2434,13 @@ pub async fn get_chat_media(
|
|||||||
.query_map(
|
.query_map(
|
||||||
"SELECT id
|
"SELECT id
|
||||||
FROM msgs
|
FROM msgs
|
||||||
WHERE chat_id=?
|
WHERE (1=? OR chat_id=?)
|
||||||
AND (type=? OR type=? OR type=?)
|
AND (type=? OR type=? OR type=?)
|
||||||
|
AND hidden=0
|
||||||
ORDER BY timestamp, id;",
|
ORDER BY timestamp, id;",
|
||||||
paramsv![
|
paramsv![
|
||||||
chat_id,
|
if chat_id.is_none() { 1i32 } else { 0i32 },
|
||||||
|
chat_id.unwrap_or_else(|| ChatId::new(0)),
|
||||||
msg_type,
|
msg_type,
|
||||||
if msg_type2 != Viewtype::Unknown {
|
if msg_type2 != Viewtype::Unknown {
|
||||||
msg_type2
|
msg_type2
|
||||||
@@ -2479,7 +2481,7 @@ pub async fn get_next_media(
|
|||||||
if let Ok(msg) = Message::load_from_db(context, curr_msg_id).await {
|
if let Ok(msg) = Message::load_from_db(context, curr_msg_id).await {
|
||||||
let list: Vec<MsgId> = get_chat_media(
|
let list: Vec<MsgId> = get_chat_media(
|
||||||
context,
|
context,
|
||||||
msg.chat_id,
|
Some(msg.chat_id),
|
||||||
if msg_type != Viewtype::Unknown {
|
if msg_type != Viewtype::Unknown {
|
||||||
msg_type
|
msg_type
|
||||||
} else {
|
} else {
|
||||||
@@ -5486,4 +5488,158 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_get_chat_media() -> Result<()> {
|
||||||
|
let t = TestContext::new_alice().await;
|
||||||
|
let chat_id1 = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
|
||||||
|
let chat_id2 = create_group_chat(&t, ProtectionStatus::Unprotected, "bar").await?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
Some(chat_id1),
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Sticker,
|
||||||
|
Viewtype::Unknown
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn send_media(
|
||||||
|
t: &TestContext,
|
||||||
|
chat_id: ChatId,
|
||||||
|
msg_type: Viewtype,
|
||||||
|
name: &str,
|
||||||
|
bytes: &[u8],
|
||||||
|
) -> Result<MsgId> {
|
||||||
|
let file = t.get_blobdir().join(name);
|
||||||
|
tokio::fs::write(&file, bytes).await?;
|
||||||
|
let mut msg = Message::new(msg_type);
|
||||||
|
msg.set_file(file.to_str().unwrap(), None);
|
||||||
|
send_msg(t, chat_id, &mut msg).await
|
||||||
|
}
|
||||||
|
|
||||||
|
send_media(
|
||||||
|
&t,
|
||||||
|
chat_id1,
|
||||||
|
Viewtype::Image,
|
||||||
|
"a.jpg",
|
||||||
|
include_bytes!("../test-data/image/rectangle200x180-rotated.jpg"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
send_media(
|
||||||
|
&t,
|
||||||
|
chat_id1,
|
||||||
|
Viewtype::Sticker,
|
||||||
|
"b.png",
|
||||||
|
include_bytes!("../test-data/image/avatar64x64.png"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
send_media(
|
||||||
|
&t,
|
||||||
|
chat_id2,
|
||||||
|
Viewtype::Image,
|
||||||
|
"c.jpg",
|
||||||
|
include_bytes!("../test-data/image/avatar64x64.png"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
send_media(
|
||||||
|
&t,
|
||||||
|
chat_id2,
|
||||||
|
Viewtype::Webxdc,
|
||||||
|
"d.xdc",
|
||||||
|
include_bytes!("../test-data/webxdc/minimal.xdc"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
Some(chat_id1),
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
Some(chat_id1),
|
||||||
|
Viewtype::Sticker,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
Some(chat_id1),
|
||||||
|
Viewtype::Sticker,
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
Some(chat_id2),
|
||||||
|
Viewtype::Webxdc,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
None,
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
2
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
None,
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Sticker,
|
||||||
|
Viewtype::Unknown,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
3
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
get_chat_media(
|
||||||
|
&t,
|
||||||
|
None,
|
||||||
|
Viewtype::Image,
|
||||||
|
Viewtype::Sticker,
|
||||||
|
Viewtype::Webxdc,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.len(),
|
||||||
|
4
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user