Making public version

This commit is contained in:
2023-03-09 10:15:19 +00:00
parent c0adc8bd08
commit 0e0b4794f6
11 changed files with 262 additions and 26 deletions

View File

@@ -2,6 +2,7 @@ import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Login from './components/LoginPage';
import ProtectedRoute from './components/ProtectedRoute';
import RandomSignUpload from './components/RandomVideoUpload';
import SignDetailpage from './components/SignDetailPage';
import SignsPage from './components/SignsPage';
@@ -9,9 +10,10 @@ function App() {
return (
<Router>
<Routes>
<Route path="/" element={<ProtectedRoute><SignsPage /></ProtectedRoute>} />
<Route path="/login" element={<Login />} />
<Route path="/signs/:id" element={<ProtectedRoute><SignDetailpage /></ProtectedRoute>} />
<Route path="/" element={<RandomSignUpload />} />
<Route path="/admin/" element={<ProtectedRoute><SignsPage /></ProtectedRoute>} />
<Route path="/admin/login" element={<Login />} />
<Route path="/admin/signs/:id" element={<ProtectedRoute><SignDetailpage /></ProtectedRoute>} />
</Routes>
</Router>
);

View File

@@ -10,14 +10,14 @@ const ProtectedRoute: React.FC<{ children: JSX.Element }> = ({ children }) => {
if (expired_at < new Date()) {
localStorage.removeItem('accessToken');
localStorage.removeItem('expirationDate');
return <Navigate to="/login" />;
return <Navigate to="/admin/login" />;
}
}
if (!isLoggedIn) {
return <Navigate to="/login" />;
return <Navigate to="/admin/login" />;
}
return (

View File

@@ -0,0 +1,175 @@
import React, { useState, useRef, useEffect, ChangeEvent } from 'react';
import { Sign, SignVideo, SimpleSign } from '../types/sign';
import { useParams } from 'react-router-dom';
import { getRandomSign, getSign } from '../services/signs';
import ReactModal from 'react-modal';
import { acceptVideo, deleteVideo, uploadSignVideo } from '../services/signvideos';
import { LoadingButton } from './loading-button/loading-button';
import VideoRecorder from 'react-video-recorder';
import SignVideoGrid from './SignVideoGrid';
import SignVideoPlayer from './SignVideoPlayer';
const RandomSignUpload: React.FC = () => {
const [sign, setSign] = useState<SimpleSign | null>(null);
const [signVideo, setSignVideo] = useState<string | null>(null);
const [recordedBlob, setRecordedBlob] = useState<Blob | null>(null);
const signVideoRef = useRef<HTMLVideoElement>(null);
const popupVideoRef = useRef<HTMLVideoElement>(null);
const [popUpShown, setPopUpShown] = useState(false);
const [recorderKey, setRecorderKey] = useState(1);
const [uploadProgress, setUploadProgress] = useState<number | null>(null);
const [videoUrl, setVideoUrl] = useState<string | null>(null);
useEffect(() => {
if (recordedBlob) {
setVideoUrl(URL.createObjectURL(recordedBlob));
setPopUpShown(true);
} else {
setVideoUrl(null);
}
}, [recordedBlob]);
const handleUploadProgress = (progess: number) => {
if (progess) {
setUploadProgress(progess);
}
}
const handleUpload = async () => {
setUploadProgress(0);
console.log("Uploading video...")
uploadSignVideo(sign!.id, recordedBlob!, handleUploadProgress).then((response) => {
console.log("upload complete");
setPopUpShown(false);
// get new random sign
get_random_sign();
// add the new sign video to the sign
const newSign = { ...sign! };
setSign(newSign);
setUploadProgress(100);
}).catch((error) => {
setUploadProgress(null);
}
);
}
useEffect(() => {
if (signVideoRef.current) {
signVideoRef.current.play();
}
}, []);
const get_random_sign = () => {
getRandomSign().then((response) => {
// check if the sign is different from the current one
if (sign === null || sign.id !== response.id) {
setSign(response);
// set the video url
setSignVideo(response.video_url);
} else {
// get a new sign
get_random_sign();
}
}
);
}
useEffect(() => {
// get random sign
get_random_sign();
}, []);
const dismissPopup = () => {
setPopUpShown(false);
// remove the recorded blob
setRecordedBlob(null);
};
return (
<div>
{
sign ? <div>
<div className="flex items-center justify-between">
<h1 className="text-8xl font-bold mx-auto text-center">{sign.name}</h1>
</div>
<div className="flex">
<button className="ml-auto p-2 rounded-full text-red-600 hover:bg-red-600 hover:text-white" onClick={get_random_sign}>
Skip
</button>
</div>
<div className="flex">
{
signVideo &&
<div className="w-1/2">
<video key={signVideo} loop controls width='100%' height='100%'>
<source src={signVideo} type='video/mp4' />
</video>
</div>
}
<div id="recorder-wrapper" className="w-1/2">
<VideoRecorder
key={recorderKey}
countdownTime={3000}
onRecordingComplete={(blob) => {
setRecordedBlob(blob)
// reset the VideoRecorder
setRecorderKey(prevKey => prevKey + 1);
}}
showReplayControls={false}
replayVideoAutoplayAndLoopOff={true}
isOnInitially
timeLimit={4000}
/>
</div>
</div>
</div > : <div>Loading...</div>
}
<ReactModal
isOpen={popUpShown}
shouldCloseOnOverlayClick={false}
className="modal bg-white rounded-3xl bg-gray-300 p-7"
ariaHideApp={false}
style={{
content: {
position: "absolute",
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
},
}}
>
{videoUrl &&
<div>
<video key="vid" ref={popupVideoRef} src={videoUrl!} controls loop className="pb-4" />
<LoadingButton title="Upload" onClick={handleUpload} progress={uploadProgress} />
</div>
}
{(uploadProgress === null || uploadProgress === 0) &&
<button onClick={dismissPopup} className="bg-white p-2 rounded-full text-red-600 hover:bg-red-600 hover:text-white absolute top-1 right-1">
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
}
</ReactModal>
</div>
);
};
export default RandomSignUpload;

View File

@@ -88,4 +88,8 @@ const deleteSign = async (id: number) => {
return response.json();
};
export { addSign, getSigns, getSign, downloadSigns, deleteSign };
const getRandomSign = async () => {
const response = await fetch(`${process.env.REACT_APP_API_URL}/signs/random`);
return response.json();
}
export { addSign, getSigns, getSign, downloadSigns, deleteSign, getRandomSign };

View File

@@ -1,17 +1,17 @@
import axios from 'axios';
const uploadSignVideo = async (id: number, recordedBlob: Blob, onUploadProgress: ((arg0: number) => void)) => {
// get access token from local storage
const token = localStorage.getItem('accessToken');
let formData = new FormData();
formData.append('video', recordedBlob);
// make request to get signs
console.log("Making request")
const response = await axios.post(`${process.env.REACT_APP_API_URL}/signs/${id}/video/`, formData, {
headers: {
Authorization: `Bearer ${token}`,
ContentType: 'multipart/form-data',
// close the connection after the request is sent
Connection: 'close',
},
onUploadProgress: (progressEvent) => {
onUploadProgress(

View File

@@ -10,4 +10,12 @@ export interface Sign {
sign_id: string;
video_url: string;
sign_videos: [SignVideo];
}
}
export interface SimpleSign {
id: number;
url: string;
name: string;
sign_id: string;
video_url: string;
}