refactor(webapi): hoist RoomMetadata to blah_types and rename last_chat to last_item

This commit is contained in:
oxalica 2024-09-10 09:19:15 -04:00
parent c0ec429c24
commit 74c6fa6f6a
5 changed files with 40 additions and 33 deletions

View file

@ -308,6 +308,29 @@ impl RichText {
pub type ChatItem = WithSig<ChatPayload>; pub type ChatItem = WithSig<ChatPayload>;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoomMetadata {
/// Room id.
pub rid: Id,
/// Plain text room title.
pub title: String,
/// Room attributes.
pub attrs: RoomAttrs,
// Extra information is only available for some APIs.
/// The last item in the room.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_item: Option<WithItemId<ChatItem>>,
/// The current user's last seen item id.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_seen_cid: Option<Id>,
/// The number of unseen messages, ie. the number of items from `last_seen_cid` to
/// `last_item.cid`.
/// This may or may not be a precise number.
#[serde(skip_serializing_if = "Option::is_none")]
pub unseen_cnt: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "typ", rename = "create_room")] #[serde(tag = "typ", rename = "create_room")]
pub struct CreateRoomPayload { pub struct CreateRoomPayload {

View file

@ -12,7 +12,7 @@ use axum::{Json, Router};
use axum_extra::extract::WithRejection as R; use axum_extra::extract::WithRejection as R;
use blah_types::{ use blah_types::{
ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp, RoomAdminPayload, ChatItem, ChatPayload, CreateRoomPayload, Id, MemberPermission, RoomAdminOp, RoomAdminPayload,
RoomAttrs, ServerPermission, Signee, UserKey, WithItemId, WithSig, RoomAttrs, RoomMetadata, ServerPermission, Signee, UserKey, WithItemId, WithSig,
}; };
use config::ServerConfig; use config::ServerConfig;
use ed25519_dalek::SIGNATURE_LENGTH; use ed25519_dalek::SIGNATURE_LENGTH;
@ -138,7 +138,7 @@ async fn handle_ws(State(st): ArcState, ws: WebSocketUpgrade) -> Response {
}) })
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoomList { pub struct RoomList {
pub rooms: Vec<RoomMetadata>, pub rooms: Vec<RoomMetadata>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@ -189,7 +189,7 @@ async fn room_list(
let rid = row.get("rid")?; let rid = row.get("rid")?;
let title = row.get("title")?; let title = row.get("title")?;
let attrs = row.get("attrs")?; let attrs = row.get("attrs")?;
let last_chat = row let last_item = row
.get::<_, Option<Id>>("cid")? .get::<_, Option<Id>>("cid")?
.map(|cid| { .map(|cid| {
Ok::<_, rusqlite::Error>(WithItemId { Ok::<_, rusqlite::Error>(WithItemId {
@ -216,7 +216,7 @@ async fn room_list(
rid, rid,
title, title,
attrs, attrs,
last_chat, last_item,
last_seen_cid, last_seen_cid,
unseen_cnt, unseen_cnt,
}) })
@ -439,8 +439,8 @@ async fn room_get_metadata(
R(Path(rid), _): RE<Path<Id>>, R(Path(rid), _): RE<Path<Id>>,
auth: MaybeAuth, auth: MaybeAuth,
) -> Result<Json<RoomMetadata>, ApiError> { ) -> Result<Json<RoomMetadata>, ApiError> {
let (title, attrs) = let conn = st.db.get();
get_room_if_readable(&st.db.get(), rid, auth.into_optional()?.as_ref(), |row| { let (title, attrs) = get_room_if_readable(&conn, rid, auth.into_optional()?.as_ref(), |row| {
Ok(( Ok((
row.get::<_, String>("title")?, row.get::<_, String>("title")?,
row.get::<_, RoomAttrs>("attrs")?, row.get::<_, RoomAttrs>("attrs")?,
@ -451,8 +451,9 @@ async fn room_get_metadata(
rid, rid,
title, title,
attrs, attrs,
// TODO: Should we include these here? // TODO: Should we include these here?
last_chat: None, last_item: None,
last_seen_cid: None, last_seen_cid: None,
unseen_cnt: None, unseen_cnt: None,
})) }))
@ -560,23 +561,6 @@ struct FeedItemExtra {
sig: [u8; SIGNATURE_LENGTH], sig: [u8; SIGNATURE_LENGTH],
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoomMetadata {
pub rid: Id,
pub title: String,
pub attrs: RoomAttrs,
// Optional extra information. Only included by the room list response.
#[serde(skip_serializing_if = "Option::is_none")]
pub last_chat: Option<WithItemId<ChatItem>>,
#[serde(skip_serializing_if = "Option::is_none")]
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>(
conn: &rusqlite::Connection, conn: &rusqlite::Connection,
rid: Id, rid: Id,

View file

@ -233,7 +233,7 @@ async fn room_create_get(server: Server, ref mut rng: impl RngCore, #[case] publ
} else { } else {
RoomAttrs::empty() RoomAttrs::empty()
}, },
last_chat: None, last_item: None,
last_seen_cid: None, last_seen_cid: None,
unseen_cnt: None, unseen_cnt: None,
}; };

View file

@ -377,7 +377,7 @@ components:
description: Room attributes bitset, see `RoomAttrs`. description: Room attributes bitset, see `RoomAttrs`.
type: integer type: integer
format: int64 format: int64
last_chat: last_item:
$ref: '#/components/schemas/WithItemId-WithSig-Chat' $ref: '#/components/schemas/WithItemId-WithSig-Chat'
last_seen_cid: last_seen_cid:
description: The `cid` of the last chat being marked as seen. description: The `cid` of the last chat being marked as seen.

View file

@ -269,11 +269,11 @@ async function loadRoomList(autoJoin) {
const resp = await fetch(`${serverUrl}/room?filter=${filter}`, await genAuthHeader()) const resp = await fetch(`${serverUrl}/room?filter=${filter}`, await genAuthHeader())
const json = await resp.json() const json = await resp.json()
if (resp.status !== 200) throw new Error(`status ${resp.status}: ${json.error.message}`); if (resp.status !== 200) throw new Error(`status ${resp.status}: ${json.error.message}`);
for (const { rid, title, attrs, last_chat, last_seen_cid } of json.rooms) { for (const { rid, title, attrs, last_item, last_seen_cid } of json.rooms) {
const el = document.createElement('option'); const el = document.createElement('option');
el.value = rid; el.value = rid;
el.innerText = `${title} (rid=${rid}, attrs=${attrs})`; el.innerText = `${title} (rid=${rid}, attrs=${attrs})`;
if (last_chat !== undefined && last_chat.cid !== last_seen_cid) { if (last_item !== undefined && last_item.cid !== last_seen_cid) {
el.innerText += ' (unread)'; el.innerText += ' (unread)';
} }
targetEl.appendChild(el); targetEl.appendChild(el);