diff --git a/package.json b/package.json index 3d8aea5..ad9992e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-with-mosh-downloader", - "version": "0.0.7", + "version": "0.1.2", "description": "Download courses from codewithmosh.com", "main": ".webpack/main", "scripts": { @@ -67,6 +67,7 @@ "@electron-forge/plugin-webpack": "6.0.0-beta.59", "@electron-forge/publisher-github": "^6.0.0-beta.59", "@electron-forge/test-utils": "^6.0.0-beta.59", + "@styled-icons/evaicons-solid": "^10.34.0", "@types/async-lock": "^1.1.3", "@types/mocha": "^9.0.0", "@types/node": "^16.4.3", @@ -88,6 +89,7 @@ "node-html-parser": "^4.1.2", "node-loader": "^2.0.0", "react-is": "^17.0.2", + "react-spring": "^9.2.4", "sanitize-filename": "^1.6.3", "spectron": "^15.0.0", "style-loader": "^3.2.1", diff --git a/src/app/appRoot.tsx b/src/app/appRoot.tsx index 8ebbb25..d0c7aac 100644 --- a/src/app/appRoot.tsx +++ b/src/app/appRoot.tsx @@ -2,6 +2,7 @@ import React, {useRef} from 'react'; import styled from 'styled-components'; import GlobalDefault from 'App/styles/globalDefault'; import {ipcRenderer} from 'electron'; +import {Notifications} from 'App/components/notifications'; const RootContainer = styled.div` width: 100vw; @@ -16,14 +17,16 @@ export default function App() { const inputRef = useRef(); const onAdd = async () => { - const a = await ipcRenderer.invoke('to-enqueue', {url: 'https://codewithmosh.com/courses', type: 'everything'}); + const a = await ipcRenderer.invoke('to-enqueue', + {url: 'https://codewithmosh.com/courses/1422300/lectures/32679735', type: 'video'}); }; return ( - - - - - + + + + + + ); } diff --git a/src/app/components/notifications.tsx b/src/app/components/notifications.tsx new file mode 100644 index 0000000..3b7e8ee --- /dev/null +++ b/src/app/components/notifications.tsx @@ -0,0 +1,135 @@ +import React, {useEffect, useRef, useState} from 'react'; +import {ipcRenderer} from 'electron'; +import styled from 'styled-components'; +import {AppNotification} from 'Types/types'; +import {Close} from '@styled-icons/evaicons-solid'; +import {animated, useTransition} from 'react-spring'; + +export type NotificationsProps = {} + +const Container = styled.div` + position: fixed; + bottom: 0; + right: 0; + z-index: 1; + display: flex; + flex-direction: column; + align-items: flex-end; +`; + +const NotificationContainer = styled.div<{ backgroundColour: string, foregroundColour: string }>` + background-color: ${props => props.backgroundColour}; + color: ${props => props.foregroundColour}; + border-radius: 10px; + display: flex; +`; + +const NotificationTextContainer = styled.div` + flex: 5 1; + padding: 0.6rem; + display: flex; +`; + +const NotificationText = styled.p` + flex: 1 0; + font-size: 12pt; + font-weight: 500; +`; + +const NotificationDismiss = styled.div<{ hoverColour: string }>` + display: flex; + flex: 1 0; + min-height: 100%; + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + align-items: center; + justify-content: center; + max-width: 30px; + transition: .2s ease-in-out background-color; + border-left: 1px solid #242424; + + &:hover { // #418B5A + background-color: ${props => props.hoverColour}; + cursor: pointer; + } +`; + +const CloseIcon = styled(Close)` + flex: 1; + width: 20px; + height: 20px; +`; + +const colours = { + 'success': { + foreground: '#000', + background: '#7FC395', + hover: '#51AC6E', + }, + 'info': { + foreground: '#000', + background: '#8FBDBD', + hover: '#589595', + }, + 'error': { + foreground: '#000', + background: '#FD9B9B', + hover: '#f56d6d', + } +}; + +export function Notifications(props: NotificationsProps) { + const [refMap] = useState(() => new WeakMap()); + const [notifications, setNotifications] = useState([]); + const closeRef = useRef(); + + const transitions = useTransition(notifications, { + from: {opacity: 0, height: 0, margin: '0 1rem 0.7rem 0.7rem', width: '450px', transform: 'translate(0px, 0px)'}, + enter: item => async next => await next({opacity: 1, height: refMap.get(item).offsetHeight}), + leave: item => async next => { + await next({transform: 'translate(475px, 0px)'}); + await next({height: 0, margin: 'unset'}); + }, + config: { + tension: 450, + friction: 65 + } + }); + + useEffect(() => { + ipcRenderer.on('to-alerts', (event, args: AppNotification) => { + setNotifications(prev => [...prev, args]); + + setTimeout(() => { + setNotifications(prev => { + return prev.filter(x => x !== args); + }); + }, 3000); + }); + }, []); + + const onCloseClick = (item: AppNotification) => { + setNotifications(prev => { + return prev.filter(x => x !== item); + }); + }; + + return ( + + {transitions((style, item) => ( + + ref && refMap.set(item, ref)}> + + {item.message} + + onCloseClick(item)}> + + + + + ))} + + ); +} diff --git a/src/app/types/types.d.ts b/src/app/types/types.d.ts index 882a675..e2c75c1 100644 --- a/src/app/types/types.d.ts +++ b/src/app/types/types.d.ts @@ -15,3 +15,10 @@ export type DownloadQueueItem = { } export type DownloadQueueItemType = 'everything' | 'course' | 'video' | 'end'; + +export type AppNotification = { + type: AppNotificationType, + message: string +} + +export type AppNotificationType = 'success' | 'error' | 'info'; diff --git a/yarn.lock b/yarn.lock index b2646bc..e35a02b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -88,6 +88,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA== +"@babel/runtime@^7.10.5", "@babel/runtime@^7.14.0": + version "7.14.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" + integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" @@ -443,7 +450,7 @@ resolved "https://registry.yarnpkg.com/@electron/remote/-/remote-1.2.0.tgz#772eb4c3ac17aaba5a9cf05a09092f6277f5671f" integrity sha512-C774t2DFVJsa+dxU9Gc2nYzylRZoJ79I0Sxrh8T9cN69fBkntfGbyBEQiD9UfZopqL0CYLzk1anY2Ywhql6h1w== -"@emotion/is-prop-valid@^0.8.8": +"@emotion/is-prop-valid@^0.8.7", "@emotion/is-prop-valid@^0.8.8": version "0.8.8" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== @@ -652,6 +659,91 @@ dependencies: "@octokit/openapi-types" "^9.1.1" +"@react-spring/animated@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.2.4.tgz#062ecc0fdfef89f2541a42d8500428b70035f879" + integrity sha512-AfV6ZM8pCCAT29GY5C8/1bOPjZrv/7kD0vedjiE/tEYvNDwg9GlscrvsTViWR2XykJoYrDfdkYArrldWpsCJ5g== + dependencies: + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/core@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.2.4.tgz#275a4a065e3a315a4f5fb28c9a6f62ce718c25d6" + integrity sha512-R+PwyfsjiuYCWqaTTfCpYpRmsP0h87RNm7uxC1Uxy7QAHUfHEm2sAHn+AdHPwq/MbVwDssVT8C5yf2WGcqiXGg== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/konva@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/konva/-/konva-9.2.4.tgz#e467b24b3b110ba496526c9001439ce561641e0d" + integrity sha512-19anDOIkfjcydDTfGgVIuZ3lruZxKubYGs9oHCswaP8SRLj7c1kkopJHUr/S4LXGxiIdqdF0XucWm0iTEPEq4w== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/native@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/native/-/native-9.2.4.tgz#0fd335a44c05023f5428df444d8f1aa3da7abfc9" + integrity sha512-xKJWKh5qOhSclpL3iuGwJRLoZzTNvlBEnIrMs8yh8xvX6z9Lmnu4uGu5DpfrnM1GzBvRoktoCoLEx/VcEYFSng== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/rafz@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.2.4.tgz#44793e9adc14dd0dcd1573d094368af11a89d73a" + integrity sha512-SOKf9eue+vAX+DGo7kWYNl9i9J3gPUlQjifIcV9Bzw9h3i30wPOOP0TjS7iMG/kLp2cdHQYDNFte6nt23VAZkQ== + +"@react-spring/shared@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.2.4.tgz#f9cc66ac5308a77293330a18518e34121f4008c1" + integrity sha512-ZEr4l2BxmyFRUvRA2VCkPfCJii4E7cGkwbjmTBx1EmcGrOnde/V2eF5dxqCTY3k35QuCegkrWe0coRJVkh8q2Q== + dependencies: + "@react-spring/rafz" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/three@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.2.4.tgz#849c97658a6e1410b6f823ad21e2ee33feada820" + integrity sha512-ljFig7XW099VWwRPKPUf+4yYLivp/sSWXN3oO5SJOF/9BSoV1quS/9chZ5Myl5J14od3CsHf89Tv4FdlX5kHlA== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/types@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.2.4.tgz#2365ce9d761f548a9adcb2cd68714bf26765a5de" + integrity sha512-zHUXrWO8nweUN/ISjrjqU7GgXXvoEbFca1CgiE0TY0H/dqJb3l+Rhx8ecPVNYimzFg3ZZ1/T0egpLop8SOv4aA== + +"@react-spring/web@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.2.4.tgz#c6d5464a954bfd0d7bc90117050f796a95ebfa08" + integrity sha512-vtPvOalLFvuju/MDBtoSnCyt0xXSL6Amyv82fljOuWPl1yGd4M1WteijnYL9Zlriljl0a3oXcPunAVYTD9dbDQ== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/zdog@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/zdog/-/zdog-9.2.4.tgz#db1d1924fe9920e917d889c4d3bb138bd0885cf1" + integrity sha512-rv7ptedS37SHr6yuCbRkUErAzAhebdgt8f4KUtZWzseC+7qLNkaZWf+uujgsb881qAuX9b9yz8rre9UKeYepgw== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -662,6 +754,22 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.1.tgz#d26729db850fa327b7cacc5522252194404226f5" integrity sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g== +"@styled-icons/evaicons-solid@^10.34.0": + version "10.34.0" + resolved "https://registry.yarnpkg.com/@styled-icons/evaicons-solid/-/evaicons-solid-10.34.0.tgz#54cff976f32aad41949da7419f6d9947d7bde431" + integrity sha512-74S0zzxovKTXNpID8bXB4Qy34EBq3vuVAlTd9xsT2gY16BtPH7JcGHLmtQpbpJcHuWkcQH4bSv+GLq+0/i/ljQ== + dependencies: + "@babel/runtime" "^7.14.0" + "@styled-icons/styled-icon" "^10.6.3" + +"@styled-icons/styled-icon@^10.6.3": + version "10.6.3" + resolved "https://registry.yarnpkg.com/@styled-icons/styled-icon/-/styled-icon-10.6.3.tgz#eae0e5e18fd601ac47e821bb9c2e099810e86403" + integrity sha512-/A95L3peioLoWFiy+/eKRhoQ9r/oRrN/qzbSX4hXU1nGP2rUXcX3LWUhoBNAOp9Rw38ucc/4ralY427UUNtcGQ== + dependencies: + "@babel/runtime" "^7.10.5" + "@emotion/is-prop-valid" "^0.8.7" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -5802,6 +5910,18 @@ react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-spring@^9.2.4: + version "9.2.4" + resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-9.2.4.tgz#9d89b0321664d594f957dca9459b13d94b3dfa39" + integrity sha512-bMjbyTW0ZGd+/h9cjtohLqCwOGqX2OuaTvalOVfLCGmhzEg/u3GgopI3LAm4UD2Br3MNdVdGgNVoESg4MGqKFQ== + dependencies: + "@react-spring/core" "~9.2.0" + "@react-spring/konva" "~9.2.0" + "@react-spring/native" "~9.2.0" + "@react-spring/three" "~9.2.0" + "@react-spring/web" "~9.2.0" + "@react-spring/zdog" "~9.2.0" + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" @@ -5915,6 +6035,11 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + regexp.prototype.flags@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"