diff --git a/src/app/core/models/seo-attribute/seo-attribute.model.ts b/src/app/core/models/seo-attribute/seo-attribute.model.ts index 3daf875304..28dbb429ba 100644 --- a/src/app/core/models/seo-attribute/seo-attribute.model.ts +++ b/src/app/core/models/seo-attribute/seo-attribute.model.ts @@ -3,4 +3,5 @@ export interface SeoAttributes { metaTitle?: string; robots?: string[]; canonical?: string; + 'og:image'?: string; } diff --git a/src/app/extensions/seo/store/seo/seo.effects.ts b/src/app/extensions/seo/store/seo/seo.effects.ts index b4778df880..9f04d63c56 100644 --- a/src/app/extensions/seo/store/seo/seo.effects.ts +++ b/src/app/extensions/seo/store/seo/seo.effects.ts @@ -1,7 +1,8 @@ -import { DOCUMENT } from '@angular/common'; -import { Inject, Injectable } from '@angular/core'; +import { DOCUMENT, isPlatformServer } from '@angular/common'; +import { Inject, Injectable, Optional, PLATFORM_ID } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; +import { REQUEST } from '@nguniversal/express-engine/tokens'; import { MetaService } from '@ngx-meta/core'; import { TranslateService } from '@ngx-translate/core'; import { mapToParam, ofRoute } from 'ngrx-router'; @@ -21,14 +22,32 @@ import { SeoActionTypes, SetSeoAttributes } from './seo.actions'; @Injectable() export class SeoEffects { canonicalLink: HTMLLinkElement; + baseURL: string; + ogImageDefault: string; constructor( private actions$: Actions, private store: Store<{}>, private meta: MetaService, private translate: TranslateService, - @Inject(DOCUMENT) private doc: Document + @Inject(DOCUMENT) private doc: Document, + @Inject(PLATFORM_ID) private platformId: string, + // tslint:disable-next-line:no-any + @Optional() @Inject(REQUEST) private request: any ) { + // get baseURL + if (isPlatformServer(this.platformId)) { + this.baseURL = `${this.request.protocol}://${this.request.get('host') + + this.doc.querySelector('base').getAttribute('href')}`; + } else { + this.baseURL = this.doc.baseURI; + } + + // set og:image default (needs to be an absolute URL) + this.ogImageDefault = `${this.baseURL}assets/img/og-image-default.jpg`; + this.meta.setTag('og:image', this.ogImageDefault); + + // set canonical default this.canonicalLink = this.doc.querySelector('link[rel="canonical"]'); if (!this.canonicalLink) { this.canonicalLink = this.doc.createElement('link'); @@ -48,7 +67,8 @@ export class SeoEffects { this.meta.setTitle(seoAttributes.metaTitle); this.meta.setTag('description', seoAttributes.metaDescription); this.meta.setTag('robots', seoAttributes.robots && seoAttributes.robots.join(',')); - this.canonicalLink.setAttribute('href', seoAttributes.canonical || this.doc.URL); + this.meta.setTag('og:image', seoAttributes['og:image'] || this.ogImageDefault); + this.canonicalLink.setAttribute('href', this.baseURL + seoAttributes.canonical || this.doc.URL); } }) ); @@ -64,7 +84,7 @@ export class SeoEffects { c => c && c.seoAttributes && { - canonical: `/category/${c.uniqueId}`, + canonical: `category/${c.uniqueId}`, ...c.seoAttributes, } ), @@ -84,14 +104,14 @@ export class SeoEffects { whenTruthy(), map(p => (ProductHelper.isVariationProduct(p) && p.productMaster()) || p), distinctUntilKeyChanged('sku'), - map( - p => - p && - p.seoAttributes && { - canonical: generateProductRoute(p, p.defaultCategory()), - ...p.seoAttributes, - } - ), + map(p => { + const productImage = ProductHelper.getPrimaryImage(p, 'L'); + const seoAttributes = { + canonical: generateProductRoute(p, p.defaultCategory()), + 'og:image': productImage && productImage.effectiveUrl, + }; + return p.seoAttributes ? { ...seoAttributes, ...p.seoAttributes } : seoAttributes; + }), whenTruthy() ) ), diff --git a/src/assets/img/og-image-default.jpg b/src/assets/img/og-image-default.jpg new file mode 100644 index 0000000000..7b7f58ea42 Binary files /dev/null and b/src/assets/img/og-image-default.jpg differ