@@ -621,15 +621,21 @@ export default function CodecastLive({ isDark }) {
621621 await connect ( token ) ;
622622 if ( cancelled ) return ;
623623 unsubscribe = subscribeCode ( room . id , sessionId , ( msg ) => {
624- if ( ! msg || msg . senderId === userId ) return ;
624+ if ( ! msg ) return ;
625+ const senderId = msg . senderId ;
626+ if ( senderId === userId ) return ;
625627 if ( typeof msg . content !== 'string' ) return ;
626628
627- const senderId = msg . senderId ;
628629 const newContent = msg . content ;
630+ const targetParticipantId =
631+ msg . targetParticipantId ||
632+ msg . ownerId ||
633+ msg . sessionOwnerId ||
634+ senderId ;
629635
630636 updateParticipants ( ( prev ) =>
631637 prev . map ( ( p ) =>
632- p . id === senderId
638+ p . id === targetParticipantId
633639 ? {
634640 ...p ,
635641 code : newContent ,
@@ -663,28 +669,79 @@ export default function CodecastLive({ isDark }) {
663669 } ;
664670 } , [ disconnect ] ) ;
665671
672+ const findById = ( id ) => participants . find ( ( p ) => p . id === id ) ;
673+
674+ const isViewingSelf = activeParticipant ?. id === ownParticipantId ;
675+
676+ const roomOwnerId = useMemo ( ( ) => {
677+ const host = participants . find ( ( p ) => p . role === 'host' ) ;
678+ if ( host ) return host . id ;
679+ if ( currentUser . role === 'host' ) return currentUser . id ;
680+ return injected . ownerId || null ;
681+ } , [ participants , injected . ownerId , currentUser . id , currentUser . role ] ) ;
682+
683+ const activeSessionMeta = useMemo ( ( ) => {
684+ if ( ! sessionId ) return null ;
685+ const ownerParticipant =
686+ participants . find ( ( p ) => p . id === sessionOwnerId ) ||
687+ participants . find ( ( p ) => p . id === roomOwnerId ) ;
688+ const permissions = participants . reduce ( ( acc , p ) => {
689+ if ( p . role === 'edit' ) acc [ p . id ] = 'edit' ;
690+ return acc ;
691+ } , { } ) ;
692+
693+ return {
694+ sessionId,
695+ ownerId : sessionOwnerId || ownerParticipant ?. id || roomOwnerId || null ,
696+ permissions,
697+ } ;
698+ } , [ sessionId , participants , roomOwnerId , sessionOwnerId ] ) ;
699+
700+ const hasEditPermissionOnActive = useMemo ( ( ) => {
701+ if ( ! activeParticipant ) return false ;
702+ if ( activeParticipant . id === ownParticipantId ) return true ;
703+ if ( ! activeSessionMeta ) return false ;
704+ if ( activeParticipant . stage !== 'editing' ) return false ;
705+
706+ const isSessionOwner = activeSessionMeta . ownerId === ownParticipantId ;
707+ const isRoomOwner = roomOwnerId === ownParticipantId ;
708+ const hasGrantedPermission = activeSessionMeta . permissions ?. [ ownParticipantId ] === 'edit' ;
709+
710+ return Boolean ( isSessionOwner || isRoomOwner || hasGrantedPermission ) ;
711+ } , [ activeParticipant , activeSessionMeta , ownParticipantId , roomOwnerId ] ) ;
712+
713+ const editorReadOnly = ! ( isViewingSelf || hasEditPermissionOnActive ) ;
714+
666715 // 에디터 변경 → 서버 publish
667716 const handleEditorChange = ( nextText ) => {
668- if ( activeParticipantId !== currentUser . id ) return ;
669- // 서버에 보내는 것은 동일
670- // 로컬 상태 업데이트
671- setCurrentUser ( ( prev ) => ( {
672- ...prev ,
673- code : nextText ,
674- file : prev . file ? { ...prev . file , content : nextText } : prev . file ,
675- } ) ) ;
717+ if ( editorReadOnly ) return ;
718+
719+ const ownId = selfParticipantId || currentUser . id ;
720+ const targetParticipantId = activeParticipant ?. id ;
721+ if ( ! targetParticipantId ) return ;
722+
676723 setParticipants ( ( prev ) =>
677724 prev . map ( ( p ) =>
678- p . id === ( selfParticipantId || currentUser . id )
725+ p . id === targetParticipantId
679726 ? { ...p , code : nextText , file : p . file ? { ...p . file , content : nextText } : p . file }
680727 : p
681728 )
682729 ) ;
683730
684- // 공유 중일 때만 업데이트를 브로드캐스트
685- if ( room . id && sessionId && currentUser . stage === 'editing' ) {
686- sendCodeUpdate ( room . id , sessionId , {
687- senderId : selfParticipantId || currentUser . id ,
731+ if ( targetParticipantId === ownId ) {
732+ setCurrentUser ( ( prev ) => ( {
733+ ...prev ,
734+ code : nextText ,
735+ file : prev . file ? { ...prev . file , content : nextText } : prev . file ,
736+ } ) ) ;
737+ }
738+
739+ const targetSessionId = activeSessionMeta ?. sessionId || sessionId ;
740+
741+ if ( room . id && targetSessionId && activeParticipant ?. stage === 'editing' ) {
742+ sendCodeUpdate ( room . id , targetSessionId , {
743+ senderId : ownId ,
744+ targetParticipantId,
688745 content : nextText ,
689746 } ) ;
690747 }
@@ -750,6 +807,13 @@ export default function CodecastLive({ isDark }) {
750807 return ;
751808 }
752809
810+ const ownId = selfParticipantId || currentUser . id ;
811+ const viewingOwnIde = activeParticipantId === ownId ;
812+ if ( ! viewingOwnIde ) {
813+ alert ( '자신의 IDE 화면을 보고 있을 때만 방송을 시작하거나 중지할 수 있습니다.' ) ;
814+ return ;
815+ }
816+
753817 let ensuredSessionId = sessionId ;
754818 let preparedFile = currentUser . file ;
755819 const ownsCurrentSession = sessionOwnerId && ( sessionOwnerId === ( selfParticipantId || currentUser . id ) ) ;
@@ -1053,49 +1117,6 @@ export default function CodecastLive({ isDark }) {
10531117 }
10541118 } ;
10551119
1056- const findById = ( id ) => participants . find ( ( p ) => p . id === id ) ;
1057-
1058- const isViewingSelf = activeParticipant ?. id === ownParticipantId ;
1059-
1060- const roomOwnerId = useMemo ( ( ) => {
1061- const host = participants . find ( ( p ) => p . role === 'host' ) ;
1062- if ( host ) return host . id ;
1063- if ( currentUser . role === 'host' ) return currentUser . id ;
1064- return injected . ownerId || null ;
1065- } , [ participants , injected . ownerId , currentUser . id , currentUser . role ] ) ;
1066-
1067- const activeSessionMeta = useMemo ( ( ) => {
1068- if ( ! sessionId ) return null ;
1069- const ownerParticipant =
1070- participants . find ( ( p ) => p . id === sessionOwnerId ) ||
1071- participants . find ( ( p ) => p . id === roomOwnerId ) ;
1072- const permissions = participants . reduce ( ( acc , p ) => {
1073- if ( p . role === 'edit' ) acc [ p . id ] = 'edit' ;
1074- return acc ;
1075- } , { } ) ;
1076-
1077- return {
1078- sessionId,
1079- ownerId : sessionOwnerId || ownerParticipant ?. id || roomOwnerId || null ,
1080- permissions,
1081- } ;
1082- } , [ sessionId , participants , roomOwnerId , sessionOwnerId ] ) ;
1083-
1084- const hasEditPermissionOnActive = useMemo ( ( ) => {
1085- if ( ! activeParticipant ) return false ;
1086- if ( activeParticipant . id === ownParticipantId ) return true ;
1087- if ( ! activeSessionMeta ) return false ;
1088- if ( activeParticipant . stage !== 'editing' ) return false ;
1089-
1090- const isSessionOwner = activeSessionMeta . ownerId === ownParticipantId ;
1091- const isRoomOwner = roomOwnerId === ownParticipantId ;
1092- const hasGrantedPermission = activeSessionMeta . permissions ?. [ ownParticipantId ] === 'edit' ;
1093-
1094- return Boolean ( isSessionOwner || isRoomOwner || hasGrantedPermission ) ;
1095- } , [ activeParticipant , activeSessionMeta , ownParticipantId , roomOwnerId ] ) ;
1096-
1097- const editorReadOnly = ! ( isViewingSelf || hasEditPermissionOnActive ) ;
1098-
10991120 const previewParticipants = useMemo ( ( ) => {
11001121 const editingList = participants . filter ( ( p ) => p . stage === 'editing' ) ;
11011122 const selfParticipant = participants . find ( ( p ) => p . id === selfParticipantId ) ;
0 commit comments