🔖 feat: Conversation Bookmarks (#3344)

* feat: add tags property in Conversation model

* feat: add ConversationTag model

* feat: add the tags parameter to getConvosByPage

* feat: add API route to ConversationTag

* feat: add types of ConversationTag

* feat: add data access functions for conversation tags

* feat: add Bookmark table component

* feat: Add an action to bookmark

* feat: add Bookmark nav component

* fix: failed test

* refactor: made 'Saved' tag a constant

* feat: add new bookmark to current conversation

* chore: Add comment

* fix: delete tag from conversations when it's deleted

* fix: Update the query cache when the tag title is changed.

* chore: fix typo

* refactor: add description of rebuilding bookmarks

* chore: remove unused variables

* fix: position when adding a new bookmark

* refactor: add comment, rename a function

* refactor: add a unique constraint in ConversationTag

* chore: add localizations
This commit is contained in:
Yuichi Oneda 2024-07-29 07:45:59 -07:00 committed by GitHub
parent d4d56281e3
commit e565e0faab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 3751 additions and 36 deletions

View file

@ -8,6 +8,7 @@ const requireJwtAuth = require('~/server/middleware/requireJwtAuth');
const { forkConversation } = require('~/server/utils/import/fork');
const { importConversations } = require('~/server/utils/import');
const { createImportLimiters } = require('~/server/middleware');
const { updateTagsForConversation } = require('~/models/ConversationTag');
const getLogStores = require('~/cache/getLogStores');
const { sleep } = require('~/server/utils');
const { logger } = require('~/config');
@ -30,8 +31,13 @@ router.get('/', async (req, res) => {
return res.status(400).json({ error: 'Invalid page size' });
}
const isArchived = req.query.isArchived === 'true';
const tags = req.query.tags
? Array.isArray(req.query.tags)
? req.query.tags
: [req.query.tags]
: undefined;
res.status(200).send(await getConvosByPage(req.user.id, pageNumber, pageSize, isArchived));
res.status(200).send(await getConvosByPage(req.user.id, pageNumber, pageSize, isArchived, tags));
});
router.get('/:conversationId', async (req, res) => {
@ -167,4 +173,9 @@ router.post('/fork', async (req, res) => {
}
});
router.put('/tags/:conversationId', async (req, res) => {
const tag = await updateTagsForConversation(req.user.id, req.params.conversationId, req.body);
res.status(200).json(tag);
});
module.exports = router;

View file

@ -21,6 +21,7 @@ const staticRoute = require('./static');
const share = require('./share');
const categories = require('./categories');
const roles = require('./roles');
const tags = require('./tags');
module.exports = {
search,
@ -46,4 +47,5 @@ module.exports = {
share,
categories,
roles,
tags,
};

44
api/server/routes/tags.js Normal file
View file

@ -0,0 +1,44 @@
const express = require('express');
const {
getConversationTags,
updateConversationTag,
createConversationTag,
deleteConversationTag,
rebuildConversationTags,
} = require('~/models/ConversationTag');
const requireJwtAuth = require('~/server/middleware/requireJwtAuth');
const router = express.Router();
router.use(requireJwtAuth);
router.get('/', async (req, res) => {
const tags = await getConversationTags(req.user.id);
if (tags) {
res.status(200).json(tags);
} else {
res.status(404).end();
}
});
router.post('/', async (req, res) => {
const tag = await createConversationTag(req.user.id, req.body);
res.status(200).json(tag);
});
router.post('/rebuild', async (req, res) => {
const tag = await rebuildConversationTags(req.user.id);
res.status(200).json(tag);
});
router.put('/:tag', async (req, res) => {
const tag = await updateConversationTag(req.user.id, req.params.tag, req.body);
res.status(200).json(tag);
});
router.delete('/:tag', async (req, res) => {
const tag = await deleteConversationTag(req.user.id, req.params.tag);
res.status(200).json(tag);
});
module.exports = router;