generated from nhcarrigan/template
feat: set up comic view and routing logic
This commit is contained in:
@ -29,6 +29,8 @@
|
||||
"@angular/forms": "20.1.0",
|
||||
"@angular/platform-browser": "20.1.0",
|
||||
"@angular/router": "20.1.0",
|
||||
"@fortawesome/angular-fontawesome": "2.0.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"rxjs": "7.8.2",
|
||||
"tslib": "2.8.1",
|
||||
"zone.js": "0.15.1"
|
||||
|
39
pnpm-lock.yaml
generated
39
pnpm-lock.yaml
generated
@ -26,6 +26,12 @@ importers:
|
||||
'@angular/router':
|
||||
specifier: 20.1.0
|
||||
version: 20.1.0(@angular/common@20.1.0(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.1.0(@angular/common@20.1.0(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)
|
||||
'@fortawesome/angular-fontawesome':
|
||||
specifier: 2.0.1
|
||||
version: 2.0.1(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1))
|
||||
'@fortawesome/free-solid-svg-icons':
|
||||
specifier: 6.7.2
|
||||
version: 6.7.2
|
||||
rxjs:
|
||||
specifier: 7.8.2
|
||||
version: 7.8.2
|
||||
@ -512,6 +518,23 @@ packages:
|
||||
resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@fortawesome/angular-fontawesome@2.0.1':
|
||||
resolution: {integrity: sha512-IdklZkuw+WS2GQWhFnr1EX/tOALnrKaj4YGnUmPaUg2Uf+Amj8Xi+M/qDrr915YJ5MaDxd9tZ1kqOHRcvQqq2A==}
|
||||
peerDependencies:
|
||||
'@angular/core': ^20.0.0
|
||||
|
||||
'@fortawesome/fontawesome-common-types@6.7.2':
|
||||
resolution: {integrity: sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
'@fortawesome/fontawesome-svg-core@6.7.2':
|
||||
resolution: {integrity: sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
'@fortawesome/free-solid-svg-icons@6.7.2':
|
||||
resolution: {integrity: sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@ -4400,6 +4423,22 @@ snapshots:
|
||||
'@eslint/core': 0.15.1
|
||||
levn: 0.4.1
|
||||
|
||||
'@fortawesome/angular-fontawesome@2.0.1(@angular/core@20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1))':
|
||||
dependencies:
|
||||
'@angular/core': 20.1.0(@angular/compiler@20.1.0)(rxjs@7.8.2)(zone.js@0.15.1)
|
||||
'@fortawesome/fontawesome-svg-core': 6.7.2
|
||||
tslib: 2.8.1
|
||||
|
||||
'@fortawesome/fontawesome-common-types@6.7.2': {}
|
||||
|
||||
'@fortawesome/fontawesome-svg-core@6.7.2':
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-common-types': 6.7.2
|
||||
|
||||
'@fortawesome/free-solid-svg-icons@6.7.2':
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-common-types': 6.7.2
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
|
@ -9,13 +9,13 @@ import {
|
||||
provideBrowserGlobalErrorListeners,
|
||||
provideZoneChangeDetection,
|
||||
} from "@angular/core";
|
||||
import { provideRouter } from "@angular/router";
|
||||
import { provideRouter, withComponentInputBinding } from "@angular/router";
|
||||
import { routes } from "./app.routes";
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
provideRouter(routes, withComponentInputBinding()),
|
||||
],
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ import { Home } from "./home/home.js";
|
||||
|
||||
export const routes: Routes = [
|
||||
{ component: Home, path: "", pathMatch: "full" },
|
||||
{ component: Comic, path: "comic/:id" },
|
||||
// This line is necessary to handle the fallback when no ID is provided.
|
||||
{ component: Comic, path: "comic" },
|
||||
{ component: About, path: "about" },
|
||||
{ component: Archive, path: "archive" },
|
||||
|
@ -72,4 +72,26 @@ export class ComicService {
|
||||
public getComics(): Array<{ number: number; title: string; date: string }> {
|
||||
return this.comics;
|
||||
}
|
||||
|
||||
public getComicById(
|
||||
id: string,
|
||||
): { number: number; title: string; date: string } | undefined {
|
||||
const comicId = Number.parseInt(id, 10);
|
||||
return this.comics.find((comic) => {
|
||||
return comic.number === comicId;
|
||||
});
|
||||
}
|
||||
|
||||
public getLatestComic():
|
||||
| { number: number; title: string; date: string }
|
||||
| undefined {
|
||||
return this.comics.at(-1);
|
||||
}
|
||||
|
||||
public getLatestComicId(): string | undefined {
|
||||
const latestComic = this.getLatestComic();
|
||||
return latestComic
|
||||
? latestComic.number.toString()
|
||||
: undefined;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
img {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.ng-fa-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
@ -1 +1,12 @@
|
||||
<p>comic works!</p>
|
||||
<div *ngIf="comic">
|
||||
<h2>{{ comic!.title }}</h2>
|
||||
<p>Published on: {{ comic!.date }}</p>
|
||||
<img [src]="`https://cdn.yurigpt.com/comics/${comic!.number}.png`" alt="{{ comic!.title }}" />
|
||||
</div>
|
||||
<div *ngIf="error">
|
||||
<p>Error loading comic: {{ error }}</p>
|
||||
</div>
|
||||
<div id="buttons">
|
||||
<a *ngIf="previousComicId" [routerLink]="['/comic', previousComicId]"><fa-icon [icon]="backIcon"></fa-icon></a>
|
||||
<a *ngIf="nextComicId" [routerLink]="['/comic', nextComicId]"><fa-icon [icon]="forwardIcon"></fa-icon></a>
|
||||
</div>
|
||||
|
@ -1,11 +1,68 @@
|
||||
import { Component } from '@angular/core';
|
||||
/**
|
||||
* @copyright nhcarrigan
|
||||
* @license Naomi's Public License
|
||||
* @author Naomi Carrigan
|
||||
*/
|
||||
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, inject, input, signal } from "@angular/core";
|
||||
import { ActivatedRoute, RouterModule } from "@angular/router";
|
||||
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
|
||||
import { faLeftLong, faRightLong } from "@fortawesome/free-solid-svg-icons";
|
||||
import { ComicService } from "../comic-service.js";
|
||||
|
||||
@Component({
|
||||
selector: 'app-comic',
|
||||
imports: [],
|
||||
templateUrl: './comic.html',
|
||||
styleUrl: './comic.css'
|
||||
imports: [ CommonModule, RouterModule, FontAwesomeModule ],
|
||||
selector: "app-comic",
|
||||
styleUrl: "./comic.css",
|
||||
templateUrl: "./comic.html",
|
||||
})
|
||||
export class Comic {
|
||||
public comicId = signal("");
|
||||
public readonly id = input<string | undefined>("id");
|
||||
// eslint-disable-next-line stylistic/max-len -- I dunno.
|
||||
public comic: { number: number; title: string; date: string } | undefined;
|
||||
public error: string | undefined;
|
||||
public previousComicId: string | undefined;
|
||||
public nextComicId: string | undefined;
|
||||
public readonly backIcon = faLeftLong;
|
||||
public readonly forwardIcon = faRightLong;
|
||||
private readonly activatedRoute = inject(ActivatedRoute);
|
||||
|
||||
public constructor(private readonly comicService: ComicService) {
|
||||
this.comicService.
|
||||
loadComics().
|
||||
then(() => {
|
||||
this.activatedRoute.params.subscribe((parameters_) => {
|
||||
const parameters: Record<string, string> = parameters_;
|
||||
console.log(`FoundID: ${parameters["id"]}`);
|
||||
this.comicId.set(
|
||||
parameters["id"] ?? this.comicService.getLatestComicId(),
|
||||
);
|
||||
this.previousComicId = this.comicService.
|
||||
getComicById(String(Number.parseInt(this.comicId(), 10) - 1))?.
|
||||
number.toString();
|
||||
this.nextComicId = this.comicService.
|
||||
getComicById(String(Number.parseInt(this.comicId(), 10) + 1))?.
|
||||
number.toString();
|
||||
|
||||
// Load the comic data for the new ID
|
||||
this.comic = this.comicService.getComicById(this.comicId());
|
||||
if (this.comic) {
|
||||
// Clear any previous error
|
||||
this.error = undefined;
|
||||
} else {
|
||||
this.error = `Cannot find comic with ID ${this.comicId()}.`;
|
||||
}
|
||||
});
|
||||
}).
|
||||
catch((error: unknown) => {
|
||||
this.error = `Failed to load comics: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Please check the browser console for more details."
|
||||
}`;
|
||||
console.error("Error loading comics:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user