├── .env ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── assets │ ├── fonts │ │ ├── Cerebri Sans Bold Italic.ttf │ │ ├── Cerebri Sans Bold.ttf │ │ ├── Cerebri Sans Book Italic.ttf │ │ ├── Cerebri Sans Book.ttf │ │ ├── Cerebri Sans ExtraBold Italic.ttf │ │ ├── Cerebri Sans ExtraBold.ttf │ │ ├── Cerebri Sans Heavy.ttf │ │ ├── Cerebri Sans Italic.ttf │ │ ├── Cerebri Sans SemiBold Italic.ttf │ │ ├── Cerebri Sans SemiBold.ttf │ │ └── abeatbyKaiRegular.otf │ ├── images │ │ ├── adele.jpg │ │ ├── future.jpg │ │ ├── imgPlaceholder.png │ │ ├── logo-2.svg │ │ ├── logo.svg │ │ ├── menu.svg │ │ ├── play-icon.svg │ │ ├── play-icon2.svg │ │ ├── profile-img.png │ │ ├── ruger.jpg │ │ ├── skip-bd.svg │ │ ├── skip-fd.svg │ │ ├── stop-icon.svg │ │ └── video-placeholder.jpg │ └── video │ │ └── Real 4K HDR .mp4 ├── favicon.ico ├── ffmpeg-core │ ├── dist │ │ ├── ffmpeg-core.js │ │ ├── ffmpeg-core.wasm │ │ └── ffmpeg-core.worker.js │ ├── package-lock.json │ └── package.json ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.test.tsx ├── App.tsx ├── assets │ ├── fonts │ │ ├── Cerebri Sans Bold Italic.ttf │ │ ├── Cerebri Sans Bold.ttf │ │ ├── Cerebri Sans Book Italic.ttf │ │ ├── Cerebri Sans Book.ttf │ │ ├── Cerebri Sans ExtraBold Italic.ttf │ │ ├── Cerebri Sans ExtraBold.ttf │ │ ├── Cerebri Sans Heavy.ttf │ │ ├── Cerebri Sans Italic.ttf │ │ ├── Cerebri Sans SemiBold Italic.ttf │ │ ├── Cerebri Sans SemiBold.ttf │ │ └── abeatbyKaiRegular.otf │ └── images │ │ ├── adele.jpg │ │ ├── future.jpg │ │ ├── gaddafi-rusli-2ueUnL4CkV8-unsplash 1.png │ │ ├── imgPlaceholder.png │ │ ├── logo.svg │ │ ├── menu.svg │ │ ├── nav1.svg │ │ ├── nav2.svg │ │ ├── nav3.svg │ │ ├── nav4.svg │ │ ├── nav5.svg │ │ ├── nav6.svg │ │ ├── nav7.svg │ │ ├── play-icon.svg │ │ ├── play-icon2.svg │ │ ├── profile-img.png │ │ ├── ruger.jpg │ │ ├── skip-bd.svg │ │ ├── skip-fd.svg │ │ └── stop-icon.svg ├── components │ ├── Assets │ │ └── SVGs.jsx │ ├── Layout │ │ ├── Body │ │ │ ├── Body.scss │ │ │ ├── Body.tsx │ │ │ └── index.js │ │ ├── BodySections │ │ │ ├── BodySection1.tsx │ │ │ ├── BodySection2.tsx │ │ │ ├── BodySection3.tsx │ │ │ ├── BodySections.scss │ │ │ └── index.js │ │ ├── Cards │ │ │ ├── Card1.tsx │ │ │ ├── Card2.tsx │ │ │ └── index.js │ │ └── Navbar │ │ │ ├── Navbar.scss │ │ │ ├── Navbar.tsx │ │ │ └── index.js │ └── UI │ │ ├── Button │ │ ├── Button.scss │ │ ├── Button.tsx │ │ └── index.js │ │ ├── Cards │ │ ├── SideCard │ │ │ ├── SideCard.scss │ │ │ ├── SideCard.tsx │ │ │ └── index.js │ │ └── VideoCard │ │ │ ├── VideoCard.scss │ │ │ ├── VideoCard.tsx │ │ │ └── index.js │ │ ├── Convert │ │ ├── Convert.scss │ │ ├── Convert.tsx │ │ └── index.js │ │ ├── Dropdown │ │ ├── Dropdown.scss │ │ ├── Dropdown.tsx │ │ └── index.js │ │ ├── Modals │ │ ├── Modal │ │ │ ├── Modal.scss │ │ │ ├── Modal.tsx │ │ │ └── index.js │ │ └── ModalChildren │ │ │ ├── ImportVideo.tsx │ │ │ ├── ModalChildren.scss │ │ │ └── index.js │ │ ├── ProfilePicture │ │ ├── ProfilePicture.scss │ │ ├── ProfilePicture.tsx │ │ └── index.js │ │ ├── Slider │ │ ├── Slider.scss │ │ ├── Slider.tsx │ │ └── index.js │ │ ├── Timeline │ │ ├── Timeline.scss │ │ ├── Timeline.tsx │ │ ├── TimelineRow.tsx │ │ └── index.js │ │ ├── Timelines │ │ ├── AudioTimeline.tsx │ │ ├── TimelineHeader.tsx │ │ ├── Timelines.scss │ │ ├── VideoTimeline.tsx │ │ └── index.js │ │ ├── VideoPlaceholder │ │ ├── VideoPlaceholder.scss │ │ ├── VideoPlaceholder.tsx │ │ └── index.js │ │ └── WaveForm │ │ ├── WaveForm.scss │ │ ├── WaveForm.tsx │ │ └── index.js ├── helpers │ └── functions.tsx ├── hooks │ └── useModal.ts ├── index.css ├── index.tsx ├── logo.svg ├── pages │ └── VideoEditing.tsx ├── react-app-env.d.ts ├── redux │ ├── slices │ │ ├── currentTaskSlice.ts │ │ ├── currentTimeSlice.ts │ │ ├── currentVideoSlice.ts │ │ ├── durationSlice.ts │ │ ├── edgeTimesSlice.ts │ │ └── storedVideoSlice.ts │ └── store.ts ├── reportWebVitals.ts ├── services │ └── apis.ts ├── setupTests.ts └── styles │ ├── _mixins.scss │ ├── _variables.scss │ └── main.scss ├── tsconfig.json └── webpack.server.js /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_BASE_URL=http://localhost:3211 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.* 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Veditor is a video editing application.You can trim and resize videos, as well as convert between file formats. 2 | 3 | ![image](https://user-images.githubusercontent.com/68392833/169719216-4a3923ab-b96a-49b5-8af2-4f0a1cd4466d.png) 4 | 5 | 6 | Noteworthy: 1656289277686--Real 4K HDR 60fps LG Jazz HDR UHD (Chromecast Ultra). This video is beautiful! 7 | link: https://www.youtube.com/watch?v=mkggXE5e2yk&t=5s 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "veditor", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.8.1", 7 | "@testing-library/jest-dom": "^5.16.4", 8 | "@testing-library/react": "^13.1.1", 9 | "@testing-library/user-event": "^13.5.0", 10 | "@types/jest": "^27.4.1", 11 | "@types/node": "^16.11.32", 12 | "@types/react": "^18.0.8", 13 | "@types/react-dom": "^18.0.3", 14 | "axios": "^0.27.2", 15 | "env-cmd": "^10.1.0", 16 | "react": "^18.1.0", 17 | "react-dom": "^18.1.0", 18 | "react-icons": "^4.3.1", 19 | "react-loader-spinner": "^5.1.5", 20 | "react-redux": "^8.0.2", 21 | "react-router-dom": "^6.3.0", 22 | "react-scripts": "5.0.1", 23 | "react-toastify": "^9.0.5", 24 | "redux": "^4.2.0", 25 | "sass": "^1.51.0", 26 | "typescript": "^4.6.4", 27 | "web-vitals": "^2.1.4" 28 | }, 29 | "scripts": { 30 | "start": "react-scripts start", 31 | "build": "react-scripts build", 32 | "test": "react-scripts test", 33 | "eject": "react-scripts eject" 34 | }, 35 | "eslintConfig": { 36 | "extends": [ 37 | "react-app", 38 | "react-app/jest" 39 | ] 40 | }, 41 | "browserslist": { 42 | "production": [ 43 | ">0.2%", 44 | "not dead", 45 | "not op_mini all" 46 | ], 47 | "development": [ 48 | "last 1 chrome version", 49 | "last 1 firefox version", 50 | "last 1 safari version" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Bold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Bold Italic.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Bold.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Book Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Book Italic.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Book.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans ExtraBold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans ExtraBold Italic.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans ExtraBold.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Heavy.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans Italic.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans SemiBold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans SemiBold Italic.ttf -------------------------------------------------------------------------------- /public/assets/fonts/Cerebri Sans SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/Cerebri Sans SemiBold.ttf -------------------------------------------------------------------------------- /public/assets/fonts/abeatbyKaiRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/fonts/abeatbyKaiRegular.otf -------------------------------------------------------------------------------- /public/assets/images/adele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/adele.jpg -------------------------------------------------------------------------------- /public/assets/images/future.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/future.jpg -------------------------------------------------------------------------------- /public/assets/images/imgPlaceholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/imgPlaceholder.png -------------------------------------------------------------------------------- /public/assets/images/logo-2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | veditor 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/images/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/assets/images/play-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/images/play-icon2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /public/assets/images/profile-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/profile-img.png -------------------------------------------------------------------------------- /public/assets/images/ruger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/ruger.jpg -------------------------------------------------------------------------------- /public/assets/images/skip-bd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/images/skip-fd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/images/stop-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/assets/images/video-placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/images/video-placeholder.jpg -------------------------------------------------------------------------------- /public/assets/video/Real 4K HDR .mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/assets/video/Real 4K HDR .mp4 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/favicon.ico -------------------------------------------------------------------------------- /public/ffmpeg-core/dist/ffmpeg-core.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/ffmpeg-core/dist/ffmpeg-core.wasm -------------------------------------------------------------------------------- /public/ffmpeg-core/dist/ffmpeg-core.worker.js: -------------------------------------------------------------------------------- 1 | var threadInfoStruct=0;var selfThreadId=0;var parentThreadId=0;var Module={};function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:selfThreadId})}var err=threadPrintErr;this.alert=threadAlert;Module["instantiateWasm"]=function(info,receiveInstance){var instance=new WebAssembly.Instance(Module["wasmModule"],info);Module["wasmModule"]=null;receiveInstance(instance);return instance.exports};this.onmessage=function(e){try{if(e.data.cmd==="load"){Module["wasmModule"]=e.data.wasmModule;Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;if(typeof e.data.urlOrBlob==="string"){importScripts(e.data.urlOrBlob)}else{var objectUrl=URL.createObjectURL(e.data.urlOrBlob);importScripts(objectUrl);URL.revokeObjectURL(objectUrl)}createFFmpegCore(Module).then(function(instance){Module=instance;postMessage({"cmd":"loaded"})})}else if(e.data.cmd==="objectTransfer"){Module["PThread"].receiveObjectTransfer(e.data)}else if(e.data.cmd==="run"){Module["__performance_now_clock_drift"]=performance.now()-e.data.time;threadInfoStruct=e.data.threadInfoStruct;Module["registerPthreadPtr"](threadInfoStruct,/*isMainBrowserThread=*/0,/*isMainRuntimeThread=*/0);selfThreadId=e.data.selfThreadId;parentThreadId=e.data.parentThreadId;var max=e.data.stackBase;var top=e.data.stackBase+e.data.stackSize;Module["establishStackSpace"](top,max);Module["_emscripten_tls_init"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].setThreadStatus(Module["_pthread_self"](),1);/*EM_THREAD_STATUS_RUNNING*/try{var result=Module["dynCall"]("ii",e.data.start_routine,[e.data.arg]);if(!Module["getNoExitRuntime"]())Module["PThread"].threadExit(result)}catch(ex){if(ex==="Canceled!"){Module["PThread"].threadCancel()}else if(ex!="unwind"){Atomics.store(Module["HEAPU32"],(threadInfoStruct+4)>>/*C_STRUCTS.pthread.threadExitCode*/2,(ex instanceof Module["ExitStatus"])?ex.status:-2);/*A custom entry specific to Emscripten denoting that the thread crashed.*/Atomics.store(Module["HEAPU32"],(threadInfoStruct+0)>>/*C_STRUCTS.pthread.threadStatus*/2,1);Module["_emscripten_futex_wake"](threadInfoStruct+0,/*C_STRUCTS.pthread.threadStatus*/2147483647);if(!(ex instanceof Module["ExitStatus"]))throw ex}}}else if(e.data.cmd==="cancel"){if(threadInfoStruct){Module["PThread"].threadCancel()}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="processThreadQueue"){if(threadInfoStruct){Module["_emscripten_current_thread_process_queued_calls"]()}}else{err("worker.js received unknown command "+e.data.cmd);err(e.data)}}catch(ex){err("worker.js onmessage() captured an uncaught exception: "+ex);if(ex&&ex.stack)err(ex.stack);throw ex}};if(typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string"){self={location:{href:__filename}};var onmessage=this.onmessage;var nodeWorkerThreads=require("worker_threads");global.Worker=nodeWorkerThreads.Worker;var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",function(data){onmessage({data:data})});var nodeFS=require("fs");var nodeRead=function(filename){return nodeFS.readFileSync(filename,"utf8")};function globalEval(x){global.require=require;global.Module=Module;eval.call(null,x)}importScripts=function(f){globalEval(nodeRead(f))};postMessage=function(msg){parentPort.postMessage(msg)};if(typeof performance==="undefined"){performance={now:function(){return Date.now()}}}} 2 | -------------------------------------------------------------------------------- /public/ffmpeg-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "@ffmpeg/core", 3 | "_id": "@ffmpeg/core@0.10.0", 4 | "_inBundle": false, 5 | "_integrity": "sha512-qunWJl5PezpXEm31tb8Qu5z37B5KVA1VYZCpXchMhuAb3X9T7PuE3SlhOwphEoRhzaOa3lpofDfzihAUMFaVPQ==", 6 | "_location": "/@ffmpeg/core", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "@ffmpeg/core", 12 | "name": "@ffmpeg/core", 13 | "escapedName": "@ffmpeg%2fcore", 14 | "scope": "@ffmpeg", 15 | "rawSpec": "", 16 | "saveSpec": null, 17 | "fetchSpec": "latest" 18 | }, 19 | "_requiredBy": [ 20 | "#USER", 21 | "/" 22 | ], 23 | "_resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.10.0.tgz", 24 | "_shasum": "f6a58361b22d7c23c6f7071b9fff6d572bc3f499", 25 | "_spec": "@ffmpeg/core", 26 | "_where": "C:\\Users\\OWNER\\Documents\\veditor", 27 | "author": { 28 | "name": "jeromewus@gmail.com" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/ffmpegwasm/FFmpeg/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "deprecated": false, 35 | "description": "ffmpeg.wasm core", 36 | "devDependencies": { 37 | "jest": "^26.4.2", 38 | "serve": "^11.3.2" 39 | }, 40 | "files": [ 41 | "dist", 42 | "package.json", 43 | "package-lock.json" 44 | ], 45 | "homepage": "https://github.com/ffmpegwasm/FFmpeg#readme", 46 | "keywords": [ 47 | "ffmpeg", 48 | "webassembly", 49 | "video", 50 | "audio", 51 | "transcode" 52 | ], 53 | "license": "MIT", 54 | "main": "dist/ffmpeg-core.js", 55 | "name": "@ffmpeg/core", 56 | "repository": { 57 | "type": "git", 58 | "url": "git+https://github.com/ffmpegwasm/FFmpeg.git" 59 | }, 60 | "scripts": { 61 | "start": "serve .", 62 | "test": "node --experimental-wasm-threads --experimental-wasm-bulk-memory ./node_modules/.bin/jest" 63 | }, 64 | "version": "0.10.0" 65 | } 66 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | veditor 28 | 29 | 30 | 31 |
32 | 36 | 46 | 47 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | /* text-align: center; */ 3 | } 4 | 5 | @font-face { 6 | font-family: AbeatbyKai; 7 | src: url(assets/fonts/abeatbyKaiRegular.otf); 8 | } 9 | 10 | @font-face { 11 | font-family: 'Cerebri Sans Book'; 12 | src: url(assets/fonts/Cerebri\ Sans\ Book.ttf); 13 | /* font-style: book; */ 14 | } 15 | 16 | @font-face { 17 | font-family: 'Cerebri Sans'; 18 | src: url(assets/fonts/Cerebri\ Sans\ Bold.ttf); 19 | font-weight: 700; 20 | font-style: normal; 21 | } 22 | @font-face { 23 | font-family: 'Cerebri Sans'; 24 | src: url(assets/fonts/Cerebri\ Sans\ SemiBold.ttf); 25 | font-weight: 600; 26 | font-style: normal; 27 | } 28 | @font-face { 29 | font-family: 'Cerebri Sans'; 30 | src: url(assets/fonts/Cerebri\ Sans\ SemiBold\ Italic.ttf); 31 | font-weight: 600; 32 | font-style: italic; 33 | } 34 | 35 | 36 | h3 { 37 | font-size: 1rem; 38 | line-height: 21px; 39 | font-family: Cerebri Sans; 40 | font-weight: 700; 41 | } 42 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | import VideoEditing from "./pages/VideoEditing"; 4 | import { BrowserRouter } from "react-router-dom"; 5 | import { Provider } from "react-redux"; 6 | import { store } from "./redux/store"; 7 | import { ToastContainer } from "react-toastify"; 8 | import "react-toastify/dist/ReactToastify.css"; 9 | 10 | function App() { 11 | return ( 12 | 13 | 14 | 26 | 27 |
28 | 29 |
30 |
31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Bold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Bold Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Bold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Book Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Book Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Book.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans ExtraBold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans ExtraBold Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans ExtraBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Heavy.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Heavy.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans SemiBold Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans SemiBold Italic.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Cerebri Sans SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/Cerebri Sans SemiBold.ttf -------------------------------------------------------------------------------- /src/assets/fonts/abeatbyKaiRegular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/fonts/abeatbyKaiRegular.otf -------------------------------------------------------------------------------- /src/assets/images/adele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/adele.jpg -------------------------------------------------------------------------------- /src/assets/images/future.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/future.jpg -------------------------------------------------------------------------------- /src/assets/images/gaddafi-rusli-2ueUnL4CkV8-unsplash 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/gaddafi-rusli-2ueUnL4CkV8-unsplash 1.png -------------------------------------------------------------------------------- /src/assets/images/imgPlaceholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/imgPlaceholder.png -------------------------------------------------------------------------------- /src/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | veditor 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/assets/images/nav1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/nav2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/assets/images/nav3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/images/nav4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/images/nav5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/assets/images/nav6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/images/nav7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/assets/images/play-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/images/play-icon2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/assets/images/profile-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/profile-img.png -------------------------------------------------------------------------------- /src/assets/images/ruger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/assets/images/ruger.jpg -------------------------------------------------------------------------------- /src/assets/images/skip-bd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/images/skip-fd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/images/stop-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/Assets/SVGs.jsx: -------------------------------------------------------------------------------- 1 | export const LogoSVG = () => ( 2 | 3 | 8 | 19 | 20 | veditor 21 | 22 | 23 | 32 | 33 | 34 | ); 35 | 36 | export const MenuSVG = () => ( 37 | 44 | 45 | 46 | 47 | 48 | 49 | 54 | 55 | 64 | 65 | 66 | 75 | 76 | 77 | 86 | 87 | 88 | 89 | ); 90 | 91 | export const NavSVG1 = () => ( 92 | 98 | 103 | 109 | 116 | 117 | 118 | ); 119 | 120 | export const NavSVG2 = () => ( 121 | 128 | 132 | 136 | 137 | ); 138 | 139 | export const NavSVG3 = () => ( 140 | 146 | 151 | 158 | 164 | 165 | 166 | ); 167 | 168 | export const NavSVG4 = () => ( 169 | 175 | 180 | 186 | 193 | 200 | 207 | 208 | 209 | ); 210 | 211 | export const NavSVG5 = () => ( 212 | 218 | 223 | 230 | 237 | 244 | 251 | 258 | 265 | 272 | 279 | 286 | 293 | 300 | 309 | 316 | 317 | 318 | ); 319 | 320 | export const NavSVG6 = () => ( 321 | 327 | 332 | 339 | 346 | 353 | 360 | 367 | 374 | 375 | 376 | ); 377 | 378 | export const NavSVG7 = () => ( 379 | 385 | 390 | 397 | 404 | 411 | 418 | 425 | 432 | 439 | 448 | 455 | 462 | 469 | 476 | 483 | 490 | 497 | 498 | 499 | ); 500 | 501 | export const PlayIcon1 = () => ( 502 | 510 | 516 | 517 | ); 518 | 519 | export const PlayIcon2 = () => ( 520 | 526 | 531 | 532 | 542 | 553 | 554 | 555 | 556 | ); 557 | 558 | export const IconlySearch = ({ color }) => ( 559 | 566 | 575 | 582 | 583 | ); 584 | 585 | export const ArrowDownCircle = ({ color }) => ( 586 | 593 | 602 | 609 | 610 | ); 611 | 612 | export const CameraIcon = ({ size }) => ( 613 | 620 | 626 | 627 | ); 628 | 629 | export const VolumeUp = ({ size }) => ( 630 | 637 | 643 | 649 | 650 | ); 651 | 652 | export const VolumeDown = ({ size }) => ( 653 | 660 | 666 | 672 | 673 | ); 674 | 675 | export const ExpandIcon = ({ size }) => ( 676 | 683 | expand-fullscreen-solid 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | ); 697 | 698 | export const StopIcon = () => ( 699 | 705 | 713 | 714 | ); 715 | 716 | export const SkipFD = () => ( 717 | 725 | 733 | 740 | 749 | 750 | ); 751 | 752 | export const SkipBD = () => ( 753 | 761 | 769 | 775 | 784 | 785 | ); 786 | 787 | export const PauseIcon = () => ( 788 | 795 | 799 | 800 | ); 801 | 802 | export const ImportVeditor = () => ( 803 | 809 | 814 | 820 | 826 | 827 | 828 | ); 829 | 830 | export const ImportDevice = () => ( 831 | 837 | 842 | 843 | 849 | 855 | 856 | 857 | 858 | ); 859 | 860 | export const MusicIcon = ({ size }) => ( 861 | 867 | 874 | 875 | ); 876 | -------------------------------------------------------------------------------- /src/components/Layout/Body/Body.scss: -------------------------------------------------------------------------------- 1 | .body { 2 | padding: 15px 21px 0 27px; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/Layout/Body/Body.tsx: -------------------------------------------------------------------------------- 1 | import { BodySection1, BodySection2, BodySection3 } from "../BodySections"; 2 | import "./Body.scss"; 3 | 4 | const Body = (): JSX.Element => { 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Body; 15 | -------------------------------------------------------------------------------- /src/components/Layout/Body/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Body"; 2 | -------------------------------------------------------------------------------- /src/components/Layout/BodySections/BodySection1.tsx: -------------------------------------------------------------------------------- 1 | import { Card1, Card2 } from "../Cards"; 2 | import VideoCard from "../../UI/Cards/VideoCard"; 3 | 4 | import "./BodySections.scss"; 5 | 6 | const BodySection1 = (): JSX.Element => ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | 14 | export default BodySection1; 15 | -------------------------------------------------------------------------------- /src/components/Layout/BodySections/BodySection2.tsx: -------------------------------------------------------------------------------- 1 | import "./BodySections.scss"; 2 | import Timeline from "../../UI/Timeline"; 3 | import { RootState } from "../../../redux/store"; 4 | import { useSelector } from "react-redux"; 5 | import Convert from "../../UI/Convert"; 6 | 7 | const BodySection2 = (): JSX.Element => { 8 | const currentTask = useSelector( 9 | (state: RootState) => state.currentTask.currentTask 10 | ); 11 | 12 | return ( 13 |
14 | {currentTask.name === "Trim" ? : } 15 |
16 | ); 17 | }; 18 | 19 | export default BodySection2; 20 | -------------------------------------------------------------------------------- /src/components/Layout/BodySections/BodySection3.tsx: -------------------------------------------------------------------------------- 1 | import Button from "../../UI/Button"; 2 | import { RootState } from "../../../redux/store"; 3 | import { useSelector } from "react-redux"; 4 | import { transcodeVid, trimVid } from "../../../services/apis"; 5 | import { toast } from "react-toastify"; 6 | import { useState } from "react"; 7 | 8 | const BodySection3 = () => { 9 | const state = useSelector((state: RootState) => state); 10 | const storedVideo = useSelector( 11 | (state: RootState) => state.storedVideo.storedVideo 12 | ); 13 | const currentTask = useSelector( 14 | (state: RootState) => state.currentTask.currentTask 15 | ); 16 | const edgeTimes = useSelector( 17 | (state: RootState) => state.edgeTimes.edgeTimes 18 | ); 19 | 20 | const [loading, setLoading] = useState(false); 21 | 22 | return ( 23 |
{}}> 24 | 100 |
101 | ); 102 | }; 103 | 104 | export default BodySection3; 105 | -------------------------------------------------------------------------------- /src/components/Layout/BodySections/BodySections.scss: -------------------------------------------------------------------------------- 1 | .BodySection1 { 2 | display: grid; 3 | grid-template-columns: 23.5fr 53fr 23.5fr; 4 | gap: 17px; 5 | max-height: 545px; 6 | margin-bottom: 10px; 7 | // overflow: scroll; 8 | } 9 | 10 | .BodySection2 { 11 | width: 100%; 12 | } 13 | 14 | .BodySection3 { 15 | padding: 1.5rem 0; 16 | } 17 | 18 | @media screen and (max-width: 1180px) { 19 | .BodySection1 { 20 | display: grid; 21 | grid-template-columns: unset; 22 | // grid-template-rows: 23.5fr 53fr 23.5fr; 23 | gap: 17px; 24 | max-height: 1000px; 25 | margin-bottom: 10px; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Layout/BodySections/index.js: -------------------------------------------------------------------------------- 1 | export { default as BodySection1 } from "./BodySection1"; 2 | export { default as BodySection2 } from "./BodySection2"; 3 | export { default as BodySection3 } from "./BodySection3"; 4 | -------------------------------------------------------------------------------- /src/components/Layout/Cards/Card1.tsx: -------------------------------------------------------------------------------- 1 | import SideCard from "../../UI/Cards/SideCard"; 2 | import VideoPlaceholder from "../../UI/VideoPlaceholder"; 3 | import Button from "../../UI/Button"; 4 | import { RootState } from "../../../redux/store"; 5 | import { useSelector, useDispatch } from "react-redux"; 6 | import { SETCURRENTTASK } from "../../../redux/slices/currentTaskSlice"; 7 | 8 | const Card1 = (): JSX.Element => { 9 | const dataList: { image: string; title: string; duration: number }[] = [ 10 | { 11 | image: "/assets/images/imgPlaceholder.png", 12 | title: "How to edit your first veditor video", 13 | duration: 60, 14 | }, 15 | ]; 16 | const taskList = ["Trim", "Convert"]; 17 | const dispatch = useDispatch(); 18 | const currentTask = useSelector( 19 | (state: RootState) => state.currentTask.currentTask 20 | ); 21 | 22 | return ( 23 | 24 |
25 |
26 |
27 |

Guides

28 |
29 |

30 | Learn all you need to know to get started on veditor 31 |

32 |
33 | {dataList.map((item, ind) => ( 34 |
35 |
36 |
37 | 41 |
42 |
43 |

{item.title}

44 |

{item.duration}

45 |
46 |
47 |
48 | ))} 49 |
50 |
51 | 52 |
53 |
54 |
55 |

What are we doing today?

56 |
57 |
58 | {taskList.map((task, ind) => ( 59 |
{ 62 | dispatch( 63 | SETCURRENTTASK({ 64 | ...currentTask, 65 | name: task, 66 | }) 67 | ); 68 | }} 69 | className={ 70 | currentTask.name === task 71 | ? "active" 72 | : "" 73 | } 74 | > 75 | {task} 76 |
77 | ))} 78 |
79 | {/*
*/} 80 |
81 |
82 |
83 |
84 | ); 85 | }; 86 | 87 | export default Card1; 88 | -------------------------------------------------------------------------------- /src/components/Layout/Cards/Card2.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import SideCard from "../../UI/Cards/SideCard"; 3 | import { IconlySearch } from "../../Assets/SVGs"; 4 | import VideoPlaceholder from "../../UI/VideoPlaceholder"; 5 | import Button from "../../UI/Button"; 6 | import { ArrowDownCircle } from "../../Assets/SVGs"; 7 | import WaveForm from "../../UI/WaveForm"; 8 | 9 | interface dataListProps { 10 | image: string; 11 | title: string; 12 | artist: string; 13 | duration: number; 14 | } 15 | interface dataListPropsOptional { 16 | image?: string; 17 | title?: string; 18 | artist?: string; 19 | duration?: number; 20 | } 21 | 22 | const Card2 = (): JSX.Element => { 23 | const dataList: dataListProps[] = [ 24 | { 25 | image: "/assets/images/ruger.jpg", 26 | title: "Dior", 27 | artist: "Ruger", 28 | duration: 165, 29 | }, 30 | { 31 | image: "/assets/images/adele.jpg", 32 | title: "Someone Like You", 33 | artist: "Adele", 34 | duration: 165, 35 | }, 36 | { 37 | image: "/assets/images/future.jpg", 38 | title: "WAIT FOR YOU", 39 | artist: "Future", 40 | duration: 60, 41 | }, 42 | ]; 43 | 44 | const [playing, setPlaying] = useState({}); 45 | 46 | return ( 47 | 48 |
49 |
50 |

Browse Music

51 | 52 |
53 | {Object.keys(playing).length ? ( 54 |
55 |
56 |
57 |
58 | 61 |
62 |
63 |

{playing.title}

64 |

65 | {playing.artist} • {playing.duration} 66 |

67 |
68 |
69 | 70 |
71 |
72 | 73 |
74 |
75 | ) : null} 76 |
77 | {dataList.map((item, ind) => ( 78 |
setPlaying(item)} 82 | > 83 |
84 |
85 | 86 |
87 |
88 |

{item.title}

89 |

90 | {item.artist} • {item.duration} 91 |

92 |
93 |
94 | 95 |
96 | ))} 97 |
98 |
99 |
100 | ); 101 | }; 102 | 103 | export default Card2; 104 | -------------------------------------------------------------------------------- /src/components/Layout/Cards/index.js: -------------------------------------------------------------------------------- 1 | export { default as Card1 } from "./Card1"; 2 | export { default as Card2 } from "./Card2"; 3 | -------------------------------------------------------------------------------- /src/components/Layout/Navbar/Navbar.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | background-color: #000000; 3 | display: grid; 4 | grid-template-columns: 319px 1fr 319px; 5 | color: #bababa; 6 | font-family: Cerebri Sans Book; 7 | font-size: 10px; 8 | 9 | &__menu { 10 | cursor: pointer; 11 | > svg { 12 | display: block; 13 | } 14 | } 15 | 16 | &__logo > svg { 17 | width: 77px; 18 | // height: 17px; 19 | } 20 | 21 | &__profileImg { 22 | width: 41px; 23 | height: 41px; 24 | } 25 | &__listItem { 26 | display: flex; 27 | flex-direction: column; 28 | align-items: center; 29 | gap: 0.25rem; 30 | padding: 9px 0 8px 0; 31 | transition: color 0.3s; 32 | 33 | &:hover { 34 | cursor: pointer; 35 | color: #18c96a; 36 | path { 37 | fill: #18c96a; 38 | transition: fill 0.3s; 39 | } 40 | } 41 | &--active { 42 | color: #18c96a; 43 | path { 44 | fill: #18c96a; 45 | } 46 | } 47 | } 48 | 49 | & > :first-child { 50 | display: flex; 51 | flex-direction: row; 52 | align-items: center; 53 | gap: 20.5px; 54 | padding-left: 51px; 55 | } 56 | 57 | & > :nth-child(2) { 58 | ul { 59 | display: flex; 60 | flex-direction: row; 61 | align-items: center; 62 | justify-content: space-around; 63 | list-style-type: none; 64 | 65 | svg { 66 | height: 30px; 67 | } 68 | } 69 | } 70 | 71 | & > :last-child { 72 | display: flex; 73 | flex-direction: row; 74 | align-items: center; 75 | margin-left: auto; 76 | margin-right: 40px; 77 | } 78 | } 79 | 80 | @media screen and (max-width: 1180px) { 81 | .navbar { 82 | display: flex; 83 | flex-direction: row; 84 | align-items: center; 85 | justify-content: space-between; 86 | 87 | & > :last-child { 88 | margin-left: 0; 89 | } 90 | } 91 | nav { 92 | display: none; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/components/Layout/Navbar/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { NavLink } from "react-router-dom"; 3 | import Modal from "../../UI/Modals/Modal"; 4 | import { 5 | LogoSVG, 6 | MenuSVG, 7 | NavSVG1, 8 | NavSVG2, 9 | NavSVG3, 10 | NavSVG4, 11 | NavSVG5, 12 | NavSVG6, 13 | NavSVG7, 14 | } from "../../Assets/SVGs"; 15 | import ProfilePicture from "../../UI/ProfilePicture"; 16 | import { ImportVideo } from "../../UI/Modals/ModalChildren"; 17 | import "./Navbar.scss"; 18 | import { useModal } from "../../../hooks/useModal"; 19 | 20 | const Navbar = (): JSX.Element => { 21 | const [active, setActive] = useState(0); 22 | 23 | const { openModal, setOpenModal, modalChild, setModalChild } = useModal(); 24 | 25 | const navData = [ 26 | { title: "Video Editing", icon: , clickFunc: () => {} }, 27 | { 28 | title: "Import Video", 29 | icon: , 30 | clickFunc: () => { 31 | console.log("button clickedd"); 32 | setModalChild(); 33 | setOpenModal(true); 34 | }, 35 | }, 36 | { title: "Add Voiceover", icon: , clickFunc: () => {} }, 37 | { title: "Filters / Effects", icon: , clickFunc: () => {} }, 38 | { title: "Add Subtitles", icon: , clickFunc: () => {} }, 39 | { title: "3D Preview", icon: , clickFunc: () => {} }, 40 | { title: "Sound Engine", icon: , clickFunc: () => {} }, 41 | ]; 42 | 43 | return ( 44 |
45 |
46 |
47 | 48 |
49 | 50 | 51 | 52 |
53 | 77 |
78 |
79 | 80 |
81 |
82 | 83 | {modalChild} 84 | 85 |
86 | ); 87 | }; 88 | 89 | export default Navbar; 90 | -------------------------------------------------------------------------------- /src/components/Layout/Navbar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Navbar"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Button/Button.scss: -------------------------------------------------------------------------------- 1 | button { 2 | font-family: Cerebri Sans; 3 | font-weight: 600; 4 | font-size: 14px; 5 | line-height: 18px; 6 | cursor: pointer; 7 | } 8 | 9 | .primary { 10 | width: 100%; 11 | display: grid; 12 | place-items: center; 13 | padding: 10px 24px; 14 | background-color: transparent; 15 | color: #ffffff; 16 | border: 1px solid #ffffff; 17 | border-radius: 20px; 18 | transition: color ease-in 0.5s, background-color ease-in 0.5s; 19 | 20 | &:hover { 21 | background-color: #ffffff; 22 | color: black; 23 | } 24 | } 25 | 26 | .secondary { 27 | display: grid; 28 | place-items: center; 29 | padding: 9px 26px; 30 | padding-right: 28px; 31 | border-radius: 53px; 32 | color: #000000; 33 | background-color: #ffffff; 34 | border: 1px solid #ffffff; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/UI/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import "./Button.scss"; 2 | import { Audio } from "react-loader-spinner"; 3 | 4 | interface ButtonProps { 5 | children: JSX.Element | string; 6 | variant: string; 7 | disabled?: boolean; 8 | onClick?: () => void; 9 | href?: string; 10 | loading?: boolean; 11 | } 12 | 13 | const Button = ({ 14 | children, 15 | variant, 16 | disabled = false, 17 | onClick, 18 | href, 19 | loading = false, 20 | }: ButtonProps): JSX.Element => { 21 | // const VARIANTS: string[] = ["primary", "secondary"]; 22 | const disabledStyle = disabled && "disabled"; 23 | 24 | const ButtonClassName = `${variant} ${disabledStyle}`; 25 | 26 | const clickFunc = () => { 27 | if (disabled || loading) return; 28 | else if (href) { 29 | return; 30 | } else { 31 | return onClick && onClick(); 32 | } 33 | }; 34 | 35 | return ( 36 | 48 | ); 49 | }; 50 | export default Button; 51 | -------------------------------------------------------------------------------- /src/components/UI/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Button"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Cards/SideCard/SideCard.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../styles/mixins"; 2 | 3 | .SideCard { 4 | @include play_tiles(); 5 | 6 | width: 100%; 7 | height: 100%; 8 | padding: 40px 19.5px 49px 15.5px; 9 | 10 | background: #080808 0% 0% no-repeat padding-box; 11 | color: #ffffff; 12 | border-radius: 10px; 13 | } 14 | 15 | .cardTitle { 16 | margin-bottom: 5px; 17 | display: flex; 18 | flex-direction: row; 19 | align-items: center; 20 | justify-content: space-between; 21 | 22 | svg { 23 | cursor: pointer; 24 | } 25 | 26 | &--song { 27 | margin-bottom: 12.5px; 28 | } 29 | } 30 | 31 | .desc { 32 | font: normal normal normal 12px/16px Cerebri Sans; 33 | letter-spacing: 0px; 34 | color: #bababa; 35 | } 36 | 37 | .videos { 38 | margin-top: 1px; 39 | } 40 | 41 | .btn { 42 | margin-top: 17px; 43 | padding-left: 10.5px; 44 | } 45 | 46 | .videoItem { 47 | display: flex; 48 | flex-direction: row; 49 | align-items: center; 50 | gap: 1rem; 51 | justify-content: space-between; 52 | 53 | border-bottom: 1px solid #161515; 54 | padding-top: 21px; 55 | padding-bottom: 18px; 56 | padding-left: 10.5px; 57 | cursor: pointer; 58 | 59 | &:hover { 60 | background-color: #242625; 61 | } 62 | 63 | & > :first-child { 64 | display: flex; 65 | flex-direction: row; 66 | align-items: center; 67 | gap: 1rem; 68 | } 69 | 70 | &--song { 71 | padding-bottom: 22.5px; 72 | padding-top: 20.5px; 73 | } 74 | 75 | &--song &__thumbnail { 76 | width: 40px; 77 | height: 40px; 78 | min-width: 40px; 79 | } 80 | 81 | &--song &__info { 82 | gap: 3px; 83 | } 84 | 85 | &__thumbnail { 86 | width: 76px; 87 | min-width: 76px; 88 | // width: max-content; 89 | height: 74px; 90 | } 91 | &__info { 92 | display: flex; 93 | flex-direction: column; 94 | align-items: flex-start; 95 | gap: 6px; 96 | } 97 | 98 | &--playing { 99 | margin-bottom: 0; 100 | 101 | &:hover { 102 | background-color: transparent; 103 | } 104 | // background-color: green; 105 | } 106 | 107 | svg { 108 | cursor: pointer; 109 | } 110 | } 111 | 112 | h3 { 113 | color: #ffffff !important; 114 | } 115 | 116 | .tasks { 117 | margin-top: 2.5rem; 118 | 119 | &__blocks { 120 | display: flex; 121 | flex-direction: row; 122 | align-items: center; 123 | justify-content: space-between; 124 | gap: 0.5rem; 125 | margin-top: 1rem; 126 | 127 | > * { 128 | background: #080808 0% 0% no-repeat padding-box; 129 | width: 100%; 130 | height: 7rem; 131 | border-radius: 0.5rem; 132 | cursor: pointer; 133 | 134 | display: grid; 135 | place-items: center; 136 | } 137 | > .active { 138 | background: #1a1a1a 0% 0% no-repeat padding-box; 139 | } 140 | } 141 | } 142 | 143 | .my-dropdown { 144 | background-color: #080808 !important; 145 | 146 | .ant-select-item { 147 | color: #ffffff !important; 148 | } 149 | } 150 | 151 | .waveDiv { 152 | padding-bottom: 12.5px; 153 | border-bottom: 1px solid #161515; 154 | } 155 | 156 | @media screen and (max-width: 1180px) { 157 | .SideCard { 158 | display: none; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/components/UI/Cards/SideCard/SideCard.tsx: -------------------------------------------------------------------------------- 1 | import "./SideCard.scss"; 2 | 3 | interface SideCardProps { 4 | children: JSX.Element; 5 | } 6 | 7 | const SideCard = ({ children }: SideCardProps): JSX.Element => { 8 | return ; 9 | }; 10 | 11 | export default SideCard; 12 | -------------------------------------------------------------------------------- /src/components/UI/Cards/SideCard/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SideCard"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Cards/VideoCard/VideoCard.scss: -------------------------------------------------------------------------------- 1 | .VideoCard { 2 | width: 100%; 3 | height: 100%; 4 | overflow: hidden; 5 | // padding: 40px 19.5px 49px 15.5px; 6 | 7 | background: #080808 0% 0% no-repeat padding-box; 8 | color: #ffffff; 9 | border-radius: 10px; 10 | // display: grid; 11 | // grid-template-rows: 85fr 15fr; 12 | 13 | &__placeholder { 14 | width: 100%; 15 | // height: 100%; 16 | overflow: hidden; 17 | display: grid; 18 | place-items: center; 19 | } 20 | 21 | video { 22 | width: 100%; 23 | overflow: hidden; 24 | } 25 | img { 26 | width: 100%; 27 | // height: 100%; 28 | max-width: 100%; 29 | // max-height: 100%; 30 | } 31 | 32 | &__controls { 33 | padding: 13px 51px 10px 17px; 34 | height: 89px; 35 | display: flex; 36 | flex-direction: column; 37 | align-items: flex-start; 38 | justify-content: space-between; 39 | gap: 1rem; 40 | 41 | > * { 42 | width: 100%; 43 | display: grid; 44 | grid-template-columns: 86.8fr 13.2fr; 45 | gap: 15px; 46 | // justify-content: center; 47 | align-items: center; 48 | } 49 | &__actions { 50 | display: flex; 51 | flex-direction: row; 52 | align-items: center; 53 | justify-content: space-between; 54 | gap: 0.5rem; 55 | > :first-child { 56 | display: flex; 57 | flex-direction: row; 58 | align-items: center; 59 | gap: 30px; 60 | } 61 | 62 | svg { 63 | cursor: pointer; 64 | } 65 | } 66 | &__other { 67 | display: flex; 68 | flex-direction: row; 69 | align-items: center; 70 | justify-content: space-between; 71 | gap: 0.5rem; 72 | svg { 73 | cursor: pointer; 74 | } 75 | } 76 | } 77 | } 78 | 79 | @media screen and (max-width: 1180px) { 80 | .VideoCard { 81 | &__controls { 82 | padding: 1rem; 83 | 84 | & > :nth-child(2) { 85 | display: flex; 86 | gap: 1rem; 87 | justify-content: space-between; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/components/UI/Cards/VideoCard/VideoCard.tsx: -------------------------------------------------------------------------------- 1 | import Slider from "../../Slider"; 2 | import "./VideoCard.scss"; 3 | import { 4 | CameraIcon, 5 | VolumeUp, 6 | VolumeDown, 7 | ExpandIcon, 8 | SkipFD, 9 | SkipBD, 10 | StopIcon, 11 | PauseIcon, 12 | PlayIcon1, 13 | } from "../../../Assets/SVGs"; 14 | import Dropdown from "../../Dropdown"; 15 | import { MutableRefObject, useEffect, useRef, useState } from "react"; 16 | import { secondsToTime } from "../../../../helpers/functions"; 17 | import { RootState } from "../../../../redux/store"; 18 | import { useSelector, useDispatch } from "react-redux"; 19 | import { SETCURRENTTIME } from "../../../../redux/slices/currentTimeSlice"; 20 | import { SETDURATION } from "../../../../redux/slices/durationSlice"; 21 | import { SETEDGETIMES } from "../../../../redux/slices/edgeTimesSlice"; 22 | 23 | declare global { 24 | interface Element { 25 | requestFullScreen?(): void; 26 | msRequestFullscreen(): void; 27 | webkitRequestFullscreen(): void; 28 | mozRequestFullScreen(): void; 29 | } 30 | } 31 | 32 | const VideoCard = (): JSX.Element => { 33 | const videoRef = useRef() as MutableRefObject; 34 | const [paused, setPaused] = useState(true); 35 | const [muted, setMuted] = useState(false); 36 | 37 | const currentVideo = useSelector( 38 | (state: RootState) => state.currentVideo.currentVideo 39 | ); 40 | const currentTime = useSelector( 41 | (state: RootState) => state.currentTime.currentTime 42 | ); 43 | const duration = useSelector((state: RootState) => state.duration.duration); 44 | 45 | const edgeTimes = useSelector( 46 | (state: RootState) => state.edgeTimes.edgeTimes 47 | ); 48 | const dispatch = useDispatch(); 49 | 50 | useEffect(() => { 51 | videoRef.current.currentTime = edgeTimes.start; 52 | }, [edgeTimes.start]); 53 | 54 | useEffect(() => { 55 | if (currentTime >= edgeTimes.end) videoRef.current.pause(); 56 | }, [currentTime, edgeTimes.end]); 57 | 58 | return ( 59 |
60 |
61 | 96 |
97 |
98 |
99 | 104 |

{secondsToTime(currentTime)}

105 |
106 |
107 |
108 |
109 | { 111 | videoRef.current.currentTime -= 5; 112 | }} 113 | > 114 | 115 | 116 | { 118 | videoRef.current.currentTime += 5; 119 | }} 120 | > 121 | 122 | 123 | { 125 | videoRef.current.currentTime = 0; 126 | videoRef.current.pause(); 127 | console.log(videoRef.current.paused); 128 | }} 129 | > 130 | 131 | 132 | { 134 | paused 135 | ? videoRef.current.play() 136 | : videoRef.current.pause(); 137 | setPaused(!paused); 138 | console.log(videoRef.current); 139 | }} 140 | > 141 | {paused ? : } 142 | 143 |
144 |
145 | 146 |
147 |
148 |
149 | 150 | { 152 | videoRef.current.muted = !muted; 153 | setMuted(!muted); 154 | console.log(videoRef.current); 155 | }} 156 | > 157 | {muted ? ( 158 | 159 | ) : ( 160 | 161 | )} 162 | 163 | { 165 | if (videoRef.current.requestFullscreen) { 166 | videoRef.current.requestFullscreen(); 167 | } else if ( 168 | videoRef.current.mozRequestFullScreen 169 | ) { 170 | videoRef.current.mozRequestFullScreen(); 171 | } else if ( 172 | videoRef.current.webkitRequestFullscreen 173 | ) { 174 | videoRef.current.webkitRequestFullscreen(); 175 | } else if ( 176 | videoRef.current.msRequestFullscreen 177 | ) { 178 | videoRef.current.msRequestFullscreen(); 179 | } 180 | }} 181 | > 182 | 183 | 184 |
185 |
186 |
187 |
188 | ); 189 | }; 190 | 191 | export default VideoCard; 192 | -------------------------------------------------------------------------------- /src/components/UI/Cards/VideoCard/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./VideoCard"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Convert/Convert.scss: -------------------------------------------------------------------------------- 1 | .Convert { 2 | width: 100%; 3 | min-height: 230px; 4 | background: #080808 0% 0% no-repeat padding-box; 5 | color: white; 6 | border-radius: 10px; 7 | 8 | display: flex; 9 | flex-direction: row; 10 | align-items: center; 11 | flex-wrap: wrap; 12 | gap: 0.5rem; 13 | justify-content: space-evenly; 14 | padding: 1rem; 15 | 16 | &__box { 17 | min-width: 200px; 18 | min-height: 120px; 19 | background-color: #080808; 20 | color: #fff; 21 | border-radius: 4px; 22 | border: 1px solid white; 23 | 24 | display: grid; 25 | place-items: center; 26 | cursor: pointer; 27 | font-size: 1.5rem; 28 | font-weight: 600; 29 | 30 | transition: color ease-in 0.5s, background-color ease-in 0.5s; 31 | 32 | &--active { 33 | background-color: #fff; 34 | color: #080808; 35 | } 36 | 37 | &:hover { 38 | background-color: #fff; 39 | color: #080808; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/UI/Convert/Convert.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import "./Convert.scss"; 3 | import { RootState } from "../../../redux/store"; 4 | import { useSelector, useDispatch } from "react-redux"; 5 | import { SETCURRENTTASK } from "../../../redux/slices/currentTaskSlice"; 6 | 7 | const Convert = (): JSX.Element => { 8 | const [active, setActive] = useState(0); 9 | 10 | const conversionList = ["gif", "mp4", "wav", "avi", "m4a", "mp3"]; 11 | 12 | const currentTask = useSelector( 13 | (state: RootState) => state.currentTask.currentTask 14 | ); 15 | 16 | const dispatch = useDispatch(); 17 | 18 | return ( 19 |
20 | {conversionList.map((item, ind) => ( 21 |
{ 27 | setActive(ind); 28 | dispatch( 29 | SETCURRENTTASK({ 30 | ...currentTask, 31 | details: conversionList[ind], 32 | }) 33 | ); 34 | }} 35 | > 36 | {item} 37 |
38 | ))} 39 |
40 | ); 41 | }; 42 | 43 | export default Convert; 44 | -------------------------------------------------------------------------------- /src/components/UI/Convert/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Convert"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Dropdown/Dropdown.scss: -------------------------------------------------------------------------------- 1 | .Dropdown { 2 | min-width: 67px; 3 | background-color: #242625; 4 | border-radius: 5px; 5 | 6 | cursor: pointer; 7 | position: relative; 8 | z-index: 2; 9 | 10 | &__current { 11 | display: flex; 12 | flex-direction: row; 13 | align-items: center; 14 | justify-content: space-between; 15 | } 16 | 17 | &__item { 18 | padding: 4px 8px; 19 | } 20 | 21 | &__list { 22 | position: absolute; 23 | left: 0; 24 | bottom: 0%; 25 | background-color: #242625; 26 | width: 100%; 27 | height: max-content; 28 | overflow: hidden; 29 | transition: max-height 0.5s; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/UI/Dropdown/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import "./Dropdown.scss"; 2 | import { BiChevronDown } from "react-icons/bi"; 3 | import { useState } from "react"; 4 | 5 | interface DropdownProps { 6 | list: string[]; 7 | up?: boolean; 8 | } 9 | 10 | const Dropdown = ({ list, up }: DropdownProps): JSX.Element => { 11 | const [collapsed, setCollapsed] = useState(true); 12 | 13 | return ( 14 |
setCollapsed(!collapsed)}> 15 |
16 | {list[0]} 17 | 18 |
19 |
26 | {list.map((item, ind) => ( 27 |
28 | {item} 29 |
30 | ))} 31 |
32 |
33 | ); 34 | }; 35 | 36 | export default Dropdown; 37 | -------------------------------------------------------------------------------- /src/components/UI/Dropdown/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Dropdown"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Modals/Modal/Modal.scss: -------------------------------------------------------------------------------- 1 | .modal-div { 2 | width: 100vw; 3 | height: 100vh; 4 | place-items: center; 5 | overflow-y: scroll; 6 | background-color: #050404cb; 7 | color: #ffffff; 8 | 9 | &:focus { 10 | outline: none; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/UI/Modals/Modal/Modal.tsx: -------------------------------------------------------------------------------- 1 | import { createPortal } from "react-dom"; 2 | import "./Modal.scss"; 3 | 4 | interface ModalProps { 5 | children: JSX.Element; 6 | openModal: boolean; 7 | setOpenModal: (argg: boolean) => void; 8 | } 9 | 10 | const Modal = ({ 11 | children, 12 | openModal, 13 | setOpenModal, 14 | }: ModalProps): JSX.Element => { 15 | return createPortal( 16 |
{ 22 | setOpenModal(false); 23 | }} 24 | onKeyDown={(ev) => { 25 | if (ev.code === "Escape") setOpenModal(false); 26 | }} 27 | tabIndex={3} 28 | > 29 | {children} 30 |
, 31 | document.getElementById("modal")! 32 | ); 33 | }; 34 | 35 | export default Modal; 36 | -------------------------------------------------------------------------------- /src/components/UI/Modals/Modal/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Modal"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Modals/ModalChildren/ImportVideo.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import "./ModalChildren.scss"; 3 | import { ImportVeditor, ImportDevice } from "../../../Assets/SVGs"; 4 | import { IoClose } from "react-icons/io5"; 5 | import { useDispatch } from "react-redux"; 6 | import { Audio } from "react-loader-spinner"; 7 | import { SETCURRENTVIDEO } from "../../../../redux/slices/currentVideoSlice"; 8 | import { SETSTOREDVIDEO } from "../../../../redux/slices/storedVideoSlice"; 9 | import { uploadVid } from "../../../../services/apis"; 10 | 11 | interface ImportVideoProps { 12 | setOpenModal: (argg: boolean) => void; 13 | } 14 | 15 | const ImportVideo = ({ setOpenModal }: ImportVideoProps): JSX.Element => { 16 | const [loading, setLoading] = useState(false); 17 | const [myForm, setMyForm] = useState({}); 18 | 19 | const dispatch = useDispatch(); 20 | 21 | useEffect(() => { 22 | setMyForm(document.querySelector("#myForm")!); 23 | }, []); 24 | 25 | return ( 26 |
ev.stopPropagation()}> 27 |
28 |

Import Video

29 |

Please select how you would like to import your video

30 |
31 |
{ 37 | ev.preventDefault(); 38 | }} 39 | > 40 |
41 |
42 | 43 |

Import From Veditor

44 |
45 | {!loading ? ( 46 |
47 | 48 |

Import From Device

49 | 50 | { 57 | dispatch( 58 | SETCURRENTVIDEO( 59 | URL.createObjectURL( 60 | ev.target.files![0] 61 | ) 62 | ) 63 | ); 64 | const data = new FormData( 65 | myForm as HTMLFormElement 66 | ); 67 | 68 | setLoading(true); 69 | try { 70 | const res: any = await uploadVid(data); 71 | dispatch(SETSTOREDVIDEO(res?.data)); 72 | console.log("stored"); 73 | setLoading(false); 74 | } catch (err) { 75 | console.log(err); 76 | setLoading(false); 77 | } 78 | }} 79 | /> 80 |
81 | ) : ( 82 |
90 |
91 |
setOpenModal(false)}> 92 | 93 |
94 |
95 | ); 96 | }; 97 | 98 | export default ImportVideo; 99 | -------------------------------------------------------------------------------- /src/components/UI/Modals/ModalChildren/ModalChildren.scss: -------------------------------------------------------------------------------- 1 | .ImportVideo { 2 | width: 711px; 3 | height: 459px; 4 | 5 | background: #1a1a1a 0% 0% no-repeat padding-box; 6 | border-radius: 20px; 7 | 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | padding: 50px 109px 50px 109px; 12 | position: relative; 13 | 14 | &__header { 15 | display: flex; 16 | flex-direction: column; 17 | align-items: center; 18 | gap: 12px; 19 | 20 | p { 21 | font: normal normal normal 14px/18px Cerebri Sans; 22 | color: #bababa; 23 | } 24 | } 25 | 26 | &__options { 27 | display: flex; 28 | flex-direction: row; 29 | align-items: center; 30 | gap: 30px; 31 | margin-top: 2rem; 32 | } 33 | 34 | &__option { 35 | position: relative; 36 | width: 231px; 37 | height: 219px; 38 | background: #080808 0% 0% no-repeat padding-box; 39 | border-radius: 12px; 40 | display: flex; 41 | flex-direction: column; 42 | align-items: center; 43 | justify-content: flex-end; 44 | gap: 32px; 45 | cursor: pointer; 46 | padding: 45px 16px; 47 | font: normal normal bold 12px/16px Cerebri Sans; 48 | } 49 | 50 | input[type="file"] { 51 | position: absolute; 52 | top: 0; 53 | left: 0; 54 | width: 100%; 55 | height: 100%; 56 | opacity: 0; 57 | cursor: pointer; 58 | } 59 | } 60 | 61 | .close { 62 | width: 20px; 63 | height: 20px; 64 | background: rgba(255, 255, 255, 0.4) 0% 0% no-repeat padding-box; 65 | border-radius: 0.4rem; 66 | 67 | display: grid; 68 | place-items: center; 69 | 70 | position: absolute; 71 | top: 20px; 72 | right: 20px; 73 | cursor: pointer; 74 | } 75 | -------------------------------------------------------------------------------- /src/components/UI/Modals/ModalChildren/index.js: -------------------------------------------------------------------------------- 1 | export { default as ImportVideo } from "./ImportVideo"; 2 | -------------------------------------------------------------------------------- /src/components/UI/ProfilePicture/ProfilePicture.scss: -------------------------------------------------------------------------------- 1 | .profileImg { 2 | width: 100%; 3 | height: 100%; 4 | border-radius: 50%; 5 | overflow: hidden; 6 | cursor: pointer; 7 | 8 | img { 9 | width: 100%; 10 | height: 100%; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/components/UI/ProfilePicture/ProfilePicture.tsx: -------------------------------------------------------------------------------- 1 | import "./ProfilePicture.scss"; 2 | 3 | const ProfilePicture = ({ imageURL }: { imageURL: string }): JSX.Element => ( 4 |
5 | profile 6 |
7 | ); 8 | 9 | export default ProfilePicture; 10 | -------------------------------------------------------------------------------- /src/components/UI/ProfilePicture/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ProfilePicture"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Slider/Slider.scss: -------------------------------------------------------------------------------- 1 | .Slider { 2 | width: 100%; 3 | height: 3px; 4 | background-color: rgba(186, 186, 186, 0.3); 5 | cursor: pointer; 6 | transition: width 0.3s; 7 | 8 | &__track { 9 | height: 100%; 10 | background-color: #18c96a; 11 | position: relative; 12 | // display: flex; 13 | // flex-direction: row; 14 | // align-items: center; 15 | } 16 | 17 | &__thumb { 18 | width: 18px; 19 | height: 18px; 20 | background-color: #18c96a; 21 | border-radius: 50%; 22 | position: absolute; 23 | top: 50%; 24 | right: 0; 25 | transform: translate(50%, -50%); 26 | cursor: pointer; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/components/UI/Slider/Slider.tsx: -------------------------------------------------------------------------------- 1 | import "./Slider.scss"; 2 | import { useDispatch } from "react-redux"; 3 | import { SETCURRENTTIME } from "../../../redux/slices/currentTimeSlice"; 4 | 5 | interface SliderProps { 6 | posn: number; 7 | duration: number; 8 | vidElement: HTMLVideoElement; 9 | } 10 | 11 | const Slider = ({ posn, duration, vidElement }: SliderProps): JSX.Element => { 12 | const dispatch = useDispatch(); 13 | // const currentTime = useSelector( 14 | // (state: RootState) => state.currentTime.currentTime 15 | // ); 16 | 17 | return ( 18 |
{ 21 | const { currentTarget, clientX } = ev; 22 | if (currentTarget) { 23 | console.log(clientX); 24 | const offsetLeft = (currentTarget as HTMLDivElement) 25 | .offsetLeft; 26 | const clientWidth = (currentTarget as HTMLDivElement) 27 | .clientWidth; 28 | let newVal = clientX - offsetLeft; 29 | if (newVal < 0) { 30 | newVal = 0; 31 | } else if (newVal > clientWidth) { 32 | newVal = clientWidth; 33 | } 34 | dispatch(SETCURRENTTIME((newVal / clientWidth) * duration)); 35 | vidElement.currentTime = (newVal / clientWidth) * duration; 36 | } 37 | }} 38 | > 39 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | 51 | export default Slider; 52 | -------------------------------------------------------------------------------- /src/components/UI/Slider/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Slider"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Timeline/Timeline.scss: -------------------------------------------------------------------------------- 1 | .TimelineGrid { 2 | display: grid; 3 | grid-template-rows: 54.5px 56px 56px 1fr; 4 | gap: 0; 5 | background: #080808 0% 0% no-repeat padding-box; 6 | color: white; 7 | border-radius: 10px; 8 | min-height: 230px; 9 | & > :last-child { 10 | border-bottom: 0; 11 | } 12 | } 13 | 14 | .TimelineGridRow { 15 | display: grid; 16 | grid-template-columns: 130px 1fr; 17 | gap: 0; 18 | border-bottom: 1px solid #bfbfbf27; 19 | 20 | & > :first-child { 21 | border-right: 1px solid #bfbfbf27; 22 | } 23 | 24 | &__icons { 25 | display: flex; 26 | flex-direction: row; 27 | align-items: center; 28 | justify-content: center; 29 | gap: 1rem; 30 | } 31 | } 32 | 33 | .timelineWrapper { 34 | padding: 11.5px 15.5px; 35 | 36 | &--header { 37 | display: flex; 38 | flex-direction: column; 39 | justify-content: flex-end; 40 | padding: 0 15.5px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/UI/Timeline/Timeline.tsx: -------------------------------------------------------------------------------- 1 | import TimelineRow from "./TimelineRow"; 2 | import "./Timeline.scss"; 3 | import { RootState } from "../../../redux/store"; 4 | import { useSelector, useDispatch } from "react-redux"; 5 | import { SETEDGETIMES } from "../../../redux/slices/edgeTimesSlice"; 6 | import { SETCURRENTTIME } from "../../../redux/slices/currentTimeSlice"; 7 | import { useEffect } from "react"; 8 | 9 | const Timeline = (): JSX.Element => { 10 | const dispatch = useDispatch(); 11 | const duration = useSelector((state: RootState) => state.duration.duration); 12 | 13 | useEffect(() => { 14 | dispatch(SETEDGETIMES({ start: 0, end: duration })); 15 | dispatch(SETCURRENTTIME(0)); 16 | console.log("mounted"); 17 | }, [dispatch, duration]); 18 | 19 | return ( 20 |
21 | 22 | 23 | 24 | 25 |
26 | ); 27 | }; 28 | 29 | export default Timeline; 30 | -------------------------------------------------------------------------------- /src/components/UI/Timeline/TimelineRow.tsx: -------------------------------------------------------------------------------- 1 | import { HiOutlineDuplicate } from "react-icons/hi"; 2 | import { FiSettings } from "react-icons/fi"; 3 | import { IoVideocam } from "react-icons/io5"; 4 | import { CgEyeAlt } from "react-icons/cg"; 5 | import { MusicIcon } from "../../Assets/SVGs"; 6 | import { AudioTimeline, VideoTimeline, TimelineHeader } from "../Timelines"; 7 | 8 | import "./Timeline.scss"; 9 | 10 | interface TimelineRowProps { 11 | variant: string; 12 | } 13 | 14 | const TimelineRow = ({ variant }: TimelineRowProps): JSX.Element => { 15 | switch (variant) { 16 | case "header": 17 | return ( 18 |
19 |
20 | 21 | 22 |
23 |
24 | 25 |
26 |
27 | ); 28 | 29 | case "video": 30 | return ( 31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 |
39 |
40 | ); 41 | 42 | case "song": 43 | return ( 44 |
45 |
46 | 47 | 48 |
49 |
50 | 51 |
52 |
53 | ); 54 | 55 | default: 56 | return ( 57 |
58 |
59 |
60 |
61 | ); 62 | } 63 | }; 64 | 65 | export default TimelineRow; 66 | -------------------------------------------------------------------------------- /src/components/UI/Timeline/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Timeline"; 2 | -------------------------------------------------------------------------------- /src/components/UI/Timelines/AudioTimeline.tsx: -------------------------------------------------------------------------------- 1 | import "./Timelines.scss"; 2 | 3 | const AudioTimeline = (): JSX.Element => { 4 | return ( 5 |
6 |   7 |
8 |
9 |
10 |
11 |
12 | ); 13 | }; 14 | 15 | export default AudioTimeline; 16 | -------------------------------------------------------------------------------- /src/components/UI/Timelines/TimelineHeader.tsx: -------------------------------------------------------------------------------- 1 | import "./Timelines.scss"; 2 | import { secondsToTime } from "../../../helpers/functions"; 3 | 4 | const TimelineHeader = (): JSX.Element => { 5 | return ( 6 |
7 |
8 | {Array(3) 9 | .fill(0) 10 | .map((item, ind) => ( 11 |
12 | ))} 13 | {Array(28) 14 | .fill(0) 15 | .map((item, ind) => ( 16 |
24 | {ind % 5 === 0 ? ( 25 | 26 | {secondsToTime(ind)} 27 | 28 | ) : null} 29 |
30 | ))} 31 | {Array(3) 32 | .fill(0) 33 | .map((item, ind) => ( 34 |
35 | ))} 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default TimelineHeader; 42 | -------------------------------------------------------------------------------- /src/components/UI/Timelines/Timelines.scss: -------------------------------------------------------------------------------- 1 | .VideoTimeline { 2 | width: 100%; 3 | height: 100%; 4 | background: #6a6b6b 0% 0% no-repeat padding-box; 5 | 6 | border-radius: 5px; 7 | position: relative; 8 | user-select: none; 9 | 10 | &__green { 11 | // width: 100%; 12 | height: 100%; 13 | padding: 2px; 14 | // border-radius: 5px; 15 | background: #28714f 0% 0% no-repeat padding-box; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | right: 0; 20 | cursor: grab; 21 | } 22 | 23 | &__edge { 24 | width: 7px; 25 | height: 100%; 26 | position: absolute; 27 | top: 0; 28 | background-color: white; 29 | cursor: e-resize; 30 | 31 | &--left { 32 | border-top-left-radius: 3px; 33 | border-bottom-left-radius: 3px; 34 | left: 0; 35 | transform: translateX(-70%); 36 | } 37 | &--right { 38 | border-top-right-radius: 3px; 39 | border-bottom-right-radius: 3px; 40 | right: 0; 41 | transform: translateX(70%); 42 | } 43 | } 44 | 45 | &__placeholder { 46 | width: 42px; 47 | height: 100%; 48 | border-radius: 3px; 49 | background-image: url("../../../assets/images/imgPlaceholder.png"); 50 | background-size: cover; 51 | } 52 | 53 | &__tracker { 54 | position: absolute; 55 | top: -25%; 56 | width: 2px; 57 | height: 48px; 58 | background: #ffffff 0% 0% no-repeat padding-box; 59 | cursor: e-resize; 60 | 61 | > :first-child { 62 | width: 6px; 63 | height: 6px; 64 | border-radius: 50%; 65 | background: #ffffff 0% 0% no-repeat padding-box; 66 | position: absolute; 67 | top: -2px; 68 | left: -100%; 69 | z-index: 1; 70 | } 71 | > :last-child { 72 | width: 6px; 73 | height: 6px; 74 | border-radius: 50%; 75 | background: #ffffff 0% 0% no-repeat padding-box; 76 | position: absolute; 77 | bottom: -2px; 78 | left: -100%; 79 | z-index: 1; 80 | } 81 | } 82 | } 83 | 84 | .AudioTimeline { 85 | width: 100%; 86 | height: 100%; 87 | background: #242625 0% 0% no-repeat padding-box; 88 | 89 | border-radius: 5px; 90 | position: relative; 91 | padding: 2px; 92 | 93 | &__tracker { 94 | position: absolute; 95 | top: -25%; 96 | width: 2px; 97 | height: 48px; 98 | background: #ffffff 0% 0% no-repeat padding-box; 99 | cursor: e-resize; 100 | 101 | > :first-child { 102 | width: 6px; 103 | height: 6px; 104 | border-radius: 50%; 105 | background: #ffffff 0% 0% no-repeat padding-box; 106 | position: absolute; 107 | top: -2px; 108 | left: -100%; 109 | z-index: 1; 110 | } 111 | > :last-child { 112 | width: 6px; 113 | height: 6px; 114 | border-radius: 50%; 115 | background: #ffffff 0% 0% no-repeat padding-box; 116 | position: absolute; 117 | bottom: -2px; 118 | left: -100%; 119 | z-index: 1; 120 | } 121 | } 122 | } 123 | 124 | .TimelineHeader { 125 | &__lines { 126 | display: flex; 127 | flex-direction: row; 128 | align-items: flex-end; 129 | gap: 3px; 130 | justify-content: space-between; 131 | } 132 | 133 | &__line { 134 | width: 0px; 135 | height: 10px; 136 | border: 1px solid #707070; 137 | position: relative; 138 | 139 | &--long { 140 | border-color: #18c96a; 141 | height: 17px; 142 | } 143 | } 144 | 145 | &__number { 146 | position: absolute; 147 | top: -150%; 148 | left: -100%; 149 | transform: translateX(-50%); 150 | font: normal normal normal 12px/16px Cerebri Sans; 151 | color: #bababa; 152 | } 153 | } 154 | 155 | @media screen and (max-width: 1180px) { 156 | .TimelineHeader { 157 | &__number { 158 | display: none; 159 | } 160 | } 161 | } 162 | 163 | @media screen and (max-width: 700px) { 164 | .TimelineHeader { 165 | &__line { 166 | display: none; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/components/UI/Timelines/VideoTimeline.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useRef, MutableRefObject } from "react"; 3 | import "./Timelines.scss"; 4 | import { RootState } from "../../../redux/store"; 5 | import { useSelector, useDispatch } from "react-redux"; 6 | import { SETCURRENTTIME } from "../../../redux/slices/currentTimeSlice"; 7 | import { SETEDGETIMES } from "../../../redux/slices/edgeTimesSlice"; 8 | 9 | const VideoTimeline = (): JSX.Element => { 10 | const vidTimelineRef = useRef() as MutableRefObject; 11 | const greenTimelineRef = useRef() as MutableRefObject; 12 | const [resizing, setResizing] = useState(false); 13 | const [initialLength, setInitialLength] = useState(0); 14 | const [leftOffset, setLeftOffset] = useState(0); 15 | const [rightOffset, setRightOffset] = useState(0); 16 | 17 | const dispatch = useDispatch(); 18 | const currentTime = useSelector( 19 | (state: RootState) => state.currentTime.currentTime 20 | ); 21 | const duration = useSelector((state: RootState) => state.duration.duration); 22 | const edgeTimes = useSelector( 23 | (state: RootState) => state.edgeTimes.edgeTimes 24 | ); 25 | 26 | const posn = 27 | ((currentTime - edgeTimes.start) / 28 | (duration - edgeTimes.start - (duration - edgeTimes.end))) * 29 | 100; 30 | 31 | useEffect(() => { 32 | setInitialLength(vidTimelineRef.current.clientWidth); 33 | // console.log(initialLength); 34 | }, []); 35 | 36 | useEffect(() => {}, [resizing]); 37 | 38 | const MouseDownFuncLeft = ( 39 | mouseDownEv: React.MouseEvent 40 | ) => { 41 | const MouseMoveFunc = (ev: MouseEvent) => { 42 | if ( 43 | ev.clientX < 44 | vidTimelineRef.current.clientWidth - 45 | rightOffset + 46 | vidTimelineRef.current.offsetLeft 47 | ) { 48 | setLeftOffset( 49 | Math.max(0, ev.clientX - mouseDownEv.clientX + leftOffset) 50 | ); 51 | 52 | console.log(vidTimelineRef.current.clientWidth); 53 | } 54 | }; 55 | const MouseUpFunc = (ev: MouseEvent) => { 56 | dispatch( 57 | SETEDGETIMES({ 58 | ...edgeTimes, 59 | start: Math.min( 60 | (Math.max( 61 | 0, 62 | ev.clientX - mouseDownEv.clientX + leftOffset 63 | ) / 64 | initialLength) * 65 | duration, 66 | edgeTimes.end 67 | ), 68 | }) 69 | ); 70 | dispatch(SETCURRENTTIME(edgeTimes.start)); 71 | 72 | window.removeEventListener("mousemove", MouseMoveFunc); 73 | window.removeEventListener("mouseup", MouseUpFunc); 74 | console.log("clean"); 75 | }; 76 | 77 | window.addEventListener("mousemove", MouseMoveFunc); 78 | window.addEventListener("mouseup", MouseUpFunc); 79 | }; 80 | 81 | const MouseDownFuncRight = ( 82 | mouseDownEv: React.MouseEvent 83 | ) => { 84 | const MouseMoveFunc = (ev: MouseEvent) => { 85 | if (ev.clientX - vidTimelineRef.current.offsetLeft > leftOffset) { 86 | console.log("ev.clientX", ev.clientX); 87 | console.log("leftOffset", leftOffset); 88 | // console.log(Math.max(0, ev.clientX - mouseDownEv.clientX)); 89 | setRightOffset( 90 | Math.max(0, mouseDownEv.clientX - ev.clientX + rightOffset) 91 | ); 92 | } 93 | }; 94 | const MouseUpFunc = (ev: MouseEvent) => { 95 | console.log( 96 | "timeline element: ", 97 | vidTimelineRef.current.offsetLeft 98 | ); 99 | console.log("Mousedown clientX: ", mouseDownEv.clientX); 100 | console.log("mouseup clientX: ", ev.clientX); 101 | console.log("rightOffset: ", rightOffset); 102 | console.log("greenTimelineRef: ", greenTimelineRef.current); 103 | dispatch( 104 | SETEDGETIMES({ 105 | ...edgeTimes, 106 | end: Math.max( 107 | edgeTimes.start, 108 | Math.min( 109 | ((ev.clientX - vidTimelineRef.current.offsetLeft) / 110 | initialLength) * 111 | duration, 112 | duration 113 | ) 114 | ), 115 | }) 116 | ); 117 | 118 | window.removeEventListener("mousemove", MouseMoveFunc); 119 | window.removeEventListener("mouseup", MouseUpFunc); 120 | setResizing(false); 121 | console.log("clean"); 122 | }; 123 | 124 | window.addEventListener("mousemove", MouseMoveFunc); 125 | window.addEventListener("mouseup", MouseUpFunc); 126 | }; 127 | 128 | return ( 129 |
130 |
{ 134 | setResizing(true); 135 | console.log("start moving"); 136 | console.log(ev.nativeEvent.offsetX); 137 | MouseDownFuncLeft(ev); 138 | }} 139 | >
140 |
{}} 150 | > 151 |
152 |   153 |
157 |
158 |
159 |
160 |
161 |
{ 165 | setResizing(true); 166 | console.log("start moving"); 167 | console.log(ev.nativeEvent.offsetX); 168 | MouseDownFuncRight(ev); 169 | }} 170 | >
171 |
172 | ); 173 | }; 174 | 175 | export default VideoTimeline; 176 | -------------------------------------------------------------------------------- /src/components/UI/Timelines/index.js: -------------------------------------------------------------------------------- 1 | export { default as AudioTimeline } from "./AudioTimeline"; 2 | export { default as VideoTimeline } from "./VideoTimeline"; 3 | export { default as TimelineHeader } from "./TimelineHeader"; 4 | -------------------------------------------------------------------------------- /src/components/UI/VideoPlaceholder/VideoPlaceholder.scss: -------------------------------------------------------------------------------- 1 | .VideoPlaceholder { 2 | width: 100%; 3 | height: 100%; 4 | border-radius: 5px; 5 | overflow: hidden; 6 | position: relative; 7 | 8 | &__play { 9 | position: absolute; 10 | top: 0; 11 | left: 0; 12 | width: 100%; 13 | height: 100%; 14 | background: #000000a8; 15 | display: grid; 16 | place-items: center; 17 | border-radius: 5px; 18 | } 19 | 20 | img { 21 | width: 100%; 22 | height: 100%; 23 | min-width: 100%; 24 | min-height: 100%; 25 | // height: 100%; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/UI/VideoPlaceholder/VideoPlaceholder.tsx: -------------------------------------------------------------------------------- 1 | import "./VideoPlaceholder.scss"; 2 | import { PlayIcon2 } from "../../Assets/SVGs"; 3 | 4 | interface VideoPlaceholderProps { 5 | imageURL: string; 6 | video?: boolean; 7 | } 8 | 9 | const VideoPlaceholder = ({ 10 | imageURL, 11 | video, 12 | }: VideoPlaceholderProps): JSX.Element => ( 13 |
14 | video 15 | {video ? ( 16 |
17 | 18 |
19 | ) : null} 20 |
21 | ); 22 | 23 | export default VideoPlaceholder; 24 | -------------------------------------------------------------------------------- /src/components/UI/VideoPlaceholder/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./VideoPlaceholder"; 2 | -------------------------------------------------------------------------------- /src/components/UI/WaveForm/WaveForm.scss: -------------------------------------------------------------------------------- 1 | .WaveForm { 2 | width: 100%; 3 | height: 40px; 4 | background-color: #242625; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/UI/WaveForm/WaveForm.tsx: -------------------------------------------------------------------------------- 1 | import "./WaveForm.scss"; 2 | 3 | const WaveForm = (): JSX.Element =>
; 4 | 5 | export default WaveForm; 6 | -------------------------------------------------------------------------------- /src/components/UI/WaveForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./WaveForm"; 2 | -------------------------------------------------------------------------------- /src/helpers/functions.tsx: -------------------------------------------------------------------------------- 1 | const doubleNumbers = (val: number): string => { 2 | return val < 10 ? `0${val}` : `${val}`; 3 | }; 4 | 5 | export const secondsToTime = (secondsTime: number): string => { 6 | let microseconds: number, seconds: number, minutes: number, hours: number; 7 | 8 | hours = Math.floor(secondsTime / 3600); 9 | minutes = Math.floor((secondsTime - hours * 3600) / 60); 10 | seconds = Math.floor(secondsTime - (hours * 3600 + minutes * 60)); 11 | microseconds = 0; 12 | return `${doubleNumbers(hours)}:${doubleNumbers(minutes)}:${doubleNumbers( 13 | seconds 14 | )}:${doubleNumbers(microseconds)}`; 15 | }; 16 | -------------------------------------------------------------------------------- /src/hooks/useModal.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export const useModal = () =>{ 4 | const [openModal, setOpenModal] = useState(false); 5 | const [modalChild, setModalChild] = useState(null) 6 | 7 | return {openModal, setOpenModal, modalChild, setModalChild} 8 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | * { 16 | margin: 0; 17 | padding: 0; 18 | box-sizing: border-box; 19 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import { Provider } from "react-redux"; 6 | import { store } from "./redux/store"; 7 | import reportWebVitals from "./reportWebVitals"; 8 | 9 | const root = ReactDOM.createRoot( 10 | document.getElementById("root") as HTMLElement 11 | ); 12 | root.render( 13 | 14 | 15 | 16 | 17 | 18 | ); 19 | 20 | // If you want to start measuring performance in your app, pass a function 21 | // to log results (for example: reportWebVitals(console.log)) 22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 23 | reportWebVitals(); 24 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/VideoEditing.tsx: -------------------------------------------------------------------------------- 1 | import Body from "../components/Layout/Body"; 2 | import Navbar from "../components/Layout/Navbar"; 3 | import "../styles/main.scss"; 4 | 5 | const VideoEditing = (): JSX.Element => { 6 | return ( 7 | <> 8 |
9 | 10 | 11 |
12 | 13 | ); 14 | }; 15 | 16 | export default VideoEditing; 17 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/redux/slices/currentTaskSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface CurrentTaskProps { 4 | name: string; 5 | details:string; 6 | } 7 | 8 | export interface CurrentTaskState { 9 | currentTask: CurrentTaskProps; 10 | } 11 | 12 | const initialState: CurrentTaskState = { 13 | currentTask:{name:"Trim", details:'gif'}, 14 | }; 15 | 16 | export const currentTaskSlice = createSlice({ 17 | name: "currentTask", 18 | initialState, 19 | reducers: { 20 | SETCURRENTTASK: (state, action: PayloadAction) => { 21 | state.currentTask = action.payload; 22 | }, 23 | }, 24 | }); 25 | 26 | export const { SETCURRENTTASK } = currentTaskSlice.actions; 27 | 28 | export default currentTaskSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/redux/slices/currentTimeSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface CurrentTimeState { 4 | currentTime: number; 5 | } 6 | 7 | const initialState: CurrentTimeState = { 8 | currentTime: 0, 9 | }; 10 | 11 | export const currentTimeSlice = createSlice({ 12 | name: "currentTime", 13 | initialState, 14 | reducers: { 15 | SETCURRENTTIME: (state, action: PayloadAction) => { 16 | state.currentTime = action.payload; 17 | }, 18 | }, 19 | }); 20 | 21 | export const { SETCURRENTTIME } = currentTimeSlice.actions; 22 | 23 | export default currentTimeSlice.reducer; 24 | -------------------------------------------------------------------------------- /src/redux/slices/currentVideoSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface CurrentVideoState { 4 | currentVideo: string; 5 | } 6 | 7 | const initialState: CurrentVideoState = { 8 | currentVideo: "", 9 | }; 10 | 11 | export const currentVideoSlice = createSlice({ 12 | name: "currentVideo", 13 | initialState, 14 | reducers: { 15 | SETCURRENTVIDEO: (state, action: PayloadAction) => { 16 | state.currentVideo = action.payload; 17 | }, 18 | }, 19 | }); 20 | 21 | export const { SETCURRENTVIDEO } = currentVideoSlice.actions; 22 | 23 | export default currentVideoSlice.reducer; 24 | -------------------------------------------------------------------------------- /src/redux/slices/durationSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface durationState { 4 | duration: number; 5 | } 6 | 7 | const initialState: durationState = { 8 | duration: 0, 9 | }; 10 | 11 | export const durationSlice = createSlice({ 12 | name: "duration", 13 | initialState, 14 | reducers: { 15 | SETDURATION: (state, action: PayloadAction) => { 16 | state.duration = action.payload; 17 | }, 18 | }, 19 | }); 20 | 21 | export const { SETDURATION } = durationSlice.actions; 22 | 23 | export default durationSlice.reducer; 24 | -------------------------------------------------------------------------------- /src/redux/slices/edgeTimesSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface EdgeTimesProps { 4 | start: number; 5 | end:number; 6 | } 7 | 8 | export interface EdgeTimesState { 9 | edgeTimes: EdgeTimesProps; 10 | } 11 | 12 | const initialState: EdgeTimesState = { 13 | edgeTimes:{start:0, end:12}, 14 | }; 15 | 16 | export const edgeTimesSlice = createSlice({ 17 | name: "edgeTimes", 18 | initialState, 19 | reducers: { 20 | SETEDGETIMES: (state, action: PayloadAction) => { 21 | state.edgeTimes = action.payload; 22 | }, 23 | }, 24 | }); 25 | 26 | export const { SETEDGETIMES } = edgeTimesSlice.actions; 27 | 28 | export default edgeTimesSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/redux/slices/storedVideoSlice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface storedVideoProps { 4 | filename:string; 5 | filepath:string 6 | } 7 | 8 | export interface storedVideoState { 9 | storedVideo: storedVideoProps; 10 | } 11 | 12 | const initialState: storedVideoState = { 13 | storedVideo: { filename: '', filepath: '' }, 14 | }; 15 | 16 | 17 | 18 | export const storedVideoSlice = createSlice({ 19 | name: "storedVideo", 20 | initialState, 21 | reducers: { 22 | SETSTOREDVIDEO: (state, action: PayloadAction) => { 23 | state.storedVideo = {...state.storedVideo, filename: action.payload.filename, filepath: action.payload.filepath}; 24 | }, 25 | }, 26 | }); 27 | 28 | export const { SETSTOREDVIDEO } = storedVideoSlice.actions; 29 | 30 | export default storedVideoSlice.reducer; 31 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import currentVideoReducer from "./slices/currentVideoSlice"; 3 | import currentTimeReducer from "./slices/currentTimeSlice"; 4 | import durationReducer from "./slices/durationSlice"; 5 | import storedVideoReducer from "./slices/storedVideoSlice"; 6 | import currentTaskReducer from "./slices/currentTaskSlice"; 7 | import edgeTimesReducer from "./slices/edgeTimesSlice"; 8 | 9 | export const store = configureStore({ reducer: { 10 | currentVideo: currentVideoReducer, 11 | currentTime: currentTimeReducer, 12 | duration: durationReducer, 13 | storedVideo: storedVideoReducer, 14 | currentTask:currentTaskReducer, 15 | edgeTimes:edgeTimesReducer 16 | } 17 | }) 18 | 19 | export type RootState = ReturnType 20 | export type AppDispatch = typeof store.dispatch -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/services/apis.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios" 2 | 3 | export const uploadVid = async (payload: FormData)=>{ 4 | try{ 5 | const response = await axios.post( 6 | `${process.env.REACT_APP_BASE_URL}/upload`, 7 | payload 8 | ) 9 | return response 10 | } 11 | catch(err) { 12 | console.log(err) 13 | throw err 14 | } 15 | } 16 | 17 | export const transcodeVid = async (payload: {filename:string; filepath:string; filetype: string})=>{ 18 | try{ 19 | const response = await axios({ 20 | method: 'post', 21 | url: `${process.env.REACT_APP_BASE_URL}/transcode`, 22 | data: payload, 23 | responseType: 'blob' 24 | }) 25 | 26 | return response 27 | } 28 | catch(err) { 29 | console.log(err) 30 | throw err 31 | } 32 | } 33 | 34 | export const trimVid = async (payload: {filename:string; filepath:string; start: number, end:number;})=>{ 35 | try{ 36 | const response = await axios({ 37 | method: 'post', 38 | url: `${process.env.REACT_APP_BASE_URL}/trim`, 39 | data: payload, 40 | responseType: 'blob' 41 | }) 42 | 43 | return response 44 | } 45 | catch(err) { 46 | console.log(err) 47 | throw err 48 | } 49 | } -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin play_tiles() { 2 | .title { 3 | font-family: Cerebri Sans; 4 | font-size: 14px; 5 | line-height: 18px; 6 | font-weight: 600; 7 | color: #ffffff; 8 | } 9 | 10 | .info { 11 | font-family: Cerebri Sans Book; 12 | font-size: 14px; 13 | line-height: 18px; 14 | font-weight: 600; 15 | color: #bababa; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elijahthis/veditor/9149005af6ebb093e9de1724a9fecba74c44f9be/src/styles/_variables.scss -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | .main { 2 | min-height: 100vh; 3 | display: grid; 4 | grid-template-rows: 63px 1fr; 5 | background: #171616 0% 0% no-repeat padding-box; 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "downlevelIteration": true, 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /webpack.server.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const nodeExternals = require("webpack-node-externals"); 3 | 4 | module.exports = { 5 | entry: "./server/index.js", 6 | target: "node", 7 | externals: [nodeExternals()], 8 | output: { 9 | path: path.resolve("server-build"), 10 | filename: "index.js", 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.js$/, 16 | use: "babel-loader", 17 | }, 18 | ], 19 | }, 20 | }; 21 | --------------------------------------------------------------------------------