|
|
|
@ -1,16 +1,15 @@
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
import ReactPlayer from 'react-player';
|
|
|
|
import ReactPlayer from 'react-player';
|
|
|
|
import { db, timestamp } from '../services/firebase';
|
|
|
|
import { db, timestamp, arrayUnion } from '../services/firebase';
|
|
|
|
import { secondsToTimestamp, SYNC_MARGIN } from '../services/utilities';
|
|
|
|
import { SYNC_MARGIN } from '../services/utilities';
|
|
|
|
|
|
|
|
|
|
|
|
type VideoPlayerProps = {
|
|
|
|
type VideoPlayerProps = {
|
|
|
|
ownerId: string;
|
|
|
|
ownerId: string;
|
|
|
|
userId: string;
|
|
|
|
userId: string;
|
|
|
|
roomId: string;
|
|
|
|
roomId: string;
|
|
|
|
stateId: string;
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId, stateId }) => {
|
|
|
|
const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId }) => {
|
|
|
|
const player = useRef<ReactPlayer>(null);
|
|
|
|
const player = useRef<ReactPlayer>(null);
|
|
|
|
const [playing, setPlaying] = useState(false);
|
|
|
|
const [playing, setPlaying] = useState(false);
|
|
|
|
const [videoUrl, setVideoUrl] = useState('');
|
|
|
|
const [videoUrl, setVideoUrl] = useState('');
|
|
|
|
@ -22,18 +21,13 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId, stat
|
|
|
|
if (ownerId === userId) {
|
|
|
|
if (ownerId === userId) {
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
const roomRef = db.collection('rooms').doc(roomId);
|
|
|
|
await db
|
|
|
|
await roomRef.collection('messages').add({
|
|
|
|
.collection('rooms')
|
|
|
|
createdAt: timestamp,
|
|
|
|
.doc(roomId)
|
|
|
|
senderId: userId,
|
|
|
|
.update({
|
|
|
|
content: 'started playing the video from ' + secondsToTimestamp(currTime),
|
|
|
|
requests: arrayUnion({ createdAt: timestamp, senderId: userId, type: 'play' }),
|
|
|
|
type: 'play',
|
|
|
|
state: { isPlaying: true, time: currTime },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await roomRef.collection('states').doc(stateId).update({
|
|
|
|
|
|
|
|
time: currTime,
|
|
|
|
|
|
|
|
isPlaying: true,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@ -44,18 +38,13 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId, stat
|
|
|
|
if (ownerId === userId) {
|
|
|
|
if (ownerId === userId) {
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
const roomRef = db.collection('rooms').doc(roomId);
|
|
|
|
await db
|
|
|
|
await roomRef.collection('messages').add({
|
|
|
|
.collection('rooms')
|
|
|
|
createdAt: timestamp,
|
|
|
|
.doc(roomId)
|
|
|
|
senderId: userId,
|
|
|
|
.update({
|
|
|
|
content: 'paused the video at ' + secondsToTimestamp(currTime),
|
|
|
|
requests: arrayUnion({ createdAt: timestamp, senderId: userId, type: 'pause' }),
|
|
|
|
type: 'pause',
|
|
|
|
state: { isPlaying: false, time: currTime },
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
await roomRef.collection('states').doc(stateId).update({
|
|
|
|
|
|
|
|
time: currTime,
|
|
|
|
|
|
|
|
isPlaying: false,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@ -71,37 +60,33 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId, stat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Listen for video state updates (member only)
|
|
|
|
// Subscribe member only listener
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
if (ownerId !== userId) {
|
|
|
|
if (ownerId !== userId) {
|
|
|
|
const stateRef = db.collection('rooms').doc(roomId).collection('states');
|
|
|
|
const stateRef = db.collection('states').doc(roomId);
|
|
|
|
const stateUnsubscribe = stateRef.onSnapshot((querySnapshot) => {
|
|
|
|
const roomRef = db.collection('rooms').doc(roomId);
|
|
|
|
const changes = querySnapshot.docChanges();
|
|
|
|
|
|
|
|
const change = changes[changes.length - 1];
|
|
|
|
// Add a listener to 'states' collection, listening for video state changes
|
|
|
|
if (change.type === 'modified') {
|
|
|
|
const stateUnsubscribe = stateRef.onSnapshot((docSnapshot) => {
|
|
|
|
const data = change.doc.data();
|
|
|
|
const docData = docSnapshot.data();
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
setPlaying(data.isPlaying);
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
if (!data.isPlaying) {
|
|
|
|
const realPlayState: boolean = docData?.isPlaying;
|
|
|
|
player.current?.seekTo(data.time);
|
|
|
|
const realTimeState: number = docData?.time;
|
|
|
|
}
|
|
|
|
setPlaying(realPlayState);
|
|
|
|
|
|
|
|
|
|
|
|
// Continue requesting an update on the video state, until synced
|
|
|
|
if (allowUpdate && Math.abs(currTime - realTimeState) > SYNC_MARGIN / 1000 && realPlayState) {
|
|
|
|
if (allowUpdate && Math.abs(currTime - data.time) > SYNC_MARGIN / 1000 && data.isPlaying) {
|
|
|
|
setAllowUpdate(false);
|
|
|
|
setAllowUpdate(false);
|
|
|
|
setTimeout(() => {
|
|
|
|
setTimeout(() => {
|
|
|
|
// throttle update requests
|
|
|
|
setAllowUpdate(true);
|
|
|
|
setAllowUpdate(true);
|
|
|
|
}, 3000);
|
|
|
|
}, 3000);
|
|
|
|
|
|
|
|
|
|
|
|
player.current?.seekTo(data.time);
|
|
|
|
player.current?.seekTo(realTimeState);
|
|
|
|
console.log('diff: ' + Math.abs(currTime - data.time));
|
|
|
|
roomRef.update({
|
|
|
|
db.collection('rooms').doc(roomId).collection('messages').add({
|
|
|
|
requests: arrayUnion({ createdAt: timestamp, senderId: userId, type: 'updateState' }),
|
|
|
|
createdAt: timestamp,
|
|
|
|
});
|
|
|
|
senderId: userId,
|
|
|
|
|
|
|
|
type: 'updateState',
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -110,48 +95,48 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({ ownerId, userId, roomId, stat
|
|
|
|
stateUnsubscribe();
|
|
|
|
stateUnsubscribe();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [ownerId, userId, roomId, allowUpdate]);
|
|
|
|
}, [ownerId, roomId, userId, allowUpdate]);
|
|
|
|
|
|
|
|
|
|
|
|
// Listen for video updateState requests (owner only)
|
|
|
|
// Subscribe owner only listener
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
if (ownerId === userId) {
|
|
|
|
if (ownerId === userId) {
|
|
|
|
const roomRef = db.collection('rooms').doc(roomId);
|
|
|
|
const roomRef = db.collection('rooms').doc(roomId);
|
|
|
|
const videoUnsubscribe = roomRef
|
|
|
|
const stateRef = db.collection('states').doc(roomId);
|
|
|
|
.collection('messages')
|
|
|
|
|
|
|
|
.where('type', '==', 'updateState')
|
|
|
|
// Add a listener to 'rooms' collection, listening for updateState requests
|
|
|
|
.onSnapshot((querySnapshot) => {
|
|
|
|
const roomUnsubscribe = roomRef.onSnapshot((docSnapshot) => {
|
|
|
|
const changes = querySnapshot.docChanges();
|
|
|
|
const requests = docSnapshot.data()?.requests;
|
|
|
|
const change = changes[changes.length - 1];
|
|
|
|
const req = requests[requests.length - 1];
|
|
|
|
if (change?.type === 'added') {
|
|
|
|
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
if (req.type === 'updateState' && req.senderId !== userId) {
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
const currTime = player.current?.getCurrentTime();
|
|
|
|
roomRef.collection('states').doc(stateId).update({
|
|
|
|
if (currTime !== undefined) {
|
|
|
|
time: currTime,
|
|
|
|
stateRef.update({
|
|
|
|
isPlaying: true,
|
|
|
|
time: currTime,
|
|
|
|
});
|
|
|
|
isPlaying: true,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
videoUnsubscribe();
|
|
|
|
roomUnsubscribe();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [ownerId, roomId, userId, stateId]);
|
|
|
|
}, [ownerId, roomId, userId]);
|
|
|
|
|
|
|
|
|
|
|
|
// Listen for video URL changes
|
|
|
|
// Listen for video URL changes
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
const urlRef = db.collection('rooms').doc(roomId).collection('playlist');
|
|
|
|
const playlistRef = db.collection('playlist').doc(roomId);
|
|
|
|
const urlUnsubscribe = urlRef.onSnapshot((querySnapshot) => {
|
|
|
|
const playlistUnsubscribe = playlistRef.onSnapshot((docSnapshot) => {
|
|
|
|
const changes = querySnapshot.docChanges();
|
|
|
|
const data = docSnapshot.data();
|
|
|
|
for (const change of changes) {
|
|
|
|
if (data !== undefined) {
|
|
|
|
const data = change.doc.data();
|
|
|
|
|
|
|
|
setVideoUrl(data.url);
|
|
|
|
setVideoUrl(data.url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
urlUnsubscribe();
|
|
|
|
playlistUnsubscribe();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}, [roomId]);
|
|
|
|
}, [roomId]);
|
|
|
|
|
|
|
|
|
|
|
|
|