feat: search bar working, still in progress

This commit is contained in:
Daniel Avila 2023-03-18 01:40:49 -04:00
parent 610cba4a60
commit 0f54ffd8b4
10 changed files with 147 additions and 24 deletions

View file

@ -85,12 +85,6 @@ const createMeiliMongooseModel = function ({ index, indexName, client, attribute
// Push new document to Meili // Push new document to Meili
async addObjectToMeili() { async addObjectToMeili() {
const object = _.pick(this.toJSON(), attributesToIndex); const object = _.pick(this.toJSON(), attributesToIndex);
// const title = (await this.getTitle()) || 'New Chat'; // Get title value
// const objectWithTitle = {
// ...this.toJSON(),
// title
// };
// const object = _.pick(objectWithTitle, attributesToIndex); // Pick desired attributes
try { try {
// console.log('Adding document to Meili', object); // console.log('Adding document to Meili', object);

View file

@ -136,6 +136,25 @@ module.exports = {
return { message: 'Error getting conversations' }; return { message: 'Error getting conversations' };
} }
}, },
getConvosQueried: async (user, convoIds, pageNumber = 1, pageSize = 12) => {
try {
if (!convoIds || convoIds.length === 0) {
return { conversations: [], pages: 1, pageNumber, pageSize };
}
const promises = convoIds.map(convo => {
return Conversation.findOne({ user, conversationId: convo.conversationId}).exec();
});
const results = await Promise.all(promises);
const startIndex = (pageNumber - 1) * pageSize;
const convos = results.slice(startIndex, startIndex + pageSize);
const totalPages = Math.ceil(results.length / pageSize);
return { conversations: convos, pages: totalPages, pageNumber, pageSize };
} catch (error) {
console.log(error);
return { message: 'Error fetching conversations' };
}
},
getConvo, getConvo,
getConvoTitle: async (user, conversationId) => { getConvoTitle: async (user, conversationId) => {
try { try {

View file

@ -1,7 +1,7 @@
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
const { Message } = require('../../models/Message'); const { Message } = require('../../models/Message');
const { Conversation } = require('../../models/Conversation'); const { Conversation, getConvosQueried } = require('../../models/Conversation');
const {reduceMessages, reduceHits} = require('../../lib/utils/reduceHits'); const {reduceMessages, reduceHits} = require('../../lib/utils/reduceHits');
// const { MeiliSearch } = require('meilisearch'); // const { MeiliSearch } = require('meilisearch');
@ -13,13 +13,18 @@ router.get('/sync', async function (req, res) {
router.get('/', async function (req, res) { router.get('/', async function (req, res) {
const { q } = req.query; const { q } = req.query;
const message = await Message.meiliSearch(q, { attributesToHighlight: ['text', 'sender'] }); console.log(req.query);
const pageNumber = req.query.pageNumber || 1;
// const message = await Message.meiliSearch(q, { attributesToHighlight: ['text', 'sender'] });
const message = await Message.meiliSearch(q);
const title = await Conversation.meiliSearch(q, { attributesToHighlight: ['title'] }); const title = await Conversation.meiliSearch(q, { attributesToHighlight: ['title'] });
// console.log('titles', title); // console.log('titles', title);
// console.log(sortedHits); // console.log(sortedHits);
const sortedHits = reduceHits(message.hits, title.hits); const sortedHits = reduceHits(message.hits, title.hits);
const result = await getConvosQueried(req?.session?.user?.username, sortedHits, pageNumber);
// const sortedHits = reduceMessages(message.hits); // const sortedHits = reduceMessages(message.hits);
res.status(200).send({sortedHits}); // res.status(200).send(sortedHits || result);
res.status(200).send(result);
}); });
router.get('/clear', async function (req, res) { router.get('/clear', async function (req, res) {

View file

@ -20,6 +20,7 @@
"clsx": "^1.2.1", "clsx": "^1.2.1",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"lodash": "^4.17.21",
"lucide-react": "^0.113.0", "lucide-react": "^0.113.0",
"markdown-to-jsx": "^7.1.9", "markdown-to-jsx": "^7.1.9",
"react": "^18.2.0", "react": "^18.2.0",
@ -7469,8 +7470,7 @@
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"dev": true
}, },
"node_modules/lodash.debounce": { "node_modules/lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",
@ -16943,8 +16943,7 @@
"lodash": { "lodash": {
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"dev": true
}, },
"lodash.debounce": { "lodash.debounce": {
"version": "4.0.8", "version": "4.0.8",

View file

@ -30,6 +30,7 @@
"clsx": "^1.2.1", "clsx": "^1.2.1",
"crypto-browserify": "^3.12.0", "crypto-browserify": "^3.12.0",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"lodash": "^4.17.21",
"lucide-react": "^0.113.0", "lucide-react": "^0.113.0",
"markdown-to-jsx": "^7.1.9", "markdown-to-jsx": "^7.1.9",
"react": "^18.2.0", "react": "^18.2.0",

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import NavLink from './NavLink'; import NavLink from './NavLink';
import LogOutIcon from '../svg/LogOutIcon'; import LogOutIcon from '../svg/LogOutIcon';
import SearchBar from './SearchBar';
import ClearConvos from './ClearConvos'; import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode'; import DarkMode from './DarkMode';
import Logout from './Logout'; import Logout from './Logout';
@ -8,6 +9,7 @@ import Logout from './Logout';
export default function NavLinks() { export default function NavLinks() {
return ( return (
<> <>
<SearchBar />
<ClearConvos /> <ClearConvos />
<DarkMode /> <DarkMode />
<Logout /> <Logout />

View file

@ -0,0 +1,66 @@
import React, { useState, useCallback } from 'react';
import { debounce } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { Search } from 'lucide-react';
import { setQuery } from '~/store/searchSlice';
import { setPage, refreshConversation } from '~/store/convoSlice';
export default function SearchBar() {
const dispatch = useDispatch();
const [inputValue, setInputValue] = useState('');
const { search } = useSelector((state) => state.search);
const debouncedChangeHandler = useCallback(
debounce((q) => {
dispatch(setQuery(q));
}, 750),
[dispatch]
);
const handleKeyUp = (e) => {
const { value } = e.target;
if (e.keyCode === 8 && value === '') {
// Value after clearing input: ""
console.log(`Value after clearing input: "${value}"`);
dispatch(setPage(1));
dispatch(setQuery(''));
dispatch(refreshConversation());
}
};
const changeHandler = (e) => {
if (!search) {
console.log('setting page to 1');
dispatch(setPage(1));
}
let q = e.target.value;
setInputValue(q);
q = q.trim();
if (q === '' || !q) {
dispatch(setPage(1));
dispatch(setQuery(''));
dispatch(refreshConversation());
} else {
debouncedChangeHandler(q);
}
};
return (
<div className="flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10">
{<Search className="h-4 w-4" />}
<input
// ref={inputRef}
type="text"
className="m-0 mr-0 w-full border-none bg-transparent p-0 text-sm leading-tight outline-none"
value={inputValue}
onChange={changeHandler}
placeholder="Search messages"
onKeyUp={handleKeyUp}
// onBlur={onRename}
/>
</div>
);
}

View file

@ -10,7 +10,8 @@ import { increasePage, decreasePage, setPage, setConvos, setPages } from '~/stor
export default function Nav({ navVisible, setNavVisible }) { export default function Nav({ navVisible, setNavVisible }) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [isHovering, setIsHovering] = useState(false); const [isHovering, setIsHovering] = useState(false);
const { conversationId, convos, search, pages, pageNumber, refreshConvoHint } = useSelector( const { search, query } = useSelector((state) => state.search);
const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector(
(state) => state.convo (state) => state.convo
); );
const onSuccess = (data) => { const onSuccess = (data) => {
@ -24,8 +25,12 @@ export default function Nav({ navVisible, setNavVisible }) {
} }
}; };
const { data, isLoading, mutate } = swr(`/api/${search ? 'search?q=' : `convos?pageNumber=${pageNumber}`}`, onSuccess, { const { data, isLoading, mutate } = swr(`/api/${search ? `search?q=${query}&pageNumber=${pageNumber}` : `convos?pageNumber=${pageNumber}`}`, onSuccess, {
revalidateOnMount: false revalidateOnMount: false,
revalidateIfStale: !search,
revalidateOnFocus: !search,
revalidateOnReconnect: !search,
populateCache: !search,
}); });
const containerRef = useRef(null); const containerRef = useRef(null);
@ -103,7 +108,7 @@ export default function Nav({ navVisible, setNavVisible }) {
ref={containerRef} ref={containerRef}
> >
<div className={containerClasses}> <div className={containerClasses}>
{isLoading && pageNumber === 1 ? ( {isLoading && (pageNumber === 1 || search) ? (
<Spinner /> <Spinner />
) : ( ) : (
<Conversations <Conversations

View file

@ -1,11 +1,12 @@
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from '@reduxjs/toolkit';
import convoReducer from './convoSlice.js'; import convoReducer from './convoSlice.js';
import messageReducer from './messageSlice.js' import messageReducer from './messageSlice.js';
import modelReducer from './modelSlice.js' import modelReducer from './modelSlice.js';
import submitReducer from './submitSlice.js' import submitReducer from './submitSlice.js';
import textReducer from './textSlice.js' import textReducer from './textSlice.js';
import userReducer from './userReducer.js' import userReducer from './userReducer.js';
import searchReducer from './searchSlice.js';
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
@ -15,6 +16,7 @@ export const store = configureStore({
text: textReducer, text: textReducer,
submit: submitReducer, submit: submitReducer,
user: userReducer, user: userReducer,
search: searchReducer
}, },
devTools: true, devTools: true
}); });

View file

@ -0,0 +1,30 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
search: false,
query: '',
};
const currentSlice = createSlice({
name: 'search',
initialState,
reducers: {
setSearchState: (state, action) => {
state.search = action.payload;
},
setQuery: (state, action) => {
const q = action.payload;
state.query = q;
if (!q || q === '') {
state.search = false;
} else {
state.search = true;
}
},
}
});
export const { setSearchState, setQuery } = currentSlice.actions;
export default currentSlice.reducer;