fix chats sorting
This commit is contained in:
parent
9b4870995f
commit
fde1f8ecfe
95
package-lock.json
generated
95
package-lock.json
generated
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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`)
|
||||||
|
|
||||||
|
39
src/backend/redux/api_slice.js
Normal file
39
src/backend/redux/api_slice.js
Normal 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;
|
12
src/backend/redux/store.js
Normal file
12
src/backend/redux/store.js
Normal 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;
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user