Impl room listing with filter=unseen

This commit is contained in:
oxalica 2024-09-06 03:32:40 -04:00
parent e74da2812b
commit 8b096ba802
2 changed files with 51 additions and 6 deletions

View file

@ -24,10 +24,11 @@ paths:
in: query in: query
required: true required: true
description: | description: |
Either "public" or "joined". Must be one of following values:
For "public", it returns all public rooms on the server. - "public": list all public rooms on the server.
For "joined", `Authorization` must be provided and it will return - "joined": list rooms the user have joined.
rooms user have joined. - "unseen": list rooms the user have joined and have unseen
messages.
top: top:
in: query in: query
description: description:
@ -41,7 +42,9 @@ paths:
should be included (as the same value) for each page fetch. should be included (as the same value) for each page fetch.
headers: headers:
Authorization: Authorization:
description: Proof of membership for private rooms. Required if `filter=joined`. description: |
Proof of membership for private rooms.
Required if `filter` is other than "public".
required: false required: false
schema: schema:
$ret: WithSig<AuthPayload> $ret: WithSig<AuthPayload>

View file

@ -213,8 +213,12 @@ struct ListRoomParams {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
enum ListRoomFilter { enum ListRoomFilter {
/// List all public rooms.
Public, Public,
/// List joined rooms (authentication required).
Joined, Joined,
/// List all joined rooms with unseen messages (authentication required).
Unseen,
} }
async fn room_list( async fn room_list(
@ -261,12 +265,14 @@ async fn room_list(
.transpose()?; .transpose()?;
let last_seen_cid = let last_seen_cid =
Some(row.get::<_, Id>("last_seen_cid")?).filter(|cid| cid.0 != 0); Some(row.get::<_, Id>("last_seen_cid")?).filter(|cid| cid.0 != 0);
let unseen_cnt = row.get("unseen_cnt").ok();
Ok(RoomMetadata { Ok(RoomMetadata {
rid, rid,
title, title,
attrs, attrs,
last_chat, last_chat,
last_seen_cid, last_seen_cid,
unseen_cnt,
}) })
})? })?
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
@ -320,6 +326,36 @@ async fn room_list(
}, },
) )
} }
ListRoomFilter::Unseen => {
let user = auth?.0;
query(
r"
SELECT
`rid`, `title`, `attrs`, `last_seen_cid`,
`cid`, `last_author`.`userkey`, `timestamp`, `nonce`, `sig`, `rich_text`,
(SELECT COUNT(*)
FROM `room_item` AS `unseen_item`
WHERE `unseen_item`.`rid` = `room`.`rid` AND
`last_seen_cid` < `unseen_item`.`cid`) AS `unseen_cnt`
FROM `user`
JOIN `room_member` USING (`uid`)
JOIN `room` USING (`rid`)
LEFT JOIN `room_item` USING (`rid`)
LEFT JOIN `user` AS `last_author` ON (`last_author`.`uid` = `room_item`.`uid`)
WHERE `user`.`userkey` = :userkey AND
`rid` > :start_rid AND
`cid` > `last_seen_cid`
GROUP BY `rid` HAVING `cid` IS MAX(`cid`)
ORDER BY `rid` ASC
LIMIT :page_len
",
named_params! {
":start_rid": start_rid,
":page_len": page_len,
":userkey": user,
},
)
}
} }
.map(Json) .map(Json)
} }
@ -466,8 +502,10 @@ async fn room_get_metadata(
rid, rid,
title, title,
attrs, attrs,
// TODO: Should we include these here?
last_chat: None, last_chat: None,
last_seen_cid: None, last_seen_cid: None,
unseen_cnt: None,
})) }))
} }
@ -579,11 +617,15 @@ pub struct RoomMetadata {
pub title: String, pub title: String,
pub attrs: RoomAttrs, pub attrs: RoomAttrs,
/// Optional extra information. Only included by the room list response. // Optional extra information. Only included by the room list response.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub last_chat: Option<WithItemId<ChatItem>>, pub last_chat: Option<WithItemId<ChatItem>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub last_seen_cid: Option<Id>, pub last_seen_cid: Option<Id>,
/// The number of unseen messages. Only available for `room_list` response with
/// "filter=unseen".
#[serde(skip_serializing_if = "Option::is_none")]
pub unseen_cnt: Option<u64>,
} }
fn get_room_if_readable<T>( fn get_room_if_readable<T>(