fix: conflicting fetch with /api/convos

This commit is contained in:
Daniel Avila 2023-03-18 14:28:10 -04:00
parent 0f54ffd8b4
commit b97594c000
10 changed files with 199 additions and 90 deletions

View file

@ -145,10 +145,11 @@ module.exports = {
const promises = convoIds.map(convo => {
return Conversation.findOne({ user, conversationId: convo.conversationId}).exec();
});
const results = await Promise.all(promises);
const results = (await Promise.all(promises)).filter(convo => convo);
const startIndex = (pageNumber - 1) * pageSize;
const convos = results.slice(startIndex, startIndex + pageSize);
const totalPages = Math.ceil(results.length / pageSize);
console.log(results.length, totalPages, convos.length);
return { conversations: convos, pages: totalPages, pageNumber, pageSize };
} catch (error) {
console.log(error);

View file

@ -12,19 +12,24 @@ router.get('/sync', async function (req, res) {
});
router.get('/', async function (req, res) {
try {
const { q } = req.query;
console.log(req.query);
console.log(req.query, req.params);
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'] });
// console.log('titles', title);
// console.log(sortedHits);
const sortedHits = reduceHits(message.hits, title.hits);
const result = await getConvosQueried(req?.session?.user?.username, sortedHits, pageNumber);
// const sortedHits = reduceMessages(message.hits);
// res.status(200).send(sortedHits || result);
const result = await getConvosQueried(
req?.session?.user?.username,
sortedHits,
pageNumber
);
console.log('result', result.pageNumber, result.pages, result.pageSize);
res.status(200).send(result);
} catch (error) {
console.log(error);
res.status(500).send({ message: 'Error searching' });
}
});
router.get('/clear', async function (req, res) {

View file

@ -0,0 +1,36 @@
import React from 'react';
export default function Pages({ pageNumber, pages, nextPage, previousPage }) {
const clickHandler = (func) => async (e) => {
e.preventDefault();
await func();
};
return (
<div className="m-auto mt-4 mb-2 flex items-center justify-center gap-2">
<button
onClick={clickHandler(previousPage)}
className={
'btn btn-small bg-transition m-auto flex gap-2 transition hover:bg-gray-800 disabled:text-gray-300 dark:text-white dark:disabled:text-gray-400' +
(pageNumber <= 1 ? ' hidden-visibility' : '')
}
disabled={pageNumber <= 1}
>
&lt;&lt;
</button>
<span className="flex-none text-gray-400">
{pageNumber} / {pages}
</span>
<button
onClick={clickHandler(nextPage)}
className={
'btn btn-small bg-transition m-auto flex gap-2 transition hover:bg-gray-800 disabled:text-gray-300 dark:text-white dark:disabled:text-gray-400' +
(pageNumber >= pages ? ' hidden-visibility' : '')
}
disabled={pageNumber >= pages}
>
&gt;&gt;
</button>
</div>
);
}

View file

@ -1,11 +1,7 @@
import React from 'react';
import Conversation from './Conversation';
export default function Conversations({ conversations, conversationId, pageNumber, pages, nextPage, previousPage, moveToTop }) {
const clickHandler = (func) => async (e) => {
e.preventDefault();
await func();
};
export default function Conversations({ conversations, conversationId, moveToTop }) {
return (
<>
@ -37,25 +33,6 @@ export default function Conversations({ conversations, conversationId, pageNumbe
/>
);
})}
<div className="m-auto mt-4 mb-2 flex justify-center items-center gap-2">
<button
onClick={clickHandler(previousPage)}
className={"flex btn btn-small transition bg-transition dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400 m-auto gap-2 hover:bg-gray-800" + (pageNumber<=1?" hidden-visibility":"")}
disabled={pageNumber<=1}
>
&lt;&lt;
</button>
<span className="flex-none text-gray-400">
{pageNumber} / {pages}
</span>
<button
onClick={clickHandler(nextPage)}
className={"flex btn btn-small transition bg-transition dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400 m-auto gap-2 hover:bg-gray-800" + (pageNumber>=pages?" hidden-visibility":"")}
disabled={pageNumber>=pages}
>
&gt;&gt;
</button>
</div>
</>
);
}

View file

@ -6,10 +6,10 @@ import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode';
import Logout from './Logout';
export default function NavLinks() {
export default function NavLinks({ onSearchSuccess, clearSearch }) {
return (
<>
<SearchBar />
<SearchBar onSuccess={onSearchSuccess} clearSearch={clearSearch}/>
<ClearConvos />
<DarkMode />
<Logout />

View file

@ -1,18 +1,34 @@
import React, { useState, useCallback } from 'react';
import { debounce } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { Search } from 'lucide-react';
import { setQuery } from '~/store/searchSlice';
import { setPage, refreshConversation } from '~/store/convoSlice';
import { setConvos, refreshConversation } from '~/store/convoSlice';
import axios from 'axios';
export default function SearchBar() {
const fetch = async (q, pageNumber, callback) => {
const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`);
console.log(data);
callback(data);
};
export default function SearchBar({ onSuccess, clearSearch }) {
const dispatch = useDispatch();
const [inputValue, setInputValue] = useState('');
const { search } = useSelector((state) => state.search);
// const onSuccess = (data) => {
// const { conversations, pages, pageNumber } = data;
// dispatch(setConvos({ convos: conversations, searchFetch: true }));
// dispatch(setPage(pageNumber));
// dispatch(setPages(pages));
// };
const debouncedChangeHandler = useCallback(
debounce((q) => {
dispatch(setQuery(q));
if (q.length > 0) {
fetch(q, 1, onSuccess);
}
}, 750),
[dispatch]
);
@ -22,27 +38,27 @@ export default function SearchBar() {
if (e.keyCode === 8 && value === '') {
// Value after clearing input: ""
console.log(`Value after clearing input: "${value}"`);
dispatch(setPage(1));
dispatch(setQuery(''));
dispatch(refreshConversation());
clearSearch();
}
};
const changeHandler = (e) => {
if (!search) {
console.log('setting page to 1');
dispatch(setPage(1));
}
// 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));
if (q === '') {
dispatch(setQuery(''));
dispatch(refreshConversation());
// dispatch(setPage(1));
// dispatch(refreshConversation());
clearSearch();
} else {
debouncedChangeHandler(q);
}

View file

@ -1,36 +1,63 @@
import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
import _ from 'lodash';
import NewChat from './NewChat';
import Spinner from '../svg/Spinner';
import Pages from '../Conversations/Pages';
import Conversations from '../Conversations';
import NavLinks from './NavLinks';
import { swr } from '~/utils/fetchers';
import { useDispatch, useSelector } from 'react-redux';
import { increasePage, decreasePage, setPage, setConvos, setPages } from '~/store/convoSlice';
import { setConvos, refreshConversation } from '~/store/convoSlice';
const fetch = async (q, pageNumber, callback) => {
const { data } = await axios.get(`/api/search?q=${q}&pageNumber=${pageNumber}`);
console.log(data);
callback(data);
};
export default function Nav({ navVisible, setNavVisible }) {
const dispatch = useDispatch();
const [isHovering, setIsHovering] = useState(false);
const [pages, setPages] = useState(1);
const [pageNumber, setPage] = useState(1);
const { search, query } = useSelector((state) => state.search);
const { conversationId, convos, pages, pageNumber, refreshConvoHint } = useSelector(
(state) => state.convo
);
const onSuccess = (data) => {
const { conversations, pages } = data;
const { conversationId, convos, refreshConvoHint } = useSelector((state) => state.convo);
const onSuccess = (data, searchFetch = false) => {
if (search) {
return;
}
const { conversations, pages } = data;
if (pageNumber > pages) {
dispatch(setPage(pages));
setPage(pages);
} else {
dispatch(setConvos(conversations));
dispatch(setPages(pages));
dispatch(setConvos({ convos: conversations, searchFetch }));
setPages(pages);
}
};
const { data, isLoading, mutate } = swr(`/api/${search ? `search?q=${query}&pageNumber=${pageNumber}` : `convos?pageNumber=${pageNumber}`}`, onSuccess, {
const onSearchSuccess = (data, expectedPage) => {
const res = data;
dispatch(setConvos({ convos: res.conversations, searchFetch: true }));
if (expectedPage) {
setPage(expectedPage);
}
setPage(res.pageNumber);
setPages(res.pages);
};
const clearSearch = () => {
setPage(1);
dispatch(refreshConversation());
};
const { data, isLoading, mutate } = swr(`/api/convos?pageNumber=${pageNumber}`, onSuccess, {
revalidateOnMount: false,
revalidateIfStale: !search,
revalidateOnFocus: !search,
revalidateOnReconnect: !search,
populateCache: !search,
// populateCache: false,
// revalidateIfStale: false,
// revalidateOnFocus: false,
// revalidateOnReconnect : false,
});
const containerRef = useRef(null);
@ -46,19 +73,29 @@ export default function Nav({ navVisible, setNavVisible }) {
const nextPage = async () => {
moveToTop();
dispatch(increasePage());
if (!search) {
setPage((prev) => prev + 1);
await mutate();
} else {
await fetch(query, +pageNumber + 1, _.partialRight(onSearchSuccess, +pageNumber + 1));
}
};
const previousPage = async () => {
moveToTop();
dispatch(decreasePage());
if (!search) {
setPage((prev) => prev - 1);
await mutate();
} else {
await fetch(query, +pageNumber - 1, _.partialRight(onSearchSuccess, +pageNumber - 1));
}
};
useEffect(() => {
if (!search) {
mutate();
}
}, [pageNumber, conversationId, refreshConvoHint]);
useEffect(() => {
@ -114,16 +151,21 @@ export default function Nav({ navVisible, setNavVisible }) {
<Conversations
conversations={convos}
conversationId={conversationId}
nextPage={nextPage}
previousPage={previousPage}
moveToTop={moveToTop}
pageNumber={pageNumber}
pages={pages}
/>
)}
<Pages
pageNumber={pageNumber}
pages={pages}
nextPage={nextPage}
previousPage={previousPage}
/>
</div>
</div>
<NavLinks />
<NavLinks
onSearchSuccess={onSearchSuccess}
clearSearch={clearSearch}
/>
</nav>
</div>
</div>

View file

@ -17,7 +17,7 @@ const initialState = {
refreshConvoHint: 0,
search: false,
latestMessage: null,
convos: [],
convos: []
};
const currentSlice = createSlice({
@ -57,9 +57,12 @@ const currentSlice = createSlice({
state.latestMessage = null;
},
setConvos: (state, action) => {
state.convos = action.payload.sort(
(a, b) => new Date(b.createdAt) - new Date(a.createdAt)
);
const { convos, searchFetch } = action.payload;
if (searchFetch) {
state.convos = convos;
} else {
state.convos = convos.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
}
},
setPages: (state, action) => {
state.pages = action.payload;
@ -72,11 +75,23 @@ const currentSlice = createSlice({
},
setLatestMessage: (state, action) => {
state.latestMessage = action.payload;
},
}
}
});
export const { refreshConversation, setConversation, setPages, setConvos, setNewConvo, setError, increasePage, decreasePage, setPage, removeConvo, removeAll, setLatestMessage } =
currentSlice.actions;
export const {
refreshConversation,
setConversation,
setPages,
setConvos,
setNewConvo,
setError,
increasePage,
decreasePage,
setPage,
removeConvo,
removeAll,
setLatestMessage
} = currentSlice.actions;
export default currentSlice.reducer;

View file

@ -16,9 +16,9 @@ const currentSlice = createSlice({
const q = action.payload;
state.query = q;
if (!q || q === '') {
if (q === '') {
state.search = false;
} else {
} else if (q?.length > 0 && !state.search) {
state.search = true;
}
},

View file

@ -4,6 +4,10 @@ import useSWR from 'swr';
import useSWRMutation from 'swr/mutation';
const fetcher = (url) => fetch(url, { credentials: 'include' }).then((res) => res.json());
const axiosFetcher = async (url, params) => {
console.log(params, 'params');
return axios.get(url, params);
};
const postRequest = async (url, { arg }) => {
return await axios.post(url, { withCredentials: true, arg });
@ -17,7 +21,7 @@ export const swr = (path, successCallback, options) => {
}
return useSWR(path, fetcher, _options);
}
};
export default function manualSWR(path, type, successCallback) {
const options = {};
@ -28,3 +32,16 @@ export default function manualSWR(path, type, successCallback) {
const fetchFunction = type === 'get' ? fetcher : postRequest;
return useSWRMutation(path, fetchFunction, options);
}
export function useManualSWR({ path, params, type, onSuccess }) {
const options = {};
if (onSuccess) {
options.onSuccess = onSuccess;
}
console.log(params, 'params');
const fetchFunction = type === 'get' ? _.partialRight(axiosFetcher, params) : postRequest;
return useSWRMutation(path, fetchFunction, options);
}