11import React , { useEffect , useRef , useState } from 'react' ;
22import './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
1120function 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
2438export 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