Skip to content
This repository has been archived by the owner on Jul 29, 2020. It is now read-only.

Commit

Permalink
feat(ChapterComponent): add page for chapters
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelSolati committed Jul 17, 2019
1 parent a5cad0c commit 7e53e4e
Show file tree
Hide file tree
Showing 20 changed files with 280 additions and 12 deletions.
4 changes: 3 additions & 1 deletion src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';

const routes: Routes = [
{path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule)}
{ path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
{ path: 'chapter', loadChildren: () => import('./chapter/chapter.module').then(m => m.ChapterModule) },
{ path: '**', redirectTo: '/' }
];

@NgModule({
Expand Down
17 changes: 17 additions & 0 deletions src/app/chapter/chapter-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { ChapterGuard } from '@guards/chapter.guard';

import { ChapterComponent } from './chapter.component';

const routes: Routes = [
{ path: ':key', component: ChapterComponent, canActivate: [ChapterGuard] },
{ path: '', redirectTo: '/' }
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ChapterRoutingModule { }
50 changes: 50 additions & 0 deletions src/app/chapter/chapter.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<section class="banner" [style.background-image]="(about?.key_photo?.highres_link) | bannerImage"></section>

<section class="over-banner">
<div class="container">
<div class="row">
<div class="col-12">
<mat-card>
<mat-card-title>{{ about?.name || 'GDG' }}</mat-card-title>
<mat-card-subtitle>{{ about?.localized_location }}</mat-card-subtitle>
<mat-card-content>
<div [innerHTML]="about?.description"></div>
</mat-card-content>
<mat-card-actions>
<a mat-button target="_blank" [href]="about?.link || 'https://www.meetup.com/pro/gdg'">JOIN MEETUP</a>
</mat-card-actions>
</mat-card>
</div>
</div>
</div>
</section>

<section>
<div class="container">
<div class="row">
<div class="col-12">
<mat-card>
<mat-card-title>Events</mat-card-title>
<mat-card-content>
<mat-list>
<div *ngFor="let event of events; last as last">
<a mat-list-item target="_blank" [href]="event.link">
<img matListAvatar src="./assets/gdg-logo.jpg" alt="GDG Logo">
<h3 matLine>{{event.name}}</h3>
<p matLine>{{event.time | date}}</p>
</a>
<mat-divider *ngIf="!last"></mat-divider>
</div>
<div *ngIf="!events || events.length === 0">
<mat-list-item>
<img matListAvatar src="./assets/gdg-logo.jpg" alt="GDG Logo">
<h3 matLine>No upcoming events</h3>
</mat-list-item>
</div>
</mat-list>
</mat-card-content>
</mat-card>
</div>
</div>
</div>
</section>
Empty file.
25 changes: 25 additions & 0 deletions src/app/chapter/chapter.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { ChapterComponent } from './chapter.component';

describe('ChapterComponent', () => {
let component: ChapterComponent;
let fixture: ComponentFixture<ChapterComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ChapterComponent ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ChapterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
37 changes: 37 additions & 0 deletions src/app/chapter/chapter.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';

import { ChaptersService } from '@services/chapters.service';

@Component({
selector: 'gdg-chapter',
templateUrl: './chapter.component.html',
styleUrls: ['./chapter.component.scss']
})
export class ChapterComponent implements OnInit, OnDestroy {
private _about: any;
private _events: any;
private _keySubscription: Subscription;

constructor(private _cs: ChaptersService, private _route: ActivatedRoute) { }

ngOnInit() {
this._keySubscription = this._route.params.subscribe((params: any) => {
this._cs.about(params.key).subscribe((about) => this._about = about);
this._cs.events(params.key).subscribe((events) => this._events = events);
});
}

ngOnDestroy() {
this._keySubscription.unsubscribe();
}

get about(): any {
return this._about;
}

get events(): any {
return this._events;
}
}
25 changes: 25 additions & 0 deletions src/app/chapter/chapter.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatListModule } from '@angular/material/list';

import { SharedModule } from '@shared';

import { ChapterRoutingModule } from './chapter-routing.module';
import { ChapterComponent } from './chapter.component';

@NgModule({
declarations: [
ChapterComponent
],
imports: [
CommonModule,
ChapterRoutingModule,
SharedModule,
MatButtonModule,
MatCardModule,
MatListModule
]
})
export class ChapterModule { }
6 changes: 5 additions & 1 deletion src/app/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { AngularFireModule, } from '@angular/fire';
import { AngularFirestoreModule, FirestoreSettingsToken } from '@angular/fire/firestore';

import { environment } from '@environment';

import { ChapterGuard } from '@guards/chapter.guard';

import { ChaptersService } from '@services/chapters.service';
import { LocationService } from '@services/location.service';
import { ToastService } from '@services/toast.service';

const PROVIDERS = [
ChapterGuard,
ChaptersService,
LocationService,
ToastService
Expand All @@ -22,6 +25,7 @@ const PROVIDERS = [
imports: [
CommonModule,
HttpClientModule,
HttpClientJsonpModule,
MatSnackBarModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule
Expand Down
15 changes: 15 additions & 0 deletions src/app/core/guards/chapter.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { TestBed, async, inject } from '@angular/core/testing';

import { ChapterGuard } from './chapter.guard';

describe('ChapterGuard', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ChapterGuard]
});
});

it('should ...', inject([ChapterGuard], (guard: ChapterGuard) => {
expect(guard).toBeTruthy();
}));
});
32 changes: 32 additions & 0 deletions src/app/core/guards/chapter.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { CanActivate, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';

import { ChaptersService } from '@services/chapters.service';
import { ToastService } from '@services/toast.service';

@Injectable({
providedIn: 'root'
})
export class ChapterGuard implements CanActivate, CanActivateChild {
constructor(private _cs: ChaptersService, private _router: Router, private _toast: ToastService) { }

canActivate(next: ActivatedRouteSnapshot): Observable<boolean> {
return this._cs.findOne(next.params.key).pipe(
map((chapter) => {
if (!chapter) { throw Error(); }
return true;
}),
catchError(() => {
this._toast.make('Chapter not found');
this._router.navigate(['/']);
return of(false);
})
);
}

canActivateChild(next: ActivatedRouteSnapshot): Observable<boolean> {
return this.canActivate(next);
}
}
16 changes: 14 additions & 2 deletions src/app/core/services/chapters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,25 @@ export class ChaptersService {
private _chapters$: Observable<WoomeraTypes.Chapter[]>;
private _collection: GeoCollectionReference;

constructor(firestore: AngularFirestore, http: HttpClient) {
constructor(firestore: AngularFirestore, private _http: HttpClient) {
const directoryUrl = 'https://firebasestorage.googleapis.com/v0/b/withgdg.appspot.com/o/directory.json?alt=media';
this._collection = new GeoFirestore(firestore.firestore).collection('chapters');
this._chapters$ = http.get(directoryUrl).pipe(
this._chapters$ = _http.get(directoryUrl).pipe(
map((result: WoomeraTypes.ChapterSimple[]) => result.map((chapter) => ({
...chapter,
coordinates: new firebase.firestore.GeoPoint(chapter.coordinates.lat, chapter.coordinates.lng)
})))
);
}

public about(key: string): Observable<any> {
return this._http.jsonp('https://api.meetup.com/' + key, 'callback').pipe(map((result: any) => result.data));
}

public events(key: string): Observable<any> {
return this._http.jsonp('https://api.meetup.com/' + key + '/events', 'callback').pipe(map((result: any) => result.data));
}

public find(): Observable<WoomeraTypes.Chapter[]> {
return this._chapters$;
}
Expand All @@ -37,4 +45,8 @@ export class ChaptersService {
.map((doc) => doc.data() as WoomeraTypes.Chapter);
}));
}

public findOne(key: string): Observable<WoomeraTypes.Chapter> {
return this._chapters$.pipe(map((chapters) => chapters.find((chapter) => chapter.$key === key)));
}
}
2 changes: 1 addition & 1 deletion src/app/home/chapters-map/chapters-map.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<section>
<agm-map [latitude]="(coordinates$ | async)?.latitude" [longitude]="(coordinates$ | async)?.longitude"
<agm-map class="banner" [latitude]="(coordinates$ | async)?.latitude" [longitude]="(coordinates$ | async)?.longitude"
[zoom]="((coordinates$ | async)?.latitude || (coordinates$ | async)?.longitude) ? 12 : 2">
<agm-marker *ngFor="let chapter of chapters$ | async" [latitude]="chapter.coordinates.latitude"
[longitude]="chapter.coordinates.longitude" [title]="chapter.name" (markerClick)="goToChapter(chapter.$key)">
Expand Down
4 changes: 0 additions & 4 deletions src/app/home/chapters-map/chapters-map.component.scss
Original file line number Diff line number Diff line change
@@ -1,4 +0,0 @@
agm-map {
height: 500px;
width: 100%;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<section>
<section class="over-banner">
<div class="container">
<div class="row">
<div class="col-12">
Expand Down
4 changes: 2 additions & 2 deletions src/app/navbar/navbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { map } from 'rxjs/operators';
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
private _isHandset$: Observable<boolean> = this._breakpointObserver.observe(Breakpoints.Small)
.pipe(map(result => !(result.matches)));
private _isHandset$: Observable<boolean> = this._breakpointObserver.observe(['(max-width: 599px)'])
.pipe(map(result => result.matches));

constructor(private _breakpointObserver: BreakpointObserver) { }

Expand Down
8 changes: 8 additions & 0 deletions src/app/shared/pipes/banner-image.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BannerImagePipe } from './banner-image.pipe';

describe('BannerImagePipe', () => {
it('create an instance', () => {
const pipe = new BannerImagePipe();
expect(pipe).toBeTruthy();
});
});
13 changes: 13 additions & 0 deletions src/app/shared/pipes/banner-image.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';

@Pipe({
name: 'bannerImage'
})
export class BannerImagePipe implements PipeTransform {
constructor(private _sanitizer: DomSanitizer) { }

transform(url: string): SafeStyle {
return url ? this._sanitizer.bypassSecurityTrustStyle('url(' + url + ')') : null;
}
}
17 changes: 17 additions & 0 deletions src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { BannerImagePipe } from './pipes/banner-image.pipe';

@NgModule({
declarations: [
BannerImagePipe
],
exports: [
BannerImagePipe
],
imports: [
CommonModule
]
})
export class SharedModule { }
13 changes: 13 additions & 0 deletions src/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ a {
section {
padding-top: 8px;
padding-bottom: 8px;

&.over-banner {
margin-top: -48px;
}
}

.banner {
padding: 0;
background: no-repeat #000000;
background-size: cover;
background-position: center;
height: 33vw;
width: 100%;
}

.card-container {
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"paths": {
"@core": ["src/app/core/core.module"],
"@environment": ["src/environments/environment"],
"@guards/*": ["src/app/core/guards/*"],
"@shared": ["src/app/shared/shared.module"],
"@services/*": ["src/app/core/services/*"],
"@types": ["functions/src/types/index"]
},
Expand Down

0 comments on commit 7e53e4e

Please sign in to comment.