Skip to content
This repository has been archived by the owner on Jun 20, 2019. It is now read-only.

Commit

Permalink
feat(notifications): navbar dropdown implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
mentos1386 committed Jun 12, 2018
1 parent dd79cb2 commit 729b82e
Show file tree
Hide file tree
Showing 22 changed files with 411 additions and 78 deletions.
1 change: 1 addition & 0 deletions src/app/app.component.scss
Expand Up @@ -7,6 +7,7 @@
left: 0;
right: 0;
z-index: 10;
border-bottom: 1px solid $app-background-color-darker;
}

#content {
Expand Down
27 changes: 25 additions & 2 deletions src/app/app.component.ts
@@ -1,4 +1,9 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { IAppState } from './app.store';
import { filter } from 'rxjs/internal/operators';
import { Connect } from './services/websocket/websocket.actions';
import { Load } from './services/notification/notification.actions';

@Component({
selector: 'app-root',
Expand All @@ -13,5 +18,23 @@ import { Component, ViewEncapsulation } from '@angular/core';
styleUrls: ['app.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class AppComponent {
export class AppComponent implements OnInit {

constructor(
public store: Store<IAppState>,
) {
}

ngOnInit(): void {
// Initially we check if we are authenticated, if we are, we try to establish connection
this.store.pipe(
select('authentication', 'authenticated'),
filter(authenticated => authenticated),
).subscribe(authenticated => {
console.log('Trying to initially connect to websocket');
this.store.dispatch(new Connect());
console.log('Trying to initially get notifications');
this.store.dispatch(new Load({ limit: 100, page: 0 }));
});
}
}
3 changes: 2 additions & 1 deletion src/app/app.module.ts
Expand Up @@ -19,6 +19,7 @@ import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import { EffectsModule } from '@ngrx/effects';
import { ApiService } from './services/api.service';

@NgModule({
declarations: [
Expand Down Expand Up @@ -50,7 +51,7 @@ import { EffectsModule } from '@ngrx/effects';
HashtagModule,
MatSnackBarModule,
],
providers: [],
providers: [ApiService],
bootstrap: [AppComponent],
})
export class AppModule {
Expand Down
1 change: 1 addition & 0 deletions src/app/home/profile/profile.component.scss
Expand Up @@ -23,6 +23,7 @@
flex-direction: column;
justify-content: center;
margin-top: 35px;
margin-left: .5em;

.name {
margin: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/app/home/welcome/welcome.component.html
Expand Up @@ -4,7 +4,7 @@
<h5>EVE Book</h5>
<h1>Hub for EVE Online community</h1>
<div class="seperator"></div>
<h3>Join in on the conversation of the <strong>New Eden</strong></h3>
<h3>Join in on the conversations of the <strong>New Eden</strong></h3>
<div class="button-wrap">
<a href="{{authenticationUrl}}authentication/sso" mat-raised-button color="primary">Log in</a>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/app/home/welcome/welcome.component.scss
Expand Up @@ -11,6 +11,7 @@
justify-content: center;
align-items: center;
flex: 1 1 auto;
text-align: center;

h1 {
text-transform: uppercase;
Expand Down
@@ -0,0 +1,53 @@
<div class="notifications">
<div class="notifications-wrapper">

<a matBadge="{{ (newNotifications$ | async).length }}"
matBadgeHidden="{{ (newNotifications$ | async).length === 0 }}"
matBadgeColor="accent"
(focus)="toggleFocus()"
(blur)="toggleBlur()"
mat-button>NOTIFICATIONS</a>

<ul class="notifications-results"
*ngIf="notificationsToggle || notificationsHover"
(mouseleave)="leftNotifications()"
(mouseenter)="enterNotifications()">

<h3 *ngIf="(newNotifications$ | async).length > 0">New notifications</h3>
<li *ngFor="let notification of newNotifications$ | async"
[ngClass]="{'not-seen': !notification.seenAt}"
(click)="openNotification(notification)">
<button mat-icon-button>
<img aria-label="Profile"
src="https://imageserver.eveonline.com/Character/96361892_64.jpg"/>
</button>
<div class="notification-content">
<p><strong>Nyota Walker</strong> has posted on <strong>your wall</strong>.</p>
<small class="mat-caption">
{{ notification.createdAt | amTimeAgo }} {{ notification.seenAt ? '' : ' - Not Seen'}}
</small>
</div>
</li>

<h3 *ngIf="(otherNotifications$ | async).length > 0">Other notifications</h3>
<li *ngFor="let notification of otherNotifications$ | async"
[ngClass]="{'not-seen': !notification.seenAt}"
(click)="openNotification(notification)">
<button mat-icon-button>
<img aria-label="Profile"
src="https://imageserver.eveonline.com/Character/96361892_64.jpg"/>
</button>
<div class="notification-content">
<p><strong>Nyota Walker</strong> has posted on <strong>your wall</strong>.</p>
<small class="mat-caption">
{{ notification.createdAt | amTimeAgo }}
</small>
</div>
</li>
</ul>

</div>
</div>



@@ -0,0 +1,88 @@
@import "../../../variables";
@import '~@angular/material/theming';

.notifications {

display: flex;
align-items: center;
height: 100%;
width: 100%;
position: relative;
margin: 0 0.5em;

.notifications-wrapper {
display: flex;
flex-direction: column;

input {
width: 100%;
border: none;
}

.notifications-results {
color: #333;
box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
background-color: #f3f3f3;
position: absolute;
width: 350px;
max-height: 70vh;
list-style: none;
padding: 0;
margin: 0;
margin-top: 36px;
overflow: auto;

h3 {
font-size: 0.6em;
line-height: 2em;
padding-left: 1em;
background: lighten($app-background-color, 50%);
text-transform: uppercase;
}

li {
display: flex;
flex-direction: row;
padding: .5em;

&.not-seen {
background: #ffff;
}

&:hover {
cursor: pointer;
background: lighten($app-background-color, 60%);
}

.mat-icon-button {
width: 48px;
height: 48px;
margin-right: 12px;

img {
width: 48px;
height: 48px;
border-radius: 50%;
}
}

.notification-content {
display: flex;
flex-direction: column;
justify-content: space-around;

p {
padding: 0;
margin: 0;
white-space: pre-line; // Fixes line going out of parent
font-size: 0.7em;
line-height: 1.2em;
}
}
}
}
}
}

@@ -0,0 +1,76 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { select, Store } from '@ngrx/store';
import { IAppState } from '../../app.store';
import { DNotification, DNotificationList } from '../../services/notification/notification.dto';
import { Observable, of } from 'rxjs/index';
import { filter, map, mergeMap } from 'rxjs/internal/operators';
import { SeenNotification } from '../../services/notification/notification.actions';
import * as moment from 'moment';

@Component({
selector: 'app-navbar-notifications',
templateUrl: './navbar-notifications.component.html',
styleUrls: ['./navbar-notifications.component.scss'],
})
export class NavbarNotificationsComponent implements OnInit {

notifications$: Observable<DNotificationList>;
newNotifications$: Observable<DNotification[]> = of([]);
otherNotifications$: Observable<DNotification[]> = of([]);

notificationsHover: boolean = false;
notificationsToggle: boolean = false;

constructor(
private store: Store<IAppState>,
private router: Router,
) {
this.notifications$ = this.store.pipe(select('notification', 'list'));
}

ngOnInit() {
this.newNotifications$ = this.notifications$.pipe(
map(notificationsData => notificationsData ? notificationsData.data : []),
map(notifications => notifications.filter(notification => {
return !notification.seenAt &&
// Not older than 2 days
moment(notification.createdAt).isAfter(moment().subtract(2, 'day'));
})),
);
this.otherNotifications$ = this.newNotifications$.pipe(
mergeMap(newNotifications => {
return this.notifications$.pipe(
map(notificationsData => notificationsData ? notificationsData.data : []),
map(notifications => notifications.filter(notification => {
return !newNotifications.find(
newNotification => newNotification.id === notification.id);
}),
),
);
}),
);
}

openNotification(notification: DNotification) {
console.log('Opening notification', notification);
this.store.dispatch(new SeenNotification(notification.id));
}

leftNotifications() {
this.notificationsHover = false;
}

enterNotifications() {
this.notificationsHover = true;
}

toggleBlur() {
this.notificationsToggle = false;
}

toggleFocus() {
this.notificationsToggle = true;
}

}
20 changes: 20 additions & 0 deletions src/app/navbar/navbar-notifications/navbar-notifications.module.ts
@@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MatBadgeModule, MatButtonModule } from '@angular/material';
import { NavbarNotificationsComponent } from './navbar-notifications.component';
import { MomentModule } from 'angular2-moment';

@NgModule({
imports: [
CommonModule,
RouterModule,
MatButtonModule,
MatBadgeModule,
MomentModule,
],
declarations: [NavbarNotificationsComponent],
exports: [NavbarNotificationsComponent],
})
export class NavbarNotificationsModule {
}
15 changes: 7 additions & 8 deletions src/app/navbar/navbar-search/navbar-search.component.ts
Expand Up @@ -46,14 +46,13 @@ export class NavbarSearchComponent implements OnInit {

this.searchCtrl.valueChanges.pipe(
debounceTime(500),
tap(query => {
if (query.length > 2) {
this.store.dispatch(new Search(query));
} else if (this.showCharacters || this.showCorporations || this.showAlliances) {
this.store.dispatch(new Clear());
}
}),
);
).subscribe(query => {
if (query.length > 2) {
this.store.dispatch(new Search(query));
} else if (this.showCharacters || this.showCorporations || this.showAlliances) {
this.store.dispatch(new Clear());
}
});

this.charactersLess = this.characters$.pipe(
map(characters => characters.splice(0, this.limit)),
Expand Down
6 changes: 4 additions & 2 deletions src/app/navbar/navbar.component.html
Expand Up @@ -2,11 +2,13 @@
<div class="button-row">
<div class="nav-container">
<a routerLink="" mat-button>HOME</a>
<a (click)="notifications()" *ngIf="authenticated$ | async" mat-button>NOTIFICATIONS</a>
<app-navbar-notifications *ngIf="authenticated$ | async"></app-navbar-notifications>
</div>
<div class="nav-container">
<app-navbar-search></app-navbar-search>
<a *ngIf="!(authenticated$ | async)" href="{{authenticationUrl}}authentication/sso" mat-button>LOG IN</a>
<a *ngIf="!(authenticated$ | async)"
href="{{authenticationUrl}}authentication/sso"
mat-button>LOG IN</a>
<a *ngIf="authenticated$ | async"
[routerLink]="['character', (character$|async).id]"
mat-button>PROFILE</a>
Expand Down
16 changes: 0 additions & 16 deletions src/app/navbar/navbar.component.ts
Expand Up @@ -6,8 +6,6 @@ import { select, Store } from '@ngrx/store';
import { IAppState } from '../app.store';
import { DCharacterShort } from '../services/character/character.dto';
import { UnAuthenticate } from '../services/authentication/authentication.actions';
import { Authenticate, Connect } from '../services/websocket/websocket.actions';
import { filter } from 'rxjs/internal/operators';

@Component({
selector: 'app-navbar',
Expand Down Expand Up @@ -38,19 +36,5 @@ export class NavbarComponent implements OnInit {
}

notifications() {
console.log('ze button');
this.store.dispatch(new Connect());
this.store.pipe(
select('websocket', 'connected'),
filter(connected => connected),
).subscribe(() => {
console.log('Heell yeah, connected!!!');
return this.store.pipe(
select('authentication', 'data', 'accessToken'),
).subscribe(token => {
console.log('going to do authentication now');
this.store.dispatch(new Authenticate(token));
});
});
}
}

0 comments on commit 729b82e

Please sign in to comment.