fix chats sorting

This commit is contained in:
Nikolai Petukhov 2024-10-16 23:16:06 +03:00
parent 9b4870995f
commit fde1f8ecfe
7 changed files with 216 additions and 52 deletions

95
package-lock.json generated
View File

@ -11,6 +11,7 @@
"@brojs/cli": "^1.0.0", "@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0", "@brojs/create": "^1.0.0",
"@ijl/cli": "^5.1.0", "@ijl/cli": "^5.1.0",
"@reduxjs/toolkit": "^2.3.0",
"@types/react": "^18.3.5", "@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@ -21,6 +22,7 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-emoji-picker": "^1.0.13", "react-emoji-picker": "^1.0.13",
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5", "react-toastify": "^10.0.5",
"socket.io": "^4.8.0", "socket.io": "^4.8.0",
@ -3057,6 +3059,40 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz",
"integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==",
"license": "MIT",
"dependencies": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@reduxjs/toolkit/node_modules/immer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.19.1", "version": "1.19.1",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz",
@ -3174,6 +3210,12 @@
"source-map": "^0.6.1" "source-map": "^0.6.1"
} }
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
"license": "MIT"
},
"node_modules/@types/webpack": { "node_modules/@types/webpack": {
"version": "4.41.39", "version": "4.41.39",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.39.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.39.tgz",
@ -8288,6 +8330,29 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"node_modules/react-redux": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz",
"integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.3",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^18.2.25",
"react": "^18.0",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-router": { "node_modules/react-router": {
"version": "6.26.1", "version": "6.26.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz",
@ -8411,6 +8476,21 @@
"recursive-watch": "bin.js" "recursive-watch": "bin.js"
} }
}, },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -8519,6 +8599,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.8", "version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@ -9829,6 +9915,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
"integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/util-deprecate": { "node_modules/util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -3,6 +3,7 @@
"@brojs/cli": "^1.0.0", "@brojs/cli": "^1.0.0",
"@brojs/create": "^1.0.0", "@brojs/create": "^1.0.0",
"@ijl/cli": "^5.1.0", "@ijl/cli": "^5.1.0",
"@reduxjs/toolkit": "^2.3.0",
"@types/react": "^18.3.5", "@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
@ -13,6 +14,7 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-emoji-picker": "^1.0.13", "react-emoji-picker": "^1.0.13",
"react-icons": "^5.3.0", "react-icons": "^5.3.0",
"react-redux": "^9.1.2",
"react-router-dom": "^6.26.1", "react-router-dom": "^6.26.1",
"react-toastify": "^10.0.5", "react-toastify": "^10.0.5",
"socket.io": "^4.8.0", "socket.io": "^4.8.0",

View File

@ -6,6 +6,9 @@ import { Dashboard } from './dashboard';
import {ToastContainer} from "react-toastify"; import {ToastContainer} from "react-toastify";
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
import { Provider } from 'react-redux';
import store from './backend/redux/store.js'; // Import your store
import './index.css' import './index.css'
import {displayMessage} from "./backend/notifications/notifications.js"; import {displayMessage} from "./backend/notifications/notifications.js";
@ -26,13 +29,13 @@ const App = () => {
}, []); }, []);
return( return(
<div> <Provider store={store}>
<BrowserRouter> <BrowserRouter>
<Dashboard /> <Dashboard />
</BrowserRouter> </BrowserRouter>
<ToastContainer/> <ToastContainer/>
</div> </Provider>
) )
} }

View File

@ -4,7 +4,7 @@ import {getConfigValue} from "@brojs/cli";
const LOCAL = "http://localhost:8099"; const LOCAL = "http://localhost:8099";
const DEV = "https://dev.bro-js.ru"; const DEV = "https://dev.bro-js.ru";
export const BASE_API_URL = DEV + getConfigValue("enterfront.api"); export const BASE_API_URL = LOCAL + getConfigValue("enterfront.api");
// fetch(`${BASE_API_URL}/books/list`) // fetch(`${BASE_API_URL}/books/list`)

View File

@ -0,0 +1,39 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import {getConfigValue} from "@brojs/cli";
const LOCAL = "http://localhost:8099";
const DEV = "https://dev.bro-js.ru";
export const BASE_API_URL = LOCAL + getConfigValue("enterfront.api");
const baseQuery = fetchBaseQuery({
baseUrl: BASE_API_URL,
prepareHeaders: (headers) => {
const token = localStorage.getItem('token');
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
});
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery,
endpoints: (builder) => ({
getChats: builder.query({
query: (username) => `/chat/list/${username}`,
}),
postChat: builder.mutation({
query: (body) => ({
url: '/chat/post',
method: 'POST',
body,
}),
}),
// Add more endpoints as needed
}),
});
// Export hooks for usage in functional components
export const { useGetChatsQuery, usePostChatMutation } = apiSlice;

View File

@ -0,0 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import { apiSlice } from './api_slice';
const store = configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(apiSlice.middleware),
});
export default store;

View File

@ -4,39 +4,54 @@ import ChatsList from "../components/home/ChatsList.jsx";
import Header from "../components/home/Header.jsx"; import Header from "../components/home/Header.jsx";
import { displayMessage } from "../backend/notifications/notifications"; import { displayMessage } from "../backend/notifications/notifications";
import { MessageType } from "../backend/notifications/message"; import { MessageType } from "../backend/notifications/message";
import { get, post } from "../backend/api"; import { useGetChatsQuery, usePostChatMutation } from "../backend/redux/api_slice"; // Update the import based on your API slice
import InputField from "../components/reg/InputField.jsx"; import InputField from "../components/reg/InputField.jsx";
import Search from "../components/home/Search.jsx"; import Search from "../components/home/Search.jsx";
import { URLs } from "../__data__/urls"; import { URLs } from "../__data__/urls";
const Home = () => { const Home = () => {
const [chats, setChats] = useState([]); const [chats, setChats] = useState([]); // Retained original variable name
const [interlocutor, setInterlocutor] = useState(""); const [interlocutor, setInterlocutor] = useState("");
async function retrieveChats() { const username = localStorage.getItem("username");
const username = localStorage.getItem("username");
if (!username) { // Use Redux Queries
displayMessage("You're not logged in!", MessageType.WARN); const { data: chatsData, error: getError, isLoading: isGetting } = useGetChatsQuery(username, {
return; skip: !username
});
console.log('From Redux:', chatsData);
const [createChat, { error: postError }] = usePostChatMutation();
useEffect(() => {
if (getError) {
displayMessage(getError.message, MessageType.ERROR);
} }
}, [getError]);
const { ok, data } = await get("/chat/list/" + username); useEffect(() => {
if (!ok) { if (chatsData) {
displayMessage(data.message, MessageType.ERROR); setChats(chatsData.chats);
return;
try {
// const sortedChats = chatsData.chats.sort((a, b) => {
// const lastMessageA = new Date(a.timestamp);
// const lastMessageB = new Date(b.timestamp);
// return lastMessageB - lastMessageA;
// });
// //
// // console.log('sorted:', sortedChats);
//
// setChats(sortedChats);
} catch (e) {
console.error(e);
}
} }
}, [chatsData]);
const sortedChats = data.chats.sort((a, b) => {
const lastMessageA = new Date(a.lastMessageTimestamp);
const lastMessageB = new Date(b.lastMessageTimestamp);
return lastMessageB - lastMessageA;
});
setChats(sortedChats); const createChatHandler = async (alias) => {
}
async function createChat(alias) {
const username = localStorage.getItem("username");
if (!username) { if (!username) {
displayMessage("You're not logged in!", MessageType.WARN); displayMessage("You're not logged in!", MessageType.WARN);
return; return;
@ -44,42 +59,40 @@ const Home = () => {
displayMessage("Sent", MessageType.INFO); displayMessage("Sent", MessageType.INFO);
const { ok, data } = await post("/chat/item/" + username + "/" + alias); try {
if (!ok) { const data = await createChat({ alias, username }).unwrap(); // Using unwrap to handle promise rejection
displayMessage(data.message, MessageType.ERROR);
} else {
localStorage.setItem("message", "Successfully opened chat!"); localStorage.setItem("message", "Successfully opened chat!");
localStorage.setItem("interlocutorId", alias); localStorage.setItem("interlocutorId", alias);
window.location.href = URLs.chat.url; window.location.href = URLs.chat.url;
} catch (error) {
displayMessage(error.data.message, MessageType.ERROR);
} }
} };
useEffect(() => { if (isGetting) return <div>Loading...</div>;
retrieveChats();
}, []);
return ( return (
<div className="homeWrapper"> <div className="homeWrapper">
<div className="headerPos"> <div className="headerPos">
<Header /> <Header />
</div>
<HomeTitle />
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<Search search={createChatHandler} item={interlocutor} />
<p>Your chats</p>
<ChatsList chats={chats} /> {/* Retained original variable name */}
</div> </div>
<HomeTitle />
<div className="search-input">
<InputField
title="Create new chat"
value={interlocutor}
setValue={setInterlocutor}
placeholder="Enter the username (id)"
/>
</div>
<Search search={createChat} item={interlocutor} />
<p>Your chats</p>
<ChatsList chats={chats} />
</div>
); );
}; };