build electron app
implement new fetch api calls use typescript
This commit is contained in:
parent
c049aa345c
commit
7d696122fa
@ -8,6 +8,7 @@ stages:
|
|||||||
- deploy
|
- deploy
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
|
key: "$CI_COMMIT_REF_SLUG" # use per branch caching
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
|
|
||||||
@ -76,6 +77,20 @@ package_debian:
|
|||||||
- deb/OpenMediaCenter-*.deb
|
- deb/OpenMediaCenter-*.deb
|
||||||
needs: ["build"]
|
needs: ["build"]
|
||||||
|
|
||||||
|
electron:
|
||||||
|
stage: packaging
|
||||||
|
image: electronuserland/builder:wine
|
||||||
|
script:
|
||||||
|
- npm run buildlinux
|
||||||
|
- npm run buildwin
|
||||||
|
artifacts:
|
||||||
|
expire_in: 2 days
|
||||||
|
paths:
|
||||||
|
- dist/*.rpm
|
||||||
|
- dist/*.deb
|
||||||
|
- dist/*.exe
|
||||||
|
needs: ["build"]
|
||||||
|
|
||||||
deploy_test1:
|
deploy_test1:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: luki42/alpineopenssh:latest
|
image: luki42/alpineopenssh:latest
|
||||||
|
21
build.js
Normal file
21
build.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const builder = require("electron-builder");
|
||||||
|
const builds = {};
|
||||||
|
process.argv.slice(1).forEach(a => {
|
||||||
|
if (a === "--linux") {
|
||||||
|
builds.linux = [];
|
||||||
|
}
|
||||||
|
if (a === "--win") {
|
||||||
|
builds.win = [];
|
||||||
|
}
|
||||||
|
if (a === "--mac") {
|
||||||
|
builds.mac = [];
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.build(builds).then(e => {
|
||||||
|
console.log(e);
|
||||||
|
}).catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
38
package.json
38
package.json
@ -2,6 +2,12 @@
|
|||||||
"name": "openmediacenter",
|
"name": "openmediacenter",
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"main": "public/electron.js",
|
||||||
|
"author": {
|
||||||
|
"email": "lukas.heiligenbrunner@gmail.com",
|
||||||
|
"name": "Lukas Heiligenbrunner",
|
||||||
|
"url": "https://heili.eu"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
"@fortawesome/fontawesome-svg-core": "^1.2.32",
|
||||||
"@fortawesome/free-regular-svg-icons": "^5.15.1",
|
"@fortawesome/free-regular-svg-icons": "^5.15.1",
|
||||||
@ -11,15 +17,33 @@
|
|||||||
"plyr-react": "^3.0.7",
|
"plyr-react": "^3.0.7",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-bootstrap": "^1.4.0",
|
"react-bootstrap": "^1.4.0",
|
||||||
"react-dom": "^17.0.1"
|
"react-dom": "^17.0.1",
|
||||||
|
"typescript": "^4.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test --reporters=jest-junit --reporters=default",
|
"test": "react-scripts test --reporters=jest-junit --reporters=default",
|
||||||
"testsec": "set NODE_ENV=test && jest --runInBand --ci --all --detectOpenHandles --forceExit --clearMocks --verbose",
|
|
||||||
"coverage": "react-scripts test --coverage --watchAll=false",
|
"coverage": "react-scripts test --coverage --watchAll=false",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject",
|
||||||
|
"buildlinux": "node build.js --linux",
|
||||||
|
"buildwin": "node build.js --win",
|
||||||
|
"electron-dev": "electron ."
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "com.heili.openmediacenter",
|
||||||
|
"files": [
|
||||||
|
"build/**/*"
|
||||||
|
],
|
||||||
|
"directories": {
|
||||||
|
"buildResources": "assets"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": ["rpm","deb","snap","AppImage"]
|
||||||
|
},
|
||||||
|
"win": {
|
||||||
|
"target": ["nsis"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"collectCoverageFrom": [
|
"collectCoverageFrom": [
|
||||||
@ -32,7 +56,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"proxy": "http://192.168.0.209",
|
"proxy": "http://192.168.0.209",
|
||||||
"homepage": "/",
|
"homepage": "./",
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
@ -49,12 +73,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"react-scripts": "^3.4.4",
|
|
||||||
"@testing-library/jest-dom": "^5.11.6",
|
"@testing-library/jest-dom": "^5.11.6",
|
||||||
"@testing-library/react": "^11.2.2",
|
"@testing-library/react": "^11.2.2",
|
||||||
"@testing-library/user-event": "^12.2.2",
|
"@testing-library/user-event": "^12.2.2",
|
||||||
|
"electron": "^11.1.0",
|
||||||
|
"electron-builder": "^22.1.0",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-adapter-react-16": "^1.15.5",
|
"enzyme-adapter-react-16": "^1.15.5",
|
||||||
"jest-junit": "^12.0.0"
|
"jest-junit": "^12.0.0",
|
||||||
|
"react-scripts": "^3.4.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
public/electron.js
Normal file
31
public/electron.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const electron = require('electron');
|
||||||
|
const app = electron.app;
|
||||||
|
const BrowserWindow = electron.BrowserWindow;
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const url = require('url');
|
||||||
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
|
|
||||||
|
let mainWindow;
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
mainWindow = new BrowserWindow({width: 1500, height: 880});
|
||||||
|
mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`);
|
||||||
|
mainWindow.on('closed', () => mainWindow = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('ready', createWindow);
|
||||||
|
|
||||||
|
console.log( process.env.NODE_ENV);
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
if (mainWindow === null) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
50
src/App.js
50
src/App.js
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import HomePage from './pages/HomePage/HomePage';
|
import HomePage from './pages/HomePage/HomePage';
|
||||||
import RandomPage from './pages/RandomPage/RandomPage';
|
import RandomPage from './pages/RandomPage/RandomPage';
|
||||||
import GlobalInfos from './GlobalInfos';
|
import GlobalInfos from './utils/GlobalInfos';
|
||||||
|
|
||||||
// include bootstraps css
|
// include bootstraps css
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
@ -9,6 +9,8 @@ import style from './App.module.css';
|
|||||||
|
|
||||||
import SettingsPage from './pages/SettingsPage/SettingsPage';
|
import SettingsPage from './pages/SettingsPage/SettingsPage';
|
||||||
import CategoryPage from './pages/CategoryPage/CategoryPage';
|
import CategoryPage from './pages/CategoryPage/CategoryPage';
|
||||||
|
import {callAPI} from './utils/Api';
|
||||||
|
import {NoBackendConnectionPopup} from './elements/Popups/NoBackendConnectionPopup/NoBackendConnectionPopup';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main App handles the main tabs and which content to show
|
* The main App handles the main tabs and which content to show
|
||||||
@ -22,7 +24,8 @@ class App extends React.Component {
|
|||||||
page: 'default',
|
page: 'default',
|
||||||
generalSettingsLoaded: false,
|
generalSettingsLoaded: false,
|
||||||
passwordsupport: null,
|
passwordsupport: null,
|
||||||
mediacentername: 'OpenMediaCenter'
|
mediacentername: 'OpenMediaCenter',
|
||||||
|
onapierror: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// bind this to the method for being able to call methods such as this.setstate
|
// bind this to the method for being able to call methods such as this.setstate
|
||||||
@ -33,24 +36,27 @@ class App extends React.Component {
|
|||||||
GlobalInfos.setViewBinding(this.constructViewBinding());
|
GlobalInfos.setViewBinding(this.constructViewBinding());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initialAPICall(){
|
||||||
|
// this is the first api call so if it fails we know there is no connection to backend
|
||||||
|
callAPI('settings.php', {action: 'loadInitialData'}, (result) =>{
|
||||||
|
// set theme
|
||||||
|
GlobalInfos.enableDarkTheme(result.DarkMode);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
generalSettingsLoaded: true,
|
||||||
|
passwordsupport: result.passwordEnabled,
|
||||||
|
mediacentername: result.mediacenter_name,
|
||||||
|
onapierror: false
|
||||||
|
});
|
||||||
|
// set tab title to received mediacenter name
|
||||||
|
document.title = result.mediacenter_name;
|
||||||
|
}, error => {
|
||||||
|
this.setState({onapierror: true});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const updateRequest = new FormData();
|
this.initialAPICall();
|
||||||
updateRequest.append('action', 'loadInitialData');
|
|
||||||
|
|
||||||
fetch('/api/settings.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
// set theme
|
|
||||||
GlobalInfos.enableDarkTheme(result.DarkMode);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
generalSettingsLoaded: true,
|
|
||||||
passwordsupport: result.passwordEnabled,
|
|
||||||
mediacentername: result.mediacenter_name
|
|
||||||
});
|
|
||||||
// set tab title to received mediacenter name
|
|
||||||
document.title = result.mediacenter_name;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +125,7 @@ class App extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.generalSettingsLoaded ? this.MainBody() : 'loading'}
|
{this.state.generalSettingsLoaded ? this.MainBody() : 'loading'}
|
||||||
|
{this.state.onapierror ? this.ApiError() : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -142,6 +149,11 @@ class App extends React.Component {
|
|||||||
page: 'lastpage'
|
page: 'lastpage'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiError() {
|
||||||
|
// on api error show popup and retry and show again if failing..
|
||||||
|
return (<NoBackendConnectionPopup onHide={() => this.initialAPICall()}/>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -2,7 +2,7 @@ import style from './ActorTile.module.css';
|
|||||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
import ActorPage from '../../pages/ActorPage/ActorPage';
|
import ActorPage from '../../pages/ActorPage/ActorPage';
|
||||||
|
|
||||||
class ActorTile extends React.Component {
|
class ActorTile extends React.Component {
|
||||||
|
@ -18,4 +18,17 @@ describe('<ActorTile/>', function () {
|
|||||||
|
|
||||||
expect(func).toBeCalledTimes(1);
|
expect(func).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('simulate click with custom handler', function () {
|
||||||
|
const func = jest.fn((_) => {});
|
||||||
|
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}} onClick={() => func()}/>);
|
||||||
|
|
||||||
|
const func1 = jest.fn();
|
||||||
|
prepareViewBinding(func1);
|
||||||
|
|
||||||
|
wrapper.simulate('click');
|
||||||
|
|
||||||
|
expect(func1).toBeCalledTimes(0);
|
||||||
|
expect(func).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import style from './PageTitle.module.css';
|
import style from './PageTitle.module.css';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for generating PageTitle with bottom Line
|
* Component for generating PageTitle with bottom Line
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
|
|
||||||
import PageTitle from './PageTitle';
|
import PageTitle, {Line} from './PageTitle';
|
||||||
|
|
||||||
describe('<Preview/>', function () {
|
describe('<Preview/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -29,3 +29,10 @@ describe('<Preview/>', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('<Line/>', () => {
|
||||||
|
it('renders without crashing', function () {
|
||||||
|
const wrapper = shallow(<Line/>);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||||||
import ActorTile from '../../ActorTile/ActorTile';
|
import ActorTile from '../../ActorTile/ActorTile';
|
||||||
import style from './AddActorPopup.module.css';
|
import style from './AddActorPopup.module.css';
|
||||||
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
|
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Popup for Adding a new Actor to a Video
|
* Popup for Adding a new Actor to a Video
|
||||||
@ -69,33 +70,21 @@ class AddActorPopup extends React.Component {
|
|||||||
*/
|
*/
|
||||||
tileClickHandler(actorid) {
|
tileClickHandler(actorid) {
|
||||||
// fetch the available actors
|
// fetch the available actors
|
||||||
const req = new FormData();
|
callAPI('actor.php', {action: 'addActorToVideo', actorid: actorid, videoid: this.props.movie_id}, result => {
|
||||||
req.append('action', 'addActorToVideo');
|
if (result.result === 'success') {
|
||||||
req.append('actorid', actorid);
|
// return back to player page
|
||||||
req.append('videoid', this.props.movie_id);
|
this.props.onHide();
|
||||||
|
} else {
|
||||||
fetch('/api/actor.php', {method: 'POST', body: req})
|
console.error('an error occured while fetching actors');
|
||||||
.then((response) => response.json()
|
console.error(result);
|
||||||
.then((result) => {
|
}
|
||||||
if (result.result === 'success') {
|
});
|
||||||
// return back to player page
|
|
||||||
this.props.onHide();
|
|
||||||
} else {
|
|
||||||
console.error('an error occured while fetching actors');
|
|
||||||
console.error(result);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadActors() {
|
loadActors() {
|
||||||
const req = new FormData();
|
callAPI('actor.php', {action: 'getAllActors'}, result => {
|
||||||
req.append('action', 'getAllActors');
|
this.setState({actors: result});
|
||||||
|
});
|
||||||
fetch('/api/actor.php', {method: 'POST', body: req})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({actors: result});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AddActorPopup from './AddActorPopup';
|
import AddActorPopup from './AddActorPopup';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
describe('<AddActorPopup/>', function () {
|
describe('<AddActorPopup/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -8,12 +9,63 @@ describe('<AddActorPopup/>', function () {
|
|||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('simulate change to other page', function () {
|
it('simulate change to other page', function () {
|
||||||
// const wrapper = shallow(<AddActorPopup/>);
|
const wrapper = shallow(<AddActorPopup/>);
|
||||||
//
|
|
||||||
// console.log(wrapper.find('PopupBase').dive().debug());
|
expect(wrapper.find('NewActorPopupContent')).toHaveLength(0);
|
||||||
//
|
wrapper.find('PopupBase').props().banner.props.onClick();
|
||||||
//
|
|
||||||
// console.log(wrapper.debug());
|
// check if new content is showing
|
||||||
// });
|
expect(wrapper.find('NewActorPopupContent')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hide new actor page', function () {
|
||||||
|
const wrapper = shallow(<AddActorPopup/>);
|
||||||
|
wrapper.find('PopupBase').props().banner.props.onClick();
|
||||||
|
|
||||||
|
// call onhide event listener manually
|
||||||
|
wrapper.find('NewActorPopupContent').props().onHide();
|
||||||
|
|
||||||
|
// expect other page to be hidden again
|
||||||
|
expect(wrapper.find('NewActorPopupContent')).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test api call and insertion of actor tiles', function () {
|
||||||
|
global.callAPIMock([{id: 1, actorname: 'test'}, {id: 2, actorname: 'test2'}]);
|
||||||
|
|
||||||
|
const wrapper = shallow(<AddActorPopup/>);
|
||||||
|
|
||||||
|
expect(wrapper.find('ActorTile')).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('simulate actortile click', function () {
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
|
||||||
|
|
||||||
|
global.callAPIMock({result: 'success'});
|
||||||
|
|
||||||
|
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
|
||||||
|
wrapper.find('ActorTile').props().onClick();
|
||||||
|
|
||||||
|
expect(callAPI).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(func).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test failing actortile click', function () {
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
|
||||||
|
|
||||||
|
global.callAPIMock({result: 'nosuccess'});
|
||||||
|
|
||||||
|
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
|
||||||
|
wrapper.find('ActorTile').props().onClick();
|
||||||
|
|
||||||
|
expect(callAPI).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
// hide funtion should not have been called on error!
|
||||||
|
expect(func).toHaveBeenCalledTimes(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Tag from '../../Tag/Tag';
|
import Tag from '../../Tag/Tag';
|
||||||
import PopupBase from '../PopupBase';
|
import PopupBase from '../PopupBase';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* component creates overlay to add a new tag to a video
|
* component creates overlay to add a new tag to a video
|
||||||
@ -13,16 +14,11 @@ class AddTagPopup extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const updateRequest = new FormData();
|
callAPI('tags.php', {action: 'getAllTags'}, (result) => {
|
||||||
updateRequest.append('action', 'getAllTags');
|
this.setState({
|
||||||
|
items: result
|
||||||
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
items: result
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -44,23 +40,15 @@ class AddTagPopup extends React.Component {
|
|||||||
* @param tagname tag name to add
|
* @param tagname tag name to add
|
||||||
*/
|
*/
|
||||||
addTag(tagid, tagname) {
|
addTag(tagid, tagname) {
|
||||||
console.log(this.props);
|
callAPI('tags.php', {action: 'addTag', id: tagid, movieid: this.props.movie_id}, result => {
|
||||||
const updateRequest = new FormData();
|
if (result.result !== 'success') {
|
||||||
updateRequest.append('action', 'addTag');
|
console.log('error occured while writing to db -- todo error handling');
|
||||||
updateRequest.append('id', tagid);
|
console.log(result.result);
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
} else {
|
||||||
|
this.props.submit(tagid, tagname);
|
||||||
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
|
}
|
||||||
.then((response) => response.json()
|
this.props.onHide();
|
||||||
.then((result) => {
|
});
|
||||||
if (result.result !== 'success') {
|
|
||||||
console.log('error occured while writing to db -- todo error handling');
|
|
||||||
console.log(result.result);
|
|
||||||
} else {
|
|
||||||
this.props.submit(tagid, tagname);
|
|
||||||
}
|
|
||||||
this.props.onHide();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PopupBase from '../PopupBase';
|
import PopupBase from '../PopupBase';
|
||||||
import style from './NewActorPopup.module.css';
|
import style from './NewActorPopup.module.css';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates modal overlay to define a new Tag
|
* creates modal overlay to define a new Tag
|
||||||
@ -41,19 +42,13 @@ export class NewActorPopupContent extends React.Component {
|
|||||||
// check if user typed in name
|
// check if user typed in name
|
||||||
if (this.value === '' || this.value === undefined) return;
|
if (this.value === '' || this.value === undefined) return;
|
||||||
|
|
||||||
const req = new FormData();
|
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result) => {
|
||||||
req.append('action', 'createActor');
|
if (result.result !== 'success') {
|
||||||
req.append('actorname', this.value);
|
console.log('error occured while writing to db -- todo error handling');
|
||||||
|
console.log(result.result);
|
||||||
fetch('/api/actor.php', {method: 'POST', body: req})
|
}
|
||||||
.then((response) => response.json())
|
this.props.onHide();
|
||||||
.then((result) => {
|
});
|
||||||
if (result.result !== 'success') {
|
|
||||||
console.log('error occured while writing to db -- todo error handling');
|
|
||||||
console.log(result.result);
|
|
||||||
}
|
|
||||||
this.props.onHide();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import React from 'react';
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import NewActorPopup, {NewActorPopupContent} from './NewActorPopup';
|
import NewActorPopup, {NewActorPopupContent} from './NewActorPopup';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
describe('<NewActorPopup/>', function () {
|
describe('<NewActorPopup/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -18,18 +19,23 @@ describe('<NewActorPopupContent/>', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('simulate button click', function () {
|
it('simulate button click', function () {
|
||||||
const wrapper = shallow(<NewActorPopupContent/>);
|
global.callAPIMock({});
|
||||||
|
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<NewActorPopupContent onHide={() => {func()}}/>);
|
||||||
|
|
||||||
// manually set typed in actorname
|
// manually set typed in actorname
|
||||||
wrapper.instance().value = 'testactorname';
|
wrapper.instance().value = 'testactorname';
|
||||||
|
|
||||||
global.fetch = prepareFetchApi({});
|
global.fetch = prepareFetchApi({});
|
||||||
|
|
||||||
expect(global.fetch).toBeCalledTimes(0);
|
expect(callAPI).toBeCalledTimes(0);
|
||||||
wrapper.find('button').simulate('click');
|
wrapper.find('button').simulate('click');
|
||||||
|
|
||||||
// fetch should have been called once now
|
// fetch should have been called once now
|
||||||
expect(global.fetch).toBeCalledTimes(1);
|
expect(callAPI).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
expect(func).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test not allowing request if textfield is empty', function () {
|
it('test not allowing request if textfield is empty', function () {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PopupBase from '../PopupBase';
|
import PopupBase from '../PopupBase';
|
||||||
import style from './NewTagPopup.module.css';
|
import style from './NewTagPopup.module.css';
|
||||||
|
import {callAPI} from '../../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates modal overlay to define a new Tag
|
* creates modal overlay to define a new Tag
|
||||||
@ -27,19 +28,13 @@ class NewTagPopup extends React.Component {
|
|||||||
* store the filled in form to the backend
|
* store the filled in form to the backend
|
||||||
*/
|
*/
|
||||||
storeselection() {
|
storeselection() {
|
||||||
const updateRequest = new FormData();
|
callAPI('tags.php', {action: 'createTag', tagname: this.value}, result => {
|
||||||
updateRequest.append('action', 'createTag');
|
if (result.result !== 'success') {
|
||||||
updateRequest.append('tagname', this.value);
|
console.log('error occured while writing to db -- todo error handling');
|
||||||
|
console.log(result.result);
|
||||||
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
|
}
|
||||||
.then((response) => response.json())
|
this.props.onHide();
|
||||||
.then((result) => {
|
});
|
||||||
if (result.result !== 'success') {
|
|
||||||
console.log('error occured while writing to db -- todo error handling');
|
|
||||||
console.log(result.result);
|
|
||||||
}
|
|
||||||
this.props.onHide();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import React from 'react';
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import NewTagPopup from './NewTagPopup';
|
import NewTagPopup from './NewTagPopup';
|
||||||
|
import {NoBackendConnectionPopup} from '../NoBackendConnectionPopup/NoBackendConnectionPopup';
|
||||||
|
import {getBackendDomain} from '../../../utils/Api';
|
||||||
|
|
||||||
describe('<NewTagPopup/>', function () {
|
describe('<NewTagPopup/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -33,4 +35,12 @@ describe('<NewTagPopup/>', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('simulate textfield change', function () {
|
||||||
|
const wrapper = shallow(<NewTagPopup/>);
|
||||||
|
|
||||||
|
wrapper.find('input').simulate('change', {target: {value: 'testvalue'}});
|
||||||
|
|
||||||
|
expect(wrapper.instance().value).toBe('testvalue');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import {shallow} from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
import {NoBackendConnectionPopup} from './NoBackendConnectionPopup';
|
||||||
|
import {getBackendDomain} from '../../../utils/Api';
|
||||||
|
|
||||||
|
describe('<NoBackendConnectionPopup/>', function () {
|
||||||
|
it('renders without crashing ', function () {
|
||||||
|
const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hides on refresh click', function () {
|
||||||
|
const func = jest.fn();
|
||||||
|
const wrapper = shallow(<NoBackendConnectionPopup onHide={func}/>);
|
||||||
|
|
||||||
|
expect(func).toBeCalledTimes(0);
|
||||||
|
wrapper.find('button').simulate('click');
|
||||||
|
|
||||||
|
expect(func).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('simulate change of textfield', function () {
|
||||||
|
const wrapper = shallow(<NoBackendConnectionPopup onHide={() => {}}/>);
|
||||||
|
|
||||||
|
wrapper.find('input').simulate('change', {target: {value: 'testvalue'}});
|
||||||
|
|
||||||
|
expect(getBackendDomain()).toBe('testvalue');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
import PopupBase from "../PopupBase";
|
||||||
|
import style from "../NewActorPopup/NewActorPopup.module.css";
|
||||||
|
import {setCustomBackendDomain} from "../../../utils/Api";
|
||||||
|
|
||||||
|
interface NBCProps {
|
||||||
|
onHide: (_: void) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NoBackendConnectionPopup(props: NBCProps): JSX.Element {
|
||||||
|
return (
|
||||||
|
<PopupBase title='No connection to backend API!' onHide={props.onHide} height='200px' width='600px'>
|
||||||
|
<div>
|
||||||
|
<input type='text' placeholder='http://192.168.0.2' onChange={(v) => {
|
||||||
|
setCustomBackendDomain(v.target.value);
|
||||||
|
}}/></div>
|
||||||
|
<button className={style.savebtn} onClick={() => props.onHide()}>Refresh</button>
|
||||||
|
</PopupBase>
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
import style from './PopupBase.module.css';
|
import style from './PopupBase.module.css';
|
||||||
import {Line} from '../PageTitle/PageTitle';
|
import {Line} from '../PageTitle/PageTitle';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -20,7 +20,8 @@ class PopupBase extends React.Component {
|
|||||||
// parse style props
|
// parse style props
|
||||||
this.framedimensions = {
|
this.framedimensions = {
|
||||||
width: (this.props.width ? this.props.width : undefined),
|
width: (this.props.width ? this.props.width : undefined),
|
||||||
height: (this.props.height ? this.props.height : undefined)
|
height: (this.props.height ? this.props.height : undefined),
|
||||||
|
minHeight: (this.props.height ? this.props.height : undefined)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
.popup {
|
.popup {
|
||||||
border: 3px #3574fe solid;
|
border: 3px #3574fe solid;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
height: 80%;
|
min-height: 80%;
|
||||||
|
height: fit-content;
|
||||||
left: 20%;
|
left: 20%;
|
||||||
opacity: 0.95;
|
opacity: 0.95;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -40,4 +41,5 @@
|
|||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,19 @@ describe('<PopupBase/>', function () {
|
|||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('simulate keypress', function () {
|
||||||
|
let events = [];
|
||||||
|
document.addEventListener = jest.fn((event, cb) => {
|
||||||
|
events[event] = cb;
|
||||||
|
});
|
||||||
|
|
||||||
|
const func = jest.fn();
|
||||||
|
shallow(<PopupBase onHide={() => func()}/>);
|
||||||
|
|
||||||
|
// trigger the keypress event
|
||||||
|
events.keyup({key: 'Escape'});
|
||||||
|
|
||||||
|
expect(func).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||||||
import style from './Preview.module.css';
|
import style from './Preview.module.css';
|
||||||
import Player from '../../pages/Player/Player';
|
import Player from '../../pages/Player/Player';
|
||||||
import {Spinner} from 'react-bootstrap';
|
import {Spinner} from 'react-bootstrap';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
import {callAPIPlain} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for single preview tile
|
* Component for single preview tile
|
||||||
@ -19,22 +20,12 @@ class Preview extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.setState({
|
callAPIPlain('video.php', {action: 'readThumbnail', movieid: this.props.movie_id}, (result) => {
|
||||||
previewpicture: null,
|
this.setState({
|
||||||
name: this.props.name
|
previewpicture: result,
|
||||||
|
name: this.props.name
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateRequest = new FormData();
|
|
||||||
updateRequest.append('action', 'readThumbnail');
|
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
|
||||||
|
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.text()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
previewpicture: result
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -9,12 +9,6 @@ describe('<Preview/>', function () {
|
|||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
// check if preview title renders correctly
|
|
||||||
it('renders title', () => {
|
|
||||||
const wrapper = shallow(<Preview name='test'/>);
|
|
||||||
expect(wrapper.find('.previewtitle').text()).toBe('test');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('click event triggered', () => {
|
it('click event triggered', () => {
|
||||||
const func = jest.fn();
|
const func = jest.fn();
|
||||||
@ -36,7 +30,7 @@ describe('<Preview/>', function () {
|
|||||||
});
|
});
|
||||||
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
|
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
|
||||||
|
|
||||||
const wrapper = shallow(<Preview/>);
|
const wrapper = shallow(<Preview name='test'/>);
|
||||||
|
|
||||||
// now called 1 times
|
// now called 1 times
|
||||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||||
@ -44,6 +38,8 @@ describe('<Preview/>', function () {
|
|||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
// received picture should be rendered into wrapper
|
// received picture should be rendered into wrapper
|
||||||
expect(wrapper.find('.previewimage').props().src).not.toBeNull();
|
expect(wrapper.find('.previewimage').props().src).not.toBeNull();
|
||||||
|
// check if preview title renders correctly
|
||||||
|
expect(wrapper.find('.previewtitle').text()).toBe('test');
|
||||||
|
|
||||||
global.fetch.mockClear();
|
global.fetch.mockClear();
|
||||||
done();
|
done();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import style from './SideBar.module.css';
|
import style from './SideBar.module.css';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* component for sidebar-info
|
* component for sidebar-info
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import styles from './Tag.module.css';
|
import styles from './Tag.module.css';
|
||||||
import CategoryPage from '../../pages/CategoryPage/CategoryPage';
|
import CategoryPage from '../../pages/CategoryPage/CategoryPage';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Component representing a single Category tag
|
* A Component representing a single Category tag
|
||||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
// don't allow console logs within production env
|
|
||||||
// don't allow console logs within production env
|
// don't allow console logs within production env
|
||||||
global.console.log = process.env.NODE_ENV !== "development" ? (s) => {} : global.console.log;
|
global.console.log = process.env.NODE_ENV !== "development" ? (s) => {} : global.console.log;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|||||||
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
||||||
import style from './ActorPage.module.css';
|
import style from './ActorPage.module.css';
|
||||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
class ActorPage extends React.Component {
|
class ActorPage extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -40,17 +41,10 @@ class ActorPage extends React.Component {
|
|||||||
*/
|
*/
|
||||||
getActorInfo() {
|
getActorInfo() {
|
||||||
// todo 2020-12-4: fetch to db
|
// todo 2020-12-4: fetch to db
|
||||||
|
callAPI('actor.php', {action: 'getActorInfo', actorid: this.props.actor.actor_id}, result => {
|
||||||
const req = new FormData();
|
console.log(result);
|
||||||
req.append('action', 'getActorInfo');
|
this.setState({data: result.videos ? result.videos : []});
|
||||||
req.append('actorid', this.props.actor.actor_id);
|
});
|
||||||
|
|
||||||
fetch('/api/actor.php', {method: 'POST', body: req})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
console.log(result);
|
|
||||||
this.setState({data: result.videos ? result.videos : []});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import {TagPreview} from '../../elements/Preview/Preview';
|
|||||||
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
|
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
|
||||||
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
||||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for Category Page
|
* Component for Category Page
|
||||||
@ -111,24 +112,11 @@ class CategoryPage extends React.Component {
|
|||||||
* @param tag tagname
|
* @param tag tagname
|
||||||
*/
|
*/
|
||||||
fetchVideoData(tag) {
|
fetchVideoData(tag) {
|
||||||
console.log(tag);
|
callAPI('video.php', {action: 'getMovies', tag: tag}, result => {
|
||||||
const updateRequest = new FormData();
|
this.videodata = result;
|
||||||
updateRequest.append('action', 'getMovies');
|
this.setState({selected: null}); // needed to trigger the state reload correctly
|
||||||
updateRequest.append('tag', tag);
|
this.setState({selected: tag});
|
||||||
|
});
|
||||||
console.log('fetching data');
|
|
||||||
|
|
||||||
// fetch all videos available
|
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.videodata = result;
|
|
||||||
this.setState({selected: null}); // needed to trigger the state reload correctly
|
|
||||||
this.setState({selected: tag});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,18 +131,9 @@ class CategoryPage extends React.Component {
|
|||||||
* load all available tags from db.
|
* load all available tags from db.
|
||||||
*/
|
*/
|
||||||
loadTags() {
|
loadTags() {
|
||||||
const updateRequest = new FormData();
|
callAPI('tags.php', {action: 'getAllTags'}, result => {
|
||||||
updateRequest.append('action', 'getAllTags');
|
this.setState({loadedtags: result});
|
||||||
|
});
|
||||||
// fetch all videos available
|
|
||||||
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({loadedtags: result});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,27 +24,6 @@ describe('<CategoryPage/>', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('test errored fetch call', done => {
|
|
||||||
global.fetch = global.prepareFetchApi({});
|
|
||||||
|
|
||||||
let message;
|
|
||||||
global.console.log = jest.fn((m) => {
|
|
||||||
message = m;
|
|
||||||
});
|
|
||||||
|
|
||||||
shallow(<CategoryPage/>);
|
|
||||||
|
|
||||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
|
||||||
|
|
||||||
process.nextTick(() => {
|
|
||||||
//callback to close window should have called
|
|
||||||
expect(message).toBe('no connection to backend');
|
|
||||||
|
|
||||||
global.fetch.mockClear();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('test new tag popup', function () {
|
it('test new tag popup', function () {
|
||||||
const wrapper = shallow(<CategoryPage/>);
|
const wrapper = shallow(<CategoryPage/>);
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
|||||||
|
|
||||||
import style from './HomePage.module.css';
|
import style from './HomePage.module.css';
|
||||||
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The home page component showing on the initial pageload
|
* The home page component showing on the initial pageload
|
||||||
@ -43,54 +44,33 @@ class HomePage extends React.Component {
|
|||||||
* @param tag tag to fetch videos
|
* @param tag tag to fetch videos
|
||||||
*/
|
*/
|
||||||
fetchVideoData(tag) {
|
fetchVideoData(tag) {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'getMovies', tag: tag}, (result) => {
|
||||||
updateRequest.append('action', 'getMovies');
|
this.setState({
|
||||||
updateRequest.append('tag', tag);
|
data: []
|
||||||
|
|
||||||
console.log('fetching data from' + tag);
|
|
||||||
|
|
||||||
// fetch all videos available
|
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
data: []
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
data: result,
|
|
||||||
selectionnr: result.length,
|
|
||||||
tag: tag + ' Videos'
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
});
|
||||||
|
this.setState({
|
||||||
|
data: result,
|
||||||
|
selectionnr: result.length,
|
||||||
|
tag: tag + ' Videos'
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* fetch the necessary data for left info box
|
* fetch the necessary data for left info box
|
||||||
*/
|
*/
|
||||||
fetchStartData() {
|
fetchStartData() {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'getStartData'}, (result) => {
|
||||||
updateRequest.append('action', 'getStartData');
|
this.setState({
|
||||||
|
sideinfo: {
|
||||||
// fetch all videos available
|
videonr: result['total'],
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
fullhdvideonr: result['fullhd'],
|
||||||
.then((response) => response.json()
|
hdvideonr: result['hd'],
|
||||||
.then((result) => {
|
sdvideonr: result['sd'],
|
||||||
this.setState({
|
tagnr: result['tags']
|
||||||
sideinfo: {
|
}
|
||||||
videonr: result['total'],
|
|
||||||
fullhdvideonr: result['fullhd'],
|
|
||||||
hdvideonr: result['hd'],
|
|
||||||
sdvideonr: result['sd'],
|
|
||||||
tagnr: result['tags']
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,26 +81,16 @@ class HomePage extends React.Component {
|
|||||||
searchVideos(keyword) {
|
searchVideos(keyword) {
|
||||||
console.log('search called');
|
console.log('search called');
|
||||||
|
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'getSearchKeyWord', keyword: keyword}, (result) => {
|
||||||
updateRequest.append('action', 'getSearchKeyWord');
|
this.setState({
|
||||||
updateRequest.append('keyword', keyword);
|
data: []
|
||||||
|
|
||||||
// fetch all videos available
|
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
data: []
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
data: result,
|
|
||||||
selectionnr: result.length,
|
|
||||||
tag: 'Search result: ' + keyword
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
});
|
||||||
|
this.setState({
|
||||||
|
data: result,
|
||||||
|
selectionnr: result.length,
|
||||||
|
tag: 'Search result: ' + keyword
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import style from './Player.module.css';
|
import style from './Player.module.css';
|
||||||
import plyrstyle from 'plyr-react/dist/plyr.css'
|
import plyrstyle from 'plyr-react/dist/plyr.css';
|
||||||
|
|
||||||
import {Plyr} from 'plyr-react';
|
import {Plyr} from 'plyr-react';
|
||||||
import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
|
import SideBar, {SideBarItem, SideBarTitle} from '../../elements/SideBar/SideBar';
|
||||||
@ -12,7 +12,8 @@ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|||||||
import {faPlusCircle} from '@fortawesome/free-solid-svg-icons';
|
import {faPlusCircle} from '@fortawesome/free-solid-svg-icons';
|
||||||
import AddActorPopup from '../../elements/Popups/AddActorPopup/AddActorPopup';
|
import AddActorPopup from '../../elements/Popups/AddActorPopup/AddActorPopup';
|
||||||
import ActorTile from '../../elements/ActorTile/ActorTile';
|
import ActorTile from '../../elements/ActorTile/ActorTile';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
import {callAPI, getBackendDomain} from '../../utils/Api';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,47 +68,40 @@ class Player extends React.Component {
|
|||||||
* @param tagName name of tag to add
|
* @param tagName name of tag to add
|
||||||
*/
|
*/
|
||||||
quickAddTag(tagId, tagName) {
|
quickAddTag(tagId, tagName) {
|
||||||
const updateRequest = new FormData();
|
callAPI('tags.php', {action: 'addTag', id: tagId, movieid: this.props.movie_id}, (result) => {
|
||||||
updateRequest.append('action', 'addTag');
|
if (result.result !== 'success') {
|
||||||
updateRequest.append('id', tagId);
|
console.error('error occured while writing to db -- todo error handling');
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
console.error(result.result);
|
||||||
|
} else {
|
||||||
|
// check if tag has already been added
|
||||||
|
const tagIndex = this.state.tags.map(function (e) {
|
||||||
|
return e.tag_name;
|
||||||
|
}).indexOf(tagName);
|
||||||
|
|
||||||
fetch('/api/tags.php', {method: 'POST', body: updateRequest})
|
// only add tag if it isn't already there
|
||||||
.then((response) => response.json()
|
if (tagIndex === -1) {
|
||||||
.then((result) => {
|
// update tags if successful
|
||||||
if (result.result !== 'success') {
|
let array = [...this.state.suggesttag]; // make a separate copy of the array (because of setState)
|
||||||
console.error('error occured while writing to db -- todo error handling');
|
const quickaddindex = this.state.suggesttag.map(function (e) {
|
||||||
console.error(result.result);
|
return e.tag_id;
|
||||||
|
}).indexOf(tagId);
|
||||||
|
|
||||||
|
// check if tag is available in quickadds
|
||||||
|
if (quickaddindex !== -1) {
|
||||||
|
array.splice(quickaddindex, 1);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
tags: [...this.state.tags, {tag_name: tagName}],
|
||||||
|
suggesttag: array
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// check if tag has already been added
|
this.setState({
|
||||||
const tagIndex = this.state.tags.map(function (e) {
|
tags: [...this.state.tags, {tag_name: tagName}]
|
||||||
return e.tag_name;
|
});
|
||||||
}).indexOf(tagName);
|
|
||||||
|
|
||||||
// only add tag if it isn't already there
|
|
||||||
if (tagIndex === -1) {
|
|
||||||
// update tags if successful
|
|
||||||
let array = [...this.state.suggesttag]; // make a separate copy of the array (because of setState)
|
|
||||||
const quickaddindex = this.state.suggesttag.map(function (e) {
|
|
||||||
return e.tag_id;
|
|
||||||
}).indexOf(tagId);
|
|
||||||
|
|
||||||
// check if tag is available in quickadds
|
|
||||||
if (quickaddindex !== -1) {
|
|
||||||
array.splice(quickaddindex, 1);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tags: [...this.state.tags, {tag_name: tagName}],
|
|
||||||
suggesttag: array
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
tags: [...this.state.tags, {tag_name: tagName}]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,7 +173,7 @@ class Player extends React.Component {
|
|||||||
<div className={style.videowrapper}>
|
<div className={style.videowrapper}>
|
||||||
{/* video component is added here */}
|
{/* video component is added here */}
|
||||||
{this.state.sources ? <Plyr
|
{this.state.sources ? <Plyr
|
||||||
style={plyrstyle}
|
style={plyrstyle}
|
||||||
source={this.state.sources}
|
source={this.state.sources}
|
||||||
options={this.options}/> :
|
options={this.options}/> :
|
||||||
<div>not loaded yet</div>}
|
<div>not loaded yet</div>}
|
||||||
@ -227,36 +221,30 @@ class Player extends React.Component {
|
|||||||
* fetch all the required infos of a video from backend
|
* fetch all the required infos of a video from backend
|
||||||
*/
|
*/
|
||||||
fetchMovieData() {
|
fetchMovieData() {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'loadVideo', movieid: this.props.movie_id}, result => {
|
||||||
updateRequest.append('action', 'loadVideo');
|
this.setState({
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
sources: {
|
||||||
|
type: 'video',
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
sources: [
|
||||||
.then((response) => response.json())
|
{
|
||||||
.then((result) => {
|
src: getBackendDomain() + result.movie_url,
|
||||||
this.setState({
|
type: 'video/mp4',
|
||||||
sources: {
|
size: 1080
|
||||||
type: 'video',
|
}
|
||||||
sources: [
|
],
|
||||||
{
|
poster: result.thumbnail
|
||||||
src: result.movie_url,
|
},
|
||||||
type: 'video/mp4',
|
movie_id: result.movie_id,
|
||||||
size: 1080
|
movie_name: result.movie_name,
|
||||||
}
|
likes: result.likes,
|
||||||
],
|
quality: result.quality,
|
||||||
poster: result.thumbnail
|
length: result.length,
|
||||||
},
|
tags: result.tags,
|
||||||
movie_id: result.movie_id,
|
suggesttag: result.suggesttag,
|
||||||
movie_name: result.movie_name,
|
actors: result.actors
|
||||||
likes: result.likes,
|
|
||||||
quality: result.quality,
|
|
||||||
length: result.length,
|
|
||||||
tags: result.tags,
|
|
||||||
suggesttag: result.suggesttag,
|
|
||||||
actors: result.actors
|
|
||||||
});
|
|
||||||
console.log(this.state);
|
|
||||||
});
|
});
|
||||||
|
console.log(this.state);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -264,21 +252,15 @@ class Player extends React.Component {
|
|||||||
* click handler for the like btn
|
* click handler for the like btn
|
||||||
*/
|
*/
|
||||||
likebtn() {
|
likebtn() {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'addLike', movieid: this.props.movie_id}, result => {
|
||||||
updateRequest.append('action', 'addLike');
|
if (result.result === 'success') {
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
// likes +1 --> avoid reload of all data
|
||||||
|
this.setState({likes: this.state.likes + 1});
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
} else {
|
||||||
.then((response) => response.json()
|
console.error('an error occured while liking');
|
||||||
.then((result) => {
|
console.error(result);
|
||||||
if (result.result === 'success') {
|
}
|
||||||
// likes +1 --> avoid reload of all data
|
});
|
||||||
this.setState({likes: this.state.likes + 1});
|
|
||||||
} else {
|
|
||||||
console.error('an error occured while liking');
|
|
||||||
console.error(result);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -293,21 +275,15 @@ class Player extends React.Component {
|
|||||||
* delete the current video and return to last page
|
* delete the current video and return to last page
|
||||||
*/
|
*/
|
||||||
deleteVideo() {
|
deleteVideo() {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'deleteVideo', movieid: this.props.movie_id}, result => {
|
||||||
updateRequest.append('action', 'deleteVideo');
|
if (result.result === 'success') {
|
||||||
updateRequest.append('movieid', this.props.movie_id);
|
// return to last element if successful
|
||||||
|
GlobalInfos.getViewBinding().returnToLastElement();
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
} else {
|
||||||
.then((response) => response.json()
|
console.error('an error occured while liking');
|
||||||
.then((result) => {
|
console.error(result);
|
||||||
if (result.result === 'success') {
|
}
|
||||||
// return to last element if successful
|
});
|
||||||
GlobalInfos.getViewBinding().returnToLastElement();
|
|
||||||
} else {
|
|
||||||
console.error('an error occured while liking');
|
|
||||||
console.error(result);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,17 +294,9 @@ class Player extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refetchActors() {
|
refetchActors() {
|
||||||
const req = new FormData();
|
callAPI('actor.php', {action: 'getActorsOfVideo', videoid: this.props.movie_id}, result => {
|
||||||
req.append('action', 'getActorsOfVideo');
|
this.setState({actors: result});
|
||||||
req.append('videoid', this.props.movie_id);
|
});
|
||||||
|
|
||||||
console.log('refrething actors');
|
|
||||||
|
|
||||||
fetch('/api/actor.php', {method: 'POST', body: req})
|
|
||||||
.then((response) => response.json()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({actors: result});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Player from './Player';
|
import Player from './Player';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
describe('<Player/>', function () {
|
describe('<Player/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -81,7 +82,7 @@ describe('<Player/>', function () {
|
|||||||
const wrapper = shallow(<Player/>);
|
const wrapper = shallow(<Player/>);
|
||||||
|
|
||||||
const func = jest.fn();
|
const func = jest.fn();
|
||||||
prepareViewBinding(func)
|
prepareViewBinding(func);
|
||||||
|
|
||||||
global.fetch = prepareFetchApi({result: 'success'});
|
global.fetch = prepareFetchApi({result: 'success'});
|
||||||
|
|
||||||
@ -163,6 +164,58 @@ describe('<Player/>', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('showspopups correctly', function () {
|
||||||
|
const wrapper = shallow(<Player/>);
|
||||||
|
|
||||||
|
wrapper.setState({popupvisible: true}, () => {
|
||||||
|
// is the AddTagpopu rendered?
|
||||||
|
expect(wrapper.find('AddTagPopup')).toHaveLength(1);
|
||||||
|
wrapper.setState({popupvisible: false, actorpopupvisible: true}, () => {
|
||||||
|
// actorpopup rendred and tagpopup hidden?
|
||||||
|
expect(wrapper.find('AddTagPopup')).toHaveLength(0);
|
||||||
|
expect(wrapper.find('AddActorPopup')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('quickadd tag correctly', function () {
|
||||||
|
const wrapper = shallow(<Player/>);
|
||||||
|
global.callAPIMock({result: 'success'});
|
||||||
|
|
||||||
|
wrapper.setState({suggesttag: [{tag_name: 'test', tag_id: 1}]}, () => {
|
||||||
|
// mock funtion should have not been called
|
||||||
|
expect(callAPI).toBeCalledTimes(0);
|
||||||
|
wrapper.find('Tag').findWhere(p => p.text() === 'test').parent().dive().simulate('click');
|
||||||
|
// mock function should have been called once
|
||||||
|
expect(callAPI).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
// expect tag added to video tags
|
||||||
|
expect(wrapper.state().tags).toMatchObject([{tag_name: 'test'}]);
|
||||||
|
// expect tag to be removed from tag suggestions
|
||||||
|
expect(wrapper.state().suggesttag).toHaveLength(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test adding of already existing tag', function () {
|
||||||
|
const wrapper = shallow(<Player/>);
|
||||||
|
global.callAPIMock({result: 'success'});
|
||||||
|
|
||||||
|
wrapper.setState({suggesttag: [{tag_name: 'test', tag_id: 1}], tags: [{tag_name: 'test', tag_id: 1}]}, () => {
|
||||||
|
// mock funtion should have not been called
|
||||||
|
expect(callAPI).toBeCalledTimes(0);
|
||||||
|
wrapper.find('Tag').findWhere(p => p.text() === 'test').last().parent().dive().simulate('click');
|
||||||
|
// mock function should have been called once
|
||||||
|
expect(callAPI).toBeCalledTimes(1);
|
||||||
|
|
||||||
|
// there should not been added a duplicate of tag so object stays same...
|
||||||
|
expect(wrapper.state().tags).toMatchObject([{tag_name: 'test'}]);
|
||||||
|
// the suggestion tag shouldn't be removed (this can't actually happen in rl
|
||||||
|
// because backennd doesn't give dupliacate suggestiontags)
|
||||||
|
expect(wrapper.state().suggesttag).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
function generatetag() {
|
function generatetag() {
|
||||||
const wrapper = shallow(<Player/>);
|
const wrapper = shallow(<Player/>);
|
||||||
|
|
||||||
@ -178,4 +231,26 @@ describe('<Player/>', function () {
|
|||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('test addactor popup showing', function () {
|
||||||
|
const wrapper = shallow(<Player/>);
|
||||||
|
|
||||||
|
expect(wrapper.find('AddActorPopup')).toHaveLength(0);
|
||||||
|
|
||||||
|
wrapper.instance().addActor();
|
||||||
|
|
||||||
|
// check if popup is visible
|
||||||
|
expect(wrapper.find('AddActorPopup')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('test hiding of addactor popup', function () {
|
||||||
|
const wrapper = shallow(<Player/>);
|
||||||
|
wrapper.instance().addActor();
|
||||||
|
|
||||||
|
expect(wrapper.find('AddActorPopup')).toHaveLength(1);
|
||||||
|
|
||||||
|
wrapper.find('AddActorPopup').props().onHide();
|
||||||
|
|
||||||
|
expect(wrapper.find('AddActorPopup')).toHaveLength(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,7 @@ import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
|
|||||||
import Tag from '../../elements/Tag/Tag';
|
import Tag from '../../elements/Tag/Tag';
|
||||||
import PageTitle from '../../elements/PageTitle/PageTitle';
|
import PageTitle from '../../elements/PageTitle/PageTitle';
|
||||||
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Randompage shuffles random viedeopreviews and provides a shuffle btn
|
* Randompage shuffles random viedeopreviews and provides a shuffle btn
|
||||||
@ -61,25 +62,15 @@ class RandomPage extends React.Component {
|
|||||||
* @param nr number of videos to load
|
* @param nr number of videos to load
|
||||||
*/
|
*/
|
||||||
loadShuffledvideos(nr) {
|
loadShuffledvideos(nr) {
|
||||||
const updateRequest = new FormData();
|
callAPI('video.php', {action: 'getRandomMovies', number: nr}, result => {
|
||||||
updateRequest.append('action', 'getRandomMovies');
|
console.log(result);
|
||||||
updateRequest.append('number', nr);
|
|
||||||
|
|
||||||
// fetch all videos available
|
this.setState({videos: []}); // needed to trigger rerender of main videoview
|
||||||
fetch('/api/video.php', {method: 'POST', body: updateRequest})
|
this.setState({
|
||||||
.then((response) => response.json()
|
videos: result.rows,
|
||||||
.then((result) => {
|
tags: result.tags
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
this.setState({videos: []}); // needed to trigger rerender of main videoview
|
|
||||||
this.setState({
|
|
||||||
videos: result.rows,
|
|
||||||
tags: result.tags
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Button, Col, Form} from 'react-bootstrap';
|
import {Button, Col, Form} from 'react-bootstrap';
|
||||||
import style from './GeneralSettings.module.css';
|
import style from './GeneralSettings.module.css';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
import InfoHeaderItem from '../../elements/InfoHeaderItem/InfoHeaderItem';
|
import InfoHeaderItem from '../../elements/InfoHeaderItem/InfoHeaderItem';
|
||||||
import {faArchive, faBalanceScaleLeft, faRulerVertical} from '@fortawesome/free-solid-svg-icons';
|
import {faArchive, faBalanceScaleLeft, faRulerVertical} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {faAddressCard} from '@fortawesome/free-regular-svg-icons';
|
import {faAddressCard} from '@fortawesome/free-regular-svg-icons';
|
||||||
import {version} from '../../../package.json';
|
import {version} from '../../../package.json';
|
||||||
|
import {callAPI, setCustomBackendDomain} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for Generalsettings tag on Settingspage
|
* Component for Generalsettings tag on Settingspage
|
||||||
@ -18,6 +19,7 @@ class GeneralSettings extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
passwordsupport: false,
|
passwordsupport: false,
|
||||||
tmdbsupport: null,
|
tmdbsupport: null,
|
||||||
|
customapi: false,
|
||||||
|
|
||||||
videopath: '',
|
videopath: '',
|
||||||
tvshowpath: '',
|
tvshowpath: '',
|
||||||
@ -77,6 +79,31 @@ class GeneralSettings extends React.Component {
|
|||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Form.Row>
|
</Form.Row>
|
||||||
|
|
||||||
|
<Form.Check
|
||||||
|
type='switch'
|
||||||
|
id='custom-switch-api'
|
||||||
|
label='Use custom API url'
|
||||||
|
checked={this.state.customapi}
|
||||||
|
onChange={() => {
|
||||||
|
if (this.state.customapi) {
|
||||||
|
setCustomBackendDomain('');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({customapi: !this.state.customapi});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{this.state.customapi ?
|
||||||
|
<Form.Group className={style.customapiform} data-testid='apipath'>
|
||||||
|
<Form.Label>API Backend url</Form.Label>
|
||||||
|
<Form.Control type='text' placeholder='https://127.0.0.1'
|
||||||
|
value={this.state.apipath}
|
||||||
|
onChange={(e) => {
|
||||||
|
this.setState({apipath: e.target.value});
|
||||||
|
setCustomBackendDomain(e.target.value);
|
||||||
|
}}/>
|
||||||
|
</Form.Group> : null}
|
||||||
|
|
||||||
|
|
||||||
<Form.Check
|
<Form.Check
|
||||||
type='switch'
|
type='switch'
|
||||||
id='custom-switch'
|
id='custom-switch'
|
||||||
@ -142,54 +169,44 @@ class GeneralSettings extends React.Component {
|
|||||||
* inital load of already specified settings from backend
|
* inital load of already specified settings from backend
|
||||||
*/
|
*/
|
||||||
loadSettings() {
|
loadSettings() {
|
||||||
const updateRequest = new FormData();
|
callAPI('settings.php', {action: 'loadGeneralSettings'}, (result) => {
|
||||||
updateRequest.append('action', 'loadGeneralSettings');
|
this.setState({
|
||||||
|
videopath: result.video_path,
|
||||||
|
tvshowpath: result.episode_path,
|
||||||
|
mediacentername: result.mediacenter_name,
|
||||||
|
password: result.password,
|
||||||
|
passwordsupport: result.passwordEnabled,
|
||||||
|
tmdbsupport: result.TMDB_grabbing,
|
||||||
|
|
||||||
fetch('/api/settings.php', {method: 'POST', body: updateRequest})
|
videonr: result.videonr,
|
||||||
.then((response) => response.json()
|
dbsize: result.dbsize,
|
||||||
.then((result) => {
|
difftagnr: result.difftagnr,
|
||||||
console.log(result);
|
tagsadded: result.tagsadded
|
||||||
this.setState({
|
});
|
||||||
videopath: result.video_path,
|
});
|
||||||
tvshowpath: result.episode_path,
|
|
||||||
mediacentername: result.mediacenter_name,
|
|
||||||
password: result.password,
|
|
||||||
passwordsupport: result.passwordEnabled,
|
|
||||||
tmdbsupport: result.TMDB_grabbing,
|
|
||||||
|
|
||||||
videonr: result.videonr,
|
|
||||||
dbsize: result.dbsize,
|
|
||||||
difftagnr: result.difftagnr,
|
|
||||||
tagsadded: result.tagsadded
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save the selected and typed settings to the backend
|
* save the selected and typed settings to the backend
|
||||||
*/
|
*/
|
||||||
saveSettings() {
|
saveSettings() {
|
||||||
const updateRequest = new FormData();
|
callAPI('settings.php', {
|
||||||
updateRequest.append('action', 'saveGeneralSettings');
|
action: 'saveGeneralSettings',
|
||||||
|
password: this.state.passwordsupport ? this.state.password : '-1',
|
||||||
updateRequest.append('password', this.state.passwordsupport ? this.state.password : '-1');
|
videopath: this.state.videopath,
|
||||||
updateRequest.append('videopath', this.state.videopath);
|
tvshowpath: this.state.tvshowpath,
|
||||||
updateRequest.append('tvshowpath', this.state.tvshowpath);
|
mediacentername: this.state.mediacentername,
|
||||||
updateRequest.append('mediacentername', this.state.mediacentername);
|
tmdbsupport: this.state.tmdbsupport,
|
||||||
updateRequest.append('tmdbsupport', this.state.tmdbsupport);
|
darkmodeenabled: GlobalInfos.isDarkTheme().toString()
|
||||||
updateRequest.append('darkmodeenabled', GlobalInfos.isDarkTheme().toString());
|
}, (result) => {
|
||||||
|
if (result.success) {
|
||||||
fetch('/api/settings.php', {method: 'POST', body: updateRequest})
|
console.log('successfully saved settings');
|
||||||
.then((response) => response.json()
|
// todo 2020-07-10: popup success
|
||||||
.then((result) => {
|
} else {
|
||||||
if (result.success) {
|
console.log('failed to save settings');
|
||||||
console.log('successfully saved settings');
|
// todo 2020-07-10: popup error
|
||||||
// todo 2020-07-10: popup success
|
}
|
||||||
} else {
|
});
|
||||||
console.log('failed to save settings');
|
|
||||||
// todo 2020-07-10: popup error
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
width: 40%;
|
width: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.customapiform{
|
||||||
|
margin-top: 15px;
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
.infoheader {
|
.infoheader {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import GeneralSettings from './GeneralSettings';
|
import GeneralSettings from './GeneralSettings';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
|
||||||
describe('<GeneralSettings/>', function () {
|
describe('<GeneralSettings/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import style from './MovieSettings.module.css';
|
import style from './MovieSettings.module.css';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component for MovieSettings on Settingspage
|
* Component for MovieSettings on Settingspage
|
||||||
@ -50,23 +51,17 @@ class MovieSettings extends React.Component {
|
|||||||
this.setState({startbtnDisabled: true});
|
this.setState({startbtnDisabled: true});
|
||||||
|
|
||||||
console.log('starting');
|
console.log('starting');
|
||||||
const request = new FormData();
|
|
||||||
request.append('action', 'startReindex');
|
callAPI('settings.php', {action: 'startReindex'}, (result) => {
|
||||||
// fetch all videos available
|
console.log(result);
|
||||||
fetch('/api/settings.php', {method: 'POST', body: request})
|
if (result.success) {
|
||||||
.then((response) => response.json()
|
console.log('started successfully');
|
||||||
.then((result) => {
|
} else {
|
||||||
console.log(result);
|
console.log('error, reindex already running');
|
||||||
if (result.success) {
|
this.setState({startbtnDisabled: true});
|
||||||
console.log('started successfully');
|
}
|
||||||
} else {
|
});
|
||||||
console.log('error, reindex already running');
|
|
||||||
this.setState({startbtnDisabled: true});
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
|
||||||
if (this.myinterval) {
|
if (this.myinterval) {
|
||||||
clearInterval(this.myinterval);
|
clearInterval(this.myinterval);
|
||||||
}
|
}
|
||||||
@ -77,49 +72,33 @@ class MovieSettings extends React.Component {
|
|||||||
* This interval function reloads the current status of reindexing from backend
|
* This interval function reloads the current status of reindexing from backend
|
||||||
*/
|
*/
|
||||||
updateStatus = () => {
|
updateStatus = () => {
|
||||||
const request = new FormData();
|
callAPI('settings.php', {action: 'getStatusMessage'}, (result) => {
|
||||||
request.append('action', 'getStatusMessage');
|
if (result.contentAvailable === true) {
|
||||||
|
console.log(result);
|
||||||
|
// todo 2020-07-4: scroll to bottom of div here
|
||||||
|
this.setState({
|
||||||
|
// insert a string for each line
|
||||||
|
text: [...result.message.split('\n'),
|
||||||
|
...this.state.text]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// clear refresh interval if no content available
|
||||||
|
clearInterval(this.myinterval);
|
||||||
|
|
||||||
fetch('/api/settings.php', {method: 'POST', body: request})
|
this.setState({startbtnDisabled: false});
|
||||||
.then((response) => response.json()
|
}
|
||||||
.then((result) => {
|
});
|
||||||
if (result.contentAvailable === true) {
|
|
||||||
console.log(result);
|
|
||||||
// todo 2020-07-4: scroll to bottom of div here
|
|
||||||
this.setState({
|
|
||||||
// insert a string for each line
|
|
||||||
text: [...result.message.split('\n'),
|
|
||||||
...this.state.text]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// clear refresh interval if no content available
|
|
||||||
clearInterval(this.myinterval);
|
|
||||||
|
|
||||||
this.setState({startbtnDisabled: false});
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* send request to cleanup db gravity
|
* send request to cleanup db gravity
|
||||||
*/
|
*/
|
||||||
cleanupGravity() {
|
cleanupGravity() {
|
||||||
const request = new FormData();
|
callAPI('settings.php', {action: 'cleanupGravity'}, (result) => {
|
||||||
request.append('action', 'cleanupGravity');
|
this.setState({
|
||||||
|
text: ['successfully cleaned up gravity!']
|
||||||
fetch('/api/settings.php', {method: 'POST', body: request})
|
|
||||||
.then((response) => response.text()
|
|
||||||
.then((result) => {
|
|
||||||
this.setState({
|
|
||||||
text: ['successfully cleaned up gravity!']
|
|
||||||
});
|
|
||||||
}))
|
|
||||||
.catch(() => {
|
|
||||||
console.log('no connection to backend');
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,18 @@ import React from 'react';
|
|||||||
import MovieSettings from './MovieSettings';
|
import MovieSettings from './MovieSettings';
|
||||||
import GeneralSettings from './GeneralSettings';
|
import GeneralSettings from './GeneralSettings';
|
||||||
import style from './SettingsPage.module.css';
|
import style from './SettingsPage.module.css';
|
||||||
import GlobalInfos from '../../GlobalInfos';
|
import GlobalInfos from '../../utils/GlobalInfos';
|
||||||
|
|
||||||
|
type SettingsPageState = {
|
||||||
|
currentpage: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Settingspage handles all kinds of settings for the mediacenter
|
* The Settingspage handles all kinds of settings for the mediacenter
|
||||||
* and is basically a wrapper for child-tabs
|
* and is basically a wrapper for child-tabs
|
||||||
*/
|
*/
|
||||||
class SettingsPage extends React.Component {
|
class SettingsPage extends React.Component<{}, SettingsPageState> {
|
||||||
constructor(props, context) {
|
constructor(props: Readonly<{}> | {}, context?: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -21,7 +25,7 @@ class SettingsPage extends React.Component {
|
|||||||
* load the selected tab
|
* load the selected tab
|
||||||
* @returns {JSX.Element|string} the jsx element of the selected tab
|
* @returns {JSX.Element|string} the jsx element of the selected tab
|
||||||
*/
|
*/
|
||||||
getContent() {
|
getContent(): JSX.Element | string {
|
||||||
switch (this.state.currentpage) {
|
switch (this.state.currentpage) {
|
||||||
case 'general':
|
case 'general':
|
||||||
return <GeneralSettings/>;
|
return <GeneralSettings/>;
|
||||||
@ -34,7 +38,7 @@ class SettingsPage extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() : JSX.Element {
|
||||||
const themestyle = GlobalInfos.getThemeStyle();
|
const themestyle = GlobalInfos.getThemeStyle();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
@ -6,7 +6,7 @@ import '@testing-library/jest-dom/extend-expect';
|
|||||||
|
|
||||||
import {configure} from 'enzyme';
|
import {configure} from 'enzyme';
|
||||||
import Adapter from 'enzyme-adapter-react-16';
|
import Adapter from 'enzyme-adapter-react-16';
|
||||||
import GlobalInfos from './GlobalInfos';
|
import GlobalInfos from './utils/GlobalInfos';
|
||||||
|
|
||||||
configure({adapter: new Adapter()});
|
configure({adapter: new Adapter()});
|
||||||
|
|
||||||
@ -45,3 +45,21 @@ global.prepareViewBinding = (func) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.callAPIMock = (resonse) => {
|
||||||
|
const helpers = require("./utils/Api");
|
||||||
|
helpers.callAPI = jest.fn().mockImplementation((_, __, func1) => {func1(resonse)});
|
||||||
|
}
|
||||||
|
|
||||||
|
// code to run before each test
|
||||||
|
global.beforeEach(() => {
|
||||||
|
// empty fetch response implementation for each test
|
||||||
|
global.fetch = prepareFetchApi({});
|
||||||
|
// todo with callAPIMock
|
||||||
|
})
|
||||||
|
|
||||||
|
global.afterEach(() => {
|
||||||
|
// clear all mocks after each test
|
||||||
|
jest.resetAllMocks();
|
||||||
|
})
|
||||||
|
|
||||||
|
88
src/utils/Api.ts
Normal file
88
src/utils/Api.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
let customBackendURL: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the domain of the api backend
|
||||||
|
* @return string domain of backend http://x.x.x.x/bla
|
||||||
|
*/
|
||||||
|
export function getBackendDomain(): string {
|
||||||
|
let userAgent = navigator.userAgent.toLowerCase();
|
||||||
|
if (userAgent.indexOf(' electron/') > -1) {
|
||||||
|
// Electron-specific code - force a custom backendurl
|
||||||
|
return (customBackendURL);
|
||||||
|
} else {
|
||||||
|
// use custom only if defined
|
||||||
|
if (customBackendURL) {
|
||||||
|
return (customBackendURL);
|
||||||
|
} else {
|
||||||
|
return (window.location.origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set a custom backend domain
|
||||||
|
* @param domain a url in format [http://x.x.x.x/somanode]
|
||||||
|
*/
|
||||||
|
export function setCustomBackendDomain(domain: string) {
|
||||||
|
customBackendURL = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a helper function to get the api path
|
||||||
|
*/
|
||||||
|
function getAPIDomain(): string {
|
||||||
|
return getBackendDomain() + '/api/';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interface how an api request should look like
|
||||||
|
*/
|
||||||
|
interface ApiBaseRequest {
|
||||||
|
action: string,
|
||||||
|
|
||||||
|
[_: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function to build a formdata for requesting post data correctly
|
||||||
|
* @param args api request object
|
||||||
|
*/
|
||||||
|
function buildFormData(args: ApiBaseRequest): FormData {
|
||||||
|
const req = new FormData();
|
||||||
|
|
||||||
|
for (const i in args) {
|
||||||
|
req.append(i, args[i]);
|
||||||
|
}
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A backend api call
|
||||||
|
* @param apinode which api backend handler to call
|
||||||
|
* @param fd the object to send to backend
|
||||||
|
* @param callback the callback with json reply from backend
|
||||||
|
* @param errorcallback a optional callback if an error occured
|
||||||
|
*/
|
||||||
|
export function callAPI(apinode: string, fd: ApiBaseRequest, callback: (_: object) => void, errorcallback: (_: object) => void = (_: object) => {
|
||||||
|
}): void {
|
||||||
|
fetch(getAPIDomain() + apinode, {method: 'POST', body: buildFormData(fd)})
|
||||||
|
.then((response) => response.json()
|
||||||
|
.then((result) => {
|
||||||
|
callback(result);
|
||||||
|
})).catch(reason => errorcallback(reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A backend api call
|
||||||
|
* @param apinode which api backend handler to call
|
||||||
|
* @param fd the object to send to backend
|
||||||
|
* @param callback the callback with PLAIN text reply from backend
|
||||||
|
*/
|
||||||
|
export function callAPIPlain(apinode: string, fd: ApiBaseRequest, callback: (_: any) => void): void {
|
||||||
|
fetch(getAPIDomain() + apinode, {method: 'POST', body: buildFormData(fd)})
|
||||||
|
.then((response) => response.text()
|
||||||
|
.then((result) => {
|
||||||
|
callback(result);
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import darktheme from './AppDarkTheme.module.css';
|
import darktheme from '../AppDarkTheme.module.css';
|
||||||
import lighttheme from './AppLightTheme.module.css';
|
import lighttheme from '../AppLightTheme.module.css';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is available for all components in project
|
* This class is available for all components in project
|
||||||
@ -7,7 +7,7 @@ import lighttheme from './AppLightTheme.module.css';
|
|||||||
*/
|
*/
|
||||||
class StaticInfos {
|
class StaticInfos {
|
||||||
#darktheme = true;
|
#darktheme = true;
|
||||||
#viewbinding = () => {console.warn("Viewbinding not set now!")}
|
#viewbinding = () => {console.warn('Viewbinding not set now!');};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check if the current theme is the dark theme
|
* check if the current theme is the dark theme
|
||||||
@ -37,7 +37,7 @@ class StaticInfos {
|
|||||||
* set the global Viewbinding for the main Navigation
|
* set the global Viewbinding for the main Navigation
|
||||||
* @param cb
|
* @param cb
|
||||||
*/
|
*/
|
||||||
setViewBinding(cb){
|
setViewBinding(cb) {
|
||||||
this.#viewbinding = cb;
|
this.#viewbinding = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class StaticInfos {
|
|||||||
* return the Viewbinding for main navigation
|
* return the Viewbinding for main navigation
|
||||||
* @returns {StaticInfos.viewbinding}
|
* @returns {StaticInfos.viewbinding}
|
||||||
*/
|
*/
|
||||||
getViewBinding(){
|
getViewBinding() {
|
||||||
return this.#viewbinding;
|
return this.#viewbinding;
|
||||||
}
|
}
|
||||||
}
|
}
|
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES5",
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user