all other staff

This commit is contained in:
2023-04-16 12:18:29 +03:00
parent 0663ed5370
commit 109d51115b
18 changed files with 681 additions and 827 deletions

78
src/pages/Journal.tsx Normal file
View File

@@ -0,0 +1,78 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import dayjs from 'dayjs';
import { Link } from 'react-router-dom'
import {
ArrowImg,
IconButton,
InputElement,
InputLabel,
InputWrapper,
StartWrapper,
LessonItem,
Lessonname,
} from './style';
import arrow from '../assets/36-arrow-right.svg';
import { connect, getSocket } from "../socket";
export const Journal = () => {
const [lessons, setLessons] = useState(null);
useEffect(() => {
connect();
const socket = getSocket();
socket.on('lessons', data => {
setLessons(data)
})
}, []);
const [value, setValue] = useState('');
const handleChange = useCallback(event => {
setValue(event.target.value.toUpperCase())
}, [setValue]);
const inputRef = useRef<HTMLInputElement>(null);
const handleSubmit = useCallback((event) => {
event.preventDefault();
const socket = getSocket();
socket.emit('create-lesson', { lessonName: value });
setValue('')
}, [value])
return (
<StartWrapper>
<form onSubmit={handleSubmit}>
<InputWrapper>
<InputLabel
htmlFor='input'
>
Название новой лекции:
</InputLabel>
<InputElement
value={value}
onChange={handleChange}
ref={inputRef}
id="input"
type="text"
autoComplete="off"
/>
<IconButton type="submit">
<ArrowImg src={arrow} />
</IconButton>
</InputWrapper>
</form>
<ul style={{ paddingLeft: 0 }}>
{lessons?.map((lesson) => (
<LessonItem key={lesson.id}>
<Link to={`/journal/l/${lesson.id}`}>
<Lessonname>{lesson.name}</Lessonname>
<span>{dayjs(lesson.ts).format('DD MMMM YYYYг.')}</span>
</Link>
</LessonItem>
))}
</ul>
</StartWrapper>
)
};

44
src/pages/Lesson.tsx Normal file
View File

@@ -0,0 +1,44 @@
import React, { useEffect, useState, useRef } from 'react';
import { useParams } from 'react-router-dom';
import dayjs from 'dayjs';
import QRCode from 'qrcode';
import { connect, getSocket } from '../socket';
import { MainWrapper, StartWrapper, QRCanvas, LessonItem, Lessonname } from './style';
export const Lesson = () => {
const { lessonId } = useParams();
const canvRef = useRef(null);
const [lesson, setLesson] = useState(null);
useEffect(() => {
connect();
const socket = getSocket();
socket.on('lessons', data => {
setLesson(data.find(lesson => lesson.id === lessonId));
})
QRCode.toCanvas(canvRef.current, `${location.origin}/journal/u/${lessonId}` , function (error) {
if (error) console.error(error)
console.log('success!');
})
}, []);
return (
<MainWrapper>
<StartWrapper>
<h1>Lesson - {lesson?.name}</h1>
<span>{dayjs(lesson?.ts).format('DD MMMM YYYYг.')}</span>
<QRCanvas ref={canvRef} />
<ul style={{ paddingLeft: 0 }}>
{lesson?.padavans?.map((padavan, index) => (
<LessonItem key={index}>
<Lessonname>{padavan.name}</Lessonname>
</LessonItem>
))}
</ul>
</StartWrapper>
</MainWrapper>
)
}

72
src/pages/UserPage.tsx Normal file
View File

@@ -0,0 +1,72 @@
import React, { useCallback, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { connect, getSocket } from '../socket';
import { ArrowImg, IconButton, InputElement, InputLabel, InputWrapper, MainWrapper, StartWrapper } from './style';
import arrow from '../assets/36-arrow-right.svg';
export const UserPage = () => {
const [socketId, setSocketId] = useState(null);
const { lessonId } = useParams();
useEffect(() => {
connect();
const socket = getSocket();
socket.on('connect', () => {
const id = localStorage.getItem('socketId');
if (!id) {
localStorage.setItem('socketId', socket.id);
setSocketId(socket.id);
} else {
setSocketId(id);
}
const name = localStorage.getItem('name');
if (name) {
const socket = getSocket();
socket.emit('add', { socketId: id || socket.id, name, lessonid: lessonId });
}
})
socket.on('lessons', data => {
// setLessons(data)
})
}, []);
const [value, setValue] = useState(localStorage.getItem('name') || '');
const handleChange = useCallback(event => {
setValue(event.target.value.toUpperCase())
}, [setValue]);
const handleSubmit = useCallback((event) => {
event.preventDefault();
const socket = getSocket();
localStorage.setItem('name', value)
socket.emit('add', { socketId, name: value, lessonid: lessonId });
}, [value])
return (
<MainWrapper>
<StartWrapper>
<form onSubmit={handleSubmit}>
<InputWrapper>
<InputLabel
htmlFor='input'
>
Как вас зовут:
</InputLabel>
<InputElement
value={value}
onChange={handleChange}
id="input"
type="text"
autoComplete="off"
/>
<IconButton type="submit">
<ArrowImg src={arrow} />
</IconButton>
</InputWrapper>
</form>
</StartWrapper>
</MainWrapper>
)
}

View File

@@ -1,41 +1,16 @@
import React, {useState, useCallback, useRef, useMemo} from 'react';
import { getFeatures } from '@ijl/cli';
import React, {useState, useCallback, useRef, useEffect} from 'react';
import logo from '../assets/logo.svg';
import arrow from '../assets/36-arrow-right.svg';
import fullScreen from '../assets/fullscreen.svg';
import logoShort from '../assets/logo-short.svg';
import { Keyboard } from '../components/keyboard';
import {
MainWrapper,
InputElement,
InputLabel,
InputWrapper,
LogoImg,
ArrowImg,
IconButton,
StartWrapper,
StartI,
StartInput,
StartLabel,
HalfLine,
HideGroup,
Circle,
LineSvg,
Svg,
CircleDiv,
FullScreenButton,
InputHolder,
Blow,
BigLogo,
} from './style';
import { socket } from "../socket";
const keyboardFeature = getFeatures('hub-video-start')?.keyboard;
const fullScreenFeature = getFeatures('hub-video-start')?.fullScreen;
const blowAnimFeature = getFeatures('hub-video-start')?.blowAnim;
import { Journal } from './Journal';
const Input = ({ onStart }) => {
const [value, setValue] = useState('');
@@ -45,9 +20,18 @@ const Input = ({ onStart }) => {
}, [setValue]);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
const pass = localStorage.getItem('pass')
if (pass) {
onStart();
}
}, []);
const handleSubmit = useCallback((event) => {
event.preventDefault();
if (value === 'SBER') {
localStorage.setItem('pass', 'true')
if (value === 'OYB0Y') {
onStart()
}
}, [value])
@@ -59,219 +43,33 @@ const Input = ({ onStart }) => {
<InputLabel
htmlFor='input'
>
Ввод:
Введите пароль:
</InputLabel>
<InputElement
value={value}
onChange={handleChange}
onFocus={() => setInfocuse(true)}
ref={inputRef}
onClick={(event) => {
if (keyboardFeature) {
event.preventDefault();
event.stopPropagation();
inputRef.current.blur();
}
}}
id="input"
type="password"
autoComplete="off"
/>
{keyboardFeature && <InputHolder onClick={() => setInfocuse(true)} />}
<IconButton type="submit">
<ArrowImg src={arrow} />
</IconButton>
</InputWrapper>
</form>
{keyboardFeature && inFocuse && (
<Keyboard onChange={setValue} />
)}
</>
)
}
export const Line = ({ hide, reverse, speed, delay, radius, progress, width, color }) => (
<LineSvg
delay={delay}
reverse={reverse}
animationSpeed={speed}
width={(radius * 2) + 20 + width}
height={(radius * 2) + 20 + width}
>
<HideGroup hide={hide} delay={delay}>
<Circle
percent={progress}
circumference={radius * 2 * Math.PI}
stroke={color}
strokeWidth={width}
fill="transparent"
r={radius}
cx={radius + 10 + width/2}
cy={radius + 10 + width/2}
/>
</HideGroup>
</LineSvg>
)
Line.defaultProps = {
reverse: false
}
const Start = () => {
const [sended, setSend] = useState(false);
const handleCheck = () => {
if (!sended) {
socket.emit('play')
setSend(true)
}
}
return (
<StartWrapper>
{sended && blowAnimFeature && (
<>
<Blow
delay={.3}
color="#83e973"
/>
<Blow
delay={.4}
color="#97e043"
/>
<Blow
delay={.5}
color="#d0eb04"
/>
<Blow
delay={.6}
color="#3fc0e4"
/>
<Blow
delay={.7}
color="#daf4ff"
/>
<BigLogo src={logoShort} />
</>
)}
{/* <Svg
xmlns="http://www.w3.org/2000/svg"
width="1200"
height="1200"
viewBox="0 0 400 400">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="50%" stop-color="#f7f7f720" />
<stop offset="100%" stop-color="#4aa9edca" />
</linearGradient>
</defs>
<circle cx="200" cy="-100" r="90" stroke="url(#gradient)" stroke-width="46" fill="none" transform='rotate(90 50 50)'/>
</Svg> */}
<Line
hide={sended}
speed={24}
delay={3}
radius={122}
progress={2}
width={3}
reverse
color="white"
/>
<Line
hide={sended}
speed={12}
delay={9}
radius={122}
progress={2}
width={3}
color="white"
/>
<Line
hide={sended}
speed={53}
radius={220}
delay={6}
progress={22}
width={12}
color="#92de22"
/>
<Line
hide={sended}
speed={53}
radius={220}
delay={17.8}
progress={.5}
width={12}
color="#92de22"
/>
<Line
hide={sended}
speed={53}
radius={220}
delay={18.3}
progress={.2}
width={12}
color="#92de22"
/>
<Line
hide={sended}
speed={54}
radius={220}
delay={30}
progress={44}
width={15}
color="#92de22"
/>
<Line
hide={sended}
speed={12}
radius={120}
delay={0}
progress={13}
width={6}
color="white"
/>
<StartInput
id="check"
type="checkbox"
onChange={handleCheck}
/>
<StartLabel
htmlFor='check'
>
<StartI>СТАРТ</StartI>
</StartLabel>
<CircleDiv />
</StartWrapper>
)
}
export const MainPage = () => {
const [showStart, setShowStart] = useState(false);
const [isFull, setFull] = useState(false);
const handleFullScreen = useCallback(() => {
const elem = document.documentElement;
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) { /* Safari */
elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) { /* IE11 */
elem.msRequestFullscreen();
}
setFull(true);
}, []);
const [shoList, setShoList] = useState(false);
return (
<MainWrapper>
{fullScreenFeature && !isFull && (
<FullScreenButton onClick={handleFullScreen}>
<img src={fullScreen} />
</FullScreenButton>
)}
<LogoImg src={logo} />
{/* <Start /> */}
{!showStart && <Input onStart={() => setShowStart(true)} />}
{showStart && <Start />}
{!shoList && <Input onStart={() => setShoList(true)} />}
{shoList && <Journal />}
</MainWrapper>
);
};

View File

@@ -33,12 +33,6 @@ export const InputElement = styled.input`
max-width: 90vw;
`;
export const LogoImg = styled.img`
position: absolute;
top: 24px;
left: 24px;
`;
export const ArrowImg = styled.img`
width: 48px;
height: 48px;
@@ -65,177 +59,12 @@ const reveal = keyframes`
export const StartWrapper = styled.div`
animation: ${reveal} 1s ease forwards;
/* box-shadow: 0 -2px 5px rgba(255,255,255,0.05), 0 2px 5px rgba(255,255,255,0.1); */
width: 350px;
height: 350px;
width: 650px;
height: calc(100vh - 300px);
/* margin: 60px auto; */
position: relative;
`;
export const StartLabel = styled.label`
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
background: #b25244;
/* background: linear-gradient(#f7f2f6, #b2ac9e); */
/* background: linear-gradient(#b52a2a, #e10ff1); */
background: linear-gradient(rgb(255 251 251), rgb(140 219 35));
position: relative;
color: #a5a39d;
font-size: 70px;
text-align: center;
line-height: 150px;
transition: all 0.3s ease-out;
text-shadow: 0 2px 1px rgba(0,0,0,0.25);
z-index: 1;
box-shadow:
inset 0 2px 3px rgba(255,255,255,0.13),
0 5px 8px rgba(0,0,0,0.3),
0 10px 10px 4px rgba(0,0,0,0.3);
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
&::after {
content: "";
position: absolute;
left: -20px;
right: -20px;
top: -20px;
bottom: -20px;
z-index: -2;
border-radius: inherit;
box-shadow:
inset 0 1px 0 rgba(255,255,255,0.1),
0 1px 2px rgba(0,0,0,0.3),
0 0 10px rgba(0,0,0,0.15);
}
&::before {
content: "";
position: absolute;
left: -10px;
right: -10px;
top: -10px;
bottom: -10px;
z-index: -1;
border-radius: inherit;
box-shadow: inset 0 10px 10px rgba(0,0,0,0.13);
-webkit-filter:blur(1px);
filter: blur(1px);
}
`;
export const StartInput = styled.input`
display: block;
width: 100%;
height: 100%;
opacity: 0;
z-index: 100;
position: absolute;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
&:checked ~ label {
box-shadow:
inset 0 2px 3px rgba(255,255,255,0.13),
0 5px 8px rgba(0,0,0,0.35),
0 3px 10px 4px rgba(0,0,0,0.2);
color: #9abb82;
}
`;
export const StartI = styled.i`
content: "";
display: flex;
position: absolute;
width: 70%;
height: 70%;
left: 50%;
top: 50%;
z-index: 1;
color: white;
margin: -35% 0 0 -35%;
border-radius: 50%;
background: linear-gradient(#22a037, #98e221);
box-shadow:
0 -2px 5px rgba(255,255,255,0.05),
0 2px 5px rgba(255,255,255,0.1);
-webkit-filter:blur(1px);
filter: blur(1px);
align-items: center;
justify-content: center;
font-style: normal;
`;
// const LineRotation = keyframes
//`
// 0% {
// transform: translate(-50%, -100%) rotate(0);
// }
// 100% {
// transform: translate(-50%, -100%) rotate(1turn);
// }
// `;
// type LineProps = {
// radius: number;
// width: number;
// }
// export const HalfLine = styled.div<LineProps>(({
// radius,
// width,
// }) =>css`
// transform-origin: 50% 100%;
// animation: ${LineRotation} 3s linear infinite;
// width: ${radius * 2}px;
// height: ${radius}px;
// border-top-left-radius: ${radius}px;
// border-top-right-radius: ${radius}px;
// border: ${width}px solid gray;
// border-bottom: 0;
// position: absolute;
// top: 50%;
// left: 50%;
// transform: translate(-50%, -100%);
// `);
const LineRotation = keyframes`
0% {
transform: translate(-50%, -50%) rotate(0);
}
100% {
transform: translate(-50%, -50%) rotate(1turn);
}
`;
const LineHideAnimation = keyframes`
0% {
/* transform: scale(1); */
opacity: 1;
}
100% {
/* transform: scale(0); */
opacity: 0;
}
`;
export const Svg = styled.svg`
position: absolute;
top: 50%;
@@ -245,120 +74,20 @@ export const Svg = styled.svg`
/* stroke-dasharray: 600; */
`;
export const CircleDiv = styled.div`
width: 700px;
height: 700px;
position: absolute;
top: 50%;
left: 50%;
transform-origin: 50% 50%;
transform: translate(-50%, -50%) rotate(270deg);
border-radius: 50%;
background: radial-gradient(
farthest-side at bottom center,
rgba(35, 235, 4, 0.709),
rgba(255, 255, 255, 0) 65%
),
radial-gradient(
farthest-corner at bottom left,
rgba(244, 244, 8, 0.9),
rgba(255, 255, 255, 0) 40%
),
radial-gradient(
farthest-side at bottom right,
rgba(0, 195, 255, 0.648),
rgba(255, 255, 255, 0) 65%
);
export const LessonItem = styled.li`
list-style: none;
background-color: #ffffff;
padding: 16px;
border-radius: 12px;
box-shadow: 2px 2px 6px #0000005c;
margin-bottom: 12px;
`;
export const LineSvg = styled.svg<{ animationSpeed: number; delay: number; reverse: boolean }>`
animation: ${LineRotation} ${({ animationSpeed }) => animationSpeed}s linear infinite${({ reverse }) => reverse ? ' reverse' : ''};
animation-delay: -${({ delay }) => delay}s;
position: absolute;
top: 50%;
left: 50%;
transform-origin: 50% 50%;
transform: translate(-50%, -50%);
z-index: 2;
export const Lessonname = styled.span`
display: inline-box;
margin-right: 12px;
`;
export const HideGroup = styled.g<{ hide: boolean; delay: number; }>`
animation: ${({ hide }) => hide ? css`${LineHideAnimation} 3s ease-in forwards`: ''};
transform-origin: 50% 50%;
animation-delay: ${({ delay }) => delay / 10}s;
`;
export const Circle = styled.circle<{ circumference: number; percent: number }>`
transition: 0.35s stroke-dashoffset;
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-dasharray: ${({ circumference }) => `${circumference} ${circumference}`};
stroke-dashoffset: ${({ circumference, percent }) => circumference - percent / 100 * circumference};
`;
export const FullScreenButton = styled.button`
padding: 12px;
position: absolute;
top: 50px;
right: 50px;
border: none;
background-color: rgba(0, 0, 0, .07);
`;
export const InputHolder = styled.div`
background-color: rgba(0, 0, 0, .0);
width: calc(100% - 70px);
height: 60px;
right: 70px;
top: 12px;
left: 12px;
position: absolute;
@media screen and (max-width: 600px) {
width: 100%;
}
`;
const blowAnim = keyframes`
to {
width: 200vw;
height: 200vw;
}
`;
type BlowProps = {
delay: number;
color: string;
}
export const Blow = styled.div<BlowProps>`
z-index: 10;
border-radius: 50%;
position: absolute;
left: 50%;
top: 50%;
width: 0;
height: 0%;
background-color: ${({ color }) => color};
animation: ${blowAnim} 1s ease-out forwards;
animation-delay: ${({ delay }) => delay}s;
transform: translate(-50%, -50%);
`;
const blowAnimShort = keyframes`
to {
width: 50vw;
height: 50vw;
}
`;
export const BigLogo = styled.img`
z-index: 10;
position: absolute;
left: 50%;
top: 50%;
width: 0;
height: 0;
animation: ${blowAnimShort} 2s ease-out forwards;
animation-delay: .7s;
transform: translate(-50%, -50%);
export const QRCanvas = styled.canvas`
display: block;
`;