Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VITE_SOCKET_SERVER_URL=http://localhost:4000
VITE_SERVER_URL=http://localhost:4000
VITE_SOCKET_SERVER_URL=http://localhost:3050
VITE_SOCKET_SERVER_PORT=3050
VITE_AUTH_KEY=_plug-auth_
15 changes: 15 additions & 0 deletions codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
overwrite: true,
schema: 'http://localhost:4000/graphql',
documents: ['src/**/*.tsx'],
ignoreNoDocuments: true,
generates: {
'./src/__api__/types.ts': {
plugins: ['typescript', 'typescript-operations'],
},
},
}

export default config
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,34 @@
"name": "react-typescript-vite",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start" : "vite serve ./dist",
"start": "vite serve ./dist",
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"prepare": "husky install"
"prepare": "husky install",
"codegen": "graphql-codegen --config codegen.ts"
},
"dependencies": {
"@apollo/client": "^3.7.7",
"@types/js-cookie": "^3.0.2",
"graphql": "^16.6.0",
"js-cookie": "^3.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.43.0",
"react-router-dom": "^6.8.0",
"socket.io-client": "^4.5.4"
"socket.io-client": "^4.5.4",
"zustand": "^4.3.2"
},
"devDependencies": {
"@commitlint/cli": "^17.4.2",
"@commitlint/config-conventional": "^17.4.2",
"@graphql-codegen/cli": "3.0.0",
"@graphql-codegen/client-preset": "2.0.0",
"@graphql-codegen/introspection": "3.0.0",
"@graphql-codegen/typescript": "^3.0.0",
"@graphql-codegen/typescript-operations": "^3.0.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@vitejs/plugin-react": "^3.0.0",
Expand Down
74 changes: 74 additions & 0 deletions src/__api__/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
DateTime: any;
};

export type LoginInput = {
nickname: Scalars['String'];
};

export type LoginOutput = {
__typename?: 'LoginOutput';
error?: Maybe<Scalars['String']>;
ok: Scalars['Boolean'];
token?: Maybe<Scalars['String']>;
user?: Maybe<User>;
};

export type Mutation = {
__typename?: 'Mutation';
login: LoginOutput;
};


export type MutationLoginArgs = {
input: LoginInput;
};

export type Query = {
__typename?: 'Query';
getMe: UserProfileOutput;
};

export type User = {
__typename?: 'User';
createdAt: Scalars['DateTime'];
id: Scalars['Float'];
nickname: Scalars['String'];
role?: Maybe<UserRole>;
updatedAt: Scalars['DateTime'];
};

export type UserProfileOutput = {
__typename?: 'UserProfileOutput';
error?: Maybe<Scalars['String']>;
ok: Scalars['Boolean'];
user?: Maybe<User>;
};

export enum UserRole {
Admin = 'Admin',
Client = 'Client'
}

export type GetMeQueryVariables = Exact<{ [key: string]: never; }>;


export type GetMeQuery = { __typename?: 'Query', getMe: { __typename?: 'UserProfileOutput', ok: boolean, error?: string | null, user?: { __typename?: 'User', id: number, nickname: string, createdAt: any, updatedAt: any, role?: UserRole | null } | null } };

export type LoginMutationVariables = Exact<{
LoginInput: LoginInput;
}>;


export type LoginMutation = { __typename?: 'Mutation', login: { __typename?: 'LoginOutput', ok: boolean, token?: string | null, error?: string | null, user?: { __typename?: 'User', id: number, nickname: string, updatedAt: any, createdAt: any, role?: UserRole | null } | null } };
7 changes: 7 additions & 0 deletions src/apollo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ApolloClient } from '@apollo/client'
import { InMemoryCache } from '@apollo/client/cache'

export const client = new ApolloClient({
uri: `${import.meta.env.VITE_SERVER_URL}/graphql`,
cache: new InMemoryCache(),
})
24 changes: 14 additions & 10 deletions src/context/socketManager.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { Manager } from 'libs/Manager'
import { createContext, FC, ReactNode } from 'react'
import { io, Manager, Socket } from 'socket.io-client'
import { Socket } from 'socket.io-client'

// export const manager = new Manager(import.meta.env.VITE_SOCKET_SERVER_URL, {
// path: '/',
// transports: ['websocket'],
// })

const socket = io(import.meta.env.VITE_SOCKET_SERVER_URL, {
path: '/',
export const manager = new Manager(import.meta.env.VITE_SOCKET_SERVER_URL, {
transports: ['websocket'],
multiplex: true,
})

export const SocketContext = createContext<{ socket: Socket | null }>({ socket: null })
export const SocketContext = createContext<{
socket: Socket | null
manager: Manager
}>({
socket: null,
manager,
})
const SocketProvider: FC<{ children: ReactNode }> = ({ children }) => {
return <SocketContext.Provider value={{ socket }}>{children}</SocketContext.Provider>
return (
<SocketContext.Provider value={{ socket: null, manager }}>{children}</SocketContext.Provider>
)
}
export default SocketProvider

Expand Down
1 change: 1 addition & 0 deletions src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ declare namespace NodeJS {
readonly VITE_SERVER_URL: string
readonly VITE_SOCKET_SERVER_URL: string
readonly VITE_SOCKET_SERVER_PORT: string
readonly VITE_AUTH_KEY: string
readonly [key: string]: string
}
}
40 changes: 40 additions & 0 deletions src/hooks/useRTCConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { useCallback, useEffect, useRef, useState } from 'react'
interface RTCConnection {
isLoading: boolean
stream: MediaStream | null
}

interface RTCConnectionProps {
onConnect?: (stream: MediaStream) => void
}

const useRTCConnection = ({ onConnect }: RTCConnectionProps = {}) => {
const returnValue = useRef<RTCConnection>({
isLoading: true,
stream: null,
})
const getConenction = useCallback(async () => {
let stream = null
try {
stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
})
onConnect && onConnect(stream)
} catch (err) {
returnValue.current = {
stream,
isLoading: false,
}
}
}, [])

useEffect(() => {
getConenction()
}, [])
return {
...returnValue.current,
}
}

export default useRTCConnection
57 changes: 56 additions & 1 deletion src/hooks/useSocket.ts
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
export {}
import { useEffect, useRef, useState } from 'react'
import { useContext } from 'react'
import { SocketContext } from 'context/socketManager'
import { Socket } from 'socket.io-client'

interface useSocketProps {
nsp: string
onConnect?: (socket: Socket) => void
onUnmounted?: (socket: Socket) => void
onMounted?: (socket: Socket) => void
}

const useSocket = ({ nsp, onConnect, onUnmounted, onMounted }: useSocketProps) => {
const { manager } = useContext(SocketContext)
const [isError, setIsError] = useState<string | null>(null)

const socket = useRef(
manager.create_socket(nsp, {
auth: {
token: localStorage.getItem('_PLUG_AUTH_') || '',
},
}),
)
useEffect(() => {
if (socket.current) {
socket.current.listen('connect_error', ({ message }: Error) => {
if (!isError) {
setIsError(message)
}
})
socket.current.listen('connect', () => {
if (isError) {
setIsError(null)
}
onConnect && onConnect(socket.current)
})
}
}, [socket.current, isError])
useEffect(() => {
if (socket.current) {
onMounted && onMounted(socket.current)
}
return () => {
onUnmounted && onUnmounted(socket.current)
}
}, [socket.current])
return {
manager,
socket: socket.current,
isError,
isConnected: socket.current.connected,
isLoading: !socket.current,
}
}

export default useSocket
66 changes: 48 additions & 18 deletions src/layout/SocketLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,52 @@
import { SocketContext } from 'context/socketManager'
import { useContext, useLayoutEffect, useState } from 'react'
import { Outlet } from 'react-router-dom'
const SocketLayout = () => {
const { socket } = useContext(SocketContext)
const [loading, setLoading] = useState(true)
useLayoutEffect(() => {
const nickname = localStorage.getItem('plug_nickname')
if (socket && nickname) {
socket?.emit('set_nickname', nickname, (nickname: string) => {
if (nickname) {
localStorage.setItem('plug_nickname', nickname)
} else {
// todo
}
})
import { gql, useQuery } from '@apollo/client'
import { Outlet, useNavigate } from 'react-router-dom'
import { useClearUser } from 'store/action'
import store from 'store/index'
import { GetMeQuery } from '__api__/types'

const GET_ME = gql`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graphQl ๋ฅผ ๋„์ž… ํ•˜์…จ๋„ค์š”.

query getMe {
getMe {
ok
user {
id
nickname
createdAt
updatedAt
role
}
error
}
setLoading(false)
}, [])
}
`

const SocketLayout = () => {
const key = import.meta.env.VITE_AUTH_KEY
const token = localStorage.getItem(key) || ''

const { setUser, clear } = store((state) => ({
setUser: state.setUser,
clear: state.clear,
}))
const { data, loading, client } = useQuery<GetMeQuery>(GET_ME, {
context: {
headers: {
[key]: token,
},
},
onCompleted({ getMe: { ok, error, user } }) {
if (ok && user) {
setUser({ user, token })
} else {
clear()
navigate('/login')
}
},
fetchPolicy: 'no-cache',
})

const navigate = useNavigate()

return <div>{loading ? 'loading...' : <Outlet />}</div>
}

Expand Down
34 changes: 34 additions & 0 deletions src/libs/Manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Manager as M, Socket } from 'socket.io-client'
import { SocketOptions } from 'socket.io-client/build/esm/socket'

export class Manager extends M {
private readonly sockets: { [key: string]: Socket } = {}
private readonly latestSocketNsp = null
create_socket(nsp: string, opts?: Partial<SocketOptions> | undefined): Socket {
//TODO: ์ตœ๊ทผ์‚ฌ์šฉํ•œ ์†Œ์ผ“ ์ปค๋„ฅ์…˜ ์ข…๋ฃŒ

if (this.sockets.hasOwnProperty(nsp)) {
return this.sockets[nsp]
}

// if (this.latestSocketNsp) {

// }
const socket = this.socket(nsp, opts)

socket.publicId = nsp
socket.listen = function bindEventListnerOnSocket(ev, lisnter) {
if (this.hasListeners(ev)) {
socket.off(ev)
}
socket.on(ev, lisnter)
return socket
}
this.sockets[nsp] = socket
return socket
}

isExistNsp(url: string) {
return !!this.sockets[url]
}
}
Loading