mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[chat] fix favorite thread
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
CreatePartyChatResponse,
|
||||
CreateThreadRequest,
|
||||
CreateThreadResponse,
|
||||
FavoriteChatResponse,
|
||||
FavoriteThreadRequest,
|
||||
form,
|
||||
json,
|
||||
@@ -572,7 +573,7 @@ async function formField(c: Context<App>, name: string): Promise<string | undefi
|
||||
|
||||
/**
|
||||
* A concise `describeRoute` spec for one of the thread-scoped actions that answers the
|
||||
* bare ChatResult integer rather than an HTTP status — rename, leave, snooze, favorite,
|
||||
* bare ChatResult integer rather than an HTTP status — rename, leave, snooze,
|
||||
* add-member and the read-pointer moves. They share the auth gate, the `:id` path param,
|
||||
* and the "3 when the caller isn't on the thread" behaviour.
|
||||
*/
|
||||
@@ -1361,28 +1362,44 @@ const app = new Hono<App>()
|
||||
|
||||
// Favorite or unfavorite a thread (`favorite=True`), for the caller alone — like
|
||||
// snoozing, it's a per-member flag that pins the thread in their own inbox.
|
||||
//
|
||||
// Answers the client's `FavoriteChatResponse` — a bare `{ ChatResult, IsFavorited }`
|
||||
// object, not the bare ChatResult integer the neighbouring thread actions answer.
|
||||
.on(
|
||||
['PUT', 'POST'],
|
||||
'/thread/:id{[0-9]+}/favorite',
|
||||
chatResultRoute(
|
||||
'Favorite or unfavorite a thread',
|
||||
[
|
||||
describeRoute({
|
||||
tags: ['Chat'],
|
||||
summary: 'Favorite or unfavorite a thread',
|
||||
description: [
|
||||
'Like snoozing, a per-member flag that pins the thread in the caller’s own inbox and',
|
||||
'leaves everyone else’s untouched.',
|
||||
'leaves everyone else’s untouched. Answers the client’s `FavoriteChatResponse`, a bare',
|
||||
'`{ ChatResult, IsFavorited }` object — not the bare ChatResult integer the other thread',
|
||||
'actions answer.',
|
||||
].join(' '),
|
||||
{ requestBody: form(FavoriteThreadRequest, 'The favorite flag') }
|
||||
security: AUTHED,
|
||||
parameters: [THREAD_ID_PARAM],
|
||||
requestBody: form(FavoriteThreadRequest, 'The favorite flag'),
|
||||
responses: {
|
||||
200: json(
|
||||
FavoriteChatResponse,
|
||||
'ChatResult 0 and the new flag on success; ChatResult 3 when the caller isn’t on the thread'
|
||||
),
|
||||
401: UNAUTHORIZED_RESPONSE,
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const id = await authedId(c)
|
||||
if (id === null) return c.body(null, 401)
|
||||
|
||||
const chatThreadId = Number.parseInt(c.req.param('id'), 10)
|
||||
if (!(await isThreadMember(c.env.DB, chatThreadId, id))) {
|
||||
return c.json(CHAT_MEMBERSHIP_NOT_FOUND)
|
||||
return c.json({ ChatResult: CHAT_MEMBERSHIP_NOT_FOUND, IsFavorited: false })
|
||||
}
|
||||
|
||||
await setThreadFavorited(c.env.DB, chatThreadId, id, await formBool(c, 'favorite'))
|
||||
return c.json(CHAT_SUCCESS)
|
||||
const isFavorited = await formBool(c, 'favorite')
|
||||
await setThreadFavorited(c.env.DB, chatThreadId, id, isFavorited)
|
||||
return c.json({ ChatResult: CHAT_SUCCESS, IsFavorited: isFavorited })
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -388,6 +388,18 @@ export const FavoriteThreadRequest = z.object({
|
||||
.describe('`True`/`False` as the client spells it (`1`/`yes` also count as true)'),
|
||||
})
|
||||
|
||||
/**
|
||||
* `PUT|POST /thread/:id/favorite` response — the client's `FavoriteChatResponse`, a bare
|
||||
* two-key object with no envelope. Unlike the other thread actions it is NOT a bare
|
||||
* ChatResult integer: the client parses this object either way.
|
||||
*/
|
||||
export const FavoriteChatResponse = z.object({
|
||||
ChatResult,
|
||||
IsFavorited: z
|
||||
.boolean()
|
||||
.describe('The caller’s favorite flag after the call (false when they aren’t on the thread)'),
|
||||
})
|
||||
|
||||
// ---- Shared parameters -----------------------------------------------------
|
||||
|
||||
/** The numeric `:id` path segment naming a thread (constrained to digits by the route). */
|
||||
|
||||
@@ -2164,13 +2164,17 @@ describe('PUT /thread/:id/favorite', () => {
|
||||
|
||||
const res = await favorite(caller, chatThreadId, 'favorite=True')
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.json()).toBe(0)
|
||||
// The client's FavoriteChatResponse: a bare 2-key object, no envelope.
|
||||
expect(await res.json()).toEqual({ ChatResult: 0, IsFavorited: true })
|
||||
|
||||
expect((await getThreadForPlayer(env.DB, chatThreadId, caller))?.isFavorited).toBe(true)
|
||||
// Per-member, like snoozing: the other player's inbox is untouched.
|
||||
expect((await getThreadForPlayer(env.DB, chatThreadId, other))?.isFavorited).toBe(false)
|
||||
|
||||
await favorite(caller, chatThreadId, 'favorite=False')
|
||||
expect(await (await favorite(caller, chatThreadId, 'favorite=False')).json()).toEqual({
|
||||
ChatResult: 0,
|
||||
IsFavorited: false,
|
||||
})
|
||||
expect((await getThreadForPlayer(env.DB, chatThreadId, caller))?.isFavorited).toBe(false)
|
||||
})
|
||||
|
||||
@@ -2178,13 +2182,19 @@ describe('PUT /thread/:id/favorite', () => {
|
||||
const caller = 893003
|
||||
const chatThreadId = await createThread(env.DB, [caller, 893004], null, caller)
|
||||
|
||||
expect(await (await favorite(caller, chatThreadId, 'favorite=True', 'POST')).json()).toBe(0)
|
||||
expect(await (await favorite(caller, chatThreadId, 'favorite=True', 'POST')).json()).toEqual({
|
||||
ChatResult: 0,
|
||||
IsFavorited: true,
|
||||
})
|
||||
expect((await getThreadForPlayer(env.DB, chatThreadId, caller))?.isFavorited).toBe(true)
|
||||
})
|
||||
|
||||
it('refuses a thread the caller is not on', async () => {
|
||||
const chatThreadId = await createThread(env.DB, [893005, 893006], null, 893005)
|
||||
expect(await (await favorite(893007, chatThreadId, 'favorite=True')).json()).toBe(3)
|
||||
expect(await (await favorite(893007, chatThreadId, 'favorite=True')).json()).toEqual({
|
||||
ChatResult: 3,
|
||||
IsFavorited: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('401s without a token', async () => {
|
||||
|
||||
Reference in New Issue
Block a user