Skip to content

Commit

Permalink
add changelogs, back buttons. closes #8
Browse files Browse the repository at this point in the history
  • Loading branch information
seiyria committed May 9, 2024
1 parent e7b6f5c commit f8ab199
Show file tree
Hide file tree
Showing 18 changed files with 335 additions and 41 deletions.
4 changes: 4 additions & 0 deletions interfaces/changelog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IChangelogEntry {
date: string;
text: string;
}
1 change: 1 addition & 0 deletions interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './carddata';
export * from './cardhelp';
export * from './changelog';
export * from './faq';
export * from './product';
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@
<div class="map-item">
<a routerLink="/faq">{{ 'Components.BelowTheFold.FAQ' | translate }}</a>
</div>
<div class="map-item">
<a routerLink="/changelog">{{ 'Components.BelowTheFold.Changelog' | translate }}</a>
</div>
<div class="map-item">
<a target="_blank" href="https://ledergames.com/pages/contact">{{ 'Components.BelowTheFold.Ask' | translate
}}</a>
Expand Down
2 changes: 0 additions & 2 deletions src/app/_shared/components/omnisearch/omnisearch.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ export class OmnisearchComponent {
this.query = this.queryString ?? '';
}

console.log(this.initialQuery(), this.query, this.queryString);

this.chosenProduct = getProductFromQuery(this.query) ?? 'default';

this.query = this.removeProductFromQuery(this.query).trim();
Expand Down
2 changes: 1 addition & 1 deletion src/app/_shared/components/topbar/topbar.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<ion-toolbar color="primary">
@if(showBackButton()) {
<ion-buttons slot="start">
<ion-back-button defaultHref="/search"></ion-back-button>
<ion-back-button [defaultHref]="defaultBackLocation()"></ion-back-button>
</ion-buttons>
}

Expand Down
1 change: 1 addition & 0 deletions src/app/_shared/components/topbar/topbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class TopbarComponent {
public searchOnEnter = input<boolean>(false);

public showBackButton = input<boolean>(false);
public defaultBackLocation = input<string>('/search');

doType($event: string) {
if (!this.searchOnType()) return;
Expand Down
1 change: 1 addition & 0 deletions src/app/_shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export function httpLoaderFactory(http: HttpClient) {
FaqPipe,
StripSpacesPipe,
TranslateModule,
LuxonModule,
],
})
export class SharedModule {}
5 changes: 5 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const routes: Routes = [
path: 'faq',
loadChildren: () => import('./faq/faq.module').then((m) => m.FaqPageModule),
},
{
path: 'changelog',
loadChildren: () =>
import('./changelog/changelog.module').then((m) => m.ChangelogPageModule),
},
{
path: '**',
redirectTo: '',
Expand Down
11 changes: 10 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CardsService } from './cards.service';
import { ChangelogService } from './changelog.service';
import { FAQService } from './faq.service';
import { LocaleService } from './locale.service';
import { MetaService } from './meta.service';
Expand All @@ -36,18 +37,26 @@ import { MetaService } from './meta.service';
{
provide: APP_INITIALIZER,
multi: true,
deps: [MetaService, LocaleService, FAQService, CardsService],
deps: [
MetaService,
LocaleService,
FAQService,
ChangelogService,
CardsService,
],
useFactory:
(
metaService: MetaService,
localeService: LocaleService,
faqService: FAQService,
changelogService: ChangelogService,
cardsService: CardsService
) =>
async () => {
await metaService.init();
await localeService.init();
await faqService.init();
await changelogService.init();
await cardsService.init();
},
},
Expand Down
68 changes: 68 additions & 0 deletions src/app/changelog.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { inject, Injectable, signal, type WritableSignal } from '@angular/core';
import { sortBy } from 'lodash';
import type { IChangelogEntry } from '../../interfaces';
import { environment } from '../environments/environment';
import { LocaleService } from './locale.service';

@Injectable({
providedIn: 'root',
})
export class ChangelogService {
private localeService = inject(LocaleService);

private changelogByProductIdAndLocale: WritableSignal<
Record<string, Record<string, IChangelogEntry[]>>
> = signal({});

public async init() {
const faqData = await fetch(`${environment.baseUrl}/changelog.json`);
const realData = await faqData.json();

this.parseLocaleFAQs(realData);
}

private parseLocaleFAQs(
faqData: Record<string, Record<string, IChangelogEntry[]>>
) {
const baseChangelogs = this.changelogByProductIdAndLocale();

Object.keys(faqData).forEach((productId) => {
baseChangelogs[productId] ??= {};

Object.keys(faqData[productId]).forEach((locale) => {
baseChangelogs[productId][locale] = sortBy(
faqData[productId][locale],
'date'
);
});
});

this.changelogByProductIdAndLocale.set(baseChangelogs);
}

public getChangelogs(): Array<{
productId: string;
locale: string;
changelog: IChangelogEntry[];
}> {
const changelogData = this.changelogByProductIdAndLocale();
const locale = this.localeService.currentLocale();

return Object.keys(changelogData)
.map((productId) => ({
productId,
locale,
changelog: changelogData[productId][locale],
}))
.filter((p) => p.changelog)
.flat();
}

public getProductChangelog(
productId: string,
locale: string
): IChangelogEntry[] | undefined {
const changelog = this.changelogByProductIdAndLocale();
return changelog?.[productId]?.[locale];
}
}
17 changes: 17 additions & 0 deletions src/app/changelog/changelog-routing.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { type Routes, RouterModule } from '@angular/router';

import { ChangelogPage } from './changelog.page';

const routes: Routes = [
{
path: '',
component: ChangelogPage,
},
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ChangelogPageRoutingModule {}
22 changes: 22 additions & 0 deletions src/app/changelog/changelog.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { IonicModule } from '@ionic/angular';

import { ChangelogPageRoutingModule } from './changelog-routing.module';

import { SharedModule } from '../_shared/shared.module';
import { ChangelogPage } from './changelog.page';

@NgModule({
imports: [
CommonModule,
FormsModule,
IonicModule,
SharedModule,
ChangelogPageRoutingModule,
],
declarations: [ChangelogPage],
})
export class ChangelogPageModule {}
64 changes: 64 additions & 0 deletions src/app/changelog/changelog.page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<ion-header>
<app-topbar [searchOnEnter]="true" [showBackButton]="!!productId()" [defaultBackLocation]="'/changelog'"></app-topbar>
</ion-header>

<header class="print-only print-header">
{{ changelogProductName() }} Changelog
</header>

<ion-content>
<div class="page-container printable">

@if(productId()) {
<header class="print-hide print-header">
{{ changelogProductName() }} Changelog
</header>
}

@if(currentChangelog(); as changelog) {
<ion-grid>
<ion-row>
<ion-col>
<ion-list>
@for(entry of changelog; track $index) {
<ion-item>
<ion-label text-wrap class="print-only">
<h3>{{ entry.date | dateTimeFromIso | dateTimeToFormat:'DDD' }}</h3>
<p [innerText]="(entry.text | faq)"></p>
</ion-label>

<ion-label text-wrap class="print-hide">
<h3>{{ entry.date | dateTimeFromIso | dateTimeToFormat:'DDD' }}</h3>
<p [innerHTML]="(entry.text | faq:productId())"></p>
</ion-label>
</ion-item>
}
</ion-list>
</ion-col>
</ion-row>
</ion-grid>
}

@else {
<ion-grid>
<ion-row>
@for(changelog of changelogs(); track $index) {
<ion-col size-lg="3" size-md="4" size-sm="6" size-xs="12">
<ion-card class="changelog-tile" (click)="loadChangelog(changelog)">
<ion-card-header>
<ion-card-title>{{ metaService.getProductNameByProductId(changelog.productId) }}</ion-card-title>
</ion-card-header>

<ion-card-content>
{{ changelog.changelog.length | number }} Change(s)
</ion-card-content>
</ion-card>
</ion-col>
}
</ion-row>
</ion-grid>
}
</div>

<app-below-the-fold></app-below-the-fold>
</ion-content>
31 changes: 31 additions & 0 deletions src/app/changelog/changelog.page.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.changelog-tile {
min-height: 200px;
padding: 4px;

border: 2px solid #000;

cursor: pointer;
user-select: none;

&:hover {
border: 2px solid var(--ion-color-primary);
}

ion-card-content {
display: flex;
justify-content: center;
align-items: center;

font-size: 2em;
padding: 1em;
}
}

.page-container .print-header {
text-align: center;

margin-top: 24px;
margin-bottom: 24px;

font-size: 3em;
}
70 changes: 70 additions & 0 deletions src/app/changelog/changelog.page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component, computed, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, EventType, Router } from '@angular/router';
import { filter } from 'rxjs';
import type { IChangelogEntry } from '../../../interfaces';
import { ChangelogService } from '../changelog.service';
import { MetaService } from '../meta.service';

@Component({
selector: 'app-changelog',
templateUrl: './changelog.page.html',
styleUrls: ['./changelog.page.scss'],
})
export class ChangelogPage {
private router = inject(Router);
private route = inject(ActivatedRoute);
private changelogService = inject(ChangelogService);
public metaService = inject(MetaService);

private locale = signal<string>('');
public productId = signal<string>('');

public changelogs = computed(() => this.changelogService.getChangelogs());
public currentChangelog = computed(() =>
this.changelogService.getProductChangelog(this.productId(), this.locale())
);
public changelogProductName = computed(() =>
this.metaService.getProductNameByProductId(this.productId())
);

constructor() {
this.router.events
.pipe(takeUntilDestroyed())
.pipe(filter((evt) => evt.type === EventType.NavigationEnd))
.subscribe(() => this.parseQueryParams());
}

ionViewDidEnter() {
this.parseQueryParams();

if (!this.locale() || !this.productId()) {
this.router.navigate(['/changelog']);
return;
}
}

loadChangelog(faq: {
productId: string;
locale: string;
changelog: IChangelogEntry[];
}) {
this.router.navigate([], {
relativeTo: this.route,
queryParams: { locale: faq.locale, productId: faq.productId },
queryParamsHandling: 'merge',
});

this.locale.set(faq.locale);
this.productId.set(faq.productId);

this.parseQueryParams();
}

private parseQueryParams() {
const urlParams = new URLSearchParams(window.location.search);

this.locale.set(urlParams.get('locale') ?? '');
this.productId.set(urlParams.get('productId') ?? '');
}
}
Loading

0 comments on commit f8ab199

Please sign in to comment.