mirror of
https://github.com/Radiquum/AniX.git
synced 2025-04-05 07:44:38 +00:00
Compare commits
3 commits
0730b7c7d4
...
762c2f324a
Author | SHA1 | Date | |
---|---|---|---|
762c2f324a | |||
5572f31c60 | |||
256ecea885 |
7 changed files with 561 additions and 84 deletions
|
@ -19,6 +19,14 @@ export const ENDPOINTS = {
|
|||
history: `${API_PREFIX}/history`,
|
||||
favorite: `${API_PREFIX}/favorite`,
|
||||
blocklist: `${API_PREFIX}/profile/blocklist`,
|
||||
friend: {
|
||||
list: `${API_PREFIX}/profile/friend/all`,
|
||||
add: `${API_PREFIX}/profile/friend/request/send`,
|
||||
remove: `${API_PREFIX}/profile/friend/request/remove`,
|
||||
hide: `${API_PREFIX}/profile/friend/request/hide`,
|
||||
in: `${API_PREFIX}/profile/friend/requests/in`,
|
||||
out: `${API_PREFIX}/profile/friend/requests/out`,
|
||||
},
|
||||
settings: {
|
||||
my: `${API_PREFIX}/profile/preference/my`,
|
||||
login: {
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
"use client";
|
||||
import { Button, ButtonGroup, Card } from "flowbite-react";
|
||||
// import Link from "next/link";
|
||||
// import { numberDeclension } from "#/api/utils";
|
||||
import { ProfileActivityCollections } from "./Profile.ActivityCollections";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CollectionCourusel } from "../CollectionCourusel/CollectionCourusel";
|
||||
import { ProfileActivityFriends } from "./Profile.ActivityFriends";
|
||||
import { ProfileActivityComment } from "./Profile.ActivityComment";
|
||||
|
||||
export function ProfileActivity(props: {
|
||||
profile_id: number;
|
||||
commentCount: number;
|
||||
videoCount: number;
|
||||
commentPreview: any;
|
||||
collectionCount: number;
|
||||
collectionPreview: any;
|
||||
friendsCount: number;
|
||||
friendsPreview: any;
|
||||
token: string;
|
||||
isMyProfile: boolean;
|
||||
}) {
|
||||
const [tab, setTab] = useState<
|
||||
"collections" | "comments" | "friends" | "videos"
|
||||
>("collections");
|
||||
const [tab, setTab] = useState<"collections" | "comments" | "friends">(
|
||||
"collections"
|
||||
);
|
||||
|
||||
const [collections, setCollections] = useState<Record<number, any>>({});
|
||||
|
||||
|
@ -62,25 +62,28 @@ export function ProfileActivity(props: {
|
|||
color={tab == "collections" ? "blue" : "light"}
|
||||
onClick={() => setTab("collections")}
|
||||
>
|
||||
Коллекции | {props.collectionCount}
|
||||
<div className="flex flex-col gap-1 sm:flex-row sm:items-center">
|
||||
<p>Коллекции</p>
|
||||
<p>( {props.collectionCount} )</p>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
color={tab == "comments" ? "blue" : "light"}
|
||||
onClick={() => setTab("comments")}
|
||||
>
|
||||
Комментарии | {props.commentCount}
|
||||
<div className="flex flex-col gap-1 sm:flex-row sm:items-center">
|
||||
<p>Комментарии</p>
|
||||
<p>( {props.commentCount} )</p>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
color={tab == "friends" ? "blue" : "light"}
|
||||
onClick={() => setTab("friends")}
|
||||
>
|
||||
Друзья | {props.friendsCount}
|
||||
</Button>
|
||||
<Button
|
||||
color={tab == "videos" ? "blue" : "light"}
|
||||
onClick={() => setTab("videos")}
|
||||
>
|
||||
Видео | {props.videoCount}
|
||||
<div className="flex flex-col gap-1 sm:flex-row sm:items-center">
|
||||
<p>Друзья</p>
|
||||
<p>( {props.friendsCount} )</p>
|
||||
</div>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
|
@ -90,9 +93,20 @@ export function ProfileActivity(props: {
|
|||
profile_id={props.profile_id}
|
||||
/>
|
||||
)}
|
||||
{tab == "comments" && <>comments</>}
|
||||
{tab == "friends" && <ProfileActivityFriends content={props.friendsPreview || []} />}
|
||||
{tab == "videos" && <>videos</>}
|
||||
{tab == "comments" && (
|
||||
<ProfileActivityComment
|
||||
content={props.commentPreview || []}
|
||||
profile_id={props.profile_id}
|
||||
/>
|
||||
)}
|
||||
{tab == "friends" && (
|
||||
<ProfileActivityFriends
|
||||
token={props.token}
|
||||
content={props.friendsPreview || []}
|
||||
isMyProfile={props.isMyProfile}
|
||||
profile_id={props.profile_id}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,11 +41,9 @@ export const ProfileActivityCollections = (props: {
|
|||
key={`col-prev-${collection.id}`}
|
||||
style={{ width: "fit-content" }}
|
||||
>
|
||||
<Link href={`/collection/${collection.id}`}>
|
||||
<div className="w-[400px] xl:w-[500px] aspect-video">
|
||||
<CollectionLink {...collection} />
|
||||
</div>
|
||||
</Link>
|
||||
<div className="w-[350px] xl:w-[500px] aspect-video">
|
||||
<CollectionLink {...collection} />
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
);
|
||||
})}
|
||||
|
@ -53,7 +51,7 @@ export const ProfileActivityCollections = (props: {
|
|||
{props.content && props.content.length > 0 ?
|
||||
<SwiperSlide style={{ width: "fit-content" }}>
|
||||
<Link href={`/profile/${props.profile_id}/collections`}>
|
||||
<div className="w-[400px] xl:w-[500px] aspect-video flex flex-col items-center justify-center w-full gap-2 text-black transition-colors bg-gray-100 border hover:bg-gray-200 border-gray-50 hover:border-gray-100 dark:border-gray-700 dark:hover:border-gray-600 dark:hover:bg-gray-500 aspect-video group dark:bg-gray-600 dark:text-white">
|
||||
<div className="w-[350px] xl:w-[500px] flex flex-col items-center justify-center gap-2 text-black transition-colors bg-gray-100 border hover:bg-gray-200 border-gray-50 hover:border-gray-100 dark:border-gray-700 dark:hover:border-gray-600 dark:hover:bg-gray-500 aspect-video group dark:bg-gray-600 dark:text-white">
|
||||
<span className="w-8 h-8 iconify mdi--arrow-right dark:fill-white"></span>
|
||||
<p>Все коллекции</p>
|
||||
</div>
|
||||
|
|
92
app/components/Profile/Profile.ActivityComment.tsx
Normal file
92
app/components/Profile/Profile.ActivityComment.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { sinceUnixDate, unixToDate } from "#/api/utils";
|
||||
|
||||
export const ProfileActivityComment = (props: {
|
||||
content: any;
|
||||
profile_id: number;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{props.content && props.content.length > 0 ?
|
||||
props.content.map((comment) => {
|
||||
let isHidden = comment.isSpoiler || comment.likes_count < -5 || false;
|
||||
return (
|
||||
<article
|
||||
className="px-4 py-2 text-sm bg-gray-100 rounded-lg sm:text-base dark:bg-gray-900"
|
||||
key={`comment-${comment.id}`}
|
||||
>
|
||||
<footer className="flex items-center justify-between mb-2">
|
||||
<div className="flex flex-col items-start gap-1 sm:items-center sm:flex-row">
|
||||
<Link
|
||||
href={`/profile/${comment.profile.id}`}
|
||||
className="inline-flex items-center mr-3 text-sm font-semibold text-gray-900 dark:text-white hover:underline"
|
||||
>
|
||||
<Image
|
||||
className="w-6 h-6 mr-2 rounded-full"
|
||||
width={24}
|
||||
height={24}
|
||||
src={comment.profile.avatar}
|
||||
alt=""
|
||||
/>
|
||||
{comment.profile.login}
|
||||
</Link>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
<time
|
||||
dateTime={comment.timestamp.toString()}
|
||||
title={unixToDate(comment.timestamp, "full")}
|
||||
>
|
||||
{sinceUnixDate(comment.timestamp)}
|
||||
</time>
|
||||
</p>
|
||||
<p
|
||||
className={`text-sm font-medium border px-1 py-0.5 rounded-md text-center ml-4 min-w-8 ${
|
||||
comment.likes_count > 0 ?
|
||||
"text-green-500 dark:text-green-400 border-green-500 dark:border-green-400"
|
||||
: comment.likes_count < 0 ?
|
||||
"text-red-500 dark:text-red-400 border-red-500 dark:border-red-400"
|
||||
: "text-gray-500 dark:text-gray-400 border-gray-500 dark:border-gray-400"
|
||||
}`}
|
||||
>
|
||||
{comment.likes_count}
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
<div className="relative flex flex-col py-2">
|
||||
{comment.release && typeof comment.release != "number" && (
|
||||
<Link href={`/release/${comment.release.id}`}>
|
||||
<p className="text-gray-900 whitespace-pre-wrap dark:text-gray-500">
|
||||
{!comment.isDeleted ?
|
||||
`К релизу: ${comment.release.title_ru || comment.release.title_alt || comment.release.title_original} (${comment.release.year || "?"}) >>`
|
||||
: ""}
|
||||
</p>
|
||||
</Link>
|
||||
)}
|
||||
<p className="text-gray-800 whitespace-pre-wrap dark:text-gray-400">
|
||||
{!comment.isDeleted ?
|
||||
comment.message
|
||||
: "Комментарий был удалён."}
|
||||
</p>
|
||||
{isHidden && (
|
||||
<button
|
||||
className="absolute top-0 bottom-0 left-0 right-0"
|
||||
onClick={() => isHidden == false}
|
||||
>
|
||||
<div className="min-w-full min-h-full px-2 py-1.5 rounded-md bg-black text-white bg-opacity-50 backdrop-blur-[8px] flex flex-col justify-center items-center">
|
||||
<p>
|
||||
{comment.likes_count < -5 ?
|
||||
"У комментария слишком низкий рейтинг."
|
||||
: "Данный комментарий может содержать спойлер."}
|
||||
</p>
|
||||
<p className="font-bold">Нажмите, чтобы прочитать</p>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})
|
||||
: <p className="text-lg">Пользователь не оставлял комментарии</p>}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -4,67 +4,84 @@ import "swiper/css/navigation";
|
|||
import "swiper/css/mousewheel";
|
||||
import "swiper/css/scrollbar";
|
||||
import { Navigation, Mousewheel, Scrollbar } from "swiper/modules";
|
||||
import { CollectionLink } from "../CollectionLink/CollectionLink";
|
||||
import Link from "next/link";
|
||||
import { Avatar, Button } from "flowbite-react";
|
||||
import { useState } from "react";
|
||||
import { ProfileFriendModal } from "./Profile.FriendsModal";
|
||||
|
||||
export const ProfileActivityFriends = (props: {
|
||||
content: any;
|
||||
token: string;
|
||||
isMyProfile: boolean;
|
||||
profile_id: number;
|
||||
}) => {
|
||||
const [isFriendModalOpen, setIsFriendModalOpen] = useState(false);
|
||||
|
||||
export const ProfileActivityFriends = (props: { content: any }) => {
|
||||
return (
|
||||
<div className="max-w-full">
|
||||
<Swiper
|
||||
modules={[Navigation, Mousewheel, Scrollbar]}
|
||||
spaceBetween={8}
|
||||
slidesPerView={"auto"}
|
||||
direction={"horizontal"}
|
||||
mousewheel={{
|
||||
enabled: true,
|
||||
sensitivity: 4,
|
||||
}}
|
||||
scrollbar={{
|
||||
enabled: true,
|
||||
draggable: true,
|
||||
}}
|
||||
allowTouchMove={true}
|
||||
style={
|
||||
{
|
||||
"--swiper-scrollbar-bottom": "0",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{props.content &&
|
||||
props.content.length > 0 &&
|
||||
props.content.map((profile) => {
|
||||
return (
|
||||
<SwiperSlide
|
||||
key={`friend-prev-${profile.id}`}
|
||||
style={{ width: "fit-content" }}
|
||||
className="px-2 py-4"
|
||||
>
|
||||
<Link href={`/profile/${profile.id}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar
|
||||
img={profile.avatar}
|
||||
size="md"
|
||||
rounded={true}
|
||||
bordered={true}
|
||||
color={profile.is_online ? "success" : "light"}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
<p className="text-lg">{profile.login}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</SwiperSlide>
|
||||
);
|
||||
})}
|
||||
{props.content && props.content.length > 0 ?
|
||||
<SwiperSlide style={{ width: "fit-content" }} className="px-2 py-4">
|
||||
<Button>
|
||||
<p className="text-lg">Все друзья</p>
|
||||
<span className="w-8 h-8 iconify mdi--arrow-right dark:fill-white"></span>
|
||||
</Button>
|
||||
</SwiperSlide>
|
||||
: <p className="text-lg">У пользователя нет друзей</p>}
|
||||
</Swiper>
|
||||
</div>
|
||||
<>
|
||||
<div className="max-w-full">
|
||||
<Swiper
|
||||
modules={[Navigation, Mousewheel, Scrollbar]}
|
||||
spaceBetween={8}
|
||||
slidesPerView={"auto"}
|
||||
direction={"horizontal"}
|
||||
mousewheel={{
|
||||
enabled: true,
|
||||
sensitivity: 4,
|
||||
}}
|
||||
scrollbar={{
|
||||
enabled: true,
|
||||
draggable: true,
|
||||
}}
|
||||
allowTouchMove={true}
|
||||
style={
|
||||
{
|
||||
"--swiper-scrollbar-bottom": "0",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
{props.content &&
|
||||
props.content.length > 0 &&
|
||||
props.content.map((profile) => {
|
||||
return (
|
||||
<SwiperSlide
|
||||
key={`friend-prev-${profile.id}`}
|
||||
style={{ width: "fit-content" }}
|
||||
className="px-2 py-4"
|
||||
>
|
||||
<Link href={`/profile/${profile.id}`}>
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar
|
||||
img={profile.avatar}
|
||||
size="md"
|
||||
rounded={true}
|
||||
bordered={true}
|
||||
color={profile.is_online ? "success" : "light"}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
<p className="text-lg">{profile.login}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</SwiperSlide>
|
||||
);
|
||||
})}
|
||||
{(props.content && props.content.length > 0) || props.isMyProfile ?
|
||||
<SwiperSlide style={{ width: "fit-content" }} className="px-2 py-4">
|
||||
<Button onClick={() => setIsFriendModalOpen(true)}>
|
||||
<p className="text-lg">Все друзья {props.isMyProfile ? "и заявки" : ""}</p>
|
||||
<span className="w-8 h-8 iconify mdi--arrow-right dark:fill-white"></span>
|
||||
</Button>
|
||||
</SwiperSlide>
|
||||
: <p className="text-lg">У пользователя нет друзей</p>}
|
||||
</Swiper>
|
||||
</div>
|
||||
<ProfileFriendModal
|
||||
isOpen={isFriendModalOpen}
|
||||
setIsOpen={setIsFriendModalOpen}
|
||||
token={props.token}
|
||||
isMyProfile={props.isMyProfile}
|
||||
profile_id={props.profile_id}
|
||||
></ProfileFriendModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
346
app/components/Profile/Profile.FriendsModal.tsx
Normal file
346
app/components/Profile/Profile.FriendsModal.tsx
Normal file
|
@ -0,0 +1,346 @@
|
|||
import { ENDPOINTS } from "#/api/config";
|
||||
import { tryCatchAPI, unixToDate, useSWRfetcher } from "#/api/utils";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
Modal,
|
||||
ModalHeader,
|
||||
useThemeMode,
|
||||
} from "flowbite-react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import useSWRInfinite from "swr/infinite";
|
||||
import { Spinner } from "../Spinner/Spinner";
|
||||
import { toast } from "react-toastify";
|
||||
import useSWR, { mutate } from "swr";
|
||||
import Link from "next/link";
|
||||
|
||||
export const ProfileFriendModal = (props: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
token: string;
|
||||
isMyProfile: boolean;
|
||||
profile_id: number;
|
||||
}) => {
|
||||
const [currentRef, setCurrentRef] = useState<any>(null);
|
||||
const theme = useThemeMode();
|
||||
const [actionsDisabled, setActionsDisabled] = useState(false);
|
||||
// const [requestInUsers, setRequestInUsers] = useState([]);
|
||||
// const [requestOutUsers, setRequestOutUsers] = useState([]);
|
||||
const [friends, setFriends] = useState([]);
|
||||
|
||||
const modalRef = useCallback((ref) => {
|
||||
setCurrentRef(ref);
|
||||
}, []);
|
||||
|
||||
const useFetchRequests = (url: string) => {
|
||||
const { data, error, isLoading } = useSWR(url, useSWRfetcher);
|
||||
return [data, error, isLoading];
|
||||
};
|
||||
|
||||
const [requestInUsersData, requestInUsersError, requestInUsersIsLoading] =
|
||||
useFetchRequests(
|
||||
props.isMyProfile ?
|
||||
`${ENDPOINTS.user.friend.in}/last?token=${props.token}&count=8`
|
||||
: ""
|
||||
);
|
||||
|
||||
const [requestOutUsersData, requestOutUsersError, requestOutUsersIsLoading] =
|
||||
useFetchRequests(
|
||||
props.isMyProfile ?
|
||||
`${ENDPOINTS.user.friend.out}/last?token=${props.token}&count=8`
|
||||
: ""
|
||||
);
|
||||
|
||||
async function _hideRequestIn(profile_id) {
|
||||
const tid = toast.loading("Скрываем заявку...", {
|
||||
position: "bottom-center",
|
||||
hideProgressBar: true,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
theme: theme.mode == "light" ? "light" : "dark",
|
||||
});
|
||||
|
||||
let url = `${ENDPOINTS.user.friend.hide}/${profile_id}?token=${props.token}`;
|
||||
const { data, error } = await tryCatchAPI(fetch(url));
|
||||
if (error) {
|
||||
toast.update(tid, {
|
||||
render: "Ошибка скрытия заявки",
|
||||
type: "error",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast.update(tid, {
|
||||
render: "Заявка скрыта",
|
||||
type: "success",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
mutate(`${ENDPOINTS.user.friend.in}/last?token=${props.token}&count=8`);
|
||||
}
|
||||
|
||||
async function _acceptRequestIn(profile_id) {
|
||||
const tid = toast.loading("Принимаем запрос...", {
|
||||
position: "bottom-center",
|
||||
hideProgressBar: true,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
theme: theme.mode == "light" ? "light" : "dark",
|
||||
});
|
||||
|
||||
let url = `${ENDPOINTS.user.friend.add}/${profile_id}?token=${props.token}`;
|
||||
const { data, error } = await tryCatchAPI(fetch(url));
|
||||
if (error) {
|
||||
toast.update(tid, {
|
||||
render: "Ошибка приёма запроса",
|
||||
type: "error",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast.update(tid, {
|
||||
render: "Запрос принят",
|
||||
type: "success",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
mutate(`${ENDPOINTS.user.friend.in}/last?token=${props.token}&count=8`);
|
||||
}
|
||||
|
||||
async function _cancelRequestOut(profile_id) {
|
||||
const tid = toast.loading("Отменяем запрос...", {
|
||||
position: "bottom-center",
|
||||
hideProgressBar: true,
|
||||
closeOnClick: false,
|
||||
pauseOnHover: false,
|
||||
draggable: false,
|
||||
theme: theme.mode == "light" ? "light" : "dark",
|
||||
});
|
||||
|
||||
let url = `${ENDPOINTS.user.friend.remove}/${profile_id}?token=${props.token}`;
|
||||
const { data, error } = await tryCatchAPI(fetch(url));
|
||||
if (error) {
|
||||
toast.update(tid, {
|
||||
render: "Ошибка отмена запроса",
|
||||
type: "error",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
toast.update(tid, {
|
||||
render: "Запрос отменён",
|
||||
type: "success",
|
||||
autoClose: 2500,
|
||||
isLoading: false,
|
||||
closeOnClick: true,
|
||||
draggable: true,
|
||||
});
|
||||
mutate(`${ENDPOINTS.user.friend.out}/last?token=${props.token}&count=8`);
|
||||
}
|
||||
|
||||
const getKey = (pageIndex: number, previousPageData: any) => {
|
||||
if (previousPageData && !previousPageData.content.length) return null;
|
||||
let url = `${ENDPOINTS.user.friend.list}/${props.profile_id}/${pageIndex}?token=${props.token}`;
|
||||
return url;
|
||||
};
|
||||
|
||||
const { data, error, isLoading, size, setSize } = useSWRInfinite(
|
||||
getKey,
|
||||
useSWRfetcher,
|
||||
{ initialSize: 2 }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
let allFriends = [];
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
allFriends.push(...data[i].content);
|
||||
}
|
||||
setFriends(allFriends);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
function handleScroll() {
|
||||
const height = currentRef.scrollHeight - currentRef.clientHeight;
|
||||
const windowScroll = currentRef.scrollTop;
|
||||
const scrolled = (windowScroll / height) * 100;
|
||||
setScrollPosition(Math.floor(scrolled));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollPosition >= 95 && scrollPosition <= 96) {
|
||||
setSize(size + 1);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [scrollPosition]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
dismissible
|
||||
show={props.isOpen}
|
||||
onClose={() => props.setIsOpen(false)}
|
||||
size={"4xl"}
|
||||
>
|
||||
<ModalHeader>Друзья</ModalHeader>
|
||||
<div
|
||||
className="flex flex-col gap-4 p-4 overflow-y-auto"
|
||||
onScroll={handleScroll}
|
||||
ref={modalRef}
|
||||
>
|
||||
{props.isMyProfile && (
|
||||
<>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-lg font-semibold">Входящие заявки</p>
|
||||
{(
|
||||
requestInUsersData &&
|
||||
requestInUsersData.content &&
|
||||
requestInUsersData.content.length > 0
|
||||
) ?
|
||||
requestInUsersData.content.map((user) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between gap-2"
|
||||
key={`friend_req_in-${user.id}`}
|
||||
>
|
||||
<Link href={`/profile/${user.id}`}>
|
||||
<div className="flex gap-2">
|
||||
<Avatar
|
||||
alt=""
|
||||
img={user.avatar}
|
||||
rounded={true}
|
||||
size={"md"}
|
||||
bordered={true}
|
||||
color={user.is_online ? "success" : "light"}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-lg font-semibold">
|
||||
{user.login}
|
||||
</p>
|
||||
<p>Друзей: {user.friend_count}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
color="blue"
|
||||
onClick={() => _acceptRequestIn(user.id)}
|
||||
>
|
||||
Принять
|
||||
</Button>
|
||||
<Button
|
||||
color="light"
|
||||
onClick={() => _hideRequestIn(user.id)}
|
||||
>
|
||||
Скрыть
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: <p className="text-sm">Нет входящих заявок</p>}
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-lg font-semibold">Исходящие заявки</p>
|
||||
{(
|
||||
requestOutUsersData &&
|
||||
requestOutUsersData.content &&
|
||||
requestOutUsersData.content.length > 0
|
||||
) ?
|
||||
requestOutUsersData.content.map((user) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between gap-2"
|
||||
key={`friend_req_out-${user.id}`}
|
||||
>
|
||||
<Link href={`/profile/${user.id}`}>
|
||||
<div className="flex gap-2">
|
||||
<Avatar
|
||||
alt=""
|
||||
img={user.avatar}
|
||||
rounded={true}
|
||||
size={"md"}
|
||||
bordered={true}
|
||||
color={user.is_online ? "success" : "light"}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-lg font-semibold">
|
||||
{user.login}
|
||||
</p>
|
||||
<p>Друзей: {user.friend_count}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
color="light"
|
||||
onClick={() => _cancelRequestOut(user.id)}
|
||||
>
|
||||
Отменить
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: <p className="text-sm">Нет исходящих заявок</p>}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-lg font-semibold">Все друзья</p>
|
||||
{friends && friends.length > 0 ?
|
||||
friends.map((user) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-between gap-2"
|
||||
key={`friend-${user.id}`}
|
||||
>
|
||||
<Link href={`/profile/${user.id}`}>
|
||||
<div className="flex gap-2">
|
||||
<Avatar
|
||||
alt=""
|
||||
img={user.avatar}
|
||||
rounded={true}
|
||||
size={"md"}
|
||||
bordered={true}
|
||||
color={user.is_online ? "success" : "light"}
|
||||
className="flex-shrink-0"
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-lg font-semibold">{user.login}</p>
|
||||
<p>Друзей: {user.friend_count}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: <p className="text-sm">Нет друзей</p>}
|
||||
</div>
|
||||
{isLoading && <Spinner />}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -129,11 +129,13 @@ export const ProfilePage = (props: any) => {
|
|||
<ProfileActivity
|
||||
profile_id={user.id}
|
||||
commentCount={user.comment_count}
|
||||
videoCount={user.video_count}
|
||||
commentPreview={user.release_comments_preview || []}
|
||||
collectionCount={user.collection_count}
|
||||
collectionPreview={user.collections_preview || []}
|
||||
friendsCount={user.friend_count}
|
||||
friendsPreview={user.friends_preview || []}
|
||||
token={authUser.token}
|
||||
isMyProfile={isMyProfile || false}
|
||||
/>
|
||||
)}
|
||||
{!user.is_stats_hidden && (
|
||||
|
|
Loading…
Add table
Reference in a new issue