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

[Merge] 6주차 메인 배포 2회차 #307

Merged
merged 48 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
0e613b6
🐛 Fix: 초기화 시 activeTool도 초기화
skdltn210 Dec 2, 2024
3cfedde
💄 Style: 팔레트 채팅창보다 위로
skdltn210 Dec 2, 2024
2b8021d
✨ Feat: 목데이터 삭제, 홈에 벅벅버니
skdltn210 Dec 2, 2024
ffbd2c8
💄 Style: Live, 시청자수 뱃지 색상변경
skdltn210 Dec 2, 2024
bf8e487
🐛 Fix: 로그인된 사용자가 /login 입력하면 원래페이지로
skdltn210 Dec 2, 2024
aa5e11a
🐛 Fix: 로그인 성공시 전에있던 페이지로 가도록
skdltn210 Dec 2, 2024
edbee4d
✨ Feat: 사이드 바 개선
chologmaesil Dec 2, 2024
99e1ac3
💄 Style: 텍스트 transition-colors 속성 삭제
chologmaesil Dec 2, 2024
53f88ca
💄 Style: LICO 로고 간격 조정, NavItem 애니메이션 시간 조절
chologmaesil Dec 2, 2024
f8d292d
✨ Feat: 사이드 바 반응형 구현
chologmaesil Dec 2, 2024
55c4685
✨ Feat: 존재하지 않는 경로에 대한 404 처리
chologmaesil Dec 2, 2024
710dcba
✨ Feat: 게스트 로그인 추가
skdltn210 Dec 2, 2024
55c8683
🐛 Fix: ChannelCard, CategoryBadge 중첩 링크 해결
skdltn210 Dec 2, 2024
b351841
🐛 Fix: li 요소에 key prop 추가
skdltn210 Dec 2, 2024
d1f5672
🐛 Fix: Link 중첩 수정
chologmaesil Dec 2, 2024
a507bf9
🐛 Fix: 리액트 쿼리 staleTime 조정
chologmaesil Dec 2, 2024
f120c68
♻️ Refactor: mock 데이터 삭제
chologmaesil Dec 2, 2024
8d228db
💄 Style: 정렬 버튼과 목록 간 간격 조절
chologmaesil Dec 2, 2024
b484976
💄 Style: 카테고리 페이지 반응형 적용, 레이아웃 main 최소 크기 지정, 목록 페이지 반응형 조절
chologmaesil Dec 2, 2024
17d6cee
💄 Style: 카테고리 페이지 뱃지 스타일 수정, 폰트 크기 변경
chologmaesil Dec 2, 2024
002ac29
✏️ Fix: OAuht 타입에 lico 추가
pc5401 Dec 2, 2024
6b123fd
🐛 Fix: 팔로워 수 수정
pc5401 Dec 2, 2024
080fb0b
♻️ Refactor: useLayoutStore을 삭제하고 useViewMode로 수정했습니다. 전역 상태로 극장 모드만 …
chologmaesil Dec 2, 2024
0b35649
♻️ Refactor: 채팅창에 바뀐 채팅 토글 상태, 극장모드를 적용했습니다.
chologmaesil Dec 2, 2024
8b9513c
♻️ Refactor: Navbar에 극장모드 상태 적용 후 리팩토링 진행
chologmaesil Dec 2, 2024
5f35c24
🐛 Fix: mocks 폴더 복원
chologmaesil Dec 2, 2024
7dd5d87
✨ Feat: 가계정 닉네임 형용사 + 명사로 변경
pc5401 Dec 2, 2024
e0841cf
✨ Feat: 유저 생성 시 live 정보 default 값 추가
pc5401 Dec 2, 2024
bf42846
💄 Style: WebStudio UI 개선
skdltn210 Dec 2, 2024
cb586c8
💄 Style: Category 시청자수 표기 패딩 수정
skdltn210 Dec 2, 2024
93085a9
Merge pull request #303 from boostcampwm-2024/Feature/fe/improve-layout
skdltn210 Dec 3, 2024
b3b471e
🔧 Chore: 가계정 url lico/guest로 변경
pc5401 Dec 3, 2024
8a19ab3
Merge pull request #304 from boostcampwm-2024/Feature/be/fake-login
gamgyul163 Dec 3, 2024
a0883bd
Merge branch 'develop' into Fix/fe/bugfix
skdltn210 Dec 3, 2024
015e9eb
♻️ Refactor: 게스트로그인 과정 수정
skdltn210 Dec 3, 2024
e3e2072
♻️ Refactor: StreamInfo 불필요한 로직 제거
skdltn210 Dec 3, 2024
fdfeb69
Merge remote-tracking branch 'origin/Fix/fe/bugfix' into Fix/fe/bugfix
skdltn210 Dec 3, 2024
6199ae0
Merge pull request #302 from boostcampwm-2024/Fix/fe/bugfix
skdltn210 Dec 3, 2024
09ae993
✨ Feat: 가계정 임시 프로필 설정
pc5401 Dec 3, 2024
053c736
Merge pull request #305 from boostcampwm-2024/Feature/be/fake-login
pc5401 Dec 3, 2024
07e4507
✨ Fix: ImageTextCanvas, DrawCanvas 분리
skdltn210 Dec 3, 2024
e594145
🐛 Fix: text input 있는 상태에서 textingMode=false이면 취소
skdltn210 Dec 3, 2024
d6f4f07
♻️ Refactor: 캔버스들 분리
skdltn210 Dec 3, 2024
ceea5bd
🐛 Fix: 스트림 변경되어도 방송 송출되도록
skdltn210 Dec 3, 2024
60dea5c
Revert "🐛 Fix: 스트림 변경되어도 방송 송출되도록"
skdltn210 Dec 3, 2024
ebecc33
🐛 Fix: 이미지 안들어가는 오류 수정
skdltn210 Dec 3, 2024
1a351dc
✨ Feat: 가계정 프로필 이미지 랜덤 생성
pc5401 Dec 3, 2024
ea8c960
Merge pull request #306 from boostcampwm-2024/Feature/be/fake-login
chologmaesil Dec 4, 2024
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
2 changes: 1 addition & 1 deletion Backend/apps/api/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class AuthController {
return this.handleOAuthCallback(req, res);
}

@Get('lico/callback')
@Get('lico/guest')
@UseGuards(AuthGuard('lico'))
async licoAuthCallback(@Req() req: Request & { user: any }, @Res() res: Response) {
return this.handleOAuthCallback(req, res);
Expand Down
2 changes: 1 addition & 1 deletion Backend/apps/api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import { ConfigService } from '@nestjs/config';

type OAuthPlatform = 'google' | 'github' | 'naver';
type OAuthPlatform = 'google' | 'github' | 'naver' | 'lico';

@Injectable()
export class AuthService {
Expand Down
14 changes: 11 additions & 3 deletions Backend/apps/api/src/auth/strategies/lico.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@ import { Strategy } from 'passport-custom';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import * as crypto from 'crypto';
import { adjectives, nouns } from './nickname-data'

@Injectable()
export class LicoStrategy extends PassportStrategy(Strategy, 'lico') {
async validate(req: Request, done: Function) {
try {
const oauthUid = crypto.randomBytes(16).toString('hex');

// 랜덤 닉네임 생성
const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
const nickname = `${randomAdjective} ${randomNoun}`;

const profileNumber = Math.floor(Math.random() * 31)

const userData = {
oauthUid,
provider: 'lico' as 'lico',
nickname: `User_${oauthUid.substring(0, 8)}`,
profileImage: null,
nickname,
profileImage: `https://kr.object.ncloudstorage.com/lico.image/default-profile-image/lico_${profileNumber}.png`,
email: null,
};

Expand All @@ -22,4 +30,4 @@ export class LicoStrategy extends PassportStrategy(Strategy, 'lico') {
done(error, false);
}
}
}
}
25 changes: 25 additions & 0 deletions Backend/apps/api/src/auth/strategies/nickname-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const adjectives = [
'용감한', '배고픈', '행복한', '슬픈', '귀여운',
'똑똑한', '멋진', '예쁜', '강한', '부드러운',
'신나는', '즐거운', '차가운', '뜨거운', '재미있는',
'친절한', '성실한', '조용한', '시끄러운', '빠른',
'느린', '젊은', '늙은', '건강한', '아픈',
'화난', '놀란', '긴장한', '자신감있는', '호기심많은',
'사랑스러운', '섹시한', '차분한', '활발한', '용의주도한',
'검소한', '풍요로운', '예의바른', '거친', '부지런한',
'게으른', '독특한', '평범한', '엄격한', '유연한',
'진지한', '명랑한', '냉정한', '따뜻한', '낙천적인',
];

export const nouns = [
'사자', '호랑이', '토끼', '코끼리', '독수리',
'고래', '감자', '토마토', '사과', '바나나',
'강아지', '고양이', '여우', '늑대', '곰',
'펭귄', '기린', '원숭이', '돼지', '닭',
'양', '염소', '소', '말', '다람쥐',
'독사', '표범', '하마', '코뿔소', '캥거루',
'수달', '돌고래', '새우', '게', '불가사리',
'달팽이', '나비', '벌', '개미', '거미',
'두꺼비', '개구리', '뱀', '도마뱀', '악어',
'코알라', '판다', '사슴', '너구리', '오소리',
];
9 changes: 5 additions & 4 deletions Backend/apps/api/src/follow/follow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,13 @@ export class FollowService {

// 팔로워 수
async getFollowerCount(streamerId: number): Promise<number> {
const count = await this.usersRepository
const result = await this.usersRepository
.createQueryBuilder('user')
.innerJoin('user.followers', 'follower')
.leftJoin('user.followers', 'follower')
.where('user.id = :streamerId', { streamerId })
.getCount();
.select('COUNT(follower.id)', 'count')
.getRawOne();

return count;
return parseInt(result.count, 10);
}
}
5 changes: 5 additions & 0 deletions Backend/apps/api/src/lives/entity/live.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import {
import { LivesDto } from '../dto/lives.dto';
import { LiveDto } from '../dto/live.dto';
import { StatusDto } from '../dto/status.dto';
import { Index } from 'typeorm';


@Index(['onAir', 'categoriesId'])
@Entity('lives')
export class LiveEntity {
@PrimaryGeneratedColumn({ name: 'lives_id' })
id: number;

@Index()
@Column({ name: 'categories_id', type: 'int', nullable: true })
categoriesId: number | null;

Expand All @@ -39,6 +43,7 @@ export class LiveEntity {
@Column({ name: 'streaming_key', type: 'varchar', length: 36 })
streamingKey: string;

@Index()
@Column({ name: 'onair', type: 'boolean', nullable: true })
onAir: boolean | null;

Expand Down
4 changes: 2 additions & 2 deletions Backend/apps/api/src/users/dto/create.user.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ export class CreateUserDto {
@IsString()
oauthUid: string;

@IsEnum(['naver', 'github', 'google'])
oauthPlatform: 'naver' | 'github' | 'google';
@IsEnum(['naver', 'github', 'google', 'lico'])
oauthPlatform: 'naver' | 'github' | 'google'| 'lico';

@IsString()
@IsOptional()
Expand Down
8 changes: 4 additions & 4 deletions Backend/apps/api/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class UsersService {

async findByOAuthUid(
oauthUid: string,
oauthPlatform: 'naver' | 'github' | 'google',
oauthPlatform: 'naver' | 'github' | 'google'|'lico',
): Promise<UserEntity | null> {
return this.usersRepository.findOne({
where: { oauthUid, oauthPlatform },
Expand All @@ -59,10 +59,10 @@ export class UsersService {
async createUser(createUserDto: CreateUserDto): Promise<UserEntity> {
return this.connection.transaction(async manager => {
const live = manager.create(LiveEntity, {
categoriesId: null,
categoriesId: 4,
channelId: randomUUID(),
name: null,
description: null,
name: `${createUserDto.nickname}의 라이브 방송`,
description: `${createUserDto.nickname}의 라이브 방송입니다`,
streamingKey: randomUUID(),
onAir: false,
startedAt: null,
Expand Down
5 changes: 5 additions & 0 deletions Frontend/src/apis/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ export const authApi = {
});
return response.data;
},

async guestLogin(): Promise<AuthResponse> {
const response = await api.get<AuthResponse>('/auth/lico/guest');
return response.data;
},
};
8 changes: 4 additions & 4 deletions Frontend/src/components/VideoPlayer/Control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { HLSQuality } from '@/types/hlsQuality';
interface ControlsProps {
isPlaying: boolean;
isFullScreen: boolean;
videoPlayerState: string;
isTheaterMode: boolean;
showControls: boolean;
volume: number;
isMuted: boolean;
Expand All @@ -26,7 +26,7 @@ const CONTROL_ICON_SIZE = 24;
export default function Controls({
isPlaying,
isFullScreen,
videoPlayerState,
isTheaterMode,
showControls,
volume,
isMuted,
Expand Down Expand Up @@ -91,8 +91,8 @@ export default function Controls({
type="button"
onClick={onFullScreenToggle}
className="hover:text-lico-orange-2"
aria-label={videoPlayerState === 'theater' ? '전체화면 종료' : '전체화면'}
aria-pressed={videoPlayerState === 'theater'}
aria-label={isTheaterMode ? '전체화면 종료' : '전체화면'}
aria-pressed={isTheaterMode}
>
{isFullScreen ? <LuMinimize size={CONTROL_ICON_SIZE} /> : <LuMaximize size={CONTROL_ICON_SIZE} />}
</button>
Expand Down
8 changes: 4 additions & 4 deletions Frontend/src/components/VideoPlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState, useRef, useEffect } from 'react';
import useLayoutStore from '@store/useLayoutStore';
import useHls from '@hooks/useHls';
import LoadingSpinner from '@components/common/LoadingSpinner';
import Badge from '@components/common/Badges/Badge';
import OfflinePlayer from '@components/VideoPlayer/OfflinePlayer';
import useViewMode from '@store/useViewMode';
import Controls from './Control/index';

interface VideoPlayerProps {
Expand All @@ -18,7 +18,7 @@ export default function VideoPlayer({ streamUrl, onAir }: VideoPlayerProps) {
const [isFullScreen, setIsFullScreen] = useState(false);
const [showControls, setShowControls] = useState(true);
const [showCursor, setShowCursor] = useState(true);
const { videoPlayerState, toggleVideoPlayer } = useLayoutStore();
const { isTheaterMode, toggleTheaterMode } = useViewMode();

const videoRef = useRef<HTMLVideoElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -162,15 +162,15 @@ export default function VideoPlayer({ streamUrl, onAir }: VideoPlayerProps) {
<Controls
isPlaying={isPlaying}
isFullScreen={isFullScreen}
videoPlayerState={videoPlayerState}
isTheaterMode={isTheaterMode}
showControls={showControls}
volume={volume}
isMuted={isMuted}
onPlayToggle={togglePlay}
onVolumeChange={handleVolumeChange}
onMuteToggle={toggleMute}
onFullScreenToggle={toggleFullScreen}
onVideoPlayerToggle={toggleVideoPlayer}
onVideoPlayerToggle={toggleTheaterMode}
onShowControls={handleShowControls}
qualities={qualities}
setQuality={setQuality}
Expand Down
10 changes: 5 additions & 5 deletions Frontend/src/components/category/CategoryCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ export default function CategoryCard({ id, name, image, totalViewers, totalLives
return (
<button type="button" className="mb-3 cursor-pointer text-left" onClick={handleClick}>
<div className="relative inline-block w-full">
<img src={image} alt={name} className="aspect-[3/4] w-[calc(20vw-12px)] rounded-xl object-cover" />
<div className="absolute left-1.5 top-1.5 flex items-center gap-1 rounded-[4px] bg-black bg-opacity-60 px-1">
<img src={image} alt={name} className="aspect-[3/4] rounded-xl object-cover" />
<div className="absolute left-1.5 top-1.5 flex items-center gap-1 rounded-md bg-black bg-opacity-60 px-1">
<FaCircle className="h-[6px] w-[6px] text-[#E02120]" />
<span className="mt-0.5 font-bold text-xs text-white">{formatUnit(totalViewers)}명</span>
<span className="py-[1px] font-bold text-xs text-white">{formatUnit(totalViewers)}명</span>
</div>
</div>
<div className="mx-0.5 mt-2 px-[3px]">
<p className="font-bold text-sm text-lico-gray-1">{name}</p>
<p className="font-medium text-xs text-lico-gray-2">라이브 {totalLives}개</p>
<p className="font-bold text-lg text-lico-gray-1">{name}</p>
<p className="font-medium text-base text-lico-gray-2">라이브 {totalLives}개</p>
</div>
</button>
);
Expand Down
16 changes: 9 additions & 7 deletions Frontend/src/components/category/CategoryGrid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ type CategoryGridProps = {

export default function CategoryGrid({ categories }: CategoryGridProps) {
return (
<ul className="grid min-w-[851px] grid-cols-5 gap-4 px-4">
{categories.map(category => (
<li key={category.id} className="min-w-[151px]">
<CategoryCard {...category} />
</li>
))}
</ul>
<div className="container mx-auto px-4">
<ul className="grid grid-cols-3 gap-4 cards-4:grid-cols-4 cards-5:grid-cols-5 cards-6:grid-cols-6">
{categories.map(category => (
<li key={category.id} className="min-w-[151px]">
<CategoryCard {...category} />
</li>
))}
</ul>
</div>
);
}
21 changes: 17 additions & 4 deletions Frontend/src/components/channel/ChannelCard/ChannelInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
import CategoryBadge from '@components/common/Badges/CategoryBadge';
import { Link } from 'react-router-dom';

interface ChannelInfoProps {
id: string;
title: string;
streamerName: string;
category: string;
categoryId: number;
profileImgUrl: string;
}

export default function ChannelInfo({ title, streamerName, category, categoryId, profileImgUrl }: ChannelInfoProps) {
export default function ChannelInfo({
id,
title,
streamerName,
category,
categoryId,
profileImgUrl,
}: ChannelInfoProps) {
return (
<div className="flex items-start gap-2 pt-2">
<img className="h-10 w-10 rounded-full bg-lico-gray-4" src={profileImgUrl} alt={streamerName} />
<Link to={`/channel/${id}`} aria-label={`${streamerName}의 ${title} 스트림으로 이동`}>
<img className="h-10 w-10 rounded-full bg-lico-gray-4" src={profileImgUrl} alt={streamerName} />
</Link>
<div>
<h3 className="line-clamp-2 max-h-[48px] font-bold text-base text-lico-orange-2">{title}</h3>
<h3 className="line-clamp-1 font-medium text-xs text-lico-gray-2">{streamerName}</h3>
<Link to={`/channel/${id}`} aria-label={`${streamerName}의 ${title} 스트림으로 이동`}>
<h3 className="line-clamp-2 max-h-[48px] font-bold text-base text-lico-orange-2">{title}</h3>
<h3 className="line-clamp-1 font-medium text-xs text-lico-gray-2">{streamerName}</h3>
</Link>
<CategoryBadge category={category} categoryId={categoryId} className="text-xs text-lico-gray-1" />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ function ChannelThumbnail({ title, thumbnailUrl, viewers }: ChannelThumbnailProp
/>
{!isLoading && !imgError && (
<div className="flex gap-2">
<Badge text="LIVE" className="absolute left-2 top-2 bg-red-600 font-bold text-sm text-white" />
<Badge text="LIVE" className="absolute left-2 top-2 bg-[#E02120] font-bold text-sm text-white" />
<Badge
text={`${formatNumber(viewers)}명`}
className="absolute bottom-2 left-2 bg-orange-500 font-bold text-sm text-white"
className="absolute bottom-2 left-2 bg-lico-orange-2 font-bold text-sm text-white"
/>
</div>
)}
Expand Down
19 changes: 14 additions & 5 deletions Frontend/src/components/channel/ChannelCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Link } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import ChannelThumbnail from '@components/channel/ChannelCard/ChannelThumbnail';
import HoverPreviewPlayer from '@components/channel/ChannelCard/HoverPreviewPlayer';
import { useRef, useState } from 'react';
Expand Down Expand Up @@ -27,6 +27,7 @@ export default function ChannelCard({
}: ChannelCardProps) {
const [showPreview, setShowPreview] = useState(false);
const timerRef = useRef<NodeJS.Timeout>();
const navigate = useNavigate();

const handleMouseEnter = () => {
timerRef.current = setTimeout(() => {
Expand All @@ -41,13 +42,20 @@ export default function ChannelCard({
setShowPreview(false);
};

const handleCardClick = (e: React.MouseEvent) => {
const target = e.target as HTMLElement;
if (!target.closest('[data-category-badge]')) {
navigate(`/live/${id}`);
}
};

return (
<Link
to={`/live/${id}`}
className="relative mb-4 block min-w-60"
<div
className="relative mb-4 block min-w-60 cursor-pointer"
aria-label={`${streamerName}의 ${title} 스트림으로 이동`}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={handleCardClick}
>
<div className="relative aspect-video">
<ChannelThumbnail title={title} thumbnailUrl={thumbnailUrl} viewers={viewers} />
Expand All @@ -58,12 +66,13 @@ export default function ChannelCard({
)}
</div>
<ChannelInfo
id={id}
title={title}
streamerName={streamerName}
category={category}
categoryId={categoryId}
profileImgUrl={profileImgUrl}
/>
</Link>
</div>
);
}
2 changes: 1 addition & 1 deletion Frontend/src/components/channel/ChannelGrid/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface ChannelGridProps {
function ChannelGrid({ channels }: ChannelGridProps) {
return (
<div className="container mx-auto px-4">
<ul className="cards-4:grid-cols-4 cards-5:grid-cols-5 cards-6:grid-cols-6 grid min-w-[752px] grid-cols-3 gap-4">
<ul className="grid min-w-[996px] grid-cols-3 gap-4 cards-4:grid-cols-4 cards-5:grid-cols-5 cards-6:grid-cols-6">
{channels.map(channel => (
<li key={channel.id}>
<ChannelCard {...channel} />
Expand Down
Loading
Loading