From 59cd5de78baa31150958e6d0d6733407c0e95805 Mon Sep 17 00:00:00 2001
From: Tobias Urdin <tobias.urdin@binero.se>
Date: Wed, 15 Mar 2023 23:36:45 +0000
Subject: web: add dark mode and theme selection

This adds a theme selection in the preferences in the
config modal and adds a new dark theme.

Removes the line.png image and instead uses CSS
linear-gradient that is available in all browsers
since around 2018, also fixes the 15 pixels spacing
issue that is there today.

You can select between three different themes.

Auto will use your system preference to choose either the
light or dark theme, changes dynamically based on your
system preference.

Light is the current theme.

Dark is the theme added by this patch series.

The UX this changes is that if somebody has their system
preferences set to dark, for example in Mac OS X that is
in System Settings -> Appearance -> Dark the user will
get the Zuul web UI in dark by default and same for the
opposite.

This uses a poor man's dark mode for swagger-ui
as per the comment in [1].

[1] https://github.com/swagger-api/swagger-ui/issues/5327#issuecomment-742375520

Change-Id: I01cf32f3decdb885307a76eb79d644667bbbf9a3
---
 web/src/containers/build/Console.jsx | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

(limited to 'web/src/containers/build/Console.jsx')

diff --git a/web/src/containers/build/Console.jsx b/web/src/containers/build/Console.jsx
index 9cd10df92..826ebe3cd 100644
--- a/web/src/containers/build/Console.jsx
+++ b/web/src/containers/build/Console.jsx
@@ -18,6 +18,7 @@ import * as React from 'react'
 import ReAnsi from '@softwarefactory-project/re-ansi'
 import PropTypes from 'prop-types'
 import ReactJson from 'react-json-view'
+import { connect } from 'react-redux'
 
 import {
   Button,
@@ -60,6 +61,7 @@ class TaskOutput extends React.Component {
   static propTypes = {
     data: PropTypes.object,
     include: PropTypes.array,
+    preferences: PropTypes.object,
   }
 
   renderResults(value) {
@@ -130,7 +132,8 @@ class TaskOutput extends React.Component {
             name={null}
             sortKeys={true}
             enableClipboard={false}
-            displayDataTypes={false}/>
+            displayDataTypes={false}
+            theme={this.props.preferences.darkMode ? 'tomorrow' : 'rjv-default'}/>
         </pre>
       )
     } else {
@@ -142,7 +145,7 @@ class TaskOutput extends React.Component {
     }
 
     return (
-      <div key={key}>
+      <div className={this.props.preferences.darkMode ? 'zuul-console-dark' : 'zuul-console-light'} key={key}>
         {ret && <h5>{key}</h5>}
         {ret && ret}
       </div>
@@ -170,6 +173,7 @@ class HostTask extends React.Component {
     errorIds: PropTypes.object,
     taskPath: PropTypes.array,
     displayPath: PropTypes.array,
+    preferences: PropTypes.object,
   }
 
   state = {
@@ -290,7 +294,7 @@ class HostTask extends React.Component {
       </DataListCell>
     )
 
-    const content = <TaskOutput data={this.props.host} include={INTERESTING_KEYS}/>
+    const content = <TaskOutput data={this.props.host} include={INTERESTING_KEYS} preferences={this.props.preferences}/>
 
     let item = null
     if (interestingKeys) {
@@ -354,7 +358,7 @@ class HostTask extends React.Component {
           isOpen={this.state.showModal}
           onClose={this.close}
           description={modalDescription}>
-          <TaskOutput data={host}/>
+          <TaskOutput data={host} preferences={this.props.preferences}/>
         </Modal>
       </>
     )
@@ -367,6 +371,7 @@ class PlayBook extends React.Component {
     errorIds: PropTypes.object,
     taskPath: PropTypes.array,
     displayPath: PropTypes.array,
+    preferences: PropTypes.object,
   }
 
   constructor(props) {
@@ -404,8 +409,8 @@ class PlayBook extends React.Component {
     dataListCells.push(
       <DataListCell key='name' width={1}>
         <strong>
-          {playbook.phase[0].toUpperCase() + playbook.phase.slice(1)} playbook<
-          /strong>
+          {playbook.phase[0].toUpperCase() + playbook.phase.slice(1)} playbook
+        </strong>
       </DataListCell>)
         dataListCells.push(
         <DataListCell key='path' width={5}>
@@ -463,7 +468,8 @@ class PlayBook extends React.Component {
                           taskPath={taskPath.concat([
                             idx.toString(), idx2.toString(), hostname])}
                           displayPath={displayPath} task={task} host={host}
-                          errorIds={errorIds}/>
+                          errorIds={errorIds}
+                          preferences={this.props.preferences}/>
                       ))))}
                   </DataList>
 
@@ -484,6 +490,7 @@ class Console extends React.Component {
     errorIds: PropTypes.object,
     output: PropTypes.array,
     displayPath: PropTypes.array,
+    preferences: PropTypes.object,
   }
 
   render () {
@@ -492,7 +499,7 @@ class Console extends React.Component {
     return (
       <React.Fragment>
         <br />
-        <span className="zuul-console">
+        <span className={`zuul-console ${this.props.preferences.darkMode ? 'zuul-console-dark' : 'zuul-console-light'}`}>
           <DataList isCompact={true}
                     style={{ fontSize: 'var(--pf-global--FontSize--md)' }}>
             {
@@ -500,6 +507,7 @@ class Console extends React.Component {
                 <PlayBook
                   key={idx} playbook={playbook} taskPath={[idx.toString()]}
                   displayPath={displayPath} errorIds={errorIds}
+                  preferences={this.props.preferences}
                 />))
             }
           </DataList>
@@ -509,5 +517,11 @@ class Console extends React.Component {
   }
 }
 
+function mapStateToProps(state) {
+  return {
+    preferences: state.preferences,
+  }
+}
+
 
-export default Console
+export default connect(mapStateToProps)(Console)
-- 
cgit v1.2.1

