Add toggle to use browser's preferred theme (#224)
authorGregory Anders <8965202+gpanders@users.noreply.github.com>
Mon, 3 Jan 2022 13:16:43 +0000 (06:16 -0700)
committerGitHub <noreply@github.com>
Mon, 3 Jan 2022 13:16:43 +0000 (18:46 +0530)
* Add Auto theme that uses browser's preferred color scheme

This will use dark mode automatically if the browser requests it.

* fixup! Add Auto theme that uses browser's preferred color scheme

* Use a toggle to use system theme

src/app/organisms/settings/Settings.jsx
src/client/action/settings.js
src/client/state/cons.js
src/client/state/settings.js
src/index.scss

index e31af8f39f79cda711b04dffd51a3538305b87b8..9a3db0fddaaaa7e93965c1bf54c8ba0709335148 100644 (file)
@@ -5,7 +5,7 @@ import './Settings.scss';
 import initMatrix from '../../../client/initMatrix';
 import cons from '../../../client/state/cons';
 import settings from '../../../client/state/settings';
-import { toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents } from '../../../client/action/settings';
+import { toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents } from '../../../client/action/settings';
 import logout from '../../../client/action/logout';
 
 import Text from '../../atoms/text/Text';
@@ -49,20 +49,34 @@ function AppearanceSection() {
   return (
     <div className="settings-content">
       <SettingTile
-        title="Theme"
-        content={(
-          <SegmentedControls
-            selected={settings.getThemeIndex()}
-            segments={[
-              { text: 'Light' },
-              { text: 'Silver' },
-              { text: 'Dark' },
-              { text: 'Butter' },
-            ]}
-            onSelect={(index) => settings.setTheme(index)}
+        title="Follow system theme"
+        options={(
+          <Toggle
+            isActive={settings.useSystemTheme}
+            onToggle={() => { toggleSystemTheme(); updateState({}); }}
           />
         )}
+        content={<Text variant="b3">Use light or dark mode based on the system's settings.</Text>}
       />
+      {(() => {
+        if (!settings.useSystemTheme) {
+          return <SettingTile
+            title="Theme"
+            content={(
+              <SegmentedControls
+                selected={settings.getThemeIndex()}
+                segments={[
+                  { text: 'Light' },
+                  { text: 'Silver' },
+                  { text: 'Dark' },
+                  { text: 'Butter' },
+                ]}
+                onSelect={(index) => settings.setTheme(index)}
+              />
+            )}
+          />
+        }
+      })()}
       <SettingTile
         title="Markdown formatting"
         options={(
index d868c1564289a49eda375bfb26bb0a3478b1f8ab..e849702b8e54766b382f0199cdb792bcb59a91a3 100644 (file)
@@ -1,6 +1,12 @@
 import appDispatcher from '../dispatcher';
 import cons from '../state/cons';
 
+export function toggleSystemTheme() {
+  appDispatcher.dispatch({
+    type: cons.actions.settings.TOGGLE_SYSTEM_THEME,
+  });
+}
+
 export function toggleMarkdown() {
   appDispatcher.dispatch({
     type: cons.actions.settings.TOGGLE_MARKDOWN,
index de22030341c15d4958ff1a52a892a4ae22bfc28f..84359a8f9327597313380bc91f801c0e6bb2c8db 100644 (file)
@@ -54,6 +54,7 @@ const cons = {
       },
     },
     settings: {
+      TOGGLE_SYSTEM_THEME: 'TOGGLE_SYSTEM_THEME',
       TOGGLE_MARKDOWN: 'TOGGLE_MARKDOWN',
       TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
       TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
@@ -110,6 +111,7 @@ const cons = {
       ATTACHMENT_CANCELED: 'ATTACHMENT_CANCELED',
     },
     settings: {
+      SYSTEM_THEME_TOGGLED: 'SYSTEM_THEME_TOGGLED',
       MARKDOWN_TOGGLED: 'MARKDOWN_TOGGLED',
       PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
       MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
index ba31fa8273be37787dabb5810e6e1a2282d15cca..13870446e0894b42331d26f5552c8264e7c135e4 100644 (file)
@@ -23,6 +23,7 @@ class Settings extends EventEmitter {
     this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme'];
     this.themeIndex = this.getThemeIndex();
 
+    this.useSystemTheme = this.getUseSystemTheme();
     this.isMarkdown = this.getIsMarkdown();
     this.isPeopleDrawer = this.getIsPeopleDrawer();
     this.hideMembershipEvents = this.getHideMembershipEvents();
@@ -56,6 +57,15 @@ class Settings extends EventEmitter {
     this.themeIndex = themeIndex;
   }
 
+  getUseSystemTheme() {
+    if (typeof this.useSystemTheme === 'boolean') return this.useSystemTheme;
+
+    const settings = getSettings();
+    if (settings === null) return false;
+    if (typeof settings.useSystemTheme === 'undefined') return false;
+    return settings.useSystemTheme;
+  }
+
   getIsMarkdown() {
     if (typeof this.isMarkdown === 'boolean') return this.isMarkdown;
 
@@ -94,6 +104,24 @@ class Settings extends EventEmitter {
 
   setter(action) {
     const actions = {
+      [cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
+        this.useSystemTheme = !this.useSystemTheme;
+        setSettings('useSystemTheme', this.useSystemTheme);
+        const appBody = document.getElementById('appBody');
+
+        if (this.useSystemTheme) {
+          appBody.classList.add('system-theme');
+          this.themes.forEach((themeName) => {
+            if (themeName === '') return;
+            appBody.classList.remove(themeName);
+          });
+        } else {
+          appBody.classList.remove('system-theme');
+          this.setTheme(this.themeIndex);
+        }
+
+        this.emit(cons.events.settings.SYSTEM_THEME_TOGGLED, this.useSystemTheme);
+      },
       [cons.actions.settings.TOGGLE_MARKDOWN]: () => {
         this.isMarkdown = !this.isMarkdown;
         setSettings('isMarkdown', this.isMarkdown);
index 52688e8dcfb62ea57fce45c7780f878e0c149f5a..e24af3206d31851ac6704b2f63de7a3dfcde9a90 100644 (file)
   --bg-surface-extra-low-transparent: hsla(0, 0%, 91%, 0);
 }
 
-.dark-theme,
-.butter-theme {
+@mixin dark-mode() {
   /* background color | --bg-[background type]: value */
   --bg-surface: hsl(208, 8%, 20%);
   --bg-surface-transparent: hsla(208, 8%, 20%, 0);
   --font-secondary: 'Inter', 'Roboto', sans-serif;
 }
 
+.dark-theme,
+.butter-theme {
+  @include dark-mode();
+}
+
+@media (prefers-color-scheme: dark) {
+  .system-theme {
+    @include dark-mode();
+  }
+}
+
 .butter-theme {
   /* background color | --bg-[background type]: value */
   --bg-surface: hsl(64, 6%, 14%);