Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIPフォロー・フォロワー一覧 #391

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/app/(menu)/(public)/[username]/_components/Following.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import FollowList from '@/app/(menu)/(public)/[username]/_components/layouts/FollowList';
import { useFollowing } from '@/swr/client/follows';
import { UserWithFollows } from '@cuculus/cuculus-api';

/**
* フォロー一覧表示コンポーネント
* @param user
* @constructor
*/
export default function Following({ user }: { user: UserWithFollows }) {
return <FollowList follows={useFollowing(user.id)} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use client';
import FollowButton from '@/app/(menu)/(public)/[username]/_components/elements/FollowButton';
import UserIcon from '@/app/(menu)/(public)/[username]/_components/elements/UserIcon';
import { Box, Typography, styled } from '@mui/material';

const UnselectableCard = styled('div')`
border-bottom: 1px solid ${({ theme }) => theme.palette.grey[100]};
background-color: ${({ theme }) => theme.palette.background.paper};
color: rgba(0, 0, 0, 0.87);
`;

const Flex = styled(Box)`
display: flex;
flex-wrap: nowrap;
`;

const VFlex = styled(Flex)`
flex-direction: column;
width: 100%;
`;

const HFlex = styled(Flex)`
flex-direction: row;
`;

const HFlexS = styled(Flex)`
flex-direction: row;
justify-content: space-between;
`;

const FillFlex = styled(Box)`
flex-grow: 1;
`;

const DisplayName = styled(Typography)`
word-wrap: break-word;
font-weight: bold;
font-size: 20px;
`;

const ButtonArea = styled(Box)`
width: 100%;
text-align: right;
`;

const UserName = styled(Typography)`
color: #8899a6;
font-size: 15px;
`;

const Avater = styled(UserIcon)`
aspect-ratio: 1;
height: 64px;
width: 64px;
margin: 0 10px auto;

${({ theme }) => theme.breakpoints.down('desktop')} {
margin: 0 10px auto;
height: 64px;
width: 64px;
}
`;

const Bio = styled(Typography)`
white-space: pre-wrap;
margin-bottom: 12px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
`;

type Props = {
id: number;
name: string;
username: string;
profileImageUrl: string;
bio: string;
};

export default function FFProfileCard({
name,
username,
profileImageUrl,
bio,
id,
}: Props) {
return (
<UnselectableCard>
<HFlex>
<Avater src={profileImageUrl} alt={'プロフィール画像'} />
<VFlex style={{ margin: '12px 0' }}>
<HFlexS>
<VFlex>
<DisplayName>{name}</DisplayName>
<UserName>@{username}</UserName>
</VFlex>
{/* フォローされてるか表示 */}
{/* <FollowedDisplay/> */}
<ButtonArea>
<FollowButton userId={id} />
</ButtonArea>
</HFlexS>
<FillFlex>
<Bio>{bio}</Bio>
</FillFlex>
</VFlex>
</HFlex>
</UnselectableCard>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { SWRInfiniteResponse } from 'swr/infinite';
import { FollowList } from '@cuculus/cuculus-api';

Check failure on line 2 in src/app/(menu)/(public)/[username]/_components/layouts/FollowList.tsx

View workflow job for this annotation

GitHub Actions / tests

Import 'FollowList' conflicts with local value, so must be declared with a type-only import when 'isolatedModules' is enabled.
import { CircularProgress } from '@mui/material';
import FFProfileCard from '@/app/(menu)/(public)/[username]/_components/layouts/FFProfileCard';

type Props = {
follows: SWRInfiniteResponse<FollowList | undefined, Error>;
};

/**
* フォロー/フォロワーリスト
* @param follows
* @constructor
*/
export default function FollowList({ follows }: Props) {
const { data, isLoading, size, setSize } = follows;

if (isLoading) {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<CircularProgress />
</div>
);
}

return (
<>
<div>現在のページ: {size}</div>
<button onClick={() => void setSize(size + 1)}>次のページへ</button>
{data?.map((follows, index) => (
<div key={index}>
{follows?.users.map((User, number) => (
<FFProfileCard
key={number}
name={User.name}
username={User.username}
bio={User.bio}
profileImageUrl={User.profileImageUrl}
id={User.id}
/>
))}
</div>
))}
</>
);
}
36 changes: 33 additions & 3 deletions src/app/(menu)/(public)/[username]/followers/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import ComingSoon from '@/app/(menu)/_components/main/ComingSoon';
import PrimaryColumn from '@/app/(menu)/_components/main/PrimaryColumn';
import { cache } from 'react';
import { usersApi } from '@/libs/cuculus-client';
import { notFound } from 'next/navigation';
import Following from '@/app/(menu)/(public)/[username]/_components/Following';

Check warning on line 5 in src/app/(menu)/(public)/[username]/followers/page.tsx

View workflow job for this annotation

GitHub Actions / tests

'Following' is defined but never used

type Params = { params: { username: string } };

const getUser = cache(async (username: string) => {
try {
return await usersApi.getUserByUsername(
{ username },
{
next: {
revalidate: 300,
},
},
);
} catch {
return undefined;
}
});

/**
* フォロワー一覧ページ
* @param params
*/
export default async function page({ params }: Params) {
const username = decodeURIComponent(params.username);
const user = await getUser(username);
if (!user) {
notFound();
}

export default function page({}: { params: { userName: string } }) {
return (
<PrimaryColumn hideHeader={true}>
<ComingSoon />
{/* <Following user={user} /> */}
</PrimaryColumn>
);
}
36 changes: 33 additions & 3 deletions src/app/(menu)/(public)/[username]/following/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
import ComingSoon from '@/app/(menu)/_components/main/ComingSoon';
import PrimaryColumn from '@/app/(menu)/_components/main/PrimaryColumn';
import { cache } from 'react';
import { usersApi } from '@/libs/cuculus-client';
import { notFound } from 'next/navigation';
import Following from '@/app/(menu)/(public)/[username]/_components/Following';

type Params = { params: { username: string } };

const getUser = cache(async (username: string) => {
try {
return await usersApi.getUserByUsername(
{ username },
{
next: {
revalidate: 300,
},
},
);
} catch {
return undefined;
}
});

/**
* フォロー一覧ページ
* @param params
*/
export default async function page({ params }: Params) {
const username = decodeURIComponent(params.username);
const user = await getUser(username);
if (!user) {
notFound();
}

export default function page({}: { params: { userName: string } }) {
return (
<PrimaryColumn hideHeader={true}>
<ComingSoon />
<Following user={user} />
</PrimaryColumn>
);
}
53 changes: 53 additions & 0 deletions src/swr/client/follows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useAuth } from '@/swr/client/auth';
import { usersApi } from '@/libs/cuculus-client';
import { getAuthorizationHeader } from '@/libs/auth';
import useSWRInfinite from 'swr/infinite';
import { FollowList } from '@cuculus/cuculus-api';

// FIXME 確認用に一旦10件にしている
const LIMIT = 10;

type SWRKey = {
key: string;
authId?: number;
userId: number;
nextUntil?: Date;
};

/**
* フォロー一覧の取得
* @param userId
*/
export const useFollowing = (userId: number) => {
const { data: authId } = useAuth();
return useSWRInfinite<
FollowList | undefined,
Error,
(index: number, prev: FollowList | undefined) => SWRKey | null
>(
(index, previousPageData) => {
// 取得結果が空の場合は次のページがないと判断して終了
if (previousPageData && !previousPageData.users.length) {
return null;
}
return {
key: 'useFollowing',
authId,
userId,
nextUntil: previousPageData?.nextUntil,
};
},
async (args) => {
try {
return await usersApi.getUserFollowing(
{ id: args.userId, until: args.nextUntil, limit: LIMIT },
{
headers: await getAuthorizationHeader(authId),
},
);
} catch (error) {
throw error;
}
},
);
};
Loading