Skip to content

Commit bd20135

Browse files
authored
Merge pull request #115 from DMU-DebugVisual/inseong_websocket
권한 표시변경
2 parents 0a97617 + 293d572 commit bd20135

File tree

3 files changed

+98
-50
lines changed

3 files changed

+98
-50
lines changed

src/components/codecast/codecastlive/CodecastLive.jsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -802,14 +802,19 @@ export default function CodecastLive({ isDark }) {
802802

803803
// 사이드바 표시용 역할 계산: 현재 세션 권한표 반영
804804
const sidebarViewParticipants = useMemo(() => {
805-
const perms = activeSessionPermissions;
806805
const ownerId = activeSessionMeta?.ownerId;
806+
const perms = activeSessionPermissions || {};
807807
return participants.map((p) => {
808-
let displayRole = 'view';
809-
if (p.id === roomOwnerId) displayRole = 'host'; // 방장 표시 유지
810-
if (p.id === ownerId) displayRole = 'host'; // 세션 소유자를 별도 'owner'로 구분하고 싶으면 이 라인을 바꿔도 됨
811-
else if (perms[p.id] === 'edit') displayRole = 'edit';
812-
return { ...p, displayRole };
808+
const idKey = String(p.id);
809+
const isSessionOwner = !!ownerId && p.id === ownerId;
810+
const hasEditPermission = perms[p.id] === 'edit' || perms[idKey] === 'edit';
811+
const sessionRole = isSessionOwner ? 'owner' : hasEditPermission ? 'edit' : 'view';
812+
813+
return {
814+
...p,
815+
sessionRole,
816+
isRoomOwner: p.id === roomOwnerId,
817+
};
813818
});
814819
}, [participants, activeSessionPermissions, activeSessionMeta?.ownerId, roomOwnerId]);
815820

src/components/codecast/codecastlive/CodecastSidebar.css

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,45 @@
156156
color: #3f51b5;
157157
}
158158

159-
.role-icon {
160-
font-size: 15px;
159+
.permission-chip {
160+
display: flex;
161+
justify-content: center;
162+
min-width: 78px;
163+
flex-shrink: 0;
164+
}
165+
166+
.role-badge {
167+
display: inline-flex;
168+
align-items: center;
169+
justify-content: center;
170+
padding: 3px 10px;
171+
font-size: 11px;
172+
font-weight: 600;
173+
border-radius: 999px;
174+
background: var(--surface-muted);
175+
color: var(--text-muted);
176+
border: 1px solid transparent;
177+
min-width: 64px;
178+
letter-spacing: -0.02em;
161179
}
162180

163-
.role-icon.host { color: #ffbf00; }
164-
.role-icon.edit { color: #00a152; }
165-
.role-icon.view { color: #999; }
181+
.role-badge.owner {
182+
background: rgba(250, 204, 21, 0.14);
183+
color: #fbbf24;
184+
border-color: rgba(250, 204, 21, 0.25);
185+
}
186+
187+
.role-badge.edit {
188+
background: rgba(56, 189, 248, 0.14);
189+
color: #38bdf8;
190+
border-color: rgba(56, 189, 248, 0.25);
191+
}
192+
193+
.role-badge.view {
194+
background: rgba(148, 163, 184, 0.12);
195+
color: #94a3b8;
196+
border-color: rgba(148, 163, 184, 0.2);
197+
}
166198

167199
.participant-item.active-user {
168200
background-color: var(--accent-soft-strong);

src/components/codecast/codecastlive/CodecastSidebar.jsx

Lines changed: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,51 @@
11
import React, { useEffect, useRef, useState } from 'react';
22
import './CodecastSidebar.css';
3-
import { FaCrown, FaPenFancy, FaEye, FaUser, FaEllipsisV, FaCheck, FaComments } from 'react-icons/fa';
3+
import { FaUser, FaEllipsisV, FaCheck, FaComments } from 'react-icons/fa';
44

5-
const RoleIcon = ({ role }) => {
6-
if (role === 'host') return <FaCrown className="role-icon host" />;
7-
if (role === 'edit') return <FaPenFancy className="role-icon edit" />;
8-
return <FaEye className="role-icon view" />;
5+
const RoleBadge = ({ role }) => {
6+
const labelMap = {
7+
owner: '세션 소유자',
8+
edit: '편집',
9+
view: '읽기',
10+
};
11+
const label = labelMap[role] ?? '';
12+
13+
return (
14+
<span className={`role-badge ${role || 'blank'}`}>
15+
{label || '\u00A0'}
16+
</span>
17+
);
918
};
1019

1120
function useClickOutside(onClose) {
1221
const ref = useRef(null);
22+
1323
useEffect(() => {
14-
const handler = (e) => {
24+
const handler = (event) => {
1525
if (!ref.current) return;
16-
if (!ref.current.contains(e.target)) onClose?.();
26+
if (!ref.current.contains(event.target)) {
27+
onClose?.();
28+
}
1729
};
30+
1831
document.addEventListener('mousedown', handler);
1932
return () => document.removeEventListener('mousedown', handler);
2033
}, [onClose]);
34+
2135
return ref;
2236
}
2337

2438
export default function CodecastSidebar({
25-
participants,
26-
currentUserId,
27-
roomOwnerId,
28-
activeSession,
29-
focusedParticipantId,
30-
onSelectParticipant,
31-
onChangePermission,
32-
onKick,
33-
onOpenChat,
34-
}) {
39+
participants,
40+
currentUserId,
41+
roomOwnerId,
42+
activeSession,
43+
focusedParticipantId,
44+
onSelectParticipant,
45+
onChangePermission,
46+
onKick,
47+
onOpenChat,
48+
}) {
3549
const [menuFor, setMenuFor] = useState(null);
3650
const closeMenu = () => setMenuFor(null);
3751
const menuRef = useClickOutside(closeMenu);
@@ -40,14 +54,6 @@ export default function CodecastSidebar({
4054
!!activeSession && (activeSession.ownerId === currentUserId || roomOwnerId === currentUserId);
4155
const canKick = currentUserId === roomOwnerId;
4256

43-
const resolveRole = (participant) => {
44-
if (participant.role === 'host') return 'host';
45-
if (participant.role === 'edit') return 'edit';
46-
if (activeSession?.permissions?.[participant.id] === 'edit') return 'edit';
47-
if (participant.role) return participant.role;
48-
return participant.id === roomOwnerId ? 'host' : 'view';
49-
};
50-
5157
return (
5258
<aside className="codecast-sidebar">
5359
<div className="sidebar-headline">
@@ -67,7 +73,8 @@ export default function CodecastSidebar({
6773

6874
<ul className="participant-list">
6975
{participants.map((participant) => {
70-
const role = resolveRole(participant);
76+
const role = participant.sessionRole || 'view';
77+
const isSessionOwnerRole = role === 'owner';
7178
const isFocused = participant.id === focusedParticipantId;
7279
const isMenuOpen = menuFor === participant.id;
7380
const isSelf = participant.id === currentUserId;
@@ -89,7 +96,10 @@ export default function CodecastSidebar({
8996
<li
9097
key={participant.id}
9198
className={`participant-item ${isFocused ? 'active-user' : ''}`}
92-
onClick={() => onSelectParticipant?.(participant.id)}
99+
onClick={() => {
100+
closeMenu();
101+
onSelectParticipant?.(participant.id);
102+
}}
93103
>
94104
{participant.avatar ? (
95105
<img src={participant.avatar} alt={participant.name} className="avatar" />
@@ -107,32 +117,29 @@ export default function CodecastSidebar({
107117
<span className={`stage ${stage}`}>{stageLabel}</span>
108118
</div>
109119

110-
<RoleIcon role={role} />
120+
<div className="permission-chip">
121+
<RoleBadge role={role} />
122+
</div>
111123

112124
<button
113125
className="more-btn"
114126
type="button"
115127
aria-label={`${participant.name} 더보기`}
116-
onClick={(e) => {
117-
e.stopPropagation();
128+
onClick={(event) => {
129+
event.stopPropagation();
118130
setMenuFor(isMenuOpen ? null : participant.id);
119131
}}
120132
>
121133
<FaEllipsisV />
122134
</button>
123135

124136
{isMenuOpen && (
125-
<div className="more-menu" ref={menuRef} onClick={(e) => e.stopPropagation()}>
137+
<div className="more-menu" ref={menuRef} onClick={(event) => event.stopPropagation()}>
126138
<div className="menu-group">
127139
<div className="menu-label">세션 권한</div>
128140

129-
<button
130-
className="menu-item"
131-
type="button"
132-
onClick={() => {}}
133-
disabled
134-
>
135-
{role === 'host' && <FaCheck className="check" />}
141+
<button className="menu-item" type="button" onClick={() => {}} disabled>
142+
{isSessionOwnerRole && <FaCheck className="check" />}
136143
세션 소유자
137144
</button>
138145

@@ -147,7 +154,11 @@ export default function CodecastSidebar({
147154
type="button"
148155
onClick={() => {
149156
if (!disabled) {
150-
onChangePermission?.(activeSession.sessionId, participant.id, nextRole);
157+
onChangePermission?.(
158+
activeSession.sessionId,
159+
participant.id,
160+
nextRole
161+
);
151162
}
152163
closeMenu();
153164
}}

0 commit comments

Comments
 (0)