Compare commits
No commits in common. "main" and "v0.5.2" have entirely different histories.
99
package-lock.json
generated
99
package-lock.json
generated
@ -1,17 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "enterfront",
|
"name": "enterfront",
|
||||||
"version": "0.5.4",
|
"version": "0.5.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "enterfront",
|
"name": "enterfront",
|
||||||
"version": "0.5.4",
|
"version": "0.5.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@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",
|
||||||
@ -22,7 +21,6 @@
|
|||||||
"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",
|
||||||
@ -3059,40 +3057,6 @@
|
|||||||
"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",
|
||||||
@ -3210,12 +3174,6 @@
|
|||||||
"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",
|
||||||
@ -8330,29 +8288,6 @@
|
|||||||
"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",
|
||||||
@ -8476,21 +8411,6 @@
|
|||||||
"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",
|
||||||
@ -8599,12 +8519,6 @@
|
|||||||
"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",
|
||||||
@ -9915,15 +9829,6 @@
|
|||||||
"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",
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
"@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",
|
||||||
@ -14,7 +13,6 @@
|
|||||||
"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",
|
||||||
@ -31,5 +29,5 @@
|
|||||||
"clean": "rimraf dist"
|
"clean": "rimraf dist"
|
||||||
},
|
},
|
||||||
"name": "enterfront",
|
"name": "enterfront",
|
||||||
"version": "0.5.4"
|
"version": "0.5.2"
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ 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";
|
||||||
@ -29,13 +26,13 @@ const App = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<Provider store={store}>
|
<div>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Dashboard />
|
<Dashboard />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|
||||||
<ToastContainer/>
|
<ToastContainer/>
|
||||||
</Provider>
|
</div>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
|
||||||
import {getConfigValue} from "@brojs/cli";
|
|
||||||
|
|
||||||
import { BASE_API_URL } from "../api.js";
|
|
||||||
|
|
||||||
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: ({ id1, id2 }) => ({
|
|
||||||
url: `/chat/item/${id1}/${id2}`,
|
|
||||||
method: 'POST',
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Export hooks for usage in functional components
|
|
||||||
export const { useGetChatsQuery, usePostChatMutation } = apiSlice;
|
|
@ -1,12 +0,0 @@
|
|||||||
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;
|
|
@ -13,7 +13,7 @@ const Account = () => {
|
|||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
|
|
||||||
localStorage.setItem("message", "Exited successfully!");
|
localStorage.setItem("message", "Exited successfully!");
|
||||||
window.location.href = URLs.baseUrl;
|
window.location.href = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
const [nickname, setNickname] = useState("");
|
const [nickname, setNickname] = useState("");
|
||||||
|
@ -4,61 +4,39 @@ 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 { useGetChatsQuery, usePostChatMutation } from "../backend/redux/api_slice"; // Update the import based on your API slice
|
import { get, post } from "../backend/api";
|
||||||
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([]); // Retained original variable name
|
const [chats, setChats] = useState([]);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
if (getError) {
|
|
||||||
displayMessage(getError.message, MessageType.ERROR);
|
const { ok, data } = await get("/chat/list/" + username);
|
||||||
|
if (!ok) {
|
||||||
|
displayMessage(data.message, MessageType.ERROR);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, [getError, postError]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const sortedChats = data.chats.sort((a, b) => {
|
||||||
if (chatsData) {
|
const lastMessageA = new Date(a.lastMessageTimestamp);
|
||||||
// setChats(chatsData.chats);
|
const lastMessageB = new Date(b.lastMessageTimestamp);
|
||||||
|
return lastMessageB - lastMessageA;
|
||||||
let data = chatsData.chats;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const sortedChats = [...data].sort((a, b) => {
|
|
||||||
const lastMessageA = a.messages[a.messages.length - 1];
|
|
||||||
const lastMessageB = b.messages[b.messages.length - 1];
|
|
||||||
|
|
||||||
const dateA = new Date(lastMessageA.timestamp);
|
|
||||||
const dateB = new Date(lastMessageB.timestamp);
|
|
||||||
|
|
||||||
return dateB - dateA;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setChats(sortedChats);
|
setChats(sortedChats);
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, [chatsData]);
|
|
||||||
|
|
||||||
|
async function createChat(alias) {
|
||||||
const createChatHandler = async (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;
|
||||||
@ -66,15 +44,19 @@ const Home = () => {
|
|||||||
|
|
||||||
displayMessage("Sent", MessageType.INFO);
|
displayMessage("Sent", MessageType.INFO);
|
||||||
|
|
||||||
try {
|
const { ok, data } = await post("/chat/item/" + username + "/" + alias);
|
||||||
const data = await createChat({ id1: alias, id2: username }).unwrap(); // Using unwrap to handle promise rejection
|
if (!ok) {
|
||||||
|
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(() => {
|
||||||
|
retrieveChats();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="homeWrapper">
|
<div className="homeWrapper">
|
||||||
@ -90,24 +72,13 @@ const Home = () => {
|
|||||||
value={interlocutor}
|
value={interlocutor}
|
||||||
setValue={setInterlocutor}
|
setValue={setInterlocutor}
|
||||||
placeholder="Enter the username (id)"
|
placeholder="Enter the username (id)"
|
||||||
|
|
||||||
enter={createChatHandler}
|
|
||||||
submit={interlocutor}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isGetting ? (
|
<Search search={createChat} item={interlocutor} />
|
||||||
<div>Loading...</div>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Search search={createChatHandler} item={interlocutor} />
|
|
||||||
|
|
||||||
<p>Your chats</p>
|
<p>Your chats</p>
|
||||||
<ChatsList chats={chats} />
|
<ChatsList chats={chats} />
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user