Skip to content

Commit

Permalink
implement profile
Browse files Browse the repository at this point in the history
  • Loading branch information
dinkelspiel committed May 9, 2024
1 parent bc59f38 commit 710da1e
Show file tree
Hide file tree
Showing 14 changed files with 816 additions and 136 deletions.
5 changes: 4 additions & 1 deletion app/(app)/dashboard/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,10 @@ const Dashboard = ({
}
return b.watchedAt.getTime() - a.watchedAt.getTime();
case 'updated':
return b.updatedAt.getTime() - a.updatedAt.getTime();
return (
new Date(b.updatedAt).getTime() -
new Date(a.updatedAt).getTime()
);
}
})
.map(userEntry => {
Expand Down
48 changes: 48 additions & 0 deletions app/(app)/users/[username]/diary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import prisma from '@/server/db';

export type Diary = Record<
string,
{
title: string;
day: number;
}[]
>;

export const getUserDiary = async (userId: number): Promise<Diary> => {
const userEntries = await prisma.userEntry.findMany({
where: {
userId,
status: 'completed',
},
orderBy: {
id: 'desc',
},
take: 10,
include: {
entry: true,
},
});

const diary: Diary = {};

userEntries.forEach(userEntry => {
const month = userEntry
.watchedAt!.toLocaleString('default', { month: 'short' })
.toUpperCase()
.slice(0, 3);
if (!diary[month]) {
diary[month] = [];
}
diary[month]!.push({
title: userEntry.entry.originalTitle,
day: userEntry.watchedAt!.getDate(),
});
});

const sortedDiary: Diary = {};
for (const key in diary) {
sortedDiary[key] = diary[key]!.sort((a, b) => b.day - a.day);
}

return sortedDiary;
};
65 changes: 65 additions & 0 deletions app/(app)/users/[username]/followButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use client';

import { Button } from '@/components/ui/button';
import { User, UserFollow } from '@prisma/client';
import React, { useEffect, useState } from 'react';
import { toast } from 'sonner';

const FollowButton = ({
authUser,
user,
}: {
authUser: User;
user: User & { followers: UserFollow[] };
}) => {
const [following, setFollowing] = useState(false);

const toggleFollow = async () => {
if (!authUser) {
return;
}

const oldFollowingState = Boolean(following);

setFollowing(!following);

let response = await fetch(`/api/user/follows/${user.id}`, {
method: following ? 'DELETE' : 'POST',
headers: {
'Content-Type': 'application/json',
},
});

if (response.status !== 200) {
toast.error(
`Failed ${following ? 'unfollowing' : 'following'} user with error "${(await response.json()).error}"`
);
setFollowing(oldFollowingState);
} else {
toast.success(
`Successfully ${following ? 'unfollowed' : 'followed'} user`
);
}
};

useEffect(() => {
setFollowing(
authUser
? user.followers.find(
e => e.userId === authUser.id && e.isFollowing
) !== undefined
: false
);
}, [authUser, user]);

return (
<Button
variant={following ? 'destructive' : 'outline'}
onClick={() => toggleFollow()}
>
{following ? 'Unfollow' : 'Follow'}
</Button>
);
};

export default FollowButton;
173 changes: 92 additions & 81 deletions app/(app)/users/[username]/header.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
'use client';

import React, { Dispatch, SetStateAction, useState } from 'react';
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import {
Header,
HeaderContent,
HeaderDescription,
HeaderHeader,
HeaderTitle,
} from '@/components/header';
import { Button } from '@/components/ui/button';
Expand All @@ -20,6 +21,7 @@ import { toast } from 'sonner';
import Link from 'next/link';
import { validateSessionToken } from '@/server/auth/validateSession';
import { User, UserEntry, UserFollow } from '@prisma/client';
import FollowButton from './followButton';

export const ProfileHeader = ({
user,
Expand All @@ -28,25 +30,34 @@ export const ProfileHeader = ({
user: User | null;
profileUser: User & {
userEntries: UserEntry[];
followers: (UserFollow & { user: User })[];
following: (UserFollow & { user: User })[];
followers: (UserFollow & {
user: User & { following: UserFollow[]; followers: UserFollow[] };
})[];
following: (UserFollow & {
follow: User & { following: UserFollow[]; followers: UserFollow[] };
})[];
};
}) => {
const [page, setPage] = useState<'Following' | 'Followers'>('Followers');

const FollowEditProfile = () => {
return user !== undefined && <></>;
return (
user &&
user.id !== profileUser.id && (
<FollowButton user={profileUser} authUser={user} />
)
);
};

return (
<Header className="col-span-2 h-max flex-col items-center justify-start gap-6 lg:flex-row">
<div className="flex w-full flex-row items-center justify-between px-6 lg:w-max lg:justify-start lg:px-0">
<HeaderContent>
<Header className="col-span-2 h-max flex-col items-center justify-center gap-6 lg:flex-row">
<div className="flex w-full flex-row items-center justify-between lg:w-max lg:justify-start">
<HeaderHeader className="pt-4 text-center lg:pt-0 lg:text-left">
<HeaderTitle>{profileUser.username}</HeaderTitle>
<HeaderDescription>
{profileUser.username}'s profile
</HeaderDescription>
</HeaderContent>
</HeaderHeader>
<div className="block lg:hidden">
<FollowEditProfile />
</div>
Expand Down Expand Up @@ -83,7 +94,7 @@ export const ProfileHeader = ({
onMouseOver={() => setPage('Following')}
>
<div className="text-center text-2xl font-semibold">
{profileUser.following.length}
{profileUser.following.filter(e => e.isFollowing).length}
</div>
<div className="text-center text-sm text-slate-500">
Following
Expand All @@ -94,7 +105,7 @@ export const ProfileHeader = ({
onMouseOver={() => setPage('Followers')}
>
<div className="text-center text-2xl font-semibold">
{profileUser.followers.length}
{profileUser.followers.filter(e => e.isFollowing).length}
</div>
<div className="text-center text-sm text-slate-500">
Followers
Expand Down Expand Up @@ -131,83 +142,83 @@ export const ProfileHeader = ({
</div>
</SheetTitle>
</SheetHeader>
{/* {page === 'Followers' ? (
<UserList
{...{
profile,
setProfile,
users: profile.followers,
userType: 'followers',
}}
/>
) : (
<UserList
{...{
profile,
setProfile,
users: profile.following,
userType: 'following',
}}
/>
)} */}
<UserList
users={
(page === 'Followers'
? profileUser.followers
: profileUser.following) as (UserFollow & {
follow: User & {
following: UserFollow[];
followers: UserFollow[];
};
} & {
user: User & {
following: UserFollow[];
followers: UserFollow[];
};
})[]
}
authUser={user}
/>
</SheetContent>
</Sheet>
</div>
</div>
</Header>
);
};
// const UserList = ({
// users,
// profile,
// setProfile,
// userType,
// }: {
// users: UserType[];
// profile: ProfileType;
// setProfile: Dispatch<SetStateAction<ProfileType | undefined | null>>;
// userType: 'following' | 'followers';
// }) => {
// const { user: authUser } = useUserContext();

// const handleSuccess = (user: UserType, isFollowing: boolean) => {
// setProfile({
// ...profile,
// [userType]: [
// ...profile[userType].filter(e => e.username !== user.username),
// { ...user, isViewerFollowing: isFollowing },
// ],
// });
// };
const UserList = ({
users,
authUser,
}: {
users: (UserFollow & {
follow: User & { following: UserFollow[]; followers: UserFollow[] };
} & { user: User & { following: UserFollow[]; followers: UserFollow[] } })[];
authUser: User | null;
}) => {
return (
<div className="py-6">
<div className="grid grid-cols-[1fr,96px] items-center gap-3">
{users
.filter(e => e.isFollowing)
.map(user => {
if (user.follow) {
return <UserCard user={user.follow} authUser={authUser} />;
}

if (user.user) {
return <UserCard user={user.user} authUser={authUser} />;
}
})}
</div>
</div>
);
};

// return (
// <div className="py-6">
// <div className="grid grid-cols-[1fr,96px] items-center gap-3">
// {users.map(user => (
// <React.Fragment key={user.username}>
// <Link href={`/@${user.username}`}>
// <div className="flex flex-col">
// <div className="font-semibold">{user.username}</div>
// <div className="text-sm text-slate-500">
// {user.followersCount} followers, following{' '}
// {user.followingCount}
// </div>
// </div>
// </Link>
// <div className="flex flex-row justify-end">
// {/* {user.username !== profile.username && authUser !== undefined && (
// <FollowButton
// username={authUser.username}
// followUsername={user.username}
// isViewerFollowing={user.isViewerFollowing}
// followSuccess={() => handleSuccess(user, true)}
// unfollowSuccess={() => handleSuccess(user, false)}
// />
// )} */}
// </div>
// </React.Fragment>
// ))}
// </div>
// </div>
// );
// };
const UserCard = ({
user,
authUser,
}: {
user: User & { following: UserFollow[]; followers: UserFollow[] };
authUser: User | null;
}) => {
return (
<React.Fragment key={user.username}>
<Link href={`/@${user.username}`}>
<div className="flex flex-col">
<div className="font-semibold">{user.username}</div>
<div className="text-sm text-slate-500">
{user.followers.filter(e => e.isFollowing).length} followers,
following {user.following.filter(e => e.isFollowing).length}
</div>
</div>
</Link>
<div className="flex flex-row justify-end">
{authUser && authUser.username !== user.username && (
<FollowButton user={user} authUser={authUser} />
)}
</div>
</React.Fragment>
);
};
Loading

0 comments on commit 710da1e

Please sign in to comment.