Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
feat(#228): move theme switcher to root
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpeterparker committed Oct 24, 2019
1 parent 22d4d30 commit 36981aa
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 80 deletions.
30 changes: 30 additions & 0 deletions remote/src/app/app-root.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {Build, Component, h} from '@stencil/core';

import {Subscription} from 'rxjs';

import {TimerService} from './services/timer/timer.service';
import {AccelerometerService} from './services/accelerometer/accelerometer.service';
import {ThemeService} from './services/theme/theme.service';

@Component({
tag: 'app-root',
Expand All @@ -12,9 +15,23 @@ export class AppRoot {
private timerService: TimerService;
private accelerometerService: AccelerometerService;

private themeSubscription: Subscription;
private themeService: ThemeService;

private domBodyClassList: DOMTokenList = document.body.classList;

constructor() {
this.timerService = TimerService.getInstance();
this.accelerometerService = AccelerometerService.getInstance();
this.themeService = ThemeService.getInstance();
}

async componentWillLoad() {
this.themeSubscription = this.themeService.watch().subscribe((dark: boolean) => {
this.updateDarkModePreferences(dark);
});

await this.themeService.initDarkModePreference();
}

async componentDidLoad() {
Expand All @@ -27,6 +44,16 @@ export class AppRoot {

componentDidUnload() {
this.timerService.destroy();

if (this.themeSubscription) {
this.themeSubscription.unsubscribe();
}
}

private updateDarkModePreferences(dark: boolean) {
!dark ?
this.domBodyClassList.add('dark') :
this.domBodyClassList.remove('dark');
}

render() {
Expand All @@ -39,6 +66,8 @@ export class AppRoot {

<ion-route url="/timer" component="app-timer"></ion-route>

<ion-route url="/settings" component="app-settings"></ion-route>

<ion-route url="/about" component="app-about"></ion-route>
</ion-router>

Expand All @@ -58,6 +87,7 @@ export class AppRoot {
<ion-menu-toggle autoHide={false}>
<ion-item detail={false} href="/" routerDirection="forward"><ion-label>Remote</ion-label></ion-item>
<ion-item detail={false} href="/timer" routerDirection="forward"><ion-label>Timer</ion-label></ion-item>
<ion-item detail={false} href="/settings" routerDirection="forward"><ion-label>Settings</ion-label></ion-item>
<ion-item detail={false} href="/about" routerDirection="forward"><ion-label>About</ion-label></ion-item>
</ion-menu-toggle>
</ion-content>
Expand Down

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion remote/src/app/pages/app-remote/app-remote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,6 @@ export class AppRemote {
];
} else {
return [
<dark-mode-switch></dark-mode-switch>,
<h1 class="ion-padding">The DeckDeckGo remote control</h1>,
<a onClick={() => this.openConnectModal()} class="link-to-modal">
<p class="ion-padding-start ion-padding-end">Not connected yet, <strong>click here</strong> to find
Expand Down
13 changes: 13 additions & 0 deletions remote/src/app/pages/app-settings/app-settings.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
div.social-links {
padding-top: 32px;
padding-bottom: 32px;

a {
font-size: 3rem;

&:not(last-child) {
padding-right: 8px;
}
}
}

54 changes: 54 additions & 0 deletions remote/src/app/pages/app-settings/app-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {Component, h, State} from '@stencil/core';

import {take} from 'rxjs/operators';

import {ThemeService} from '../../services/theme/theme.service';

@Component({
tag: 'app-settings',
styleUrl: 'app-settings.scss'
})
export class AppSettings {

private themeService: ThemeService;

@State()
private darkTheme: boolean;

constructor() {
this.themeService = ThemeService.getInstance();
}

componentWillLoad() {
this.themeService.watch().pipe(take(1)).subscribe((dark: boolean) => {
this.darkTheme = dark;
});
}

async toggleTheme() {
this.darkTheme = !this.darkTheme;
await this.themeService.switch(this.darkTheme);
}

render() {
return [
<ion-header>
<ion-toolbar color="primary">
<ion-buttons slot="start">
<ion-menu-toggle>
<ion-button>
<ion-icon slot="icon-only" name="menu"></ion-icon>
</ion-button>
</ion-menu-toggle>
</ion-buttons>

<ion-title class="ion-text-uppercase">DeckDeckGo</ion-title>
</ion-toolbar>
</ion-header>,

<ion-content class="ion-padding">
<ion-toggle checked={this.darkTheme} mode="md" color="switcher" onIonChange={() => this.toggleTheme()}></ion-toggle>
</ion-content>
];
}
}
55 changes: 55 additions & 0 deletions remote/src/app/services/theme/theme.service.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {Observable, Subject} from 'rxjs';

import {get, set} from 'idb-keyval';

export class ThemeService {

private static instance: ThemeService;

private darkTheme: Subject<boolean> = new Subject<boolean>();

private constructor() {
// Private constructor, singleton
}

static getInstance() {
if (!ThemeService.instance) {
ThemeService.instance = new ThemeService();
}
return ThemeService.instance;
}

watch(): Observable<boolean> {
return this.darkTheme.asObservable();
}

async switch(dark: boolean) {
this.darkTheme.next(dark);

try {
await set('deckdeckgo_dark_mode', dark);
} catch (err) {
// We ignore this error. In worst case scenario, the application will be displayed in another theme after next refresh.
}
}

async initDarkModePreference(): Promise<void> {
try {
const savedDarkModePreference: boolean = await get('deckdeckgo_dark_mode');

// If user already specified once a preference, we use that as default
if (savedDarkModePreference !== undefined) {
this.switch(savedDarkModePreference);
return;
}
} catch (err) {
this.switch(false);
return;
}

// Otherwise we check the prefers-color-scheme of the OS
const darkModePreferenceFromMedia: MediaQueryList = window.matchMedia('(prefers-color-scheme: dark)');

this.switch(darkModePreferenceFromMedia.matches);
}
}
22 changes: 11 additions & 11 deletions remote/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ export namespace Components {
'slides': DeckdeckgoSlideDefinition[];
}
interface AppRoot {}
interface AppSettings {}
interface AppStopwatch {
'length': number;
'remaining': number;
}
interface AppStopwatchTime {}
interface AppTimer {}
interface DarkModeSwitch {}
}

declare global {
Expand Down Expand Up @@ -99,6 +99,12 @@ declare global {
new (): HTMLAppRootElement;
};

interface HTMLAppSettingsElement extends Components.AppSettings, HTMLStencilElement {}
const HTMLAppSettingsElement: {
prototype: HTMLAppSettingsElement;
new (): HTMLAppSettingsElement;
};

interface HTMLAppStopwatchElement extends Components.AppStopwatch, HTMLStencilElement {}
const HTMLAppStopwatchElement: {
prototype: HTMLAppStopwatchElement;
Expand All @@ -116,12 +122,6 @@ declare global {
prototype: HTMLAppTimerElement;
new (): HTMLAppTimerElement;
};

interface HTMLDarkModeSwitchElement extends Components.DarkModeSwitch, HTMLStencilElement {}
const HTMLDarkModeSwitchElement: {
prototype: HTMLDarkModeSwitchElement;
new (): HTMLDarkModeSwitchElement;
};
interface HTMLElementTagNameMap {
'app-about': HTMLAppAboutElement;
'app-accelerometer': HTMLAppAccelerometerElement;
Expand All @@ -132,10 +132,10 @@ declare global {
'app-remote-settings': HTMLAppRemoteSettingsElement;
'app-remote-slide-picker': HTMLAppRemoteSlidePickerElement;
'app-root': HTMLAppRootElement;
'app-settings': HTMLAppSettingsElement;
'app-stopwatch': HTMLAppStopwatchElement;
'app-stopwatch-time': HTMLAppStopwatchTimeElement;
'app-timer': HTMLAppTimerElement;
'dark-mode-switch': HTMLDarkModeSwitchElement;
}
}

Expand All @@ -161,13 +161,13 @@ declare namespace LocalJSX {
'slides'?: DeckdeckgoSlideDefinition[];
}
interface AppRoot {}
interface AppSettings {}
interface AppStopwatch {
'length'?: number;
'remaining'?: number;
}
interface AppStopwatchTime {}
interface AppTimer {}
interface DarkModeSwitch {}

interface IntrinsicElements {
'app-about': AppAbout;
Expand All @@ -179,10 +179,10 @@ declare namespace LocalJSX {
'app-remote-settings': AppRemoteSettings;
'app-remote-slide-picker': AppRemoteSlidePicker;
'app-root': AppRoot;
'app-settings': AppSettings;
'app-stopwatch': AppStopwatch;
'app-stopwatch-time': AppStopwatchTime;
'app-timer': AppTimer;
'dark-mode-switch': DarkModeSwitch;
}
}

Expand All @@ -201,10 +201,10 @@ declare module "@stencil/core" {
'app-remote-settings': LocalJSX.AppRemoteSettings & JSXBase.HTMLAttributes<HTMLAppRemoteSettingsElement>;
'app-remote-slide-picker': LocalJSX.AppRemoteSlidePicker & JSXBase.HTMLAttributes<HTMLAppRemoteSlidePickerElement>;
'app-root': LocalJSX.AppRoot & JSXBase.HTMLAttributes<HTMLAppRootElement>;
'app-settings': LocalJSX.AppSettings & JSXBase.HTMLAttributes<HTMLAppSettingsElement>;
'app-stopwatch': LocalJSX.AppStopwatch & JSXBase.HTMLAttributes<HTMLAppStopwatchElement>;
'app-stopwatch-time': LocalJSX.AppStopwatchTime & JSXBase.HTMLAttributes<HTMLAppStopwatchTimeElement>;
'app-timer': LocalJSX.AppTimer & JSXBase.HTMLAttributes<HTMLAppTimerElement>;
'dark-mode-switch': LocalJSX.DarkModeSwitch & JSXBase.HTMLAttributes<HTMLDarkModeSwitchElement>;
}
}
}
Expand Down

0 comments on commit 36981aa

Please sign in to comment.