Commit 1625d42a by Sergey

add screencust option

parent 1eb73454
......@@ -9,7 +9,6 @@
<option name="SPACE_AFTER_UNARY_NOT" value="true" />
</JSCodeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="BLOCK_COMMENT_ADD_SPACE" value="true" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
......
<component name="ProjectDictionaryState">
<dictionary name="ubuntu">
<words>
<w>cust</w>
</words>
</dictionary>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager">
<output url="file://$PROJECT_DIR$/out" />
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>
\ No newline at end of file
......@@ -14,7 +14,7 @@ const App = () => {
console.log('sads', value);
}}
uploadOptions={{
url: 'https://cdn.atmaguru.online/upload/?sid=test&md5=r7or90O0oY-ZENaz2voSLQ&expires=1656059260',
url: 'https://cdn.atmaguru.online/upload/?sid=test&md5=RfC4jdfFN05c9ZAc0bZqBg&expires=1694259932',
errorMessage: 'Загрузка временно невозможна'
}}
style={{
......
......@@ -71,9 +71,11 @@
"@tiptap/starter-kit": "^2.0.0-beta.185",
"axios": "^0.27.2",
"katex": "^0.15.3",
"prosemirror-state": "^1.4.1",
"prosemirror-state": "1.4.0",
"rc-upload": "^4.3.3",
"react": "^17.0.2",
"react-media-recorder": "^1.6.6",
"react-webcam": "^7.0.1",
"sass": "^1.49.9"
}
}
......@@ -25,11 +25,16 @@ import Video from './extensions/Video'
import Iframe from './extensions/Iframe'
import CustomLink from './extensions/CustomLink'
import DragAndDrop from "./extensions/DragAndDrop";
import Webcamera from "./extensions/Webcamera";
//import Formula from './extensions/Formula'
import { useReactMediaRecorder } from "react-media-recorder";
import axios from "axios";
const initialBubbleItems = ['bold', 'italic', 'underline', 'strike', '|', 'colorText', 'highlight'];
const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions = { type: 'all' } }) => {
global.uploadUrl = uploadOptions.url;
const [innerModalType, setInnerModalType] = useState(null);
const [embedContent, setEmbedContent] = useState('');
const [uploaderUid, setUploaderUid] = useState('uid' + new Date());
......@@ -40,10 +45,22 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
const [colorsSelected, setColorsSelected] = useState(null);
const [focusFromTo, setFocusFromTo] = useState(null);
const [oldFocusFromTo, setOldFocusFromTo] = useState(null);
const [isUploading, setIsUploading] = useState(false);
const getRgb = (hex) => {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? `rgb(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)})` : null;
}
const { status, startRecording, stopRecording, mediaBlobUrl, previewStream, muteAudio, unMuteAudio, isAudioMuted, clearBlobUrl } = useReactMediaRecorder({ screen: true });
const [isRecord, setIsRecord] = useState(false);
const videoRef = useRef(null);
useEffect(() => {
if (videoRef.current && previewStream) {
videoRef.current.srcObject = previewStream;
}
}, [previewStream]);
useEffect(() => {
if(focusFromTo !== oldFocusFromTo){
......@@ -52,6 +69,14 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
}
}, [focusFromTo])
useEffect(() => {
if (status === "recording" || status === "acquiring_media") {
setIsRecord(true)
} else if (status === "stopped"){
setIsRecord(false)
}
}, [status])
const modalOpener = (type, title) => {
setModalTitle(title);
setInnerModalType(type);
......@@ -123,6 +148,10 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
title: 'Загрузить видео',
onClick: () => modalOpener('video', 'Загрузить видео')
},
webcamera: {
title: 'Записать экран',
onClick: () => modalOpener('webcamera', 'Записать экран')
},
iframe: {
title: 'Видео по ссылке',
onClick: () => modalOpener('iframe', 'Видео по ссылке')
......@@ -324,7 +353,10 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
className: 'atma-editor-focused',
mode: "all"
}),
DragAndDrop,
DragAndDrop.configure({
linkUpload: uploadOptions.url
}),
Webcamera
],
content: value,
onUpdate: ({editor}) => onChange(editor.getHTML()),
......@@ -394,6 +426,26 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
/>
}
const saveScreenCust = async (fileBlob) => {
console.log(fileBlob);
setIsUploading(true)
let blobData = await fetch(fileBlob).then((res) => res.blob());
const data = new FormData();
let file = new File([blobData], "name.mp4");
data.append("file", file);
console.log(blobData);
const headers = {'Content-Type': 'multipart/form-data'};
await axios.post(uploadOptions.url, data, {headers: headers} ).then(response => {
if (response.data.state === "success"){
console.log(response.data);
setUploadedPaths(response.data)
}
setIsUploading(false)
});
};
const getInnerModal = () => {
switch (innerModalType) {
case 'iframe':
......@@ -422,6 +474,35 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
return (
<Fragment>{ getUploader({ accept: '*', afterParams: ['no_convert=1'] }) }</Fragment>
)
case 'webcamera':
return (
<>
<Fragment>
<div className={"webwrap"}>
{
mediaBlobUrl ?
<video id={"id-video"} width={window.innerWidth / 2} height={window.innerHeight / 2} src={mediaBlobUrl} autoPlay controls /> : status === "recording" &&
<video ref={videoRef} width={window.innerWidth / 2} height={window.innerHeight / 2} src={previewStream} autoPlay controls={false} />
}
<div className={"btn-video-wrap"}>
{
isRecord ?
<button className={"btn-video btn-video-stop"} onClick={stopRecording}>Остановить запись</button> : mediaBlobUrl ?
(<>
<button className={"btn-video btn-video-stop"} onClick={clearBlobUrl}>Перезаписать</button>
<button className={"btn-video btn-video-record "} style={{ backgroundColor: isUploading && "#AAB2BD" }} disabled={isUploading} onClick={() => saveScreenCust(mediaBlobUrl)}>{isUploading ? "Загрузка..." : "Сохранить запись"}</button>
</>) :
<button className={"btn-video btn-video-record"} onClick={startRecording}>Начать запись</button>
}
{
isRecord &&
<button className={"btn-video btn-video-mute"} onClick={isAudioMuted ? unMuteAudio : muteAudio}>{isAudioMuted ? "Вернуть звук" : "Убрать звук"}</button>
}
</div>
</div>
</Fragment>
</>
)
default:
return <div>Пусто</div>
}
......@@ -576,6 +657,9 @@ const QEditor = ({ value, onChange = ()=>{}, style, uploadOptions, toolsOptions
editor.chain().focus().setVideo({ src: file.path, poster: file.path + '.jpg' }).run();
});
break
case 'webcamera':
editor.chain().focus().setVideo({ src: uploadedPaths.file_path }).run();
break
case 'iframe':
let _url = embedContent;
let reg = /(http|https):\/\/([\w.]+\/?)\S*/;
......
......@@ -49,7 +49,12 @@ const toolsInit = {
'iframe',
]
},
{
type: 'g',
items: [
'webcamera',
]
},
{
type: 'g',
items: [
......@@ -100,6 +105,12 @@ const toolsInit = {
{
type: 'g',
items: [
'webcamera',
]
},
{
type: 'g',
items: [
'bulletList',
'orderedList',
'blockquote',
......
......@@ -7,7 +7,7 @@ const upload = async (file) => {
formData.append('file', file);
const headers = {'Content-Type': 'multipart/form-data'};
const response = await axios.post("https://cdn.atmaguru.online/upload/?sid=test&md5=RfC4jdfFN05c9ZAc0bZqBg&expires=1694259932", formData, {headers: headers} );
const response = await axios.post(global.uploadUrl, formData, {headers: headers} );
return response.data.file_path;
};
......
......@@ -6,6 +6,58 @@ body{
min-height: 204px;
padding-bottom: 12px;
}
.webwrap {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.btn {
&-video {
border-radius: 66px;
width: 200px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
color: white;
margin: 5px;
margin-top: 30px;
border: none;
font-size: 17px;
&:hover {
-webkit-box-shadow: 4px 4px 26px 12px rgba(34, 60, 80, 0.2);
-moz-box-shadow: 4px 4px 26px 12px rgba(34, 60, 80, 0.2);
box-shadow: 4px 4px 26px 12px rgba(34, 60, 80, 0.2);
}
&-disabled {
background-color: #AAB2BD;
}
&-wrap {
display: flex;
flex-direction: row;
}
&-record {
background-color: #72c85f;
}
&-stop {
background-color: #EE505A;
}
&-mute {
background-color: #AAB2BD;
}
}
}
.atma-editor {
position: relative;
border-radius: 8px;
......@@ -735,6 +787,9 @@ body{
&.qfile{
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%229%22%20height%3D%2216%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M4.85%2015c-1.062%200-1.969-.365-2.721-1.094C1.376%2013.177%201%2012.282%201%2011.22V3.695c0-.747.265-1.382.796-1.908A2.627%202.627%200%200%201%203.713%201c.758%200%201.4.262%201.925.787.524.526.787%201.167.787%201.926v6.894c0%20.444-.152.82-.455%201.13-.303.308-.677.463-1.12.463-.443%200-.817-.166-1.12-.499a1.694%201.694%200%200%201-.455-1.181V4.01c0-.093.035-.175.105-.245a.336.336%200%200%201%20.49%200c.07.07.105.152.105.245v6.562c0%20.257.085.476.254.657.169.18.376.271.621.271a.83.83%200%200%200%20.621-.262.874.874%200%200%200%20.254-.63V3.694c0-.56-.195-1.033-.586-1.417A1.956%201.956%200%200%200%203.713%201.7c-.56%200-1.036.193-1.427.578-.39.384-.586.857-.586%201.417v7.56c0%20.852.31%201.572.928%202.161.618.59%201.359.884%202.222.884.875%200%201.619-.298%202.231-.893.613-.595.919-1.324.919-2.187V4.01c0-.093.035-.175.105-.245a.336.336%200%200%201%20.49%200c.07.07.105.152.105.245v7.192c0%201.062-.376%201.96-1.129%202.696C6.82%2014.632%205.911%2015%204.85%2015z%22%20fill%3D%22%231D1D1F%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%231D1D1F%22%20stroke-width%3D%22.5%22%2F%3E%3C%2Fsvg%3E');
}
&.qwebcamera {
background-image: url('data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cg%20transform%3D%22translate%281.107%20.622%29%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20stroke%3D%22%231F1E1D%22%20stroke-width%3D%221.2%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20width%3D%2212%22%20height%3D%2212%22%20rx%3D%222%22%2F%3E%3Ccircle%20fill%3D%22%231F1E1D%22%20cx%3D%223.667%22%20cy%3D%223.667%22%20r%3D%221%22%2F%3E%3Cpath%20stroke%3D%22%231F1E1D%22%20stroke-width%3D%221.2%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22m11.467%208.006-3-3.273-6.6%207.2%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E');
}
}
.qcolors{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment