Skip to content

Commit

Permalink
feat(dashboard): added profile and settings pages
Browse files Browse the repository at this point in the history
also added app.state
  • Loading branch information
xmlking committed Nov 19, 2018
1 parent 78a41a2 commit 1a77689
Show file tree
Hide file tree
Showing 26 changed files with 556 additions and 42 deletions.
5 changes: 3 additions & 2 deletions PLAYBOOK.md
Expand Up @@ -243,6 +243,7 @@ ng g service services/MediaQuery --project=core --dry-run
ng g service services/DeepLink --project=core --dry-run
ng g service services/Feature --project=core --dry-run
ng g service services/GoogleAnalytics --project=core --dry-run
ng g service PushNotification --project=core --dry-run

# `material` module to encapulate material libs which is impoted into any `Lazy-loaded Feature Modules` that need material components
ng g lib material --prefix=ngx --spec=false --tags=shared-module --unit-test-runner=jest --dry-run
Expand Down Expand Up @@ -300,7 +301,6 @@ ng g lib Notifications --prefix=ngx --tags=public-module --publishable=true --un
ng g component notifications --project=notifications --flat --dry-run
ng g class notification --type=model --project=notifications --dry-run
ng g service notifications --project=notifications --dry-run
ng g service PushNotification --project=notifications --dry-run

# generate components for `Quickpanel` Module
ng g lib Quickpanel --prefix=ngx --tags=private-module --unit-test-runner=jest
Expand Down Expand Up @@ -403,7 +403,8 @@ ng g component containers/about --project=home
ng g component components/rainbow --project=dashboard --dry-run
ng g component containers/dashboardLayout --project=dashboard --dry-run
ng g component containers/overview --project=dashboard --dry-run

ng g component containers/profile --project=dashboard --dry-run
ng g component containers/settings --project=dashboard --dry-run

# generate containers, components for `widgets` Module
ng g component containers/wizdash --project=widgets --dry-run
Expand Down
3 changes: 2 additions & 1 deletion libs/auth/src/lib/oauth.init.ts
Expand Up @@ -21,7 +21,8 @@ export function initializeAuth(oauthService: OAuthService, store: Store) {
if (oauthService.hasValidAccessToken()) {
// This is called when using ImplicitFlow or page reload, no effect for ROPC Flow
console.log('hasValidAccessToken');
const profile: any = oauthService.getIdentityClaims();
// const profile: any = oauthService.getIdentityClaims();
const profile: any = await oauthService.loadUserProfile();
store.dispatch(new LoginSuccess(profile));
}
return true; // need to return.
Expand Down
3 changes: 3 additions & 0 deletions libs/core/src/index.ts
@@ -1,9 +1,12 @@
export * from './lib/core.module';
export * from './lib/state/preference.state';
export * from './lib/state/app.state';
export { PageTitleService } from './lib/services/page-title.service';
export { ServiceWorkerService } from './lib/services/service-worker.service';
export { MediaQueryService } from './lib/services/media-query.service';
export { DeepLinkService } from './lib/services/deep-link.service';
export { RouterStateData} from './lib/state/custom-router-state.serializer';
export { FeatureService, BrowserFeatureKey, BrowserFeature } from './lib/services/feature.service';
export { GoogleAnalyticsService } from './lib/services/google-analytics.service';
export { PushNotificationService } from './lib/services/push-notification.service';
export { WINDOW } from './lib/services/window.token';
7 changes: 6 additions & 1 deletion libs/core/src/lib/core.module.ts
Expand Up @@ -8,6 +8,7 @@ import { NgxsFormPluginModule } from '@ngxs/form-plugin';
import { NgxPageScrollModule } from 'ngx-page-scroll';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsRouterPluginModule, RouterStateSerializer } from '@ngxs/router-plugin';
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faTwitter, faGithub, faGoogle } from '@fortawesome/free-brands-svg-icons';
Expand All @@ -19,6 +20,7 @@ import { environment } from '@env/environment';
import { EventBus } from './state/eventbus';
import { defaultMenu, demoMenu, adminMenu } from './menu-data';
import { PreferenceState } from './state/preference.state';
import { AppState } from './state/app.state';
import { InMemoryDataService } from './services/in-memory-data.service';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { CustomRouterStateSerializer } from './state/custom-router-state.serializer';
Expand All @@ -45,7 +47,10 @@ library.add(faTwitter, faGithub, faGoogle);
MatSnackBarModule,
NgxPageScrollModule,
NavigatorModule.forRoot(defaultMenu),
NgxsModule.forRoot([AuthState, MenuState, PreferenceState]),
NgxsModule.forRoot([AuthState, MenuState, PreferenceState, AppState]),
NgxsStoragePluginModule.forRoot({
key: ['preference', 'app.installed']
}),
NgxsReduxDevtoolsPluginModule.forRoot({
disabled: environment.production, // Set to true for prod mode
maxAge: 10,
Expand Down
1 change: 1 addition & 0 deletions libs/core/src/lib/services/google-analytics.service.ts
Expand Up @@ -6,6 +6,7 @@ export enum EventCategory {
SideNav = 'sideNav',
Outbound = 'outboundLink',
Login = 'login',
Install = 'install',
}

export enum EventAction {
Expand Down
Expand Up @@ -26,7 +26,7 @@ export class PushNotificationService {
const subscription = await this.swPush.requestSubscription({ serverPublicKey: environment.webPush.publicVapidKey });
console.log('Push subscription endpoint: ', subscription.endpoint);
this.pushSubscription = subscription;
// this.apiService.post('push/register', subscription).subscribe();
// return this.apiService.post('push/register', subscription).subscribe();
}

unregister(): Observable<boolean> {
Expand Down
1 change: 1 addition & 0 deletions libs/core/src/lib/services/service-worker.service.ts
Expand Up @@ -10,6 +10,7 @@ import { MatSnackBar } from '@angular/material';
export class ServiceWorkerService {
constructor(private swUpdate: SwUpdate, @Inject(WINDOW) private window: Window, private snackBar: MatSnackBar) {}

// TODO: move to appState/eventBus?
checkSWUpdate(): void {
if (environment.production) {
// Subscribe new worker is available
Expand Down
85 changes: 85 additions & 0 deletions libs/core/src/lib/state/app.state.ts
@@ -0,0 +1,85 @@
import { Action, NgxsOnInit, Selector, State, StateContext } from '@ngxs/store';
import { Inject } from '@angular/core';
import { WINDOW } from '../services/window.token';

export interface BeforeInstallPromptEvent extends Event {
readonly platforms: string[]; // ["web", "android", "windows"]
readonly userChoice: Promise<'accepted' | 'dismissed'>;
prompt(): void;
}

export class ChangeInstallStatus {
static readonly type = '[App] Change Install Status';
constructor(public payload: boolean) {}
}
export class SetInstallPrompt {
static readonly type = '[App] Set Install Prompt';
constructor(public payload: BeforeInstallPromptEvent) {}
}
export class Online {
static readonly type = '[App] Network Online';
}
export class Offline {
static readonly type = '[App] Network Offline';
}

export interface AppStateModel {
online: boolean;
installPrompt: BeforeInstallPromptEvent;
installed: boolean;
}

@State<AppStateModel>({
name: 'app',
defaults: {
online: window.navigator.onLine,
installPrompt: null,
installed: false
},
})
export class AppState {
constructor() {}

@Selector()
static online(state: AppStateModel) {
return state.online;
}

@Selector()
static installPrompt(state: AppStateModel) {
return state.installPrompt;
}

@Selector()
static installed(state: AppStateModel) {
return state.installed;
}

@Action(SetInstallPrompt)
setInstallPrompt({ patchState }: StateContext<AppStateModel>, { payload }: SetInstallPrompt) {
patchState({
installPrompt: payload,
});
}

@Action(ChangeInstallStatus)
changeInstallStatus({ patchState }: StateContext<AppStateModel>, { payload }: ChangeInstallStatus) {
patchState({
installed: payload,
});
}

@Action(Online)
enableNotifications({ patchState }: StateContext<AppStateModel>) {
patchState({
online: true,
});
}

@Action(Offline)
disableNotifications({ patchState }: StateContext<AppStateModel>) {
patchState({
online: false,
});
}
}
55 changes: 46 additions & 9 deletions libs/core/src/lib/state/eventbus.ts
@@ -1,5 +1,5 @@
import { Actions, ofActionErrored, ofActionSuccessful, Store } from '@ngxs/store';
import { Injectable } from '@angular/core';
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
import { Login, LoginSuccess } from '@ngx-starter-kit/auth';
import {
AuthenticateWebSocket,
Expand All @@ -9,30 +9,68 @@ import {
WebSocketDisconnected,
} from '@ngx-starter-kit/socketio-plugin';
import { RouterNavigation, RouterState } from '@ngxs/router-plugin';
import { RouterStateData } from '@ngx-starter-kit/core';
import { distinctUntilChanged, filter, map } from 'rxjs/operators';
import { PageTitleService } from '../services/page-title.service';
import { GoogleAnalyticsService } from '../services/google-analytics.service';
import { EventCategory, GoogleAnalyticsService } from '../services/google-analytics.service';
import { NavigationEnd, Router, RouterEvent } from '@angular/router';
import { SetInstallPrompt, AppState, Online, Offline, ChangeInstallStatus } from './app.state';
import { WINDOW } from '../services/window.token';

@Injectable({
providedIn: 'root',
})
export class EventBus {
private renderer: Renderer2;
constructor(
private actions$: Actions,
private store: Store,
private router: Router,
private analytics: GoogleAnalyticsService,
private pageTitle: PageTitleService,
private rendererFactory: RendererFactory2,
@Inject(WINDOW) private readonly window: Window,
) {
this.renderer = rendererFactory.createRenderer(this.window, null);
this.renderer.listen(this.window, 'online', () => {
this.store.dispatch(new Online());
});
this.renderer.listen(this.window, 'offline', () => {
this.store.dispatch(new Offline());
});

const installed = this.store.selectSnapshot(AppState.installed);
if (!installed) {
this.renderer.listen(this.window, 'beforeinstallprompt', event => {
event.preventDefault(); // Prevent default for older Chrome versions to prevent double install prompt
this.analytics.emitEvent(EventCategory.Install, 'available');
this.store.dispatch(new SetInstallPrompt(event));
});
this.renderer.listen(this.window, 'appinstalled', event => {
event.preventDefault(); // Prevent default for older Chrome versions to prevent double install prompt
ga('send', 'event', 'install', 'installed');
this.analytics.emitEvent(EventCategory.Install, 'installed');
this.store.dispatch(new ChangeInstallStatus(true));
});

this.actions$.pipe(ofActionSuccessful(SetInstallPrompt)).subscribe(async (action: SetInstallPrompt) => {
try {
console.log('platforms', action.payload.platforms);
// TODO: prompt user with MatSnackBar and call below if clicked.
action.payload.prompt();
const choiceResult = await action.payload.userChoice;
console.log('choiceResult', choiceResult);
this.analytics.emitEvent(EventCategory.Install, choiceResult);
} catch (installError) {
this.analytics.emitEvent(EventCategory.Install, 'errored');
}
});
}

this.actions$.pipe(ofActionSuccessful(Login)).subscribe(action => console.log('Login........Action Successful'));
this.actions$.pipe(ofActionErrored(Login)).subscribe(action => console.log('Login........Action Errored'));
this.actions$
.pipe(ofActionSuccessful(LoginSuccess))
.subscribe((action: LoginSuccess) => {
this.analytics.setUsername(action.payload.preferred_username);
});
this.actions$.pipe(ofActionSuccessful(LoginSuccess)).subscribe((action: LoginSuccess) => {
this.analytics.setUsername(action.payload.preferred_username);
});

this.actions$
.pipe(ofActionSuccessful(ConnectWebSocket))
Expand Down Expand Up @@ -75,6 +113,5 @@ export class EventBus {
this.pageTitle.setTitle(data.breadcrumbs);
this.analytics.setPage(data.url);
});

}
}

0 comments on commit 1a77689

Please sign in to comment.