Przeglądaj źródła

[web] Theme and layout settings

Innokentiy Enikeev 4 lat temu
rodzic
commit
9587e92e31

+ 30 - 5
web/src/App.css

@@ -1,5 +1,15 @@
-:root {
-  --accent: darkseagreen;
+.App {
+  --accent: #388E3C;
+  --header-background: #000;
+  --header-color: white;
+  --text-color: black;
+}
+
+.App[data-theme='dark'] {
+  --header-background: #000;
+  --accent: #A5D6A7;
+  --text-color: rgba(255, 255, 255, 87%);
+  --background: #121212;
 }
 
 .App {
@@ -9,13 +19,27 @@
         "browser queue" 1fr
         "footer footer" auto
         / 4fr 5fr;
+  background: var(--background);
+  color: var(--text-color);
+}
+
+.App[data-layout='left'] {
+  grid: "header header header" auto
+        "footer browser queue" 1fr
+        / auto 4fr 4fr;
+}
+
+.App[data-layout='right'] {
+  grid: "header header header" auto
+        "browser queue footer" 1fr
+        / 4fr 4fr auto;
 }
+
 .App-header {
   grid-area: header;
-  background-color: #222;
-  color: white;
+  background-color: var(--header-background);
   display: grid;
-  grid: 1fr / auto auto 1fr;
+  grid: 1fr / auto 1fr 1fr auto;
   align-items: center;
 }
 
@@ -26,6 +50,7 @@
 }
 
 .App-title {
+  color: var(--header-color);
   font-size: 1.5em;
 }
 

+ 6 - 3
web/src/App.js

@@ -3,11 +3,12 @@ import './App.css';
 import logo from './chad-logo-256.png';
 import {storeToken, loadToken} from './Api';
 import Login from './Login';
+import Settings from './Settings';
 import UserInfo from './UserInfo';
 import Player from './Player';
 
 function storeSettings(settings) {
-  localStorage.setItem(JSON.stringify(settings));
+  localStorage.setItem('settings', JSON.stringify(settings));
   return settings;
 }
 function loadSettings() {
@@ -16,6 +17,7 @@ function loadSettings() {
 }
 
 function settingsReducer(state, action) {
+  console.log(action);
   switch(action.type) {
   case 'LOGIN_SUCCESS':
     storeToken(action.payload);
@@ -29,9 +31,9 @@ function settingsReducer(state, action) {
 }
 
 function App() {
-  const [settings, dispatcher] = useReducer(settingsReducer, loadSettings());
+  const [settings, dispatch] = useReducer(settingsReducer, loadSettings());
   if(!loadToken()) {
-    return <Login state={settings} dispatcher={dispatcher} />
+    return <Login state={settings} dispatch={dispatch} />
   }
 
   return (
@@ -39,6 +41,7 @@ function App() {
       <header className="App-header">
         <img src={logo} className="App-logo" alt="Chad music" />
         <h1 className="App-title">Chad Music</h1>
+        <Settings state={settings} dispatch={dispatch} />
         <UserInfo/>
       </header>
 

+ 59 - 6
web/src/Controls.css

@@ -1,5 +1,19 @@
-:root {
-  --controls: #EEE;
+.App {
+  --controls-background: #EEE;
+  --controls-button-background: rgb(240,240,240);
+  --controls-button-hover-background: rgba(0,0,0,0.2);
+  --controls-button-hover-disabled: rgba(0,0,0,0.1);
+}
+.App[data-theme='dark'] {
+  --controls-background: rgba(255,255,255,12%);
+  --controls-button-background: rgba(240,240,240, 12%);
+}
+.App[data-theme='dark'] button {
+  background: var(--controls-background);
+  border: 1px transparent;
+}
+.App[data-theme='dark'] svg path {
+  fill: var(--text-color);
 }
 
 .Controls {
@@ -9,11 +23,47 @@
         "cover    buttons time album"  auto
         "cover    buttons time artist"  auto
         / auto auto 1fr 10fr;
-  background-color: var(--controls);
+  background-color: var(--controls-background);
   align-items: center;
   grid-column-gap: 16px;
   padding-bottom: 4px;
 }
+.App[data-layout='left'] .Controls,
+.App[data-layout='right'] .Controls {
+  grid: "cover cover" auto
+        "progress progress" auto
+        "buttons time" auto
+        "title title"  auto
+        "album album"  auto
+        "artist artist" 1fr
+        / auto 1fr;
+  grid-column-gap: 0;
+  width: 300px;
+}
+.App[data-layout='left'] .Controls img,
+.App[data-layout='right'] .Controls img {
+  width: 300px;
+  height: 300px;
+}
+.App[data-layout='left'] .Controls-artist,
+.App[data-layout='right'] .Controls-artist {
+  padding: 0 8px;
+  align-self: start;
+}
+
+.App[data-layout='left'] .Controls-time,
+.App[data-layout='left'] .Controls-buttons,
+.App[data-layout='left'] .Controls-title,
+.App[data-layout='left'] .Controls-album,
+.App[data-layout='left'] .Controls-artist,
+.App[data-layout='right'] .Controls-time,
+.App[data-layout='right'] .Controls-buttons,
+.App[data-layout='right'] .Controls-title,
+.App[data-layout='right'] .Controls-album,
+.App[data-layout='right'] .Controls-artist {
+  padding: 0 8px;
+}
+
 .Controls-progress {
   grid-area: progress;
 }
@@ -27,9 +77,10 @@
   box-sizing: border-box;
   border: 0;
   display: block;
+  background-color: var(--controls-background);
 }
 .Controls progress::-webkit-progress-bar {
-  background-color: var(--controls);
+  background-color: var(--controls-background);
 }
 
 .Controls progress::-webkit-progress-value {
@@ -60,6 +111,7 @@
   grid-template-columns: repeat(4, auto);
   align-items: center;
   grid-column-gap: 8px;
+  justify-self: left;
 }
 .Controls-buttons button {
   height: 32px;
@@ -68,12 +120,13 @@
   border: 0;
   border-radius: 4px;
   cursor: pointer;
+  background: var(--button-background);
 }
 .Controls-buttons button:hover {
-  background: rgba(0,0,0,0.2);
+  background: var(--controls-button-hover-background);
 }
 .Controls-buttons button:disabled {
-  background: rgba(0,0,0,0.1);
+  background: var(--controls-button-hover-disabled);
   cursor: initial;
 }
 .Controls-buttons button:nth-child(2) {

+ 5 - 9
web/src/Controls.js

@@ -4,7 +4,7 @@ import Sound from 'react-sound';
 import {formatDuration} from './utils.js';
 
 export default function Controls({queue, error, sound, dispatch}) {
-  const track = queue.items[queue.pos];
+  const track = queue.items[queue.pos] || {};
   const playing = sound.playStatus === Sound.status.PLAYING;
   const elapsed = sound.position ? formatDuration(sound.position/1000) : '';
   const duration = sound.duration ? formatDuration(sound.duration/1000) : '';
@@ -16,14 +16,10 @@ export default function Controls({queue, error, sound, dispatch}) {
       </div>
       {duration && <span className="Controls-time">{elapsed} / {duration}</span>}
       {error && <span className="Controls-error">{error.code}: {error.desc}</span>}
-      {track &&
-        <>
-          <img src={track.cover || logo} alt={track.album} />
-          <div className="Controls-artist">{track.artist}</div>
-          <div className="Controls-album">{track.album}{track.year && ` [${track.year}]`}</div>
-          <div className="Controls-title">{track.no && `${track.no}. `}{track.title}</div>
-        </>
-      }
+      <img src={track.cover || logo} alt={track.album} />
+      <div className="Controls-artist">{track.artist}</div>
+      <div className="Controls-album">{track.album}{track.year && ` [${track.year}]`}</div>
+      <div className="Controls-title">{track.no && `${track.no}. `}{track.title}</div>
       <div className="Controls-buttons">
         <button disabled={queue.pos < 1} onClick={()=>dispatch({type: 'CONTROL_PREV'})}>
           <svg viewBox="0 0 24 24"><g><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"></path></g></svg>

+ 7 - 2
web/src/Login.css

@@ -1,10 +1,15 @@
 .Login {
-  background-color: #282c34;
+  --background-color: #282c34;
+  --color: white;
+}
+
+.Login {
+  background-color: var(--background-color);
   min-height: 100vh;
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   font-size: calc(10px + 2vmin);
-  color: white;
+  color: var(--color);
 }

+ 1 - 4
web/src/Queue.css

@@ -1,6 +1,3 @@
-.Queue {
-}
-
 .Queue button {
   padding: 0;
   width: 24px;
@@ -9,5 +6,5 @@
 }
 
 .Queue-active {
-  background: darkseagreen;
+  background: var(--accent);
 }

+ 9 - 0
web/src/Settings.css

@@ -0,0 +1,9 @@
+.Settings {
+  justify-self: right;
+  padding-right: 64px;
+
+  display: grid;
+  grid: auto / 1fr 1fr;
+  width: 384px;
+  grid-column-gap: 10px;
+}

+ 25 - 0
web/src/Settings.js

@@ -0,0 +1,25 @@
+import './Settings.css';
+import Select from 'react-select';
+
+const THEMES = [
+  {value: 'light', label: 'Light'},
+  {value: 'dark', label: 'Dark'}
+];
+
+const LAYOUTS = [
+  {value: 'bottom', label: 'Bottom controls'},
+  {value: 'left', label: 'Left controls'},
+  {value: 'right', label: 'Right controls'},
+];
+
+export default function Settings({state, dispatch}) {
+  const theme = THEMES.find(e => e.value === state.theme);
+  const layout = LAYOUTS.find(e => e.value === state.layout);
+
+  return (
+    <div className="Settings">
+      <Select defaultValue={theme} options={THEMES} onChange={(e)=>dispatch({type: 'THEME_SET', payload: e.value})} />
+      <Select defaultValue={layout} options={LAYOUTS} onChange={(e)=>dispatch({type: 'LAYOUT_SET', payload: e.value})} />
+    </div>
+  );
+}

+ 2 - 0
web/src/UserInfo.css

@@ -3,7 +3,9 @@
   display: grid;
   grid: 1fr / 1fr auto;
   align-items: center;
+  color: var(--header-color);
 }
+
 .UserInfo-loading { color: #999; }
 .UserInfo-error { color: #C00; }