diff --git a/api/Settings.php b/api/Settings.php index f45410c..12e4347 100644 --- a/api/Settings.php +++ b/api/Settings.php @@ -22,13 +22,15 @@ class Settings extends RequestBase { $videopath = $_POST['videopath']; $tvshowpath = $_POST['tvshowpath']; $tmdbsupport = $_POST['tmdbsupport']; + $darkmodeenabled = $_POST['darkmodeenabled']; $query = "UPDATE settings SET video_path='$videopath', episode_path='$tvshowpath', password='$password', mediacenter_name='$mediacentername', - TMDB_grabbing=$tmdbsupport + TMDB_grabbing=$tmdbsupport, + DarkMode=$darkmodeenabled WHERE 1"; if ($this->conn->query($query) === true) { @@ -44,12 +46,10 @@ class Settings extends RequestBase { $result = $this->conn->query($query); $r = mysqli_fetch_assoc($result); - if ($r['password'] != "-1") { - $r['passwordEnabled'] = true; - } else { - $r['passwordEnabled'] = false; - } + + $r['passwordEnabled'] = $r['password'] != "-1"; unset($r['password']); + $r['DarkMode'] = (bool)($r['DarkMode'] != '0'); echo json_encode($r); }); } diff --git a/database.sql b/database.sql index ba71abd..b83c0fe 100644 --- a/database.sql +++ b/database.sql @@ -36,6 +36,8 @@ create table settings episode_path varchar(255) null, password varchar(32) default '-1' null, mediacenter_name varchar(32) default 'OpenMediaCenter' null, + TMDB_grabbing tinyint null, + DarkMode tinyint default 0 null PRIMARY KEY (id) ); diff --git a/src/App.css b/src/App.css deleted file mode 100644 index af62d60..0000000 --- a/src/App.css +++ /dev/null @@ -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); -} diff --git a/src/App.js b/src/App.js index 6793af3..6aba9c4 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,12 @@ import React from 'react'; -import "./App.css" import HomePage from "./pages/HomePage/HomePage"; import RandomPage from "./pages/RandomPage/RandomPage"; +import GlobalInfos from "./GlobalInfos"; // include bootstraps css import 'bootstrap/dist/css/bootstrap.min.css'; +import style from './App.module.css' + import SettingsPage from "./pages/SettingsPage/SettingsPage"; import CategoryPage from "./pages/CategoryPage/CategoryPage"; @@ -32,7 +34,9 @@ class App extends React.Component { fetch('/api/Settings.php', {method: 'POST', body: updateRequest}) .then((response) => response.json() .then((result) => { - console.log(result); + // set theme + GlobalInfos.enableDarkTheme(result.DarkMode); + this.setState({ generalSettingsLoaded: true, passwordsupport: result.passwordEnabled, @@ -79,38 +83,27 @@ class App extends React.Component { } render() { + const themeStyle = GlobalInfos.getThemeStyle(); + // add the main theme to the page body + document.body.className = themeStyle.backgroundcolor; return (
- +
this.setState({page: "default"})}>Home +
+
this.setState({page: "random"})}>Random Video +
+
this.setState({page: "categories"})}>Categories +
+
this.setState({page: "settings"})}>Settings +
+
{this.state.generalSettingsLoaded ? this.MainBody() : "loading"} ); diff --git a/src/App.module.css b/src/App.module.css new file mode 100644 index 0000000..adb0857 --- /dev/null +++ b/src/App.module.css @@ -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; +} + + diff --git a/src/App.test.js b/src/App.test.js index e2b2d33..cc12113 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -10,19 +10,19 @@ describe('', function () { it('renders title', () => { const wrapper = shallow(); - expect(wrapper.find('.navbar-brand').text()).toBe('OpenMediaCenter'); + expect(wrapper.find('.navbrand').text()).toBe('OpenMediaCenter'); }); it('are navlinks correct', function () { const wrapper = shallow(); - expect(wrapper.find('nav').find('li')).toHaveLength(4); + expect(wrapper.find('.navitem')).toHaveLength(4); }); it('simulate video view change ', function () { const wrapper = shallow(); wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed - wrapper.instance().changeRootElement(
); + wrapper.instance().changeRootElement(
); expect(wrapper.find("#testit")).toHaveLength(1); }); @@ -31,7 +31,7 @@ describe('', function () { const wrapper = shallow(); wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed - wrapper.instance().changeRootElement(
); + wrapper.instance().changeRootElement(
); expect(wrapper.find("#testit")).toHaveLength(1); @@ -44,9 +44,9 @@ describe('', function () { const wrapper = shallow(); 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(
); + wrapper.instance().changeRootElement(
); expect(wrapper.find("#testit")).toHaveLength(1); @@ -61,7 +61,7 @@ describe('', function () { wrapper.setState({page: "wrongvalue"}); 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); }); @@ -70,7 +70,7 @@ describe('', function () { wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed 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); }); @@ -79,7 +79,7 @@ describe('', function () { wrapper.setState({generalSettingsLoaded: true}); // simulate fetch to have already finisheed 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); }); diff --git a/src/AppDarkTheme.module.css b/src/AppDarkTheme.module.css new file mode 100644 index 0000000..d951ea9 --- /dev/null +++ b/src/AppDarkTheme.module.css @@ -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; +} + diff --git a/src/AppLightTheme.module.css b/src/AppLightTheme.module.css new file mode 100644 index 0000000..72ace73 --- /dev/null +++ b/src/AppLightTheme.module.css @@ -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; +} diff --git a/src/GlobalInfos.js b/src/GlobalInfos.js new file mode 100644 index 0000000..af86c41 --- /dev/null +++ b/src/GlobalInfos.js @@ -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; diff --git a/src/GlobalInfos.test.js b/src/GlobalInfos.test.js new file mode 100644 index 0000000..c87cf85 --- /dev/null +++ b/src/GlobalInfos.test.js @@ -0,0 +1,24 @@ +import React from "react"; +import GlobalInfos from "./GlobalInfos"; + +describe('', 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(); + }); +}); diff --git a/src/elements/PageTitle/PageTitle.js b/src/elements/PageTitle/PageTitle.js index cce4ec9..a71d946 100644 --- a/src/elements/PageTitle/PageTitle.js +++ b/src/elements/PageTitle/PageTitle.js @@ -1,5 +1,6 @@ import React from "react"; import style from "./PageTitle.module.css" +import GlobalInfos from "../../GlobalInfos"; class PageTitle extends React.Component { constructor(props) { @@ -10,17 +11,33 @@ class PageTitle extends React.Component { } render() { + const themeStyle = GlobalInfos.getThemeStyle(); return ( -
- {this.props.title} - {this.props.subtitle} +
+ {this.props.title} + {this.props.subtitle} <> {this.props.children} -
+
); } } -export default PageTitle; \ No newline at end of file +/** + * class to override default
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 ( + <> +
+ + ); + } +} + +export default PageTitle; diff --git a/src/elements/PageTitle/PageTitle.module.css b/src/elements/PageTitle/PageTitle.module.css index c2ae436..76b3fcb 100644 --- a/src/elements/PageTitle/PageTitle.module.css +++ b/src/elements/PageTitle/PageTitle.module.css @@ -1,8 +1,5 @@ .pageheader { - margin-bottom: 20px; - margin-top: 20px; - padding-left: 22%; - padding-right: 12%; + padding: 20px 12% 20px 22%; } .pageheadertitle { @@ -11,7 +8,7 @@ } .pageheadersubtitle { - font-size: 23pt; margin-left: 20px; + font-size: 23pt; opacity: 0.6; } diff --git a/src/elements/PageTitle/PageTitle.test.js b/src/elements/PageTitle/PageTitle.test.js index 5d57908..6ed16c2 100644 --- a/src/elements/PageTitle/PageTitle.test.js +++ b/src/elements/PageTitle/PageTitle.test.js @@ -19,7 +19,7 @@ describe('', function () { it('renders pagetitle prop', function () { const wrapper = shallow(); - expect(wrapper.find(".pageheader").text()).toBe("testtitle"); + expect(wrapper.find(".pageheader").text()).toBe("testtitle"); }); it('renders subtitle prop', function () { diff --git a/src/elements/Preview/Preview.js b/src/elements/Preview/Preview.js index 4e31520..0a1cb5a 100644 --- a/src/elements/Preview/Preview.js +++ b/src/elements/Preview/Preview.js @@ -2,6 +2,7 @@ import React from "react"; import style from "./Preview.module.css"; import Player from "../../pages/Player/Player"; import {Spinner} from "react-bootstrap"; +import GlobalInfos from "../../GlobalInfos"; class Preview extends React.Component { constructor(props, context) { @@ -33,9 +34,10 @@ class Preview extends React.Component { } render() { + const themeStyle = GlobalInfos.getThemeStyle(); return ( -
this.itemClick()}> -
{this.state.name}
+
this.itemClick()}> +
{this.state.name}
{this.state.previewpicture !== null ? this.itemClick()}> -
+
this.itemClick()}> +
{this.props.name}
diff --git a/src/elements/Preview/Preview.module.css b/src/elements/Preview/Preview.module.css index 9122a92..ae23695 100644 --- a/src/elements/Preview/Preview.module.css +++ b/src/elements/Preview/Preview.module.css @@ -1,5 +1,6 @@ .previewtitle { - color: #3d3d3d; + height: 20px; + text-align: center; font-size: smaller; font-weight: bold; height: 20px; @@ -33,10 +34,8 @@ } .videopreview { - background-color: #a8c3ff; border-radius: 20px; cursor: pointer; - /*background-color: #7F7F7F;*/ float: left; margin-left: 25px; margin-top: 25px; @@ -44,7 +43,6 @@ } .videopreview:hover { - box-shadow: rgba(2, 12, 27, 0.7) 0 0 0 5px; opacity: 1; transition: all 300ms; } diff --git a/src/elements/SideBar/SideBar.js b/src/elements/SideBar/SideBar.js index 9375f33..1b050a8 100644 --- a/src/elements/SideBar/SideBar.js +++ b/src/elements/SideBar/SideBar.js @@ -1,9 +1,11 @@ import React from "react"; import style from "./SideBar.module.css" +import GlobalInfos from "../../GlobalInfos"; class SideBar extends React.Component { render() { - return (
+ const themeStyle = GlobalInfos.getThemeStyle(); + return (
{this.props.children}
); } @@ -11,16 +13,18 @@ class SideBar extends React.Component { export class SideBarTitle extends React.Component { render() { + const themeStyle = GlobalInfos.getThemeStyle(); return ( -
{this.props.children}
+
{this.props.children}
); } } export class SideBarItem extends React.Component { render() { + const themeStyle = GlobalInfos.getThemeStyle(); return ( -
{this.props.children}
+
{this.props.children}
); } } diff --git a/src/elements/SideBar/SideBar.module.css b/src/elements/SideBar/SideBar.module.css index e20342b..1ced68e 100644 --- a/src/elements/SideBar/SideBar.module.css +++ b/src/elements/SideBar/SideBar.module.css @@ -1,5 +1,4 @@ .sideinfo { - background-color: #b4c7fe; border: 2px #3574fe solid; border-radius: 20px; float: left; @@ -16,7 +15,6 @@ } .sidebarinfo { - background-color: #8ca3fc; border-radius: 5px; margin-top: 5px; padding: 2px 10px 2px 15px; diff --git a/src/index.css b/src/index.css deleted file mode 100644 index f2297d6..0000000 --- a/src/index.css +++ /dev/null @@ -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; -} diff --git a/src/index.js b/src/index.js index 88fa6af..7384bc7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import './index.css'; import App from './App'; ReactDOM.render( diff --git a/src/pages/CategoryPage/CategoryPage.js b/src/pages/CategoryPage/CategoryPage.js index 8ca56d1..2e26468 100644 --- a/src/pages/CategoryPage/CategoryPage.js +++ b/src/pages/CategoryPage/CategoryPage.js @@ -5,7 +5,7 @@ import videocontainerstyle from "../../elements/VideoContainer/VideoContainer.mo import {TagPreview} from "../../elements/Preview/Preview"; 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"; class CategoryPage extends React.Component { @@ -56,7 +56,7 @@ class CategoryPage extends React.Component { this.loadTag(e.props.category) } }}>HD -
+