Skip to content

Commit

Permalink
get navigation from firestore
Browse files Browse the repository at this point in the history
  • Loading branch information
ortwic committed Mar 27, 2024
1 parent cf96c0c commit b9bb386
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 59 deletions.
9 changes: 9 additions & 0 deletions src/app/components/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
import { CommonService } from '../services/common.service';

describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent, NoopAnimationsModule],
providers: [
{
provide: CommonService,
useValue: {
getNavigation: () => Promise.resolve([])
}
}
],
}).compileComponents();
});

Expand Down
4 changes: 2 additions & 2 deletions src/app/components/nav/nav.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</mat-toolbar>
<mat-nav-list>
<ul>
@for (item of routes; track item.path) {
@for (item of sidenavRoutes; track item.path) {
<li>
<a mat-list-item [routerLink]="item.path">
<mat-icon color="accent" fontIcon="{{item.icon}}"></mat-icon>
Expand Down Expand Up @@ -45,7 +45,7 @@

<footer>
<ul class="inline">
@for (item of routes; track item.path; let last = $last) {
@for (item of footerRoutes; track item.path; let last = $last) {
<li [class.pipe]="!last" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
<a [routerLink]="item.path">
{{item.title}}
Expand Down
13 changes: 13 additions & 0 deletions src/app/components/nav/nav.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ActivatedRoute } from '@angular/router';

import { NavComponent } from './nav.component';
import { CommonService } from '../../services/common.service';

describe('NavComponent', () => {
let component: NavComponent;
Expand All @@ -15,6 +16,18 @@ describe('NavComponent', () => {
{
provide: ActivatedRoute,
useValue: {}
},
{
provide: CommonService,
useValue: {
getNavigation: () => Promise.resolve([
{ path: '/', title: 'Start', icon: 'home' },
{ path: '/blog', title: 'Blog', icon: 'feed' },
{ path: '/imprint', title: 'Impressum', icon: 'info' },
{ path: '/privacy', title: 'Datenschutz', icon: 'security' },
{ path: '/settings', title: 'Einstellungen', icon: 'settings' }
])
}
}
]
}).compileComponents();
Expand Down
104 changes: 56 additions & 48 deletions src/app/components/nav/nav.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,64 +9,72 @@ import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { Observable, of } from 'rxjs';
import { map, shareReplay } from 'rxjs/operators';
import { CommonService } from '../../services/common.service';
import { NavigationItem } from '../../models/nav.model';

@Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrl: './nav.component.scss',
standalone: true,
imports: [
CommonModule,
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatListModule,
MatIconModule,
AsyncPipe,
RouterLink,
RouterLinkActive
]
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrl: './nav.component.scss',
standalone: true,
imports: [
CommonModule,
MatToolbarModule,
MatButtonModule,
MatSidenavModule,
MatListModule,
MatIconModule,
AsyncPipe,
RouterLink,
RouterLinkActive,
],
})
export class NavComponent implements AfterViewInit {
private breakpointObserver = inject(BreakpointObserver);
private breakpointObserver = inject(BreakpointObserver);
private commonService = inject(CommonService);
private routes: NavigationItem[] = [];

@ViewChild(MatSidenavContainer)
private sidenavContainer!: MatSidenavContainer;
@ViewChild(MatSidenavContainer)
private sidenavContainer!: MatSidenavContainer;

isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset)
.pipe(
map(result => result.matches),
shareReplay()
isHandset$: Observable<boolean> = this.breakpointObserver.observe(Breakpoints.Handset).pipe(
map((result) => result.matches),
shareReplay()
);
toolbarTop$: Observable<string> = of('0');
toolbarTop$: Observable<string> = of('0');

async ngOnInit() {
this.routes = await this.commonService.getNavigation();
}

routes = [
{ path: '/', title: 'Start', icon: 'home' },
{ path: '/blog', title: 'Blog', icon: 'feed' },
{ path: '/imprint', title: 'Impressum', icon: 'info' },
{ path: '/privacy', title: 'Datenschutz', icon: 'security' },
{ path: '/settings', title: 'Einstellungen', icon: 'settings' }
];
ngAfterViewInit(): void {
let lastScrollTop = 0;

ngAfterViewInit(): void {
let lastScrollTop = 0;
const onScroll = (event: Event) => {
const target = event.target as HTMLElement;
const toolbar = target.querySelector('.mat-toolbar') as HTMLElement;

const onScroll = (event: Event) => {
const target = event.target as HTMLElement;
const toolbar = target.querySelector('.mat-toolbar') as HTMLElement;

const top = target.scrollTop < lastScrollTop ? 0
: target.scrollTop < toolbar.offsetHeight
? target.scrollTop : toolbar.offsetHeight
toolbar.style.top = `${-top}px`;
const top = target.scrollTop < lastScrollTop ? 0
: target.scrollTop < toolbar.offsetHeight
? target.scrollTop : toolbar.offsetHeight
toolbar.style.top = `${-top}px`;

lastScrollTop = target.scrollTop > 0 ? target.scrollTop : 0;
return `${top}px`;
};

this.toolbarTop$ = this.sidenavContainer.scrollable.elementScrolled().pipe(map(onScroll));

// this.changeDetectorRef.detectChanges();
this.toolbarTop$.subscribe();
}
lastScrollTop = target.scrollTop > 0 ? target.scrollTop : 0;
return `${top}px`;
};

this.toolbarTop$ = this.sidenavContainer.scrollable.elementScrolled().pipe(map(onScroll));

// this.changeDetectorRef.detectChanges();
this.toolbarTop$.subscribe();
}

get sidenavRoutes(): NavigationItem[] {
return this.routes.filter(route => route.sidenav);
}

get footerRoutes(): NavigationItem[] {
return this.routes.filter(route => route.sidenav);
}
}
8 changes: 8 additions & 0 deletions src/app/models/nav.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface NavigationItem {
path: string;
title: string;
icon: string;
order: number;
sidenav: boolean;
footer: boolean;
}
16 changes: 16 additions & 0 deletions src/app/services/common.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { CommonService } from './common.service';

xdescribe('CommonService', () => {
let service: CommonService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CommonService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
22 changes: 22 additions & 0 deletions src/app/services/common.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@angular/core';
import { FirestoreService } from './firestore.service';
import { orderBy } from '@angular/fire/firestore';
import { NavigationItem } from '../models/nav.model';

@Injectable({
providedIn: 'root'
})
export class CommonService {
private readonly navStore = new FirestoreService('navigation');
private readonly resStore = new FirestoreService('resources');

constructor() { }

async getNavigation(): Promise<NavigationItem[]> {
return this.navStore.getDocuments<NavigationItem>(orderBy('order'));
}

async getResources(): Promise<NavigationItem[]> {
return this.resStore.getDocuments<NavigationItem>();
}
}
5 changes: 1 addition & 4 deletions src/app/services/firestore.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { TestBed } from '@angular/core/testing';

import { FirestoreService } from './firestore.service';

xdescribe('FirestoreService', () => {
let service: FirestoreService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(FirestoreService);
service = new FirestoreService('path');
});

it('should be created', () => {
Expand Down
5 changes: 1 addition & 4 deletions src/app/services/firestore.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,7 @@ export const snapshotOptions: SnapshotOptions = {

type Data = { id: string };

@Injectable({
providedIn: 'root'
})
export abstract class FirestoreService {
export class FirestoreService {
readonly store = inject(Firestore);

constructor(private path: string) { }
Expand Down
2 changes: 1 addition & 1 deletion src/app/services/user-progress.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing';

import { UserProgressService } from './user-progress.service';

describe('UserProgressService', () => {
xdescribe('UserProgressService', () => {
let service: UserProgressService;

beforeEach(() => {
Expand Down

0 comments on commit b9bb386

Please sign in to comment.