Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/jungle-6-3/code-sync-fe int…
Browse files Browse the repository at this point in the history
…o feat/121
  • Loading branch information
Y-minseong committed Nov 10, 2024
2 parents 2c9c78f + f8c2463 commit c8eb363
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 101 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/fe-cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
echo VITE_WS_URL=${{ secrets.VITE_WS_URL }} >> .env
echo VITE_PR_URL=${{ secrets.VITE_PR_URL }} >> .env
echo VITE_YJS_URL=${{ secrets.VITE_YJS_URL }} >> .env
echo VITE_RTC_ICE=${{ secrets.VITE_RTC_ICE }} >> .env
echo VITE_ICE_USER=${{ secrets.VITE_ICE_USER }} >> .env
echo VITE_ICE_PASS=${{ secrets.VITE_ICE_PASS }} >> .env
cat .env
- name: run deploy
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@tanstack/react-query": "^5.59.15",
"@types/dom-speech-recognition": "^0.0.4",
"axios": "^1.7.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/components/File/PRBottomFileExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ const CommentViewer = ({ comments }: CommentViewerProps) => {
<img
src={firstComment.user.avatar_url}
alt=""
loading="lazy"
className="h-6 w-6 rounded-full"
/>
<div className="min-w-0 flex-1">
Expand Down Expand Up @@ -138,6 +139,7 @@ const CommentViewer = ({ comments }: CommentViewerProps) => {
<img
src={comment.user.avatar_url}
alt=""
loading="lazy"
className="h-6 w-6 rounded-full"
/>
<div className="min-w-0 flex-1">
Expand Down
92 changes: 39 additions & 53 deletions src/components/Frame/MainFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import ConversationChatting from "@/components/Conversation/useConversationChatt
import { PrFileNameViewer } from "@/components/File/PrSelectedFileViewer/PrFileNameViewer";
import { PrFilePathViewer } from "@/components/File/PrSelectedFileViewer/PrFilePathViewer";
import { PRBottomFileExplorer } from "@/components/File/PRBottomFileExplorer";
import { initFileStructSync } from "@/lib/yjs";
import { initFileStructSync, RemoteCursorIndicator } from "@/lib/yjs";
import { SocketManager } from "@/lib/socketManager";
import { useCommunicationStore } from "@/stores/communicationState.store";
import {
Expand Down Expand Up @@ -65,6 +65,13 @@ export const MainFrame = () => {
(state) => state.setOtherUserSelectedCommitFile,
);

const removeAllCursorStle = () => {
const cursorStyles = document.querySelectorAll(
'[class*="yRemoteSelectionHead-"]',
);
cursorStyles.forEach((style) => style.remove());
};

useEffect(() => {
const onChatting = (msg: {
name: string;
Expand All @@ -89,34 +96,15 @@ export const MainFrame = () => {

useEffect(() => {
if (!editor || !provider || !ydoc || !checkUser?.data) return;
const position = editor.getPosition();
const cursorStyles = document.querySelectorAll(
'[class*="yRemoteSelectionHead-"]',
);
cursorStyles.forEach((style) => style.remove());
if (position) {
provider.awareness.setLocalStateField("user", {
name: checkUser.data.name,
color: "#ff6161",
colorLight: "#30bced33",
cursor: {
position: {
lineNumber: position.lineNumber,
column: position.column,
},
filename: selectedCommitFile.filename,
},
});
} else {
provider.awareness.setLocalStateField("user", {
name: checkUser.data.name,
color: "#ff6161",
colorLight: "#30bced33",
cursor: {
filename: selectedCommitFile.filename,
},
});
}
removeAllCursorStle();
provider.awareness.setLocalStateField("user", {
name: checkUser.data.name,
color: "#ff6161",
colorLight: "#30bced33",
cursor: {
current_file_path: selectedCommitFile.filename,
},
});

return () => {
provider?.awareness.setLocalStateField("user", null);
Expand All @@ -125,32 +113,17 @@ export const MainFrame = () => {

useEffect(() => {
if (!editor || !provider || !ydoc || !checkUser?.data) return;
provider.awareness.on("change", () => {
// if (selectedCommitFile.filename !== otherUserSelectedCommitFile) return
removeAllCursorStle();
const handleAwarnessChange = () => {
const statesArray = Array.from(provider.awareness.getStates());
statesArray.forEach((state) => {
const [clientId, clientState] = state;
if (clientState?.user) {
const styleSheet = document.createElement("style");
styleSheet.innerText = `
.yRemoteSelectionHead-${clientId}{
border: 2px solid ${clientState.user.color};
position:relative;
}
.yRemoteSelectionHead-${clientId}::before {
content: '${clientState.user.name}';
color: white;
top: -15px;
position:absolute;
left: -2px;
background-color:${clientState.user.color};
font-size:12px;
padding:4px 4px 2px 2px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
border-top-left-radius:5px;
}
`;
styleSheet.innerText = RemoteCursorIndicator(
clientId,
clientState.user,
);
document.head.appendChild(styleSheet);
}
});
Expand All @@ -168,9 +141,18 @@ export const MainFrame = () => {

if (!myInfo?.[1]?.user?.cursor || !otherInfo?.[1]?.user?.cursor) return;
const otherUserCurrentCursor = otherInfo[1].user.cursor;
if (otherUserSelectedCommitFile !== otherUserCurrentCursor.filename)
setOtherUserSelectedCommitFile(otherUserCurrentCursor.filename);
});
if (
otherUserSelectedCommitFile !== otherUserCurrentCursor.current_file_path
)
setOtherUserSelectedCommitFile(
otherUserCurrentCursor.current_file_path,
);
};
provider.awareness.on("change", handleAwarnessChange);
return () => {
removeAllCursorStle();
provider.awareness.off("change", handleAwarnessChange);
};
}, [
provider,
editor,
Expand All @@ -181,6 +163,10 @@ export const MainFrame = () => {
setOtherUserSelectedCommitFile,
]);

useEffect(() => {
removeAllCursorStle();
}, [selectedCommitFile.filename]);

// 파일 내용 초기화
useEffect(() => {
if (!commitFileList || commitFileList.length === 0) return;
Expand Down
11 changes: 11 additions & 0 deletions src/lib/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,14 @@ export const getDirectoryContents = (
// 폴더를 먼저, 그 다음 파일을 반환
return [...folders.sort(), ...files.sort()];
};

export const removeFileFromList = (
fileList: PrChangedFileInfo[],
removeFile: PrChangedFileInfo,
) => {
return fileList.filter((file) => file.filename !== removeFile.filename);
};

export const getNextSelectedFile = (fileList:PrChangedFileInfo[],defaultFile:PrChangedFileInfo) => {
return fileList.length > 0 ? fileList[fileList.length -1] : defaultFile
}
10 changes: 9 additions & 1 deletion src/lib/peer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ export interface PeerConnection {

export const initializePeerConnection = async () => {
return new Promise<PeerConnection>((resolve) => {
const peer = new Peer();
const peer = new Peer({
config: {
'iceServers': [
{ url: import.meta.env.VITE_RTC_ICE, username: import.meta.env.VITE_ICE_USER, credential: import.meta.env.VITE_ICE_PASS },
{ url: 'stun:stun.l.google.com:19302' },
{ url: 'stun:stun1.l.google.com:19302' },
],
}
});
peer.once("open", (id) => {
resolve({ peer, id, peers: {} });
});
Expand Down
79 changes: 58 additions & 21 deletions src/lib/yjs.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { PrChangedFileInfo } from '@/stores/github.store';
import { WebsocketProvider } from 'y-websocket';
import * as Y from 'yjs';
import { PrChangedFileInfo } from "@/stores/github.store";
import { WebsocketProvider } from "y-websocket";
import * as Y from "yjs";
const YJS_SOCKET = import.meta.env.VITE_YJS_URL || "wss://demos.yjs.dev/ws";

export const initializeYjsSocket = async ({ roomUuid }: { roomUuid: string }) => {
return new Promise<{ ydoc: Y.Doc, provider: WebsocketProvider }>((resolve) => {
const ydoc = new Y.Doc();
const provider = new WebsocketProvider(YJS_SOCKET, roomUuid, ydoc, {
connect: true,
maxBackoffTime: 2500,
});
provider.on("status", (event: { status: string }) => {
if (event.status === "connected") {
resolve({ ydoc, provider });
}
});
});
}
export const initializeYjsSocket = async ({
roomUuid,
}: {
roomUuid: string;
}) => {
return new Promise<{ ydoc: Y.Doc; provider: WebsocketProvider }>(
(resolve) => {
const ydoc = new Y.Doc();
const provider = new WebsocketProvider(YJS_SOCKET, roomUuid, ydoc, {
connect: true,
maxBackoffTime: 2500,
});
provider.on("status", (event: { status: string }) => {
if (event.status === "connected") {
resolve({ ydoc, provider });
}
});
},
);
};


export const initFileStructSync = (ydoc: Y.Doc, provider: WebsocketProvider,
commitFileList: PrChangedFileInfo[], initCommitFileList: (commitFileList: PrChangedFileInfo[]) => void) => {
export const initFileStructSync = (
ydoc: Y.Doc,
provider: WebsocketProvider,
commitFileList: PrChangedFileInfo[],
initCommitFileList: (commitFileList: PrChangedFileInfo[]) => void,
) => {
const fileMetadata = ydoc.getArray<PrChangedFileInfo>("fileMetadata");
provider.on("sync", (isSynced: boolean) => {
if (isSynced) {
Expand Down Expand Up @@ -60,4 +69,32 @@ export const initFileStructSync = (ydoc: Y.Doc, provider: WebsocketProvider,
}
}
});
}
};

export const RemoteCursorIndicator = (
clientId: number,
user: {
name: string;
color: string;
},
) => {
return `
.yRemoteSelectionHead-${clientId} {
border: 2px solid ${user.color};
position: relative;
}
.yRemoteSelectionHead-${clientId}::before {
content: '${user.name}';
color: white;
top: -15px;
position: absolute;
left: -2px;
background-color: ${user.color};
font-size: 12px;
padding: 4px 4px 2px 2px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
border-top-left-radius: 5px;
}
`;
};
51 changes: 25 additions & 26 deletions src/stores/github.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import {
getPrCommitsData,
getPrData,
} from "@/apis/pr/pr";
import { getLanguageFromFileName } from "@/lib/file";
import {
getLanguageFromFileName,
getNextSelectedFile,
removeFileFromList,
} from "@/lib/file";
import { GitHubCommentsResponse } from "@/apis/pr/dto";

interface PrInfoProps {
Expand Down Expand Up @@ -147,16 +151,18 @@ export const prInfoStore = create<PrInfoPropsStore>()((set) => ({
}),
}));

const DEFAULT_FILE: PrChangedFileInfo = {
filename: "",
status: "init",
language: "",
additions: 0,
deletions: 0,
afterContent: "",
beforeContent: "",
};

export const fileSysyemStore = create<fileSysyemPropsStore>()((set, get) => ({
selectedCommitFile: {
filename: "",
status: "init",
language: "",
additions: 0,
deletions: 0,
afterContent: "",
beforeContent: "",
},
selectedCommitFile: DEFAULT_FILE,
otherUserSelectedCommitFile: "",
commitFileList: [],
commentsList: [],
Expand All @@ -183,23 +189,16 @@ export const fileSysyemStore = create<fileSysyemPropsStore>()((set, get) => ({
})),
removeClickedFileList: (removeFile) =>
set((state) => {
const updateClickedFileList = state.clickedFileList.filter(
(file) => file.filename !== removeFile.filename,
const updateClickedFileList = removeFileFromList(
state.clickedFileList,
removeFile,
);
if (state.selectedCommitFile.filename === removeFile.filename) {
const newSelectedFile =
updateClickedFileList.length > 0
? updateClickedFileList[updateClickedFileList.length - 1]
: {
filename: "",
status: "init" as PrChangedFileStatusInfo["status"],
language: "",
additions: 0,
deletions: 0,
afterContent: "",
beforeContent: "",
};
get().setSelectedCommitFile(newSelectedFile);
const isCurrentFileCanRemoved =
state.selectedCommitFile.filename === removeFile.filename;
if (isCurrentFileCanRemoved) {
get().setSelectedCommitFile(
getNextSelectedFile(updateClickedFileList, DEFAULT_FILE),
);
}
return { clickedFileList: updateClickedFileList };
}),
Expand Down

0 comments on commit c8eb363

Please sign in to comment.