diff --git a/blahd/docs/webapi.yaml b/blahd/docs/webapi.yaml index 1064664..e9f1b4e 100644 --- a/blahd/docs/webapi.yaml +++ b/blahd/docs/webapi.yaml @@ -24,10 +24,11 @@ paths: in: query required: true description: | - Either "public" or "joined". - For "public", it returns all public rooms on the server. - For "joined", `Authorization` must be provided and it will return - rooms user have joined. + Must be one of following values: + - "public": list all public rooms on the server. + - "joined": list rooms the user have joined. + - "unseen": list rooms the user have joined and have unseen + messages. top: in: query description: @@ -41,7 +42,9 @@ paths: should be included (as the same value) for each page fetch. headers: 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 schema: $ret: WithSig diff --git a/blahd/src/main.rs b/blahd/src/main.rs index 7a6da24..23adac9 100644 --- a/blahd/src/main.rs +++ b/blahd/src/main.rs @@ -213,8 +213,12 @@ struct ListRoomParams { #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] enum ListRoomFilter { + /// List all public rooms. Public, + /// List joined rooms (authentication required). Joined, + /// List all joined rooms with unseen messages (authentication required). + Unseen, } async fn room_list( @@ -261,12 +265,14 @@ async fn room_list( .transpose()?; let last_seen_cid = Some(row.get::<_, Id>("last_seen_cid")?).filter(|cid| cid.0 != 0); + let unseen_cnt = row.get("unseen_cnt").ok(); Ok(RoomMetadata { rid, title, attrs, last_chat, last_seen_cid, + unseen_cnt, }) })? .collect::, _>>()?; @@ -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) } @@ -466,8 +502,10 @@ async fn room_get_metadata( rid, title, attrs, + // TODO: Should we include these here? last_chat: None, last_seen_cid: None, + unseen_cnt: None, })) } @@ -579,11 +617,15 @@ pub struct RoomMetadata { pub title: String, 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")] pub last_chat: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub last_seen_cid: Option, + /// 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, } fn get_room_if_readable(