Merge branch 'csstheming' into 'master'

Csstheming

See merge request lukas/openmediacenter!8
This commit is contained in:
Lukas Heiligenbrunner 2020-08-05 19:38:28 +00:00
commit 13336cbf1c
27 changed files with 315 additions and 114 deletions

View File

@ -22,13 +22,15 @@ class Settings extends RequestBase {
$videopath = $_POST['videopath']; $videopath = $_POST['videopath'];
$tvshowpath = $_POST['tvshowpath']; $tvshowpath = $_POST['tvshowpath'];
$tmdbsupport = $_POST['tmdbsupport']; $tmdbsupport = $_POST['tmdbsupport'];
$darkmodeenabled = $_POST['darkmodeenabled'];
$query = "UPDATE settings SET $query = "UPDATE settings SET
video_path='$videopath', video_path='$videopath',
episode_path='$tvshowpath', episode_path='$tvshowpath',
password='$password', password='$password',
mediacenter_name='$mediacentername', mediacenter_name='$mediacentername',
TMDB_grabbing=$tmdbsupport TMDB_grabbing=$tmdbsupport,
DarkMode=$darkmodeenabled
WHERE 1"; WHERE 1";
if ($this->conn->query($query) === true) { if ($this->conn->query($query) === true) {
@ -44,12 +46,10 @@ class Settings extends RequestBase {
$result = $this->conn->query($query); $result = $this->conn->query($query);
$r = mysqli_fetch_assoc($result); $r = mysqli_fetch_assoc($result);
if ($r['password'] != "-1") {
$r['passwordEnabled'] = true; $r['passwordEnabled'] = $r['password'] != "-1";
} else {
$r['passwordEnabled'] = false;
}
unset($r['password']); unset($r['password']);
$r['DarkMode'] = (bool)($r['DarkMode'] != '0');
echo json_encode($r); echo json_encode($r);
}); });
} }

View File

@ -36,6 +36,8 @@ create table settings
episode_path varchar(255) null, episode_path varchar(255) null,
password varchar(32) default '-1' null, password varchar(32) default '-1' null,
mediacenter_name varchar(32) default 'OpenMediaCenter' null, mediacenter_name varchar(32) default 'OpenMediaCenter' null,
TMDB_grabbing tinyint null,
DarkMode tinyint default 0 null
PRIMARY KEY (id) PRIMARY KEY (id)
); );

View File

@ -1,12 +0,0 @@
.nav-item {
cursor: pointer;
}
.nav-link {
color: rgba(255, 255, 255, .5);
font-weight: bold;
}
.nav-link:hover {
color: rgba(255, 255, 255, 1);
}

View File

@ -1,10 +1,12 @@
import React from 'react'; import React from 'react';
import "./App.css"
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";
// include bootstraps css // include bootstraps css
import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/css/bootstrap.min.css';
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";
@ -32,7 +34,9 @@ class App extends React.Component {
fetch('/api/Settings.php', {method: 'POST', body: updateRequest}) fetch('/api/Settings.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()
.then((result) => { .then((result) => {
console.log(result); // set theme
GlobalInfos.enableDarkTheme(result.DarkMode);
this.setState({ this.setState({
generalSettingsLoaded: true, generalSettingsLoaded: true,
passwordsupport: result.passwordEnabled, passwordsupport: result.passwordEnabled,
@ -79,38 +83,27 @@ class App extends React.Component {
} }
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
// add the main theme to the page body
document.body.className = themeStyle.backgroundcolor;
return ( return (
<div className="App"> <div className="App">
<nav className="navbar navbar-expand-sm bg-primary navbar-dark"> <div className={[style.navcontainer, themeStyle.backgroundcolor, themeStyle.textcolor, themeStyle.hrcolor].join(' ')}>
<div className="navbar-brand">{this.state.mediacentername}</div> <div className={style.navbrand}>{this.state.mediacentername}</div>
<ul className="navbar-nav"> <div className={[style.navitem, themeStyle.navitem, this.state.page === "default" ? style.navitemselected : {}].join(' ')}
<li className="nav-item"> onClick={() => this.setState({page: "default"})}>Home
<div className="nav-link" </div>
style={this.state.page === "default" ? {color: "rgba(255,255,255,.75"} : {}} <div className={[style.navitem, themeStyle.navitem, this.state.page === "random" ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "default"})}>Home onClick={() => this.setState({page: "random"})}>Random Video
</div> </div>
</li> <div className={[style.navitem, themeStyle.navitem, this.state.page === "categories" ? style.navitemselected : {}].join(' ')}
<li className="nav-item"> onClick={() => this.setState({page: "categories"})}>Categories
<div className="nav-link" </div>
style={this.state.page === "random" ? {color: "rgba(255,255,255,.75"} : {}} <div className={[style.navitem, themeStyle.navitem, this.state.page === "settings" ? style.navitemselected : {}].join(' ')}
onClick={() => this.setState({page: "random"})}>Random Video onClick={() => this.setState({page: "settings"})}>Settings
</div> </div>
</li> </div>
<li className="nav-item">
<div className="nav-link"
style={this.state.page === "categories" ? {color: "rgba(255,255,255,.75"} : {}}
onClick={() => this.setState({page: "categories"})}>Categories
</div>
</li>
<li className="nav-item">
<div className="nav-link"
style={this.state.page === "settings" ? {color: "rgba(255,255,255,.75"} : {}}
onClick={() => this.setState({page: "settings"})}>Settings
</div>
</li>
</ul>
</nav>
{this.state.generalSettingsLoaded ? this.MainBody() : "loading"} {this.state.generalSettingsLoaded ? this.MainBody() : "loading"}
</div> </div>
); );

54
src/App.module.css Normal file
View File

@ -0,0 +1,54 @@
.navitem {
float: left;
margin-left: 20px;
cursor: pointer;
opacity: 0.6;
font-size: large;
font-weight: bold;
text-transform: capitalize;
}
.navitem:hover {
opacity: 1;
transition: opacity .5s;
}
.navitem::after {
content: '';
display: block;
width: 0;
height: 2px;
transition: width .3s;
}
.navitem:hover::after {
width: 100%;
}
.navitemselected {
opacity: 0.85;
}
.navcontainer {
padding-top: 20px;
width: 100%;
padding-bottom: 40px;
border-width: 0;
border-style: dotted;
border-bottom-width: 2px;
}
.navbrand {
margin-left: 20px;
margin-right: 20px;
font-size: large;
font-weight: bold;
text-transform: capitalize;
float: left;
}

View File

@ -10,19 +10,19 @@ describe('<App/>', function () {
it('renders title', () => { it('renders title', () => {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
expect(wrapper.find('.navbar-brand').text()).toBe('OpenMediaCenter'); expect(wrapper.find('.navbrand').text()).toBe('OpenMediaCenter');
}); });
it('are navlinks correct', function () { it('are navlinks correct', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
expect(wrapper.find('nav').find('li')).toHaveLength(4); expect(wrapper.find('.navitem')).toHaveLength(4);
}); });
it('simulate video view change ', function () { it('simulate video view change ', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
wrapper.instance().changeRootElement(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
}); });
@ -31,7 +31,7 @@ describe('<App/>', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
wrapper.instance().changeRootElement(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
@ -44,9 +44,9 @@ describe('<App/>', function () {
const wrapper = shallow(<App/>); const wrapper = shallow(<App/>);
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
wrapper.find(".nav-link").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click"); wrapper.find(".navitem").findWhere(t => t.text() === "Random Video" && t.type() === "div").simulate("click");
wrapper.instance().changeRootElement(<div id='testit'></div>); wrapper.instance().changeRootElement(<div id='testit'/>);
expect(wrapper.find("#testit")).toHaveLength(1); expect(wrapper.find("#testit")).toHaveLength(1);
@ -61,7 +61,7 @@ describe('<App/>', function () {
wrapper.setState({page: "wrongvalue"}); wrapper.setState({page: "wrongvalue"});
expect(wrapper.find("HomePage")).toHaveLength(0); expect(wrapper.find("HomePage")).toHaveLength(0);
wrapper.find(".nav-link").findWhere(t => t.text() === "Home" && t.type() === "div").simulate("click"); wrapper.find(".navitem").findWhere(t => t.text() === "Home" && t.type() === "div").simulate("click");
expect(wrapper.find("HomePage")).toHaveLength(1); expect(wrapper.find("HomePage")).toHaveLength(1);
}); });
@ -70,7 +70,7 @@ describe('<App/>', function () {
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
expect(wrapper.find("CategoryPage")).toHaveLength(0); expect(wrapper.find("CategoryPage")).toHaveLength(0);
wrapper.find(".nav-link").findWhere(t => t.text() === "Categories" && t.type() === "div").simulate("click"); wrapper.find(".navitem").findWhere(t => t.text() === "Categories" && t.type() === "div").simulate("click");
expect(wrapper.find("CategoryPage")).toHaveLength(1); expect(wrapper.find("CategoryPage")).toHaveLength(1);
}); });
@ -79,7 +79,7 @@ describe('<App/>', function () {
wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed
expect(wrapper.find("SettingsPage")).toHaveLength(0); expect(wrapper.find("SettingsPage")).toHaveLength(0);
wrapper.find(".nav-link").findWhere(t => t.text() === "Settings" && t.type() === "div").simulate("click"); wrapper.find(".navitem").findWhere(t => t.text() === "Settings" && t.type() === "div").simulate("click");
expect(wrapper.find("SettingsPage")).toHaveLength(1); expect(wrapper.find("SettingsPage")).toHaveLength(1);
}); });

View File

@ -0,0 +1,40 @@
/**
* The coloring elements for dark theme
*/
.backgroundcolor {
background-color: #141520;
}
.textcolor {
color: white;
}
.subtextcolor {
color: #dedad6;
}
.lighttextcolor {
color: #d5d5d5;
}
.navitem::after {
background: white;
}
.hrcolor {
border-color: rgba(255, 255, 255, .1);
}
.secbackground {
background-color: #3c3d48;
}
.thirdbackground {
background-color: #141520;
}
.preview:hover {
box-shadow: rgba(255, 255, 255, 0.7) 0 0 0 5px;
}

View File

@ -0,0 +1,39 @@
/**
* The coloring elements for light theme
*/
.navitem::after {
background: black;
}
.backgroundcolor {
background-color: white;
}
.textcolor {
color: black;
}
.subtextcolor {
color: #212529;
}
.lighttextcolor {
color: #3d3d3d;
}
.hrcolor {
border-color: rgba(0, 0, 0, 0.1);
}
.secbackground {
background-color: #a8c3ff;
}
.thirdbackground {
background-color: #8ca3fc;
}
.preview:hover {
box-shadow: rgba(2, 12, 27, 0.7) 0 0 0 5px;
}

23
src/GlobalInfos.js Normal file
View File

@ -0,0 +1,23 @@
import darktheme from "./AppDarkTheme.module.css";
import lighttheme from "./AppLightTheme.module.css";
class StaticInfos {
#darktheme = true;
isDarkTheme() {
return this.#darktheme;
};
enableDarkTheme(enable = true){
this.#darktheme = enable;
}
getThemeStyle(){
return this.isDarkTheme() ? darktheme : lighttheme;
}
}
const GlobalInfos = new StaticInfos();
//Object.freeze(StaticInfos);
export default GlobalInfos;

24
src/GlobalInfos.test.js Normal file
View File

@ -0,0 +1,24 @@
import React from "react";
import GlobalInfos from "./GlobalInfos";
describe('<GlobalInfos/>', function () {
it('always same instance ', function () {
GlobalInfos.enableDarkTheme(true);
expect(GlobalInfos.isDarkTheme()).toBe(true);
GlobalInfos.enableDarkTheme(false);
expect(GlobalInfos.isDarkTheme()).toBe(false);
});
it('test default theme', function () {
expect(GlobalInfos.isDarkTheme()).toBe(false);
});
it('test receive of stylesheet', function () {
const style = GlobalInfos.getThemeStyle();
expect(style.navitem).not.toBeNull();
});
});

View File

@ -1,5 +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";
class PageTitle extends React.Component { class PageTitle extends React.Component {
constructor(props) { constructor(props) {
@ -10,17 +11,33 @@ class PageTitle extends React.Component {
} }
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<div className={style.pageheader}> <div className={style.pageheader + ' ' + themeStyle.backgroundcolor}>
<span className={style.pageheadertitle}>{this.props.title}</span> <span className={style.pageheadertitle + ' ' + themeStyle.textcolor}>{this.props.title}</span>
<span className={style.pageheadersubtitle}>{this.props.subtitle}</span> <span className={style.pageheadersubtitle + ' ' + themeStyle.textcolor}>{this.props.subtitle}</span>
<> <>
{this.props.children} {this.props.children}
</> </>
<hr/> <Line/>
</div> </div>
); );
} }
} }
export default PageTitle; /**
* class to override default <hr> color and styling
* use this for horizontal lines to use the current active theming
*/
export class Line extends React.Component {
render() {
const themeStyle = GlobalInfos.getThemeStyle();
return (
<>
<hr className={themeStyle.hrcolor}/>
</>
);
}
}
export default PageTitle;

View File

@ -1,8 +1,5 @@
.pageheader { .pageheader {
margin-bottom: 20px; padding: 20px 12% 20px 22%;
margin-top: 20px;
padding-left: 22%;
padding-right: 12%;
} }
.pageheadertitle { .pageheadertitle {
@ -11,7 +8,7 @@
} }
.pageheadersubtitle { .pageheadersubtitle {
font-size: 23pt;
margin-left: 20px; margin-left: 20px;
font-size: 23pt;
opacity: 0.6; opacity: 0.6;
} }

View File

@ -19,7 +19,7 @@ describe('<Preview/>', function () {
it('renders pagetitle prop', function () { it('renders pagetitle prop', function () {
const wrapper = shallow(<PageTitle title='testtitle'/>); const wrapper = shallow(<PageTitle title='testtitle'/>);
expect(wrapper.find(".pageheader").text()).toBe("testtitle"); expect(wrapper.find(".pageheader").text()).toBe("testtitle<Line />");
}); });
it('renders subtitle prop', function () { it('renders subtitle prop', function () {

View File

@ -2,6 +2,7 @@ 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";
class Preview extends React.Component { class Preview extends React.Component {
constructor(props, context) { constructor(props, context) {
@ -33,9 +34,10 @@ class Preview extends React.Component {
} }
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<div className={style.videopreview} onClick={() => this.itemClick()}> <div className={style.videopreview + ' ' + themeStyle.secbackground + ' '+ themeStyle.preview} onClick={() => this.itemClick()}>
<div className={style.previewtitle}>{this.state.name}</div> <div className={style.previewtitle + ' '+ themeStyle.lighttextcolor}>{this.state.name}</div>
<div className={style.previewpic}> <div className={style.previewpic}>
{this.state.previewpicture !== null ? {this.state.previewpicture !== null ?
<img className={style.previewimage} <img className={style.previewimage}
@ -63,9 +65,10 @@ class Preview extends React.Component {
export class TagPreview extends React.Component { export class TagPreview extends React.Component {
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<div className={style.videopreview + ' ' + style.tagpreview} onClick={() => this.itemClick()}> <div className={style.videopreview + ' ' + style.tagpreview + ' ' + themeStyle.secbackground + ' '+ themeStyle.preview} onClick={() => this.itemClick()}>
<div className={style.tagpreviewtitle}> <div className={style.tagpreviewtitle + ' ' + themeStyle.lighttextcolor}>
{this.props.name} {this.props.name}
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
.previewtitle { .previewtitle {
color: #3d3d3d; height: 20px;
text-align: center;
font-size: smaller; font-size: smaller;
font-weight: bold; font-weight: bold;
height: 20px; height: 20px;
@ -33,10 +34,8 @@
} }
.videopreview { .videopreview {
background-color: #a8c3ff;
border-radius: 20px; border-radius: 20px;
cursor: pointer; cursor: pointer;
/*background-color: #7F7F7F;*/
float: left; float: left;
margin-left: 25px; margin-left: 25px;
margin-top: 25px; margin-top: 25px;
@ -44,7 +43,6 @@
} }
.videopreview:hover { .videopreview:hover {
box-shadow: rgba(2, 12, 27, 0.7) 0 0 0 5px;
opacity: 1; opacity: 1;
transition: all 300ms; transition: all 300ms;
} }

View File

@ -1,9 +1,11 @@
import React from "react"; import React from "react";
import style from "./SideBar.module.css" import style from "./SideBar.module.css"
import GlobalInfos from "../../GlobalInfos";
class SideBar extends React.Component { class SideBar extends React.Component {
render() { render() {
return (<div className={style.sideinfo}> const themeStyle = GlobalInfos.getThemeStyle();
return (<div className={style.sideinfo + ' '+ themeStyle.secbackground}>
{this.props.children} {this.props.children}
</div>); </div>);
} }
@ -11,16 +13,18 @@ class SideBar extends React.Component {
export class SideBarTitle extends React.Component { export class SideBarTitle extends React.Component {
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<div className={style.sidebartitle}>{this.props.children}</div> <div className={style.sidebartitle + ' '+ themeStyle.subtextcolor}>{this.props.children}</div>
); );
} }
} }
export class SideBarItem extends React.Component { export class SideBarItem extends React.Component {
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<div className={style.sidebarinfo}>{this.props.children}</div> <div className={style.sidebarinfo + ' ' + themeStyle.thirdbackground + ' ' + themeStyle.lighttextcolor}>{this.props.children}</div>
); );
} }
} }

View File

@ -1,5 +1,4 @@
.sideinfo { .sideinfo {
background-color: #b4c7fe;
border: 2px #3574fe solid; border: 2px #3574fe solid;
border-radius: 20px; border-radius: 20px;
float: left; float: left;
@ -16,7 +15,6 @@
} }
.sidebarinfo { .sidebarinfo {
background-color: #8ca3fc;
border-radius: 5px; border-radius: 5px;
margin-top: 5px; margin-top: 5px;
padding: 2px 10px 2px 15px; padding: 2px 10px 2px 15px;

View File

@ -1,8 +0,0 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
margin: 0;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.css';
import App from './App'; import App from './App';
ReactDOM.render( ReactDOM.render(

View File

@ -5,7 +5,7 @@ import videocontainerstyle from "../../elements/VideoContainer/VideoContainer.mo
import {TagPreview} from "../../elements/Preview/Preview"; import {TagPreview} from "../../elements/Preview/Preview";
import NewTagPopup from "../../elements/NewTagPopup/NewTagPopup"; import NewTagPopup from "../../elements/NewTagPopup/NewTagPopup";
import PageTitle from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from "../../elements/PageTitle/PageTitle";
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from "../../elements/VideoContainer/VideoContainer";
class CategoryPage extends React.Component { class CategoryPage extends React.Component {
@ -56,7 +56,7 @@ class CategoryPage extends React.Component {
this.loadTag(e.props.category) this.loadTag(e.props.category)
} }
}}>HD</Tag> }}>HD</Tag>
<hr/> <Line/>
<button data-testid='btnaddtag' className='btn btn-success' onClick={() => { <button data-testid='btnaddtag' className='btn btn-success' onClick={() => {
this.setState({popupvisible: true}) this.setState({popupvisible: true})
}}>Add a new Tag! }}>Add a new Tag!

View File

@ -4,7 +4,7 @@ import Tag from "../../elements/Tag/Tag";
import VideoContainer from "../../elements/VideoContainer/VideoContainer"; import VideoContainer from "../../elements/VideoContainer/VideoContainer";
import style from "./HomePage.module.css" import style from "./HomePage.module.css"
import PageTitle from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from "../../elements/PageTitle/PageTitle";
class HomePage extends React.Component { class HomePage extends React.Component {
/** keyword variable needed temporary store search keyword */ /** keyword variable needed temporary store search keyword */
@ -138,13 +138,13 @@ class HomePage extends React.Component {
</PageTitle> </PageTitle>
<SideBar> <SideBar>
<SideBarTitle>Infos:</SideBarTitle> <SideBarTitle>Infos:</SideBarTitle>
<hr/> <Line/>
<SideBarItem><b>{this.state.sideinfo.videonr}</b> Videos Total!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.videonr}</b> Videos Total!</SideBarItem>
<SideBarItem><b>{this.state.sideinfo.fullhdvideonr}</b> FULL-HD Videos!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.fullhdvideonr}</b> FULL-HD Videos!</SideBarItem>
<SideBarItem><b>{this.state.sideinfo.hdvideonr}</b> HD Videos!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.hdvideonr}</b> HD Videos!</SideBarItem>
<SideBarItem><b>{this.state.sideinfo.sdvideonr}</b> SD Videos!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.sdvideonr}</b> SD Videos!</SideBarItem>
<SideBarItem><b>{this.state.sideinfo.tagnr}</b> different Tags!</SideBarItem> <SideBarItem><b>{this.state.sideinfo.tagnr}</b> different Tags!</SideBarItem>
<hr/> <Line/>
<SideBarTitle>Default Tags:</SideBarTitle> <SideBarTitle>Default Tags:</SideBarTitle>
<Tag viewbinding={this.props.viewbinding}>All</Tag> <Tag viewbinding={this.props.viewbinding}>All</Tag>
<Tag viewbinding={this.props.viewbinding}>FullHd</Tag> <Tag viewbinding={this.props.viewbinding}>FullHd</Tag>

View File

@ -1,10 +1,11 @@
import React from "react"; import React from "react";
import style from "./Player.module.css" import style from "./Player.module.css"
import {PlyrComponent} from 'plyr-react'; import {PlyrComponent} from 'plyr-react';
import SideBar, {SideBarItem, SideBarTitle} from "../../elements/SideBar/SideBar"; import SideBar, {SideBarItem, SideBarTitle} from "../../elements/SideBar/SideBar";
import Tag from "../../elements/Tag/Tag"; import Tag from "../../elements/Tag/Tag";
import AddTagPopup from "../../elements/AddTagPopup/AddTagPopup"; import AddTagPopup from "../../elements/AddTagPopup/AddTagPopup";
import PageTitle from "../../elements/PageTitle/PageTitle"; import PageTitle, {Line} from "../../elements/PageTitle/PageTitle";
class Player extends React.Component { class Player extends React.Component {
@ -53,14 +54,13 @@ class Player extends React.Component {
<SideBar> <SideBar>
<SideBarTitle>Infos:</SideBarTitle> <SideBarTitle>Infos:</SideBarTitle>
<hr/> <Line/>
<SideBarItem><b>{this.state.likes}</b> Likes!</SideBarItem> <SideBarItem><b>{this.state.likes}</b> Likes!</SideBarItem>
{this.state.quality !== 0 ? {this.state.quality !== 0 ?
<SideBarItem><b>{this.state.quality}p</b> Quality!</SideBarItem> : null} <SideBarItem><b>{this.state.quality}p</b> Quality!</SideBarItem> : null}
{this.state.length !== 0 ? {this.state.length !== 0 ?
<SideBarItem><b>{Math.round(this.state.length / 60)}</b> Minutes of <SideBarItem><b>{Math.round(this.state.length / 60)}</b> Minutes of length!</SideBarItem>: null}
length!</SideBarItem> : null} <Line/>
<hr/>
<SideBarTitle>Tags:</SideBarTitle> <SideBarTitle>Tags:</SideBarTitle>
{this.state.tags.map((m) => ( {this.state.tags.map((m) => (
<Tag <Tag

View File

@ -14,6 +14,7 @@
margin-left: 20px; margin-left: 20px;
margin-top: 25px; margin-top: 25px;
width: 60%; width: 60%;
margin-top: 20px;
} }
.videoactions { .videoactions {

View File

@ -1,6 +1,7 @@
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";
class GeneralSettings extends React.Component { class GeneralSettings extends React.Component {
constructor(props) { constructor(props) {
@ -37,9 +38,10 @@ class GeneralSettings extends React.Component {
} }
render() { render() {
const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<> <>
<div className={style.GeneralForm}> <div className={style.GeneralForm + ' ' + themeStyle.subtextcolor}>
<Form data-testid='mainformsettings' onSubmit={(e) => { <Form data-testid='mainformsettings' onSubmit={(e) => {
e.preventDefault(); e.preventDefault();
this.saveSettings(); this.saveSettings();
@ -70,6 +72,14 @@ class GeneralSettings extends React.Component {
}} }}
/> />
{this.state.passwordsupport ?
<Form.Group data-testid="passwordfield">
<Form.Label>Password</Form.Label>
<Form.Control type="password" placeholder="**********" value={this.state.password}
onChange={(e) => this.setState({password: e.target.value})}/>
</Form.Group> : null
}
<Form.Check <Form.Check
type="switch" type="switch"
id="custom-switch-2" id="custom-switch-2"
@ -81,13 +91,18 @@ class GeneralSettings extends React.Component {
}} }}
/> />
{this.state.passwordsupport ? <Form.Check
<Form.Group data-testid="passwordfield"> type="switch"
<Form.Label>Password</Form.Label> id="custom-switch-3"
<Form.Control type="password" placeholder="**********" value={this.state.password} data-testid='darktheme-switch'
onChange={(e) => this.setState({password: e.target.value})}/> label="Enable Dark-Theme"
</Form.Group> : null checked={GlobalInfos.isDarkTheme()}
} onChange={() => {
GlobalInfos.enableDarkTheme(!GlobalInfos.isDarkTheme());
this.forceUpdate();
// todo initiate rerender
}}
/>
<Form.Group className={style.mediacenternameform} data-testid="nameform"> <Form.Group className={style.mediacenternameform} data-testid="nameform">
<Form.Label>The name of the Mediacenter</Form.Label> <Form.Label>The name of the Mediacenter</Form.Label>
@ -113,6 +128,7 @@ class GeneralSettings extends React.Component {
updateRequest.append('tvshowpath', this.state.tvshowpath); updateRequest.append('tvshowpath', this.state.tvshowpath);
updateRequest.append('mediacentername', this.state.mediacentername); updateRequest.append('mediacentername', this.state.mediacentername);
updateRequest.append("tmdbsupport", this.state.tmdbsupport); updateRequest.append("tmdbsupport", this.state.tmdbsupport);
updateRequest.append("darkmodeenabled", GlobalInfos.isDarkTheme());
fetch('/api/Settings.php', {method: 'POST', body: updateRequest}) fetch('/api/Settings.php', {method: 'POST', body: updateRequest})
.then((response) => response.json() .then((response) => response.json()

View File

@ -1,6 +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";
describe('<GeneralSettings/>', function () { describe('<GeneralSettings/>', function () {
it('renders without crashing ', function () { it('renders without crashing ', function () {
@ -17,6 +18,15 @@ describe('<GeneralSettings/>', function () {
expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(1); expect(wrapper.find("[data-testid='passwordfield']")).toHaveLength(1);
}); });
it('test theme switchbutton', function () {
const wrapper = shallow(<GeneralSettings/>);
GlobalInfos.enableDarkTheme(false);
expect(GlobalInfos.isDarkTheme()).toBe(false);
wrapper.find("[data-testid='darktheme-switch']").simulate("change");
expect(GlobalInfos.isDarkTheme()).toBe(true);
});
it('test savesettings', done => { it('test savesettings', done => {
const wrapper = shallow(<GeneralSettings/>); const wrapper = shallow(<GeneralSettings/>);

View File

@ -2,6 +2,7 @@ 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";
class SettingsPage extends React.Component { class SettingsPage extends React.Component {
@ -27,10 +28,11 @@ class SettingsPage extends React.Component {
} }
render() { render() {
const themestyle = GlobalInfos.getThemeStyle();
return ( return (
<div> <div>
<div className={style.SettingsSidebar}> <div className={style.SettingsSidebar + ' ' + themestyle.secbackground}>
<div className={style.SettingsSidebarTitle}>Settings</div> <div className={style.SettingsSidebarTitle + ' ' + themestyle.lighttextcolor}>Settings</div>
<div onClick={() => this.setState({currentpage: "general"})} <div onClick={() => this.setState({currentpage: "general"})}
className={style.SettingSidebarElement}>General className={style.SettingSidebarElement}>General
</div> </div>

View File

@ -1,7 +1,8 @@
.SettingsSidebar { .SettingsSidebar {
background-color: #d3dcef; border-bottom-right-radius: 10px;
border-top-right-radius: 10px;
float: left; float: left;
min-height: calc(100vh - 56px); min-height: calc(100vh - 62px);
min-width: 110px; min-width: 110px;
padding-top: 20px; padding-top: 20px;
width: 10%; width: 10%;
@ -23,7 +24,7 @@
} }
.SettingSidebarElement { .SettingSidebarElement {
background-color: #a8b2de; background-color: #919fd9;
border-radius: 7px; border-radius: 7px;
font-weight: bold; font-weight: bold;
margin: 10px 5px 5px; margin: 10px 5px 5px;