Compare commits
No commits in common. "main" and "refactor/main" have entirely different histories.
main
...
refactor/m
23
README.md
23
README.md
|
|
@ -1,23 +0,0 @@
|
||||||
# watchtogether
|
|
||||||
|
|
||||||
This was a project that I created over 7 days of working while having covid :)
|
|
||||||
|
|
||||||
I probably won't do much with the codebase, but enjoy how I basically figured out how to sync video streams with multiple clients
|
|
||||||
|
|
||||||
## The Stack
|
|
||||||
|
|
||||||
- frontend
|
|
||||||
|
|
||||||
- next js
|
|
||||||
- websockets
|
|
||||||
- chakra-ui
|
|
||||||
- react-player
|
|
||||||
|
|
||||||
- backend
|
|
||||||
|
|
||||||
- go
|
|
||||||
- gorilla/websockets
|
|
||||||
|
|
||||||
## how 2 deploy?
|
|
||||||
|
|
||||||
you don't
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
<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$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/../frontend/src/components/Player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/components/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/pages/player.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/../frontend/src/pages/player.tsx" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
# backend
|
|
||||||
|
|
@ -1 +1,39 @@
|
||||||
# frontend
|
# Example app with [chakra-ui](https://github.com/chakra-ui/chakra-ui) and TypeScript
|
||||||
|
|
||||||
|
This example features how to use [chakra-ui](https://github.com/chakra-ui/chakra-ui) as the component library within a Next.js app with TypeScript.
|
||||||
|
|
||||||
|
Next.js and chakra-ui have built-in TypeScript declarations, so we'll get autocompletion for their modules straight away.
|
||||||
|
|
||||||
|
We are connecting the Next.js `_app.js` with `chakra-ui`'s Provider and theme so the pages can have app-wide dark/light mode. We are also creating some components which shows the usage of `chakra-ui`'s style props.
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
|
||||||
|
Preview the example live on [StackBlitz](http://stackblitz.com/):
|
||||||
|
|
||||||
|
[](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/with-chakra-ui-typescript)
|
||||||
|
|
||||||
|
## Deploy your own
|
||||||
|
|
||||||
|
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):
|
||||||
|
|
||||||
|
[](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-chakra-ui-typescript&project-name=with-chakra-ui-typescript&repository-name=with-chakra-ui-typescript)
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
### Using `create-next-app`
|
||||||
|
|
||||||
|
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx create-next-app --example with-chakra-ui-typescript with-chakra-ui-typescript-app
|
||||||
|
# or
|
||||||
|
yarn create next-app --example with-chakra-ui-typescript with-chakra-ui-typescript-app
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Chakra has supported Gradients and RTL in `v1.1`. To utilize RTL, [add RTL direction and swap](https://chakra-ui.com/docs/features/rtl-support).
|
||||||
|
|
||||||
|
If you don't have multi-direction app, you should make `<Html lang="ar" dir="rtl">` inside `_document.ts`.
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,9 @@ const Player = forwardRef<ReactPlayer, ReactPlayerProps>((props, ref) => {
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
config={config}
|
config={config}
|
||||||
|
controls
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
playing={false}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ const useWS = ({ user }: useWSProps): PlayerSocket | null => {
|
||||||
// todo checkout usecallback
|
// todo checkout usecallback
|
||||||
const [socket, setSocket] = useState<PlayerSocket>(null);
|
const [socket, setSocket] = useState<PlayerSocket>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (socket !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let internalSocket = new PlayerSocket(user);
|
let internalSocket = new PlayerSocket(user);
|
||||||
setSocket(internalSocket);
|
setSocket(internalSocket);
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import IUser from "./IUser";
|
||||||
|
|
||||||
interface IdentityData {
|
interface IdentityData {
|
||||||
admin?: boolean;
|
admin?: boolean;
|
||||||
hasController?: boolean;
|
controller?: boolean;
|
||||||
clientID?: string;
|
clientID?: string;
|
||||||
playlist?: string;
|
playlist?: string;
|
||||||
playhead?: number;
|
playhead?: number;
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,10 @@ import { Container } from "../components/Container";
|
||||||
import Player from "../components/Player";
|
import Player from "../components/Player";
|
||||||
import useWS from "../hooks/useWS";
|
import useWS from "../hooks/useWS";
|
||||||
import IdentityData from "../interfaces/Identity";
|
import IdentityData from "../interfaces/Identity";
|
||||||
import { MessageTypes } from "../interfaces/IMessage";
|
|
||||||
import SetPlayheadEvent from "../interfaces/Playhead";
|
import SetPlayheadEvent from "../interfaces/Playhead";
|
||||||
import SocketEvents from "../interfaces/SocketEvents";
|
import SocketEvents from "../interfaces/SocketEvents";
|
||||||
import { isBrowser } from "../util";
|
import { isBrowser } from "../util";
|
||||||
import Message from "../util/Message";
|
import PlayerSocket from "../ws/websocket";
|
||||||
import MessageUtil from "../util/MessageUtil";
|
|
||||||
|
|
||||||
interface PlayerPageProps {
|
interface PlayerPageProps {
|
||||||
user: User;
|
user: User;
|
||||||
|
|
@ -25,74 +23,25 @@ const PlayerPage: NextPage<PlayerPageProps> = ({ user }) => {
|
||||||
const playerRef = useRef<ReactPlayer>();
|
const playerRef = useRef<ReactPlayer>();
|
||||||
const [id, setID] = useState<string>("");
|
const [id, setID] = useState<string>("");
|
||||||
const [identity, setIdentity] = useState<IdentityData>();
|
const [identity, setIdentity] = useState<IdentityData>();
|
||||||
const [paused, setPaused] = useState<boolean>(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isBrowser() && typeof socket !== "undefined") {
|
if (isBrowser() && typeof socket !== "undefined") {
|
||||||
socket?.emitter.once(SocketEvents.Identify, (e: IdentityData) => {
|
socket?.emitter.once(SocketEvents.Identify, (e: IdentityData) => {
|
||||||
setID(e.playlist);
|
setID(e.playlist);
|
||||||
setIdentity(e);
|
setIdentity(e);
|
||||||
playerRef?.current.seekTo(e.playhead);
|
|
||||||
setPaused(e.paused);
|
|
||||||
});
|
});
|
||||||
socket?.emitter.on(SocketEvents.SetPlayhead, (e: SetPlayheadEvent) => {
|
socket?.emitter.on(SocketEvents.SetPlayhead, (e: SetPlayheadEvent) => {
|
||||||
console.log(e.paused);
|
console.log(e);
|
||||||
setPaused(e.paused);
|
|
||||||
playerRef.current.seekTo(e.playhead);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
const onPlay = () => {
|
const onSeek = () => {};
|
||||||
if (!identity.admin) return;
|
|
||||||
setPaused(false);
|
|
||||||
socket?.send(
|
|
||||||
MessageUtil.encode(
|
|
||||||
new Message(MessageTypes.SetPlayhead, {
|
|
||||||
playhead: playerRef.current.getCurrentTime(),
|
|
||||||
paused: false,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const onSeek = (playedSeconds: number) => {
|
|
||||||
if (!identity.admin) return;
|
|
||||||
socket.send(
|
|
||||||
MessageUtil.encode(
|
|
||||||
new Message(MessageTypes.SetPlayhead, {
|
|
||||||
playhead: playedSeconds,
|
|
||||||
paused: paused,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const onPause = () => {
|
|
||||||
console.log("running now");
|
|
||||||
if (!identity.admin) return;
|
|
||||||
setPaused(true);
|
|
||||||
socket?.send(
|
|
||||||
MessageUtil.encode(
|
|
||||||
new Message(MessageTypes.SetPlayhead, {
|
|
||||||
playhead: playerRef.current.getCurrentTime(),
|
|
||||||
paused: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Watch Together</title>
|
<title>Watch Together</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Container height="100vh" background={"#000"}>
|
<Container height="100vh" background={"#000"}>
|
||||||
<Player
|
<Player ref={playerRef} />
|
||||||
url={id}
|
|
||||||
onPlay={onPlay}
|
|
||||||
onPause={onPause}
|
|
||||||
onSeek={onSeek}
|
|
||||||
controls={identity?.hasController}
|
|
||||||
playing={!paused}
|
|
||||||
ref={playerRef}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue