diff --git a/.codeclimate.yml b/.codeclimate.yml
index 0870063..9a947e2 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -1,4 +1,43 @@
version: "2"
+plugins:
+ csslint:
+ enabled: true
+ coffeelint:
+ enabled: true
+ duplication:
+ enabled: true
+ config:
+ languages:
+ - ruby
+ - javascript
+ - python
+ - php
+ eslint:
+ enabled: true
+ channel: __ESLINT_CHANNEL__
+ fixme:
+ enabled: true
+ rubocop:
+ enabled: true
+exclude_patterns:
+ - config/
+ - db/
+ - dist/
+ - features/
+ - "**/node_modules/"
+ - script/
+ - "**/spec/"
+ - "**/test/"
+ - "**/tests/"
+ - Tests/
+ - "**/vendor/"
+ - "**/*_test.go"
+ - "**/*.d.ts"
+ - "**/*.min.js"
+ - "**/*.min.css"
+ - "**/__tests__/"
+ - "**/__mocks__/"
+ - "**/*.test.js"
checks:
argument-count:
config:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2c95f53..15ece61 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,6 +21,7 @@ prepare:
stage: prepare
script:
- npm install --progress=false
+ - npm update --progress=false
build:
stage: build
diff --git a/api/src/handlers/Settings.php b/api/src/handlers/Settings.php
index 7ef4412..21f3ef8 100644
--- a/api/src/handlers/Settings.php
+++ b/api/src/handlers/Settings.php
@@ -14,12 +14,35 @@ class Settings extends RequestBase {
/**
* handle settings stuff to load from db
*/
- private function getFromDB(){
+ private function getFromDB() {
/**
* load currently set settings form db for init of settings page
*/
$this->addActionHandler("loadGeneralSettings", function () {
- $query = "SELECT * from settings";
+ // query settings and infotile values
+ $query = "
+ SELECT (
+ SELECT COUNT(*)
+ FROM videos
+ ) AS videonr,
+ (
+ SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS Size
+ FROM information_schema.TABLES
+ WHERE TABLE_SCHEMA = '" . Database::getInstance()->getDatabaseName() . "'
+ GROUP BY table_schema
+ ) AS dbsize,
+ (
+ SELECT COUNT(*)
+ FROM tags
+ ) AS difftagnr,
+ (
+ SELECT COUNT(*)
+ FROM video_tags
+ ) AS tagsadded,
+ settings.*
+ FROM settings
+ LIMIT 1
+ ";
$result = $this->conn->query($query);
@@ -51,7 +74,7 @@ class Settings extends RequestBase {
/**
* handle setting stuff to save to db
*/
- private function saveToDB(){
+ private function saveToDB() {
/**
* save changed settings to db
*/
diff --git a/package.json b/package.json
index c13873a..8dc0e34 100644
--- a/package.json
+++ b/package.json
@@ -3,12 +3,16 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "bootstrap": "^4.5.0",
+ "@fortawesome/fontawesome-svg-core": "^1.2.30",
+ "@fortawesome/free-regular-svg-icons": "^5.15.1",
+ "@fortawesome/free-solid-svg-icons": "^5.15.1",
+ "@fortawesome/react-fontawesome": "^0.1.11",
+ "bootstrap": "^4.5.3",
"plyr-react": "^2.2.0",
- "react": "^16.13.1",
- "react-bootstrap": "^1.0.1",
- "react-dom": "^16.13.1",
- "react-scripts": "^3.4.1"
+ "react": "^16.14.0",
+ "react-bootstrap": "^1.3.0",
+ "react-dom": "^16.14.0",
+ "react-scripts": "^3.4.3"
},
"scripts": {
"start": "react-scripts start",
@@ -49,7 +53,7 @@
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"enzyme": "^3.11.0",
- "enzyme-adapter-react-16": "^1.15.2",
+ "enzyme-adapter-react-16": "^1.15.5",
"jest-junit": "^10.0.0"
}
}
diff --git a/src/App.module.css b/src/App.module.css
index b3e63ec..10a8a7e 100644
--- a/src/App.module.css
+++ b/src/App.module.css
@@ -37,9 +37,8 @@
}
.navcontainer {
- border-bottom-width: 2px;
+ border-width: 0 0 2px 0;
border-style: dotted;
- border-width: 0;
padding-bottom: 40px;
padding-top: 20px;
diff --git a/src/elements/AddTagPopup/AddTagPopup.test.js b/src/elements/AddTagPopup/AddTagPopup.test.js
index 9728dbd..5adb711 100644
--- a/src/elements/AddTagPopup/AddTagPopup.test.js
+++ b/src/elements/AddTagPopup/AddTagPopup.test.js
@@ -39,7 +39,7 @@ describe('', function () {
global.fetch = prepareFetchApi({result: "success"});
wrapper.setProps({
- submit: jest.fn((arg1, arg2) => {}),
+ submit: jest.fn(() => {}),
onHide: jest.fn()
}, () => {
wrapper.instance().addTag(1, "test");
@@ -62,7 +62,7 @@ describe('', function () {
global.fetch = prepareFetchApi({result: "fail"});
wrapper.setProps({
- submit: jest.fn((arg1, arg2) => {}),
+ submit: jest.fn(() => {}),
onHide: jest.fn()
}, () => {
wrapper.instance().addTag(1, "test");
diff --git a/src/elements/InfoHeaderItem/InfoHeaderItem.js b/src/elements/InfoHeaderItem/InfoHeaderItem.js
new file mode 100644
index 0000000..00a95db
--- /dev/null
+++ b/src/elements/InfoHeaderItem/InfoHeaderItem.js
@@ -0,0 +1,34 @@
+import React from "react";
+import style from './InfoHeaderItem.module.css';
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {Spinner} from "react-bootstrap";
+
+/**
+ * a component to display one of the short quickinfo tiles on dashboard
+ */
+class InfoHeaderItem extends React.Component {
+ render() {
+ return (
+
{
+ // call clicklistener if defined
+ if (this.props.onClick != null) this.props.onClick();
+ }} className={style.infoheaderitem} style={{backgroundColor: this.props.backColor}}>
+
+
+
+ {this.props.text !== null && this.props.text !== undefined ?
+ <>
+
{this.props.text}
+
{this.props.subtext}
+ >
+ :
+ }
+
+ );
+ }
+}
+
+export default InfoHeaderItem;
diff --git a/src/elements/InfoHeaderItem/InfoHeaderItem.module.css b/src/elements/InfoHeaderItem/InfoHeaderItem.module.css
new file mode 100644
index 0000000..d67f20b
--- /dev/null
+++ b/src/elements/InfoHeaderItem/InfoHeaderItem.module.css
@@ -0,0 +1,58 @@
+/* styling for tile */
+.infoheaderitem {
+ background-color: lightblue;
+ border-radius: 5px;
+ flex: calc(25% - 10px);
+ margin: 5px;
+}
+
+/* On screens that are 1317px wide or less, go from four columns to two columns */
+@media screen and (max-width: 1317px) {
+ .infoheaderitem {
+ flex: calc(50% - 10px);
+ }
+}
+
+/* change opacity of icon when hovering whole tile */
+.infoheaderitem:hover .icon {
+ opacity: 0.75;
+ transition: opacity linear 0.4s;
+}
+
+/* change cursor on hover */
+.infoheaderitem:hover {
+ cursor: pointer;
+}
+
+.icon {
+ float: left;
+ height: 130px;
+ margin-left: 5px;
+ margin-right: 17px;
+ margin-top: 20px;
+ opacity: 0.5;
+ text-align: center;
+
+ width: 30%;
+}
+
+/* big main text in tile */
+.maintext {
+ font-size: x-large;
+ font-weight: bold;
+ margin-top: 30px;
+}
+
+/* small subtext in tile */
+.subtext {
+ font-size: large;
+ margin-top: 5px;
+ opacity: 0.7;
+}
+
+.loadAnimation {
+ display: inline-block;
+ line-height: 145px;
+ margin-left: calc(25% - 15px);
+ vertical-align: middle;
+}
diff --git a/src/elements/InfoHeaderItem/InfoHeaderItem.test.js b/src/elements/InfoHeaderItem/InfoHeaderItem.test.js
new file mode 100644
index 0000000..e20a581
--- /dev/null
+++ b/src/elements/InfoHeaderItem/InfoHeaderItem.test.js
@@ -0,0 +1,43 @@
+import {shallow} from "enzyme";
+import React from "react";
+import InfoHeaderItem from "./InfoHeaderItem";
+
+describe('', function () {
+ it('renders without crashing ', function () {
+ const wrapper = shallow();
+ wrapper.unmount();
+ });
+
+ it('renders correct text', function () {
+ const wrapper = shallow();
+ expect(wrapper.find(".maintext").text()).toBe("mytext");
+ });
+
+ it('renders correct subtext', function () {
+ const wrapper = shallow();
+ expect(wrapper.find(".subtext").text()).toBe("testtext");
+ });
+
+ it('test no subtext if no text defined', function () {
+ const wrapper = shallow();
+ expect(wrapper.find(".subtext")).toHaveLength(0);
+ });
+
+ it('test custom click handler', function () {
+ const func = jest.fn();
+ const wrapper = shallow( func()}/>);
+ expect(func).toBeCalledTimes(0);
+ wrapper.simulate("click");
+ expect(func).toBeCalledTimes(1);
+ });
+
+ it('test insertion of loading spinner', function () {
+ const wrapper = shallow();
+ expect(wrapper.find("Spinner").length).toBe(1);
+ });
+
+ it('test loading spinner if undefined', function () {
+ const wrapper = shallow();
+ expect(wrapper.find("Spinner").length).toBe(1);
+ });
+});
diff --git a/src/elements/NewTagPopup/NewTagPopup.js b/src/elements/NewTagPopup/NewTagPopup.js
index bbe6a04..481cfe1 100644
--- a/src/elements/NewTagPopup/NewTagPopup.js
+++ b/src/elements/NewTagPopup/NewTagPopup.js
@@ -18,21 +18,21 @@ class NewTagPopup extends React.Component {
-
+
Create a new Tag!
Tag Name:
- {
+ {
this.value = v.target.value
}}/>
-
+
This Tag will automatically show up on category page.
diff --git a/src/elements/Preview/Preview.js b/src/elements/Preview/Preview.js
index ff3aa8b..066d819 100644
--- a/src/elements/Preview/Preview.js
+++ b/src/elements/Preview/Preview.js
@@ -48,7 +48,7 @@ class Preview extends React.Component {
:
- }
+ }
diff --git a/src/elements/Tag/Tag.js b/src/elements/Tag/Tag.js
index 21c73ce..330b981 100644
--- a/src/elements/Tag/Tag.js
+++ b/src/elements/Tag/Tag.js
@@ -10,7 +10,7 @@ class Tag extends React.Component {
render() {
return (
+ data-testid='Test-Tag'>{this.props.children}
);
}
diff --git a/src/pages/CategoryPage/CategoryPage.js b/src/pages/CategoryPage/CategoryPage.js
index d10b834..b89b143 100644
--- a/src/pages/CategoryPage/CategoryPage.js
+++ b/src/pages/CategoryPage/CategoryPage.js
@@ -84,7 +84,7 @@ class CategoryPage extends React.Component {
: null}
-
> :
diff --git a/src/pages/CategoryPage/CategoryPage.test.js b/src/pages/CategoryPage/CategoryPage.test.js
index 5f806d7..fdafef3 100644
--- a/src/pages/CategoryPage/CategoryPage.test.js
+++ b/src/pages/CategoryPage/CategoryPage.test.js
@@ -98,7 +98,7 @@ describe('', function () {
const func = jest.fn();
CategoryPage.prototype.fetchVideoData = func;
- shallow();
+ shallow();
expect(func).toBeCalledTimes(1);
});
@@ -106,7 +106,7 @@ describe('', function () {
it('test sidebar tag clicks', function () {
const func = jest.fn();
- const wrapper = mount();
+ const wrapper = mount();
wrapper.instance().loadTag = func;
console.log(wrapper.debug());
diff --git a/src/pages/HomePage/HomePage.js b/src/pages/HomePage/HomePage.js
index cd05329..4192613 100644
--- a/src/pages/HomePage/HomePage.js
+++ b/src/pages/HomePage/HomePage.js
@@ -131,12 +131,12 @@ class HomePage extends React.Component {
e.preventDefault();
this.searchVideos(this.keyword);
}}>
- {
this.keyword = e.target.value
}}/>
-
+
diff --git a/src/pages/Player/Player.js b/src/pages/Player/Player.js
index faaa564..d8186a3 100644
--- a/src/pages/Player/Player.js
+++ b/src/pages/Player/Player.js
@@ -126,7 +126,7 @@ class Player extends React.Component {
{this.state.quality}p Quality! : null}
{this.state.length !== 0 ?
{Math.round(this.state.length / 60)} Minutes of
- length! : null}
+ length! : null}
Tags:
{this.state.tags.map((m) => (
@@ -167,8 +167,10 @@ class Player extends React.Component {
not loaded yet
}
-
-
+
+
diff --git a/src/pages/Player/Player.module.css b/src/pages/Player/Player.module.css
index 6a21fec..cd686b3 100644
--- a/src/pages/Player/Player.module.css
+++ b/src/pages/Player/Player.module.css
@@ -12,7 +12,6 @@
display: block;
float: left;
margin-left: 20px;
- margin-top: 25px;
margin-top: 20px;
width: 60%;
}
diff --git a/src/pages/RandomPage/RandomPage.js b/src/pages/RandomPage/RandomPage.js
index e360889..8613629 100644
--- a/src/pages/RandomPage/RandomPage.js
+++ b/src/pages/RandomPage/RandomPage.js
@@ -25,9 +25,8 @@ class RandomPage extends React.Component {
render() {
return (
-
+
Visible Tags:
diff --git a/src/pages/SettingsPage/GeneralSettings.js b/src/pages/SettingsPage/GeneralSettings.js
index d2443de..a3d8e90 100644
--- a/src/pages/SettingsPage/GeneralSettings.js
+++ b/src/pages/SettingsPage/GeneralSettings.js
@@ -2,6 +2,9 @@ import React from "react";
import {Button, Col, Form} from "react-bootstrap";
import style from "./GeneralSettings.module.css"
import GlobalInfos from "../../GlobalInfos";
+import InfoHeaderItem from "../../elements/InfoHeaderItem/InfoHeaderItem";
+import {faArchive, faBalanceScaleLeft, faRulerVertical} from "@fortawesome/free-solid-svg-icons";
+import {faAddressCard} from "@fortawesome/free-regular-svg-icons";
/**
* Component for Generalsettings tag on Settingspage
@@ -18,7 +21,12 @@ class GeneralSettings extends React.Component {
videopath: "",
tvshowpath: "",
mediacentername: "",
- password: ""
+ password: "",
+
+ videonr: null,
+ dbsize: null,
+ difftagnr: null,
+ tagsadded: null
};
}
@@ -30,31 +38,49 @@ class GeneralSettings extends React.Component {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<>
+
+
+
+
+
+
-
+
Video Path
- this.setState({videopath: ee.target.value})}/>
-
+
TV Show Path
- this.setState({tvshowpath: e.target.value})}/>
{
this.setState({passwordsupport: !this.state.passwordsupport})
@@ -62,18 +88,18 @@ class GeneralSettings extends React.Component {
/>
{this.state.passwordsupport ?
-
+
Password
- this.setState({password: e.target.value})}/>
: null
}
{
this.setState({tmdbsupport: !this.state.tmdbsupport})
@@ -81,10 +107,10 @@ class GeneralSettings extends React.Component {
/>
{
GlobalInfos.enableDarkTheme(!GlobalInfos.isDarkTheme());
@@ -93,13 +119,13 @@ class GeneralSettings extends React.Component {
}}
/>
-
+
The name of the Mediacenter
- this.setState({mediacentername: e.target.value})}/>
-