websocket stuff
This commit is contained in:
parent
013e6c5b01
commit
cad2d85ce4
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,19 @@
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment="">
|
<list default="true" id="8a64704d-5500-41a6-aa4c-e275933fc58c" name="Changes" comment="">
|
||||||
<change beforePath="$PROJECT_DIR$/../frontend/src/components/Player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/components/Player.tsx" afterDir="false" />
|
<change afterPath="$PROJECT_DIR$/Caddyfile" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/docker-compose.yml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/internal/ws/handlers.go" beforeDir="false" afterPath="$PROJECT_DIR$/internal/ws/handlers.go" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/.env.example" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/.env.example" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/package.json" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/interfaces/IMessage.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/interfaces/IMessage.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/interfaces/Identity.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/interfaces/Identity.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/pages/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/index.tsx" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/util/Message.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/util/Message.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/types/environment.d.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/types/environment.d.ts" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/yarn.lock" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/yarn.lock" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
localhost:3001
|
||||||
|
|
||||||
|
file_server
|
||||||
|
reverse_proxy 127.0.0.1:8080
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
|
@ -3,12 +3,15 @@ package ws
|
||||||
//todo better data deserialization
|
//todo better data deserialization
|
||||||
|
|
||||||
type IdentityData struct {
|
type IdentityData struct {
|
||||||
ClientID string `json:"clientId"`
|
ClientID string `json:"clientID"`
|
||||||
User User `json:"user"`
|
User User `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleIdentifyEvent(message *Message) {
|
func handleIdentifyEvent(message *Message) {
|
||||||
d := message.Data.(map[string]interface{})
|
d := message.Data.(map[string]interface{})
|
||||||
|
if id, ok := d["clientID"]; ok {
|
||||||
|
log.Infof("Client %s has sent identify event", id.(string))
|
||||||
|
}
|
||||||
m := Message{
|
m := Message{
|
||||||
MessageData: MessageData{
|
MessageData: MessageData{
|
||||||
Type: Identify,
|
Type: Identify,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
DISCORD_ID=
|
DISCORD_ID=
|
||||||
DISCORD_SECRET=
|
DISCORD_SECRET=
|
||||||
SECRET=
|
SECRET=
|
||||||
CLIENT_ID=
|
NEXT_PUBLIC_CLIENT_ID=
|
||||||
NEXTAUTH_URL=
|
NEXTAUTH_URL=
|
||||||
|
NEXT_PUBLIC_WS_URI=
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-player": "^2.9.0",
|
"react-player": "^2.9.0",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2",
|
||||||
|
"ws": "^8.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.0.8",
|
"@next/bundle-analyzer": "^12.0.8",
|
||||||
|
|
@ -31,6 +32,7 @@
|
||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@types/websocket": "^1.0.4",
|
"@types/websocket": "^1.0.4",
|
||||||
|
"@types/ws": "^8.2.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"typescript": "4.3.2"
|
"typescript": "4.3.2"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { User } from "next-auth";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import PlayerSocket from "../ws/websocket";
|
||||||
|
|
||||||
|
interface useWSProps {
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo write websocket reconnector
|
||||||
|
const useWS = ({ user }: useWSProps) => {
|
||||||
|
// todo checkout usecallback
|
||||||
|
const [socket, setSocket] = useState<PlayerSocket>();
|
||||||
|
useEffect(() => {
|
||||||
|
let socket = new PlayerSocket(user);
|
||||||
|
setSocket(socket);
|
||||||
|
return () => {
|
||||||
|
return socket.close();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useWS;
|
||||||
|
|
@ -8,7 +8,7 @@ export enum MessageTypes {
|
||||||
|
|
||||||
interface IMessage {
|
interface IMessage {
|
||||||
type: MessageTypes;
|
type: MessageTypes;
|
||||||
data?: Record<string, unknown>;
|
data?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default IMessage;
|
export default IMessage;
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,5 @@ interface IdentityData {
|
||||||
playHead?: number;
|
playHead?: number;
|
||||||
user: IUser;
|
user: IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default IdentityData;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Button, Link as ChakraLink, Text } from "@chakra-ui/react";
|
import { Button, Link as ChakraLink, Text } from "@chakra-ui/react";
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
import { getSession, signIn } from "next-auth/react";
|
import { getSession, signIn } from "next-auth/react";
|
||||||
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Container } from "../components/Container";
|
import { Container } from "../components/Container";
|
||||||
|
|
@ -10,6 +11,10 @@ import { Main } from "../components/Main";
|
||||||
|
|
||||||
const Index: NextPage = () => {
|
const Index: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Watch Together</title>
|
||||||
|
</Head>
|
||||||
<Container height="100vh">
|
<Container height="100vh">
|
||||||
<Hero />
|
<Hero />
|
||||||
<Main>
|
<Main>
|
||||||
|
|
@ -17,7 +22,6 @@ const Index: NextPage = () => {
|
||||||
maxWidth="200"
|
maxWidth="200"
|
||||||
alignSelf="center"
|
alignSelf="center"
|
||||||
onClick={() => signIn("discord")}
|
onClick={() => signIn("discord")}
|
||||||
disabled
|
|
||||||
>
|
>
|
||||||
Login With Discord
|
Login With Discord
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -30,6 +34,7 @@ const Index: NextPage = () => {
|
||||||
</ChakraLink>
|
</ChakraLink>
|
||||||
</Footer>
|
</Footer>
|
||||||
</Container>
|
</Container>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,12 @@
|
||||||
import consola from "consola";
|
|
||||||
import { GetServerSideProps, NextPage } from "next";
|
import { GetServerSideProps, NextPage } from "next";
|
||||||
import { Session, User } from "next-auth";
|
import { User } from "next-auth";
|
||||||
import { getSession, useSession } from "next-auth/react";
|
import { getSession } from "next-auth/react";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import React, { useEffect, useRef } from "react";
|
import Head from "next/head";
|
||||||
|
import React, { useRef } from "react";
|
||||||
import ReactPlayer from "react-player";
|
import ReactPlayer from "react-player";
|
||||||
import { Container } from "../components/Container";
|
import { Container } from "../components/Container";
|
||||||
import { MessageTypes } from "../interfaces/IMessage";
|
import useWS from "../hooks/useWS";
|
||||||
import Message from "../util/Message";
|
|
||||||
import MessageUtil from "../util/MessageUtil";
|
|
||||||
|
|
||||||
const Player = dynamic(() => import("../components/Player"), { ssr: false });
|
const Player = dynamic(() => import("../components/Player"), { ssr: false });
|
||||||
|
|
||||||
|
|
@ -17,58 +15,52 @@ interface PlayerPageProps {
|
||||||
user: User;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pingEvent = (ws: WebSocket) => {
|
|
||||||
let interval = setInterval(() => {
|
|
||||||
if (ws.readyState === ws.CLOSED) {
|
|
||||||
clearInterval(interval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("running ping event");
|
|
||||||
ws.send(MessageUtil.encode(new Message(MessageTypes.Ping)));
|
|
||||||
}, 20000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PlayerPage: NextPage<PlayerPageProps> = ({ URI, user }) => {
|
const PlayerPage: NextPage<PlayerPageProps> = ({ URI, user }) => {
|
||||||
const playerRef = useRef<ReactPlayer>();
|
const playerRef = useRef<ReactPlayer>();
|
||||||
consola.wrapAll();
|
const socket = useWS({ user });
|
||||||
useEffect(() => {
|
// useEffect(() => {
|
||||||
if (typeof window === "undefined") return;
|
// if (typeof window === "undefined") return;
|
||||||
const ws = new WebSocket(URI);
|
// const ws = new WebSocket(URI);
|
||||||
ws.onopen = () => {
|
// ws.onopen = () => {
|
||||||
ws.send(
|
// ws.send(
|
||||||
MessageUtil.encode(
|
// MessageUtil.encode(
|
||||||
new Message(MessageTypes.Identify, {
|
// new Message(MessageTypes.Identify, {
|
||||||
clientID: process.env.CLIENT_ID,
|
// clientID: process.env.CLIENT_ID,
|
||||||
user: {
|
// user: {
|
||||||
ID: user.id,
|
// ID: user.id,
|
||||||
Name: user.name,
|
// Name: user.name,
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
pingEvent(ws);
|
// pingEvent(ws);
|
||||||
};
|
// };
|
||||||
ws.onmessage = (event) => {
|
// ws.onmessage = (event) => {
|
||||||
console.log(event);
|
// console.log(event);
|
||||||
console.log(JSON.parse(event.data));
|
// console.log(JSON.parse(event.data));
|
||||||
};
|
// };
|
||||||
ws.onclose = () => {
|
// ws.onclose = () => {
|
||||||
ws.close();
|
// ws.close();
|
||||||
};
|
// };
|
||||||
ws.onerror = (err) => {
|
// ws.onerror = (err) => {
|
||||||
console.log(err);
|
// console.log(err);
|
||||||
return () => {
|
// return () => {
|
||||||
ws.close();
|
// ws.close();
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
return () => {
|
// return () => {
|
||||||
ws.close();
|
// ws.close();
|
||||||
};
|
// };
|
||||||
}, []);
|
// }, []);
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Watch Together</title>
|
||||||
|
</Head>
|
||||||
<Container height="100vh">
|
<Container height="100vh">
|
||||||
<Player id="" ref={playerRef} />
|
<Player id="" ref={playerRef} />
|
||||||
</Container>
|
</Container>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -84,7 +76,6 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
URI: process.env.WS_URI,
|
|
||||||
user: session.user,
|
user: session.user,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import IMessage, { MessageTypes } from "../interfaces/IMessage";
|
import IMessage, { MessageTypes } from "../interfaces/IMessage";
|
||||||
|
|
||||||
export default class Message implements IMessage {
|
export default class Message implements IMessage {
|
||||||
constructor(
|
constructor(public type: MessageTypes, public data?: unknown) {}
|
||||||
public type: MessageTypes,
|
|
||||||
public data?: Record<string, unknown>
|
|
||||||
) {}
|
|
||||||
|
|
||||||
toJSON(): Record<string, unknown> {
|
toJSON(): Record<string, unknown> {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// nice and easy way to get types for the
|
||||||
|
|
||||||
|
import { User } from "next-auth";
|
||||||
|
import IdentityData from "../interfaces/Identity";
|
||||||
|
import { MessageTypes } from "../interfaces/IMessage";
|
||||||
|
import Message from "../util/Message";
|
||||||
|
import MessageUtil from "../util/MessageUtil";
|
||||||
|
|
||||||
|
// browser socket
|
||||||
|
let Websocket: typeof WebSocket;
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
Websocket = window.WebSocket;
|
||||||
|
} else {
|
||||||
|
Websocket = require("ws");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class PlayerSocket extends Websocket {
|
||||||
|
constructor(private user: User) {
|
||||||
|
super(process.env.NEXT_PUBLIC_WS_URI);
|
||||||
|
this.onopen = this.onOpen;
|
||||||
|
}
|
||||||
|
onOpen() {
|
||||||
|
this.send(
|
||||||
|
MessageUtil.encode(
|
||||||
|
new Message(MessageTypes.Identify, {
|
||||||
|
clientID: process.env.NEXT_PUBLIC_CLIENT_ID,
|
||||||
|
user: {
|
||||||
|
id: this.user.id,
|
||||||
|
name: this.user.name,
|
||||||
|
},
|
||||||
|
} as IdentityData)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.pingEvent();
|
||||||
|
}
|
||||||
|
pingEvent() {
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
if (!this.open) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log("[WS] running ping event");
|
||||||
|
this.send(MessageUtil.encode(new Message(MessageTypes.Ping)));
|
||||||
|
}, 20000);
|
||||||
|
}
|
||||||
|
get open() {
|
||||||
|
return this.readyState === this.OPEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ declare namespace NodeJS {
|
||||||
DISCORD_ID: string;
|
DISCORD_ID: string;
|
||||||
DISCORD_SECRET: string;
|
DISCORD_SECRET: string;
|
||||||
SECRET: string;
|
SECRET: string;
|
||||||
CLIENT_ID: string;
|
NEXT_PUBLIC_CLIENT_ID: string;
|
||||||
WS_URI: string;
|
NEXT_PUBLIC_WS_URI: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1029,6 +1029,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ws@^8.2.2":
|
||||||
|
version "8.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21"
|
||||||
|
integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
acorn-walk@^8.0.0:
|
acorn-walk@^8.0.0:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||||
|
|
@ -3227,6 +3234,11 @@ ws@^7.3.1:
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
|
||||||
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
|
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
|
||||||
|
|
||||||
|
ws@^8.4.2:
|
||||||
|
version "8.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b"
|
||||||
|
integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA==
|
||||||
|
|
||||||
xtend@^4.0.2:
|
xtend@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue