Skip to content

Commit

Permalink
feat: support for multiple themes and switching via environment (#49, #…
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian-Haehnlein authored and shauke committed Jan 17, 2020
1 parent fdb836c commit 9f53006
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Expand Up @@ -612,6 +612,7 @@ deploy_review_b2b:
-e LOGGING=true
-e SENTRY_DSN=${SENTRY_DSN}
-e ICM_BASE_URL=${ICM_BASE_URL}
-e THEME=blue
-e ICM_CHANNEL=inSPIRED-inTRONICS_Business-Site
-e FEATURES=quoting,recently,compare,businessCustomerRegistration,advancedVariationHandling,sentry
--add-host $ICM_HOST:$ICM_IP
Expand Down
1 change: 1 addition & 0 deletions nginx/README.md
Expand Up @@ -28,6 +28,7 @@ Setup at least one PWA channel configuration:
- use optional `PWA_X_APPLICATION` for the application name
- use optional `PWA_X_LANG` for the default locale in the form of `lang_COUNTRY`
- use optional `PWA_X_FEATURES` for a comma separated list of active feature toggles
- use optional `PWA_X_THEME` for setting the theme of the channel

Temper with the default Page Speed configuration:

Expand Down
2 changes: 1 addition & 1 deletion nginx/channel.conf.tmpl
Expand Up @@ -29,7 +29,7 @@ server {
if (-f /etc/nginx/conf.d/icm.conf) {
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;icmScheme=$scheme;icmHost=$http_host";
}
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;channel=$CHANNEL;application=$APPLICATION;features=$FEATURES" break;
rewrite ^(?!/INTERSHOP.*$)(?!/assets.*$)(?!.*\.js$)(?!.*\.css$)(?!.*\.ico$)(?!.*\.json$)(?!.*\.txt$)(?!.*\.webmanifest$)(.*)$ "$1;channel=$CHANNEL;application=$APPLICATION;features=$FEATURES;theme=$THEME" break;

proxy_pass $UPSTREAM_PWA;
}
Expand Down
3 changes: 2 additions & 1 deletion nginx/entrypoint.sh
Expand Up @@ -25,10 +25,11 @@ do
eval "export APPLICATION=\${PWA_${i}_APPLICATION:-'-'}"
eval "export LANG=\${PWA_${i}_LANG:-'default'}"
eval "export FEATURES=\${PWA_${i}_FEATURES:-'default'}"
eval "export THEME=\${PWA_${i}_THEME:-''}"

echo "$i SUBDOMAIN=$SUBDOMAIN CHANNEL=$CHANNEL APPLICATION=$APPLICATION LANG=$LANG FEATURES=$FEATURES"

envsubst '$UPSTREAM_PWA,$SUBDOMAIN,$CHANNEL,$APPLICATION,$LANG,$FEATURES,$ICM_INCLUDE' </etc/nginx/conf.d/channel.conf.tmpl >/etc/nginx/conf.d/channel$i.conf
envsubst '$UPSTREAM_PWA,$SUBDOMAIN,$CHANNEL,$APPLICATION,$LANG,$FEATURES,$THEME,$ICM_INCLUDE' </etc/nginx/conf.d/channel.conf.tmpl >/etc/nginx/conf.d/channel$i.conf

i=$((i+1))
done
Expand Down
5 changes: 4 additions & 1 deletion src/app/core/configuration.module.ts
Expand Up @@ -9,6 +9,7 @@ import { environment } from '../../environments/environment';

import * as injectionKeys from './configurations/injection-keys';
import { FeatureToggleModule } from './feature-toggle.module';
import { ThemeService } from './utils/theme/theme.service';

@NgModule({
imports: [FeatureToggleModule],
Expand All @@ -34,10 +35,12 @@ import { FeatureToggleModule } from './feature-toggle.module';
{ provide: injectionKeys.MEDIUM_BREAKPOINT_WIDTH, useValue: environment.mediumBreakpointWidth },
{ provide: injectionKeys.LARGE_BREAKPOINT_WIDTH, useValue: environment.largeBreakpointWidth },
{ provide: injectionKeys.EXTRALARGE_BREAKPOINT_WIDTH, useValue: environment.extralargeBreakpointWidth },
{ provide: injectionKeys.THEME, useValue: environment.theme },
],
})
export class ConfigurationModule {
constructor(@Inject(LOCALE_ID) lang: string, translateService: TranslateService) {
constructor(@Inject(LOCALE_ID) lang: string, translateService: TranslateService, themeService: ThemeService) {
themeService.init();
registerLocaleData(localeDe);
registerLocaleData(localeFr);

Expand Down
5 changes: 5 additions & 0 deletions src/app/core/configurations/injection-keys.ts
Expand Up @@ -45,3 +45,8 @@ export const EXTRALARGE_BREAKPOINT_WIDTH = new InjectionToken<number>('extralarg
* The captcha configuration siteKey
*/
export const CAPTCHA_SITE_KEY = new InjectionToken<string>('captchaSiteKey');

/**
* The configured theme for the application (or 'default' if not configured)
*/
export const THEME = new InjectionToken<string>('theme');
11 changes: 8 additions & 3 deletions src/app/core/store/configuration/configuration.effects.ts
Expand Up @@ -49,11 +49,12 @@ export class ConfigurationEffects {
this.stateProperties.getStateOrEnvOrDefault<string>('ICM_APPLICATION', 'icmApplication'),
this.stateProperties
.getStateOrEnvOrDefault<string | string[]>('FEATURES', 'features')
.pipe(map(x => (typeof x === 'string' ? x.split(/,/g) : x)))
.pipe(map(x => (typeof x === 'string' ? x.split(/,/g) : x))),
this.stateProperties.getStateOrEnvOrDefault<string>('THEME', 'theme').pipe(map(x => x || 'default'))
),
map(
([, baseURL, server, serverStatic, channel, application, features]) =>
new ApplyConfiguration({ baseURL, server, serverStatic, channel, application, features })
([, baseURL, server, serverStatic, channel, application, features, theme]) =>
new ApplyConfiguration({ baseURL, server, serverStatic, channel, application, features, theme })
)
);

Expand Down Expand Up @@ -87,6 +88,10 @@ export class ConfigurationEffects {
}
}

if (paramMap.has('theme')) {
properties.theme = paramMap.get('theme');
}

return Object.keys(properties).length ? [new ApplyConfiguration(properties)] : [];
}

Expand Down
2 changes: 2 additions & 0 deletions src/app/core/store/configuration/configuration.reducer.ts
Expand Up @@ -8,6 +8,7 @@ export interface ConfigurationState {
application?: string;
features?: string[];
gtmToken?: string;
theme?: string;
}

const initialState: ConfigurationState = {
Expand All @@ -18,6 +19,7 @@ const initialState: ConfigurationState = {
application: undefined,
features: [],
gtmToken: undefined,
theme: undefined,
};

export function configurationReducer(state = initialState, action: ConfigurationAction): ConfigurationState {
Expand Down
5 changes: 5 additions & 0 deletions src/app/core/store/configuration/configuration.selectors.ts
Expand Up @@ -46,3 +46,8 @@ export const getGTMToken = createSelector(
getConfigurationState,
state => state.gtmToken
);

export const getTheme = createSelector(
getConfigurationState,
state => state.theme
);
56 changes: 56 additions & 0 deletions src/app/core/utils/theme/theme.service.ts
@@ -0,0 +1,56 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { distinctUntilChanged, filter } from 'rxjs/operators';

import { getTheme } from 'ish-core/store/configuration';

/**
* Service to add the configured/selected theme’s CSS file in the HTML’s head.
*
* See: "Angular: Multiple Themes Without Killing Bundle Size (With Material or Not)" by @Kmathy15
* https://medium.com/better-programming/angular-multiple-themes-without-killing-bundle-size-with-material-or-not-5a80849b6b34
*/
@Injectable({ providedIn: 'root' })
export class ThemeService {
private renderer: Renderer2;
private head: HTMLElement;
private themeLinks: HTMLElement[] = [];

constructor(
private rendererFactory: RendererFactory2,
@Inject(DOCUMENT) private document: Document,
private store: Store<{}>
) {}

init() {
this.head = this.document.head;
this.renderer = this.rendererFactory.createRenderer(undefined, undefined);
this.store
.pipe(select(getTheme))
.pipe(
distinctUntilChanged(),
filter(x => !!x)
)
.subscribe(async theme => {
await this.loadCss(`${theme}.css`);

// remove style of previous theme
if (this.themeLinks.length === 2) {
this.renderer.removeChild(this.head, this.themeLinks.shift());
}
});
}

private async loadCss(filename: string) {
return new Promise(resolve => {
const linkEl: HTMLElement = this.renderer.createElement('link');
this.renderer.setAttribute(linkEl, 'rel', 'stylesheet');
this.renderer.setAttribute(linkEl, 'type', 'text/css');
this.renderer.setAttribute(linkEl, 'href', filename);
this.renderer.setProperty(linkEl, 'onload', resolve);
this.renderer.appendChild(this.head, linkEl);
this.themeLinks = [...this.themeLinks, linkEl];
});
}
}
3 changes: 3 additions & 0 deletions src/environments/environment.model.ts
Expand Up @@ -73,4 +73,7 @@ export interface Environment {

// configuration of the available locales - hard coded for now
locales: Locale[];

// configuration of the styling theme ('default' if not configured)
theme?: string;
}

0 comments on commit 9f53006

Please sign in to comment.