group posts
This commit is contained in:
1436
package-lock.json
generated
1436
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,13 +18,14 @@
|
|||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"framer-motion": "^11.9.0",
|
"framer-motion": "^11.9.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"monaco-editor": "^0.54.0",
|
"monaco-editor": "^0.53.0",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-markdown": "^10.1.0",
|
"react-markdown": "^10.1.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-router-dom": "^7.9.4",
|
"react-router-dom": "^7.9.4",
|
||||||
|
"react-toastify": "^11.0.5",
|
||||||
"rehype-highlight": "^7.0.2",
|
"rehype-highlight": "^7.0.2",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"rehype-sanitize": "^6.0.0",
|
"rehype-sanitize": "^6.0.0",
|
||||||
@@ -45,6 +46,6 @@
|
|||||||
"globals": "^15.9.0",
|
"globals": "^15.9.0",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.0.1",
|
"typescript-eslint": "^8.0.1",
|
||||||
"vite": "^5.4.1"
|
"vite": "^7.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ export const SearchInput: React.FC<searchInputProps> = ({
|
|||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative bg-liquid-lighter w-[200px] h-[40px] rounded-full px-[16px] pl-[50px] py-[8px] cursor-text',
|
'relative bg-liquid-lighter w-[200px] h-[40px] flex rounded-full px-[16px] pl-[50px] cursor-text',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
className={cn(
|
className={cn(
|
||||||
'placeholder:text-liquid-light h-[28px] w-[200px] bg-transparent outline-none text-liquid-white ',
|
'placeholder:text-liquid-light h-[28px] w-[200px] bg-transparent outline-none text-liquid-white my-[6px]',
|
||||||
)}
|
)}
|
||||||
value={value}
|
value={value}
|
||||||
name={name}
|
name={name}
|
||||||
|
|||||||
34
src/lib/toastNotification.ts
Normal file
34
src/lib/toastNotification.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
export const toastSuccess = (mes: string, autoClose: number = 3000) => {
|
||||||
|
toast.success(mes, {
|
||||||
|
position: 'top-right',
|
||||||
|
autoClose: autoClose,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toastWarning = (mes: string, autoClose: number = 3000) => {
|
||||||
|
toast.warning(mes, {
|
||||||
|
position: 'top-right',
|
||||||
|
autoClose: autoClose,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toastError = (mes: string, autoClose: number = 3000) => {
|
||||||
|
toast.error(mes, {
|
||||||
|
position: 'top-right',
|
||||||
|
autoClose: autoClose,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -6,11 +6,13 @@ import './styles/palette/theme-light.css';
|
|||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { store } from './redux/store';
|
import { store } from './redux/store';
|
||||||
|
import { ToastContainer } from 'react-toastify';
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
|
<ToastContainer />
|
||||||
</Provider>
|
</Provider>
|
||||||
</BrowserRouter>,
|
</BrowserRouter>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ import { MissionsRightPanel } from '../views/home/rightpanel/Missions';
|
|||||||
import { ArticlesRightPanel } from '../views/home/rightpanel/Articles';
|
import { ArticlesRightPanel } from '../views/home/rightpanel/Articles';
|
||||||
import { GroupRightPanel } from '../views/home/rightpanel/Group';
|
import { GroupRightPanel } from '../views/home/rightpanel/Group';
|
||||||
import GroupInvite from '../views/home/groupinviter/GroupInvite';
|
import GroupInvite from '../views/home/groupinviter/GroupInvite';
|
||||||
|
import {
|
||||||
|
toastError,
|
||||||
|
toastSuccess,
|
||||||
|
toastWarning,
|
||||||
|
} from '../lib/toastNotification';
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const name = useAppSelector((state) => state.auth.username);
|
const name = useAppSelector((state) => state.auth.username);
|
||||||
@@ -75,6 +80,30 @@ const Home = () => {
|
|||||||
>
|
>
|
||||||
выйти
|
выйти
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
|
|
||||||
|
<div className="flex mt-[20px] gap-[20px]">
|
||||||
|
<PrimaryButton
|
||||||
|
color="success"
|
||||||
|
text="Toast"
|
||||||
|
onClick={() => {
|
||||||
|
toastSuccess('Success');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PrimaryButton
|
||||||
|
color="warning"
|
||||||
|
text="Toast"
|
||||||
|
onClick={() => {
|
||||||
|
toastWarning('Warning');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<PrimaryButton
|
||||||
|
color="error"
|
||||||
|
text="Toast"
|
||||||
|
onClick={() => {
|
||||||
|
toastError('Error');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
@import 'tailwindcss/utilities';
|
@import 'tailwindcss/utilities';
|
||||||
|
|
||||||
@import './latex-container.css';
|
@import './latex-container.css';
|
||||||
|
@import './toast.css';
|
||||||
|
|
||||||
* {
|
* {
|
||||||
-webkit-tap-highlight-color: transparent; /* Отключаем выделение синим при тапе на телефоне*/
|
-webkit-tap-highlight-color: transparent; /* Отключаем выделение синим при тапе на телефоне*/
|
||||||
|
|||||||
32
src/styles/toast.css
Normal file
32
src/styles/toast.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.Toastify__progress-bar--success {
|
||||||
|
background: #10be59 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast--success .Toastify__toast-icon svg path {
|
||||||
|
fill: #10be59 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__progress-bar--error {
|
||||||
|
background: #f13e5f !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast--error .Toastify__toast-icon svg path {
|
||||||
|
fill: #f13e5f !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__progress-bar--success {
|
||||||
|
background: #10be59 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast--success .Toastify__toast-icon svg path {
|
||||||
|
fill: #10be59 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast {
|
||||||
|
background: #292929 !important;
|
||||||
|
color: var(--color-liquid-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Toastify__toast > button > svg {
|
||||||
|
fill: var(--color-liquid-white);
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useAppSelector, useAppDispatch } from '../../../../redux/hooks';
|
import { useAppSelector, useAppDispatch } from '../../../../redux/hooks';
|
||||||
import { fetchGroupPosts } from '../../../../redux/slices/groupfeed';
|
import { fetchGroupPosts } from '../../../../redux/slices/groupfeed';
|
||||||
import { SearchInput } from '../../../../components/input/SearchInput';
|
import { SearchInput } from '../../../../components/input/SearchInput';
|
||||||
import { setMenuActiveGroupPage } from '../../../../redux/slices/store';
|
import { setMenuActiveGroupPage } from '../../../../redux/slices/store';
|
||||||
|
import { fetchGroupById } from '../../../../redux/slices/groups';
|
||||||
|
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
|
||||||
|
|
||||||
interface PostsProps {
|
interface PostsProps {
|
||||||
groupId: number;
|
groupId: number;
|
||||||
@@ -12,28 +14,59 @@ interface PostsProps {
|
|||||||
export const Posts: FC<PostsProps> = ({ groupId }) => {
|
export const Posts: FC<PostsProps> = ({ groupId }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||||
const { pages, status } = useAppSelector(
|
const { pages, status } = useAppSelector(
|
||||||
(state) => state.groupfeed.fetchPosts,
|
(state) => state.groupfeed.fetchPosts,
|
||||||
);
|
);
|
||||||
|
const { id: userId } = useAppSelector((state) => state.auth);
|
||||||
|
|
||||||
|
const { group, status: statusGroup } = useAppSelector(
|
||||||
|
(state) => state.groups.fetchGroupById,
|
||||||
|
);
|
||||||
|
|
||||||
// Загружаем только первую страницу
|
// Загружаем только первую страницу
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(fetchGroupPosts({ groupId, page: 0, pageSize: 20 }));
|
dispatch(fetchGroupPosts({ groupId, page: 0, pageSize: 20 }));
|
||||||
|
dispatch(fetchGroupById(groupId));
|
||||||
}, [groupId]);
|
}, [groupId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setMenuActiveGroupPage('home'));
|
dispatch(setMenuActiveGroupPage('home'));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!group) return;
|
||||||
|
|
||||||
|
const isUserAdmin =
|
||||||
|
group.members?.some(
|
||||||
|
(m) =>
|
||||||
|
Number(m.userId) === Number(userId) &&
|
||||||
|
m.role.includes('Administrator'),
|
||||||
|
) || false;
|
||||||
|
|
||||||
|
setIsAdmin(isUserAdmin);
|
||||||
|
}, [group, userId]);
|
||||||
|
|
||||||
const page0 = pages[0];
|
const page0 = pages[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full overflow-y-scroll thin-dark-scrollbar">
|
<div className="h-full overflow-y-scroll thin-dark-scrollbar">
|
||||||
<div className="h-[40px] mb-[20px]">
|
<div className="h-[40px] mb-[20px] relative">
|
||||||
<SearchInput
|
<SearchInput
|
||||||
|
className="w-[216px]"
|
||||||
onChange={(v) => {}}
|
onChange={(v) => {}}
|
||||||
placeholder="Поиск сообщений"
|
placeholder="Поиск сообщений"
|
||||||
/>
|
/>
|
||||||
|
{isAdmin && (
|
||||||
|
<div className=" h-[40px] w-[200px] absolute top-0 right-0 flex items-center">
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
// setModalActive(true);
|
||||||
|
}}
|
||||||
|
text="Добавить задачу"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{status === 'loading' && <div>Загрузка...</div>}
|
{status === 'loading' && <div>Загрузка...</div>}
|
||||||
|
|||||||
Reference in New Issue
Block a user