diff --git a/.env b/.env new file mode 100644 index 0000000..7d910f1 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +SKIP_PREFLIGHT_CHECK=true \ No newline at end of file diff --git a/R.md b/R.md new file mode 100644 index 0000000..fe1019d --- /dev/null +++ b/R.md @@ -0,0 +1,94 @@ +# 🪒Captain Barber 🪒 + +Captain Barber is a portfolio project where you can search for barbershops in Amsterdam, see and write their reviews, rate them and add new barbershops. You can see the information about the services they provide so you can compare the prices and decide where to book your appointment. You can also find contact information for each barbershop and locate them on a map, so you always know where to go to get that clean cut. + +## Table of contents: + +- **[Goals for this Project](#goals-for-this-project)** +- **[User Stories](#user-stories)** +- **[App Demo](#app-demo)** +- **[Working Version](#working-version)** +- **[Database Diagram](#database-diagram)** +- **[Project-Board](#kanban-task-board)** +- **[Wireframe](#wireframe)** +- **[Backend](#backend)** +- **[Technologies Used](#technologies-used)** +- **[Git Version Control](#git-version-control)** +- **[SETUP](#getting-started)** + +## Goals for this Project + +The goal of this project is to build a frontend app, in a working group of three. +With this project, we are trying to: + +- build a demo version in four days +- apply the technologies we learned in Codaisseur Bootcamp +- showcase implementation of new technologies +- work in a team + +## User Stories + +- As a page visitor, I can sign up and log in as a user. I must register before I can post reviews and like barbershops. +- As a page visitor, I am able see existing barbershops, their rates and reviews. I can see them sorted by their rates in a descending order. +- As a page visitor, I am able to see the name of the user, that posted a review and the time and date of when it was posted. +- As a page visitor, I am able to see the existing barbershops on a map and get to their detail page when clicking on them. +- As a logged in user, I am able to rate and review barbershops. +- As a barbershop owner, I can log in as a barbershop owner. +- As a logged in barbershop owner, I am able to add a new barbershop to the list of barbershops. + +## App Demo + +to do [] + +![Captain Barber Demo](CaptainBarberAppDemo.gif) + +## Working Version + +to do [] + +[Link to Captain Barber](https://captainbarber.netlify.app/) + +## Database Diagram + +[Link to the DB diagram](https://dbdiagram.io/d/5f5fa0db7da1ea736e2dd012) + +(made for future development) + +## Project Board + +[Link to the project board](https://github.com/RokPopov/Noteassieur-FrontEnd/projects/2) + +## Wireframe + +[Link to the wireframe](https://www.figma.com/file/9adXlJp320LdAFDy2icKdD/Untitled?node-id=1%3A2) + +## Backend + +[Link to the server side of the project](https://github.com/RokPopov/Noteassieur-BackEnd) + +(made for future development) + +## Technologies Used + +- TypeScript +- React +- React Router DOM +- Redux +- Redux Thunk +- Html, JSX +- CSS, Material-UI + +## Git Version Control + +In this project I try to implement solid version control: + +- write clear commit messages +- name branches by features +- do pull requests with meaningful summaries + +## SETUP Getting started + +- clone your app +- cd into your project +- npm install +- npm start diff --git a/_redirects b/_redirects new file mode 100644 index 0000000..ad37e2c --- /dev/null +++ b/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/package-lock.json b/package-lock.json index 387474e..dc54af0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,6 +56,11 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -67,6 +72,13 @@ "@babel/types": "^7.11.5", "jsesc": "^2.5.1", "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } } }, "@babel/helper-annotate-as-pure": { @@ -1161,6 +1173,11 @@ "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-10.1.0.tgz", "integrity": "sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==" }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -1294,13 +1311,6 @@ "slash": "^2.0.0", "source-map": "^0.6.0", "string-length": "^2.0.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "@jest/source-map": { @@ -1317,11 +1327,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -1367,13 +1372,6 @@ "slash": "^2.0.0", "source-map": "^0.6.1", "write-file-atomic": "2.4.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "@jest/types": { @@ -1386,6 +1384,88 @@ "@types/yargs": "^13.0.0" } }, + "@material-ui/core": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz", + "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/styles": "^4.10.0", + "@material-ui/system": "^4.9.14", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.10.2", + "@types/react-transition-group": "^4.2.0", + "clsx": "^1.0.4", + "hoist-non-react-statics": "^3.3.2", + "popper.js": "1.16.1-lts", + "prop-types": "^15.7.2", + "react-is": "^16.8.0", + "react-transition-group": "^4.4.0" + } + }, + "@material-ui/styles": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", + "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/hash": "^0.8.0", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.9.6", + "clsx": "^1.0.4", + "csstype": "^2.5.2", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.0.3", + "jss-plugin-camel-case": "^10.0.3", + "jss-plugin-default-unit": "^10.0.3", + "jss-plugin-global": "^10.0.3", + "jss-plugin-nested": "^10.0.3", + "jss-plugin-props-sort": "^10.0.3", + "jss-plugin-rule-value-function": "^10.0.3", + "jss-plugin-vendor-prefixer": "^10.0.3", + "prop-types": "^15.7.2" + }, + "dependencies": { + "csstype": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", + "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" + } + } + }, + "@material-ui/system": { + "version": "4.9.14", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz", + "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.9.6", + "csstype": "^2.5.2", + "prop-types": "^15.7.2" + }, + "dependencies": { + "csstype": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", + "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" + } + } + }, + "@material-ui/types": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" + }, + "@material-ui/utils": { + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz", + "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==", + "requires": { + "@babel/runtime": "^7.4.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + } + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -1547,6 +1627,11 @@ "@types/yargs-parser": "*" } }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -1556,15 +1641,6 @@ "color-convert": "^2.0.1" } }, - "aria-query": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", - "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", - "requires": { - "@babel/runtime": "^7.10.2", - "@babel/runtime-corejs3": "^7.10.2" - } - }, "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", @@ -1700,6 +1776,20 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" + }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -1735,6 +1825,15 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==" }, + "@types/material-ui": { + "version": "0.21.8", + "resolved": "https://registry.npmjs.org/@types/material-ui/-/material-ui-0.21.8.tgz", + "integrity": "sha512-Rsx3tRNoYkidDKfMfh+cegtOHMl73akzKnQ5pzxTrbx5oaUXLtG6YVlvtS43uebOSTDf8GQNaseB52r3zVagEg==", + "requires": { + "@types/react": "*", + "@types/react-addons-linked-state-mixin": "*" + } + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -1769,6 +1868,14 @@ "csstype": "^3.0.2" } }, + "@types/react-addons-linked-state-mixin": { + "version": "0.14.21", + "resolved": "https://registry.npmjs.org/@types/react-addons-linked-state-mixin/-/react-addons-linked-state-mixin-0.14.21.tgz", + "integrity": "sha512-3UF7Szd3JyuU+z90kqu8L4VdDWp7SUC0eRjV2QmMEliaHODGLi5XyO5ctS50K/lG6fjC0dSAPVbvnqv0nPoGMQ==", + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "16.9.8", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", @@ -1777,6 +1884,44 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.9.tgz", + "integrity": "sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, + "@types/react-router": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz", + "integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==", + "requires": { + "@types/history": "*", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.5.tgz", + "integrity": "sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==", + "requires": { + "@types/history": "*", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "@types/react-transition-group": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", + "requires": { + "@types/react": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -1819,6 +1964,11 @@ "@types/yargs-parser": "*" } }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -2264,9 +2414,9 @@ "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "ansi-styles": { "version": "3.2.1", @@ -2299,12 +2449,12 @@ } }, "aria-query": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", - "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", "requires": { - "ast-types-flow": "0.0.7", - "commander": "^2.11.0" + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" } }, "arity-n": { @@ -2530,6 +2680,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" }, + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -3559,13 +3717,6 @@ "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", "requires": { "source-map": "~0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "clean-stack": { @@ -3630,6 +3781,11 @@ "shallow-clone": "^0.1.2" } }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4006,13 +4162,6 @@ "source-map": "^0.6.1", "source-map-resolve": "^0.5.2", "urix": "^0.1.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "css-blank-pseudo": { @@ -4120,13 +4269,15 @@ "requires": { "mdn-data": "2.0.4", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } + } + }, + "css-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "requires": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" } }, "css-what": { @@ -4241,11 +4392,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz", "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -4352,6 +4498,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "default-gateway": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", @@ -4588,6 +4739,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dom-serializer": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", @@ -4916,14 +5076,6 @@ "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true - } } }, "eslint": { @@ -5277,6 +5429,15 @@ "jsx-ast-utils": "^2.2.1" }, "dependencies": { + "aria-query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", + "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", + "requires": { + "ast-types-flow": "0.0.7", + "commander": "^2.11.0" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -6370,6 +6531,19 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "requires": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -6586,6 +6760,11 @@ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, + "hyphenate-style-name": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -6723,6 +6902,11 @@ "through": "^2.3.6" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -6969,6 +7153,11 @@ "is-extglob": "^2.1.1" } }, + "is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + }, "is-negative-zero": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", @@ -7154,13 +7343,6 @@ "make-dir": "^2.1.0", "rimraf": "^2.6.3", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "istanbul-reports": { @@ -7606,11 +7788,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" } } }, @@ -7836,6 +8013,84 @@ "verror": "1.10.0" } }, + "jss": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.4.0.tgz", + "integrity": "sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw==", + "requires": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-camel-case": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz", + "integrity": "sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw==", + "requires": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.4.0" + } + }, + "jss-plugin-default-unit": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz", + "integrity": "sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-global": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz", + "integrity": "sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-nested": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz", + "integrity": "sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz", + "integrity": "sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz", + "integrity": "sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "jss": "10.4.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz", + "integrity": "sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.4.0" + } + }, "jsx-ast-utils": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz", @@ -7920,6 +8175,11 @@ "strip-bom": "^3.0.0" } }, + "load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=" + }, "loader-fs-cache": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", @@ -8145,6 +8405,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -8284,6 +8549,15 @@ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" }, + "mini-create-react-context": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz", + "integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==", + "requires": { + "@babel/runtime": "^7.5.5", + "tiny-warning": "^1.0.3" + } + }, "mini-css-extract-plugin": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", @@ -9323,6 +9597,11 @@ "ts-pnp": "^1.1.6" } }, + "popper.js": { + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + }, "portfinder": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", @@ -9358,11 +9637,6 @@ "supports-color": "^6.1.0" }, "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -10306,13 +10580,6 @@ "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0", "react-is": "^16.8.4" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - } } }, "process": { @@ -10580,11 +10847,6 @@ "@babel/highlight": "^7.8.3" } }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, "browserslist": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.10.0.tgz", @@ -10758,11 +11020,28 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz", "integrity": "sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==" }, + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-player": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.6.2.tgz", + "integrity": "sha512-Wi9DynNSVgddKxac5OzsH0Upk6VRYssvLLGgCRw6vsjzqMX6S5N26WDRNYnLaHykxFNtpPSDc53fXDe52hMaCg==", + "requires": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + } + }, "react-redux": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz", @@ -10775,6 +11054,52 @@ "react-is": "^16.9.0" } }, + "react-router": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", + "integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "hoist-non-react-statics": "^3.1.0", + "loose-envify": "^1.3.1", + "mini-create-react-context": "^0.4.0", + "path-to-regexp": "^1.7.0", + "prop-types": "^15.6.2", + "react-is": "^16.6.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + } + } + } + }, + "react-router-dom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz", + "integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==", + "requires": { + "@babel/runtime": "^7.1.2", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + } + }, "react-scripts": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz", @@ -10835,6 +11160,17 @@ "workbox-webpack-plugin": "4.3.1" } }, + "react-transition-group": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -10906,6 +11242,11 @@ "symbol-observable": "^1.2.0" } }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -11166,6 +11507,11 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, + "resolve-pathname": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", + "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -11221,11 +11567,6 @@ "supports-color": "^6.1.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -11764,6 +12105,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -11881,9 +12227,9 @@ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.3", @@ -11904,13 +12250,6 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "source-map-url": { @@ -12170,6 +12509,11 @@ "strip-ansi": "^6.0.0" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", @@ -12309,13 +12653,6 @@ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - } } }, "strip-bom": { @@ -12482,13 +12819,6 @@ "commander": "^2.20.0", "source-map": "~0.6.1", "source-map-support": "~0.5.12" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "terser-webpack-plugin": { @@ -12577,11 +12907,6 @@ "find-up": "^4.0.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12669,6 +12994,16 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -12811,9 +13146,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==" + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==" }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", @@ -13065,6 +13400,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "value-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", + "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -13339,11 +13679,6 @@ "ajv-keywords": "^3.1.0" } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, "ssri": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", @@ -13605,13 +13940,6 @@ "requires": { "source-list-map": "^2.0.0", "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } } }, "websocket-driver": { diff --git a/package.json b/package.json index c34aeba..872c962 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,26 @@ "version": "0.1.0", "private": true, "dependencies": { + "@material-ui/core": "^4.11.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "@types/jest": "^24.9.1", + "@types/material-ui": "^0.21.8", "@types/node": "^12.12.58", "@types/react": "^16.9.49", "@types/react-dom": "^16.9.8", + "@types/react-redux": "^7.1.9", + "@types/react-router-dom": "^5.1.5", + "axios": "^0.20.0", "react": "^16.13.1", "react-dom": "^16.13.1", + "react-player": "^2.6.2", "react-redux": "^7.2.1", + "react-router-dom": "^5.2.0", "react-scripts": "3.4.3", "redux": "^4.0.5", + "redux-thunk": "^2.3.0", "typescript": "^3.7.5" }, "scripts": { diff --git a/src/App.tsx b/src/App.tsx index 483e75c..9deb5b7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,41 @@ -import React from "react"; -import logo from "./logo.svg"; -import "./App.css"; +import React, { useEffect, useState } from "react" +import "./App.css" +import NoteTaker from "./components/canvas/NoteTaker" + +import VideoList from "./components/canvas/VideoPlayer/VideoList" +import { useHistory, useLocation } from "react-router-dom" +import { useDispatch, useSelector } from "react-redux" +import { stageCurVideo } from "./store/video/actions" function App() { + const history = useHistory() + + const location = useLocation() + + const dispatch = useDispatch() + + const [num, setNum] = useState(5) + + useEffect(() => { + if (num > 0) { + setNum(num - 1) + const params = new URLSearchParams(location.search) + const VideoUrl = params.get("url") + + console.log("Hello?") + + if (VideoUrl) { + dispatch(stageCurVideo(VideoUrl)) + } + } + }, []) + return (
-

Something arbitrary

+

Noteassieur

+
- ); + ) } -export default App; +export default App diff --git a/src/components/NoteRangeSetter.tsx b/src/components/NoteRangeSetter.tsx new file mode 100644 index 0000000..f750dd0 --- /dev/null +++ b/src/components/NoteRangeSetter.tsx @@ -0,0 +1,68 @@ +import React, { useEffect, useState } from "react" +import { makeStyles } from "@material-ui/core/styles" +import Typography from "@material-ui/core/Typography" +import Slider from "@material-ui/core/Slider" +import { useDispatch, useSelector } from "react-redux" +import { selectBtnRangeVisible, selectCurTimeNoteId } from "../store/appstore/selector" +import { selectMinMaxValueById } from "../store/timenotes/selectors" +import { stageSetTimeIn, stageSetTimeOut } from "../store/timenotes/actions" +import { selectLengthOfVid } from "../store/video/selector" + +const useStyles = makeStyles({ + root: { + width: 500 + } +}) + +function valuetext(value: any) { + return `${value}°C` +} + +export default function RangeSlider() { + const show = useSelector(selectBtnRangeVisible) + const curtimenotid = useSelector(selectCurTimeNoteId) + + const inoutRange = useSelector(selectMinMaxValueById(curtimenotid)) + + console.log("I AM INOUT", inoutRange) + + const classes = useStyles() + + const lengthOfVid = useSelector(selectLengthOfVid) + + const inValue = inoutRange?.timeIn !== undefined ? inoutRange?.timeIn * lengthOfVid : 25 + const outValue = inoutRange?.timeOut !== undefined ? inoutRange.timeOut * lengthOfVid : 75 + + useEffect(() => { + setValue([inValue, outValue]) + }, [show]) + + const initialValue = [inValue, outValue] + + const [value, setValue] = useState(initialValue) + + const dispatch = useDispatch() + + const handleChange = (event: any, newValue: any) => { + dispatch(stageSetTimeIn(newValue[0] / lengthOfVid, curtimenotid)) + dispatch(stageSetTimeOut(newValue[1] / lengthOfVid, curtimenotid)) + + setValue(newValue) + } + return ( + <> + {show ? ( +
+
+ + Temperature range + + +
+
+ ) : ( + "" + )} + + ) +} diff --git a/src/components/NoteRangeToggleBtn.tsx b/src/components/NoteRangeToggleBtn.tsx new file mode 100644 index 0000000..de63c91 --- /dev/null +++ b/src/components/NoteRangeToggleBtn.tsx @@ -0,0 +1,23 @@ +import React from "react" +import { useDispatch } from "react-redux" +import { stageTimeNoteId, toggleNoteRange } from "../store/appstore/actions" + +type Props = { + timenoteId: any +} + +export default function NoteRangeToggleBtn(props: Props) { + const dispatch = useDispatch() + + function handleToggle() { + dispatch(stageTimeNoteId(parseInt(props.timenoteId))) + + dispatch(toggleNoteRange()) + } + + return ( +
+ +
+ ) +} diff --git a/src/components/NotesOnTimeLine.tsx b/src/components/NotesOnTimeLine.tsx new file mode 100644 index 0000000..24bfe5e --- /dev/null +++ b/src/components/NotesOnTimeLine.tsx @@ -0,0 +1,47 @@ +import React from "react" +import { useSelector } from "react-redux" +import { selectAllTimenotes } from "../store/timenotes/selectors" +import { selectLengthOfVid } from "../store/video/selector" + +export default function NotesOnTimeLine() { + const allTimeNotes = useSelector(selectAllTimenotes) + + const lengthOfVid = useSelector(selectLengthOfVid) + + return ( +
+ {allTimeNotes.map((timenote: any) => { + return ( +
+ {/* // make a tooltip or modal for displaying the user indications of notes */} + {/* {timenote.notes.map((note) => { + return {note.content} + })} */} + + {console.log("THIS IS TIMENOTE TIME IN: ", timenote.timeIn)} + {console.log("LEN OF VID:", lengthOfVid)} + {console.log("CALC 1", timenote.timeIn * lengthOfVid)} + + + ✍︎ + +
+ ) + })} +
+ ) +} + +// width: `${(timenote.timeOut - timenote.timeIn) * 100}px`, +// position: "absolute", +// left: `${timenote.timeIn * 100}px`, diff --git a/src/components/canvas/NoteTaker/index.tsx b/src/components/canvas/NoteTaker/index.tsx new file mode 100644 index 0000000..5d7e5b0 --- /dev/null +++ b/src/components/canvas/NoteTaker/index.tsx @@ -0,0 +1,228 @@ +import React, { useEffect, useState } from "react" +import { useDispatch, useSelector } from "react-redux" +import { Timenote } from "../../../global" +import { Typography, Slider } from "@material-ui/core" +import { stageAddNote, stageEditNote, stageNewtimenote, stageRemoveNote, stageSetTimeIn, stageSetTimeOut } from "../../../store/timenotes/actions" +import { selectAllTimenotes, selectCurTime, selectTotalTime, selectMinMaxValueById, selectTimenotes } from "../../../store/timenotes/selectors" +import { stageCurTimeOfVideo } from "../../../store/video/actions" +import Duration from "../VideoPlayer/Duration" +import VideoList from "../VideoPlayer/VideoList" +import NoteRangeToggleBtn from "../../NoteRangeToggleBtn" +import { stageTimeNoteId } from "../../../store/appstore/actions" + +// import Player from "../VideoPlayer/Index"; + +export default function NoteTaker() { + const dispatch = useDispatch() + + const curTime = useSelector(selectCurTime) + const TotalTime = useSelector(selectTotalTime) + + const notesAtPointInTime = useSelector(selectTimenotes(curTime)) + + function timelineout(e: any) { + console.log(e.target.value) + dispatch(stageCurTimeOfVideo(parseFloat(e.target.value))) + } + + function removeNote(id: number, timenoteId: number) { + dispatch(stageRemoveNote(id, timenoteId)) + } + + function newNote() { + dispatch(stageAddNote(curTime)) + } + + function newTimenote() { + dispatch(stageNewtimenote(curTime)) + } + + const buttonshow = notesAtPointInTime.length === 0 ? true : false + + const [id, setId] = useState(0) + const minMaxValue: Timenote | undefined = useSelector(selectMinMaxValueById(id)) + + console.log(minMaxValue) + + function setTimeIn(e: any) { + const timeIn = e.target.value + + dispatch(stageSetTimeIn(timeIn, id)) + } + + function setTimeOut(e: any) { + const timeOut = e.target.value + + dispatch(stageSetTimeOut(timeOut, id)) + } + + function editNote(e: React.ChangeEvent) { + const content = e.target.value + + const [noteId, timenoteId] = e.target.id.split(" ") + + dispatch(stageEditNote(content, parseInt(noteId), parseInt(timenoteId))) + } + + const allTimeNotes = useSelector(selectAllTimenotes) + + const [videoLength, setVideoLength] = useState(60 * 5) + + const [deleteBtn, setDelete] = useState(false) + const [opacityDelete, setstate] = useState(1) + + function hoverIn() { + setDelete(!deleteBtn) + } + + function hoverAway() { + setTimeout(() => { + setDelete(!deleteBtn) + }, 1000) + } + + console.log("time", curTime) + + return ( +
+
+
+ {notesAtPointInTime.map((timenote) => { + return timenote.notes.map((note) => { + return ( +
+ {note.id === 1 ?
: ""} + {deleteBtn ? ( +
+ + + +
+ ) : ( + "" + )} + +
+ ) + }) + })} + + +
+
+
+ + {/* */} + + {id !== 0 ? ( + <> +
+ + Show note at + + + Moment when note starts showing: {minMaxValue?.timeIn ? : minMaxValue?.timeIn} + + + Stop showing note at: + + + + Moment when note stops showing: + {minMaxValue?.timeIn ? : minMaxValue?.timeOut} + + + +
+
+ {allTimeNotes.map((timenote) => { + return ( +
+ {/* // make a tooltip or modal for displaying the user indications of notes */} + {/* {timenote.notes.map((note) => { + return {note.content} + })} */} + + ✍︎ + +
+ ) + })} +
+ + ) : ( + "" + )} +
+ ) +} diff --git a/src/components/canvas/VideoPlayer/Duration.tsx b/src/components/canvas/VideoPlayer/Duration.tsx new file mode 100644 index 0000000..2837c37 --- /dev/null +++ b/src/components/canvas/VideoPlayer/Duration.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +export default function Duration({ seconds }: { seconds: any }) { + return ; +} + +function format(seconds: number) { + const date = new Date(seconds * 1000); + const hh = date.getUTCHours(); + const mm = date.getUTCMinutes(); + const ss = pad(date.getUTCSeconds()); + if (hh) { + return `${hh}:${pad(mm)}:${ss}`; + } + return `${mm}:${ss}`; +} + +function pad(string: number) { + return ("0" + string).slice(-2); +} diff --git a/src/components/canvas/VideoPlayer/Index.tsx b/src/components/canvas/VideoPlayer/Index.tsx new file mode 100644 index 0000000..017f604 --- /dev/null +++ b/src/components/canvas/VideoPlayer/Index.tsx @@ -0,0 +1,192 @@ +import { Button, Container } from "@material-ui/core" +import React, { useState, useRef, useEffect } from "react" +import ReactPlayer from "react-player" +// import VideoList from "./VideoList"; +import Duration from "./Duration" + +import { createStyles, makeStyles, Theme } from "@material-ui/core/styles" +import Paper from "@material-ui/core/Paper" +import Grid from "@material-ui/core/Grid" +import { useDispatch, useSelector } from "react-redux" +import { selectMyVideo } from "../../../store/video/selector" +import { stageCurTimeOfVideo, stageTotalTimeOfVideo } from "../../../store/video/actions" +import { selectCurTime } from "../../../store/timenotes/selectors" + +import NoteTaker from "../NoteTaker" + +import { stageAddNote } from "../../../store/timenotes/actions" +import NoteRangeSetter from "../../NoteRangeSetter" +import NotesOnTimeLine from "../../NotesOnTimeLine" + +function Player() { + const [played, setPlayed] = useState(0) + const ref = useRef(null) + const [, setSeeking] = useState(true) + const [playing, setPlaying] = useState(false) + const [volume, setVolume] = useState(0.8) + const [duration, setDuration] = useState(1) + const [note, setNote] = useState(0) + + function handleSeekChange(e: any) { + console.log("waarde", parseFloat(e.target.value)) + setPlayed(parseFloat(e.target.value)) + } + + function handleSeekMouseUp(e: any) { + setSeeking(false) + console.log("önchange", e.target.value) + ref?.current?.seekTo(parseFloat(e.target.value)) + } + + function handlePlayPause() { + setPlaying(!playing) + } + + function handleVolumeChange(e: any) { + setVolume(parseFloat(e.target.value)) + } + + function handleDuration(duration: number) { + console.log("duration", duration) + setDuration(duration) + } + + function handleProgress(data: any) { + setPlayed(data.played) + } + + function handleAddNote() { + setPlaying(false) + // setNote(Math.round(duration * played)); + dispatch(stageAddNote(curTime)) + } + + const dispatch = useDispatch() + + useEffect(() => { + const curTime = played + const TotalTime = Math.round(duration) + dispatch(stageCurTimeOfVideo(curTime)) + dispatch(stageTotalTimeOfVideo(TotalTime)) + }, [played]) + + const curTime = useSelector(selectCurTime) + + const gridStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + flexGrow: 1 + }, + paper: { + padding: theme.spacing(2), + textAlign: "center", + color: theme.palette.text.secondary + } + }) + ) + + const buttonStyles = makeStyles((theme: Theme) => + createStyles({ + button: { + margin: theme.spacing(1) + } + }) + ) + + const gridClass = gridStyles() + const buttonClass = buttonStyles() + + const myVideo = useSelector(selectMyVideo) + + return ( + + + + + + + + + + + + + + + + {/* This Button uses a Font Icon, see the installation instructions in the Icon component docs. */} + +

+ +

+ + {playing ? ( +

+ + +

+ ) : ( +

+ +

+ )} +

+ Volume +

+ + {/*

+ Jump to{" "} + +

*/} + +

+ duration +

+

+ elapsed +

+

+ remaining +

+ +

Note Time: {note}

+
+
+
+
+ ) +} + +export default Player diff --git a/src/components/canvas/VideoPlayer/VideoList.tsx b/src/components/canvas/VideoPlayer/VideoList.tsx new file mode 100644 index 0000000..33ef6ea --- /dev/null +++ b/src/components/canvas/VideoPlayer/VideoList.tsx @@ -0,0 +1,135 @@ +import React, { useState, useEffect } from "react" +import axios from "axios" +import Player from "./Index" +import { makeStyles } from "@material-ui/core/styles" +import Card from "@material-ui/core/Card" + +import CardActions from "@material-ui/core/CardActions" +import CardContent from "@material-ui/core/CardContent" +import { stageCurVideo } from "../../../store/video/actions" +import Button from "@material-ui/core/Button" +import Typography from "@material-ui/core/Typography" +import Grid from "@material-ui/core/Grid" +import { Container, Input } from "@material-ui/core" +import { VideoId } from "../../../global" +import { useDispatch } from "react-redux" +function VideoList() { + const [data, setData] = useState([]) + const [search, setSearch] = useState("React Native") + + const [videoId, setVideoId] = useState("") + + const apiKey = "AIzaSyC416j2n68IPRe62Am2UuCowF007P0tco0" + + function handleSubmit(e: any) { + e.preventDefault() + + fetchData() + } + + function onChange(e: any) { + e.preventDefault() + setSearch(e.target.value) + } + + const fetchData = async () => { + try { + const result = await axios(`https://www.googleapis.com/youtube/v3/search?part=snippet&q=${search}&key=${apiKey}`) + + console.log("THIS IS FETCH FROM YOUTUBE:", result) + setData(result.data.items) + } catch (error) { + console.log(error.message) + } + } + + useEffect(() => { + fetchData() + }, []) + + const dispatch = useDispatch() + + useEffect(() => { + const curVideo = videoId + + dispatch(stageCurVideo(curVideo)) + }, [videoId]) + + const useStyles = makeStyles({ + root: { + maxWidth: 345 + }, + media: { + height: 140 + } + }) + + const classes = useStyles() + + const videos: any = ( + <> + + {data.map((video: any) => { + return ( + + + + + {video.snippet.title} + + + {video.snippet.title.slice(0, 30)} + + {video.snippet.title} + + + + + + + + ) + })} + + + ) + + // const videoPlay: any = !data ? ( + //

loading

+ // ) : ( + // data.map((video: any) => { + // return video.id.videoId; + // }) + // ); + + return ( + <> + +
+ + + +
+
+ + +

{videos}

+ + ) +} + +export default VideoList diff --git a/src/global.ts b/src/global.ts new file mode 100644 index 0000000..39ca7bd --- /dev/null +++ b/src/global.ts @@ -0,0 +1,25 @@ +export type Note = { + id: number + content: string +} + +export type Timenote = { + id: number + timeIn: number + timeOut: number + notes: Note[] +} + +export type VideoId = string + +export type Video = { + title: string + url: VideoId + lengthOfVid: number + curTimeOfVideo: number +} + +export type Appstore = { + btnRangeVisible: boolean + curTimeNoteId: number | null +} diff --git a/src/global_types.d.ts b/src/global_types.d.ts new file mode 100644 index 0000000..1c8b041 --- /dev/null +++ b/src/global_types.d.ts @@ -0,0 +1 @@ +type textAreaOnChange = React.ChangeEvent diff --git a/src/index.tsx b/src/index.tsx index f5185c1..26e77dd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,17 +1,22 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import * as serviceWorker from './serviceWorker'; +import React from "react" +import ReactDOM from "react-dom" +import "./index.css" +import App from "./App" +import * as serviceWorker from "./serviceWorker" +import { Provider } from "react-redux" +import store from "./store/index" +import { BrowserRouter as Router } from "react-router-dom" ReactDOM.render( - - - , - document.getElementById('root') -); + + + + + , + document.getElementById("root") +) // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); +serviceWorker.unregister() diff --git a/src/store/appstore/actions.ts b/src/store/appstore/actions.ts new file mode 100644 index 0000000..d68a998 --- /dev/null +++ b/src/store/appstore/actions.ts @@ -0,0 +1,19 @@ +import { Dispatch } from "redux" +import { CURTIMENOTE_ID, TOGGLE_RANGE_BTN } from "./types" + +export function toggleNoteRange() { + return (dispatch: Dispatch) => { + dispatch({ + type: TOGGLE_RANGE_BTN + }) + } +} + +export function stageTimeNoteId(curTimeNoteId: number | string) { + return (dispatch: Dispatch) => { + dispatch({ + type: CURTIMENOTE_ID, + curTimeNoteId + }) + } +} diff --git a/src/store/appstore/reducer.ts b/src/store/appstore/reducer.ts new file mode 100644 index 0000000..2da01c4 --- /dev/null +++ b/src/store/appstore/reducer.ts @@ -0,0 +1,20 @@ +import { Appstore } from "../../global" +import { CURTIMENOTE_ID, myAppStoreActionTypes, TOGGLE_RANGE_BTN } from "./types" + +const initialState: Appstore = { + btnRangeVisible: false, + curTimeNoteId: null +} + +export default (state = initialState, action: myAppStoreActionTypes) => { + switch (action.type) { + case TOGGLE_RANGE_BTN: + return { ...state, btnRangeVisible: !state.btnRangeVisible } + + case CURTIMENOTE_ID: + return { ...state, curTimeNoteId: action.curTimeNoteId } + + default: + return state + } +} diff --git a/src/store/appstore/selector.ts b/src/store/appstore/selector.ts new file mode 100644 index 0000000..ffac62e --- /dev/null +++ b/src/store/appstore/selector.ts @@ -0,0 +1,9 @@ +import { StoreState } from "../types" + +export const selectBtnRangeVisible = (state: StoreState) => { + return state.appstore.btnRangeVisible +} + +export const selectCurTimeNoteId = (state: StoreState) => { + return state.appstore.curTimeNoteId +} diff --git a/src/store/appstore/types.ts b/src/store/appstore/types.ts new file mode 100644 index 0000000..4aca247 --- /dev/null +++ b/src/store/appstore/types.ts @@ -0,0 +1,13 @@ +export const TOGGLE_RANGE_BTN = "TOGGLE_RANGE_BTN" +export const CURTIMENOTE_ID = "CURTIMENOTE_ID" + +type ToggleRangeBtn = { + type: typeof TOGGLE_RANGE_BTN +} + +type CurTimeNoteId = { + type: typeof CURTIMENOTE_ID + curTimeNoteId: number +} + +export type myAppStoreActionTypes = ToggleRangeBtn | CurTimeNoteId diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..2296b99 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,11 @@ +import { createStore, applyMiddleware, compose } from "redux" +import ReduxThunk from "redux-thunk" +import reducer from "./rootReducer" + +// @ts-ignore +const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose +const enhancer = composeEnhancers(applyMiddleware(ReduxThunk)) + +const store = createStore(reducer, enhancer) + +export default store diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts new file mode 100644 index 0000000..1f71c32 --- /dev/null +++ b/src/store/rootReducer.ts @@ -0,0 +1,10 @@ +import { combineReducers } from "redux" +import timenotes from "./timenotes/reducer" +import video from "./video/reducer" +import appstore from "./appstore/reducer" + +export default combineReducers({ + timenotes, + video, + appstore +}) diff --git a/src/store/timenotes/actions.ts b/src/store/timenotes/actions.ts new file mode 100644 index 0000000..dafaece --- /dev/null +++ b/src/store/timenotes/actions.ts @@ -0,0 +1,105 @@ +import { Dispatch } from "redux" +import { GetState } from "../types" +import { CHANGE_NOTE_BY_ID, NEW_NOTE_AT_CUR_TIME, REMOVE_NOT_BY_IDS, SET_TIME_IN_BY_ID, SET_TIME_OUT_BY_ID, STORE_A_NOTE_BY_TIMENOTE_ID } from "./types" + +export function stageRemoveNote(id: number, timenoteId: number) { + return (dispatch: Dispatch, getState: GetState) => { + dispatch(removeNoteByIds(id, timenoteId)) + } +} + +function removeNoteByIds(id: number, timenoteId: number) { + return { + type: REMOVE_NOT_BY_IDS, + id, + timenoteId + } +} + +export function stageNewtimenote(curtime: number) { + return (dispatch: Dispatch, getState: GetState) => { + const HighestId = getState().timenotes.reduce((accumulator: any, timenote) => { + if (timenote.id > accumulator) { + return timenote.id + } + return accumulator + }, 0) + + const newId = HighestId + 1 + + dispatch({ + type: NEW_NOTE_AT_CUR_TIME, + curtime, + newId + }) + } +} + +export function stageAddNote(timeLineValue: number) { + return (dispatch: Dispatch, GetState: GetState) => { + const timenoteMatchingTimecode = GetState().timenotes.filter((timenote) => { + const startsAfter = timenote.timeIn <= timeLineValue + const endsBefore = timenote.timeOut >= timeLineValue + const isInside = startsAfter && endsBefore + return isInside + }) + console.log(timeLineValue) + console.log("this is it:", timenoteMatchingTimecode[0].id) + + const timenoteId = timenoteMatchingTimecode[0].id + + const highestID = timenoteMatchingTimecode[0].notes.reduce((accumulator: any, note) => { + if (note.id > accumulator) { + return note.id + } + return accumulator + }, 0) + + dispatch(storeANoteByTimenoteId(timenoteId, highestID + 1)) + } +} + +function storeANoteByTimenoteId(timenoteId: Number, highestID: number) { + return { + type: STORE_A_NOTE_BY_TIMENOTE_ID, + timenoteId, + highestID + } +} + +export function stageSetTimeIn(timeIn: number, timenoteId: number | null) { + return (dispatch: Dispatch) => { + console.log(timeIn) + console.log(timenoteId) + + dispatch({ + type: SET_TIME_IN_BY_ID, + timeIn, + timenoteId + }) + } +} + +export function stageSetTimeOut(timeOut: number, timenoteId: number | null) { + return (dispatch: Dispatch) => { + console.log(timeOut) + console.log(timenoteId) + + dispatch({ + type: SET_TIME_OUT_BY_ID, + timeOut, + timenoteId + }) + } +} + +export function stageEditNote(content: string, id: number, timenoteId: number) { + return (dispatch: Dispatch) => { + dispatch({ + type: CHANGE_NOTE_BY_ID, + content, + id, + timenoteId + }) + } +} diff --git a/src/store/timenotes/reducer.ts b/src/store/timenotes/reducer.ts new file mode 100644 index 0000000..a382462 --- /dev/null +++ b/src/store/timenotes/reducer.ts @@ -0,0 +1,101 @@ +import { Timenote } from "../../global" +import { CHANGE_NOTE_BY_ID, myNoteActionTypes, NEW_NOTE_AT_CUR_TIME, REMOVE_NOT_BY_IDS, SET_TIME_IN_BY_ID, SET_TIME_OUT_BY_ID, STORE_A_NOTE_BY_TIMENOTE_ID } from "./types" + +const initialState: Timenote[] = [] + +export default (state = initialState, action: myNoteActionTypes) => { + switch (action.type) { + case NEW_NOTE_AT_CUR_TIME: + return [ + ...state, + { + id: action.newId, + timeIn: action.curtime, + timeOut: action.curtime + 0.1, + notes: [ + { + id: 1, + content: "Here your first note! 😜" + } + ] + } + ] + + case REMOVE_NOT_BY_IDS: + return state.map((timenote: Timenote) => { + if (timenote.id === action.timenoteId) { + return { + ...timenote, + notes: timenote.notes.filter((note) => note.id !== action.id) + } + } else { + return timenote + } + }) + + case STORE_A_NOTE_BY_TIMENOTE_ID: + return state.map((timenote: Timenote) => { + if (timenote.id === action.timenoteId) { + return { + ...timenote, + notes: [ + ...timenote.notes, + { + id: action.highestID, + content: `note ${action.highestID}` + } + ] + } + } else { + return timenote + } + }) + + case SET_TIME_IN_BY_ID: + return state.map((timenote: Timenote) => { + if (timenote.id === action.timenoteId) { + return { + ...timenote, + timeIn: action.timeIn + } + } else { + return timenote + } + }) + + case SET_TIME_OUT_BY_ID: + return state.map((timenote: Timenote) => { + if (timenote.id === action.timenoteId) { + return { + ...timenote, + timeOut: action.timeOut + } + } else { + return timenote + } + }) + + case CHANGE_NOTE_BY_ID: + return state.map((timenote: Timenote) => { + if (timenote.id === action.timenoteId) { + return { + ...timenote, + notes: [ + ...timenote.notes.map((note) => { + if (note.id === action.id) { + return { ...note, content: action.content } + } else { + return { ...note } + } + }) + ] + } + } else { + return timenote + } + }) + + default: + return state + } +} diff --git a/src/store/timenotes/selectors.ts b/src/store/timenotes/selectors.ts new file mode 100644 index 0000000..0c09431 --- /dev/null +++ b/src/store/timenotes/selectors.ts @@ -0,0 +1,36 @@ +import { StoreState } from "../types"; + +export const selectTimenotes = (timecode: any) => { + return (state: StoreState) => { + return state.timenotes.filter((timenote) => { + const startsAfter = timenote.timeIn <= timecode; + const endsBefore = timenote.timeOut >= timecode; + const isInside = startsAfter && endsBefore; + return isInside; + }); + }; +}; + +export const selectMinMaxValueById = (id: number | null) => { + return (state: StoreState) => { + return state.timenotes.find((timenote) => { + if (timenote.id === id) { + return timenote; + } + }); + + console.log(id); + }; +}; + +export const selectAllTimenotes = (state: StoreState) => { + return state.timenotes; +}; + +export const selectCurTime = (state: StoreState) => { + return state.video.curTimeOfVideo; +}; + +export const selectTotalTime = (state: StoreState) => { + return state.video.lengthOfVid; +}; diff --git a/src/store/timenotes/types.ts b/src/store/timenotes/types.ts new file mode 100644 index 0000000..02d71a7 --- /dev/null +++ b/src/store/timenotes/types.ts @@ -0,0 +1,47 @@ +export const STORE_NOTE = "STORE_NOTE" + +export const REMOVE_NOT_BY_IDS = "REMOVE_NOT_BY_IDS" +export const CHANGE_NOTE_BY_ID = "CHANGE_NOTE_BY_ID" +export const STORE_A_NOTE_BY_TIMENOTE_ID = "STORE_A_NOTE_BY_TIMENOTE_ID" +export const SET_TIME_IN_BY_ID = "SET_TIME_IN_BY_ID" +export const SET_TIME_OUT_BY_ID = "SET_TIME_OUT_BY_ID" +export const NEW_NOTE_AT_CUR_TIME = "NEW_NOTE_AT_CUR_TIME" + +type RemoveNoteByIds = { + type: typeof REMOVE_NOT_BY_IDS + id: number + timenoteId: number +} + +type StoreANotByTimenoteId = { + type: typeof STORE_A_NOTE_BY_TIMENOTE_ID + timenoteId: number + highestID: number +} + +type SetTimeInById = { + type: typeof SET_TIME_IN_BY_ID + timenoteId: number + timeIn: number +} + +type SetTimeOutById = { + type: typeof SET_TIME_OUT_BY_ID + timenoteId: number + timeOut: number +} + +type ChangeNoteById = { + type: typeof CHANGE_NOTE_BY_ID + id: number + timenoteId: number + content: string +} + +type NewNoteAtCurTime = { + type: typeof NEW_NOTE_AT_CUR_TIME + curtime: number + newId: number +} + +export type myNoteActionTypes = RemoveNoteByIds | StoreANotByTimenoteId | SetTimeInById | SetTimeOutById | ChangeNoteById | NewNoteAtCurTime diff --git a/src/store/types.ts b/src/store/types.ts new file mode 100644 index 0000000..b63ff88 --- /dev/null +++ b/src/store/types.ts @@ -0,0 +1,9 @@ +import { Appstore, Timenote, Video } from "../global" + +export type StoreState = { + timenotes: Timenote[] + video: Video + appstore: Appstore +} + +export type GetState = () => StoreState diff --git a/src/store/video/actions.ts b/src/store/video/actions.ts new file mode 100644 index 0000000..3754316 --- /dev/null +++ b/src/store/video/actions.ts @@ -0,0 +1,34 @@ +import { Dispatch } from "redux"; +import { GetState } from "../types"; +import { STORE_CURTIME, STORE_TOTALTIME, STORE_CURVIDEO } from "./types"; + +export function stageCurTimeOfVideo(curtime: number) { + return (dispatch: Dispatch, getState: GetState) => { + dispatch({ + type: STORE_CURTIME, + curtime, + }); + }; +} + + +export function stageTotalTimeOfVideo(totaltime: number) { + return (dispatch: Dispatch, getState: GetState) => { + dispatch({ + type: STORE_TOTALTIME, + totaltime, + }); + }; +} + +export function stageCurVideo(curvideo: string) { + return (dispatch: Dispatch, getState: GetState) => { + dispatch({ + type: STORE_CURVIDEO, + curvideo, + }); + }; +} + + + diff --git a/src/store/video/reducer.ts b/src/store/video/reducer.ts new file mode 100644 index 0000000..20ea24c --- /dev/null +++ b/src/store/video/reducer.ts @@ -0,0 +1,31 @@ +import { Video } from "../../global"; +import { + myVideoActionTypes, + STORE_CURTIME, + STORE_TOTALTIME, + STORE_CURVIDEO, +} from "./types"; + +const initialState: Video = { + title: "test video", + url: "", // some video + lengthOfVid: 200, + curTimeOfVideo: 0.0, + // lengthOfVid: 200 // this should be dynamically +}; + +export default (state = initialState, action: myVideoActionTypes) => { + switch (action.type) { + case STORE_CURTIME: + return { ...state, curTimeOfVideo: action.curtime }; + + case STORE_TOTALTIME: + return { ...state, lengthOfVid: action.totaltime }; + + case STORE_CURVIDEO: + return { ...state, url: action.curvideo }; + + default: + return state; + } +}; diff --git a/src/store/video/selector.ts b/src/store/video/selector.ts new file mode 100644 index 0000000..5d485b9 --- /dev/null +++ b/src/store/video/selector.ts @@ -0,0 +1,9 @@ +import { StoreState } from "../types" + +export const selectMyVideo = (state: StoreState) => { + return state.video +} + +export const selectLengthOfVid = (state: StoreState) => { + return state.video.lengthOfVid +} diff --git a/src/store/video/types.ts b/src/store/video/types.ts new file mode 100644 index 0000000..1eb8076 --- /dev/null +++ b/src/store/video/types.ts @@ -0,0 +1,22 @@ +import { constants } from "http2"; + +export const STORE_CURTIME = "STORE_CURTIME"; +export const STORE_TOTALTIME = "STORE_TOTALTIME"; +export const STORE_CURVIDEO = "STORE_CURVIDEO"; + +type StoreCurtime = { + type: typeof STORE_CURTIME; + curtime: string; +}; + +type StoreTotalTime = { + type: typeof STORE_TOTALTIME; + totaltime: string; +}; + +type StoreCurVideo = { + type: typeof STORE_CURVIDEO; + curvideo: string; +}; + +export type myVideoActionTypes = StoreCurtime | StoreTotalTime | StoreCurVideo;