diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 21df0a2..2b57730 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -17,6 +17,7 @@ const routes: Routes = [ import('./advanced/advanced.module').then((m) => m.AdvancedPageModule), data: { title: 'Leder Card Library - Advanced Search', + description: 'Craft a custom search query to find the cards you need.', }, }, { @@ -25,6 +26,8 @@ const routes: Routes = [ import('./search/search.module').then((m) => m.SearchPageModule), data: { title: 'Leder Card Library - Card Search', + description: 'View card search results.', + noindex: true, }, }, { @@ -41,6 +44,7 @@ const routes: Routes = [ import('./syntax/syntax.module').then((m) => m.SyntaxPageModule), data: { title: 'Leder Card Library - Search Help', + description: 'Get help with the search operators and syntax.', }, }, { @@ -49,6 +53,7 @@ const routes: Routes = [ import('./sets/sets.module').then((m) => m.SetsPageModule), data: { title: 'Leder Card Library - Product List', + description: 'View all products in the card library catalog.', }, }, { @@ -56,6 +61,8 @@ const routes: Routes = [ loadChildren: () => import('./faq/faq.module').then((m) => m.FaqPageModule), data: { title: 'Leder Card Library - FAQs', + description: + 'View a list of frequently asked questions on a per-product basis.', }, }, { @@ -64,6 +71,7 @@ const routes: Routes = [ import('./errata/errata.module').then((m) => m.ErrataPageModule), data: { title: 'Leder Card Library - Errata', + description: 'View a list of errata on a per-product basis.', }, }, { @@ -72,6 +80,7 @@ const routes: Routes = [ import('./changelog/changelog.module').then((m) => m.ChangelogPageModule), data: { title: 'Leder Card Library - Changelogs', + description: 'View a list of changelogs on a per-product basis.', }, }, { diff --git a/src/app/app.component.ts b/src/app/app.component.ts index d781a83..71a6a73 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,7 +1,7 @@ import { Component, inject, type OnInit } from '@angular/core'; -import { Title } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { filter, map, mergeMap } from 'rxjs'; +import { SEOService } from './seo.service'; @Component({ selector: 'app-root', @@ -11,7 +11,7 @@ import { filter, map, mergeMap } from 'rxjs'; export class AppComponent implements OnInit { private router = inject(Router); private route = inject(ActivatedRoute); - private title = inject(Title); + private seo = inject(SEOService); constructor() {} @@ -29,7 +29,17 @@ export class AppComponent implements OnInit { ) .subscribe((event) => { if (event['title']) { - this.title.setTitle(event['title']); + this.seo.updatePageTitle(event['title']); + } + + if (event['description']) { + this.seo.updateMetaDescription(event['description']); + } + + if (event['noindex']) { + this.seo.makePageUnindexable(); + } else { + this.seo.makePageIndexable(); } }); } diff --git a/src/app/card/card.page.ts b/src/app/card/card.page.ts index c582ac1..26dcd23 100644 --- a/src/app/card/card.page.ts +++ b/src/app/card/card.page.ts @@ -20,7 +20,6 @@ import { CardsService } from '../cards.service'; import { MetaService } from '../meta.service'; import { DOCUMENT } from '@angular/common'; -import { Meta, Title } from '@angular/platform-browser'; import { NavController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; import Handlebars from 'handlebars'; @@ -29,6 +28,7 @@ import { WINDOW } from '../_shared/helpers'; import { ErrataService } from '../errata.service'; import { FAQService } from '../faq.service'; import { NotifyService } from '../notify.service'; +import { SEOService } from '../seo.service'; @Component({ selector: 'app-card', @@ -43,8 +43,7 @@ export class CardPage implements OnInit, OnDestroy { private nav = inject(NavController); private window = inject(WINDOW); private document = inject(DOCUMENT); - private pageMeta = inject(Meta); - private title = inject(Title); + private seo = inject(SEOService); private translateService = inject(TranslateService); private cardsService = inject(CardsService); @@ -167,21 +166,15 @@ export class CardPage implements OnInit, OnDestroy { ? this.removeEmojis(cardData.text) : 'No text entered for this card.'; - this.pageMeta.updateTag({ property: 'og:title', content: cardData.name }); - this.pageMeta.updateTag({ property: 'og:image', content: cardData.image }); - this.pageMeta.updateTag({ - property: 'og:description', - content: text, - }); - this.pageMeta.updateTag({ - property: 'og:url', - content: `${environment.baseAppUrl}/card/${encodeURIComponent( - cardData.id - )}`, - }); - this.pageMeta.updateTag({ - name: 'description', - content: `${cardData.name} (${ + this.seo.updateOGTitle(cardData.name); + this.seo.updateOGImage(cardData.image); + this.seo.updateOGDescription(text); + this.seo.updateOGURL( + `${environment.baseAppUrl}/card/${encodeURIComponent(cardData.id)}` + ); + + this.seo.updateMetaDescription( + `${cardData.name} (${ cardData.id }) is a card in the ${this.metaService.getProductNameByProductId( cardData.game @@ -189,10 +182,10 @@ export class CardPage implements OnInit, OnDestroy { cardData.product )} set. It has ${this.faq().length} FAQ and ${ this.errata().length - } errata associated with it.`, - }); + } errata associated with it.` + ); - this.title.setTitle(`Leder Card Library - ${cardData.name}`); + this.seo.updatePageTitle(`Leder Card Library - ${cardData.name}`); const ldData = this.document.createElement('script'); ldData.id = 'card-metadata'; diff --git a/src/app/seo.service.ts b/src/app/seo.service.ts new file mode 100644 index 0000000..7283adb --- /dev/null +++ b/src/app/seo.service.ts @@ -0,0 +1,52 @@ +import { inject, Injectable } from '@angular/core'; + +import { Meta, Title } from '@angular/platform-browser'; + +@Injectable({ + providedIn: 'root', +}) +export class SEOService { + private pageMeta = inject(Meta); + private title = inject(Title); + + public updateOGTitle(newTitle: string): void { + this.pageMeta.updateTag({ property: 'og:title', content: newTitle }); + } + + public updateOGImage(newImage: string): void { + this.pageMeta.updateTag({ property: 'og:image', content: newImage }); + } + + public updateOGDescription(newDesc: string): void { + this.pageMeta.updateTag({ + property: 'og:description', + content: newDesc, + }); + } + + public updateOGURL(newURL: string): void { + this.pageMeta.updateTag({ + property: 'og:url', + content: newURL, + }); + } + + public updatePageTitle(newTitle: string): void { + this.title.setTitle(newTitle); + } + + public updateMetaDescription(newDesc: string): void { + this.pageMeta.updateTag({ + name: 'description', + content: newDesc, + }); + } + + public makePageIndexable() { + this.pageMeta.removeTag('property="robots"'); + } + + public makePageUnindexable() { + this.pageMeta.addTag({ property: 'robots', content: 'noindex' }); + } +}