Commit c546247b authored by Mark Harding's avatar Mark Harding

(chore): more seo tweaks

parent 266c3f0d
No related merge requests found
Pipeline #118171043 passed with stages
in 37 minutes and 21 seconds
......@@ -442,24 +442,7 @@ const routes: Routes = [
),
deps: [Client, Injector, RedirectService, Location],
},
{
provide: MetaService,
useFactory: (
titleService,
metaService,
siteService,
location,
configsService
) =>
new MetaService(
titleService,
metaService,
siteService,
location,
configsService
),
deps: [Title, Meta, SiteService, Location, ConfigsService],
},
MetaService,
MediaProxyService,
V2TopbarService,
{
......
......@@ -39,6 +39,7 @@ describe('ChannelModeSelector', () => {
subscribers_count: 182,
impressions: 18200,
mode: ChannelMode.PUBLIC,
nsfw: [],
};
clientMock.response['api/v1/channel/info'] = { status: 'success' };
......
import { Injectable, Optional } from '@angular/core';
import { Injectable, Optional, Inject } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';
import { SiteService } from './site.service';
import { Location } from '@angular/common';
import { ConfigsService } from './configs.service';
import { DOCUMENT } from '@angular/common';
const DEFAULT_META_TITLE = 'Minds';
const DEFAULT_META_DESCRIPTION = '...';
......@@ -19,7 +20,8 @@ export class MetaService {
private metaService: Meta,
private site: SiteService,
private location: Location,
private configs: ConfigsService
private configs: ConfigsService,
@Inject(DOCUMENT) private dom
) {
this.reset();
}
......@@ -30,6 +32,12 @@ export class MetaService {
? this.site.title + ' - ' + this.site.oneLineHeadline
: DEFAULT_META_TITLE;
value = this.stripHtml(value);
if (value.length > 60) {
value = value.substr(0, 57) + '...';
}
if (value && join) {
title = [value, defaultTitle]
.filter(fragment => Boolean(fragment))
......@@ -39,12 +47,17 @@ export class MetaService {
} else {
title = defaultTitle;
}
this.title = title;
this.applyTitle();
return this;
}
setDescription(value: string): MetaService {
value = this.stripHtml(value);
if (value.length > 160) {
value = value.substr(0, 157) + '...';
}
this.metaService.updateTag({ name: 'description', content: value });
return this;
}
......@@ -55,6 +68,33 @@ export class MetaService {
return this;
}
setCanonicalUrl(value: string): MetaService {
// Find and clear or canonical links
const links: HTMLLinkElement[] = this.dom.head.querySelectorAll(
'[rel="canonical"]'
);
if (links.length) {
for (const link of links) {
this.dom.head.removeChild(link);
}
}
if (value) {
// TODO: fix duplicated code with ogUrl here...
if (value && value.indexOf('/') === 0) {
// Relative path
value = this.site.baseUrl + value.substr(1);
}
let link: HTMLLinkElement;
link = this.dom.createElement('link');
link.setAttribute('rel', 'canonical');
link.setAttribute('href', value);
this.dom.head.appendChild(link);
}
return this;
}
setOgUrl(value: string): MetaService {
if (value && value.indexOf('/') === 0) {
// Relative path
......@@ -101,11 +141,24 @@ export class MetaService {
return this;
}
setLanguage(language: string): MetaService {
return this;
}
setRobots(value: string): MetaService {
this.metaService.updateTag({ name: 'robots', content: value });
return this;
}
setNsfw(value: boolean): MetaService {
if (value) {
this.metaService.updateTag({ name: 'rating', content: 'adult' });
} else {
this.metaService.removeTag("name='rating'");
}
return this;
}
reset(
data: {
title?: string;
......@@ -120,7 +173,9 @@ export class MetaService {
.setOgType('website')
.setOgUrl(data.ogUrl || this.location.path())
.setOgImage(data.ogImage || null, { width: 0, height: 0 })
.setRobots(data.robots || 'all');
.setCanonicalUrl('') // Only user canonical when required
.setRobots(data.robots || 'all')
.setNsfw(false);
}
private applyTitle(): void {
......@@ -134,4 +189,16 @@ export class MetaService {
content: this.title,
});
}
/**
* Removes any html found and returns on text
* @param value
* @return string
*/
private stripHtml(value: string): string {
if (!value) return '';
const fakeEl = this.dom.createElement('span');
fakeEl.innerHTML = value;
return fakeEl.textContent || fakeEl.innerText;
}
}
......@@ -115,6 +115,7 @@ export interface MindsUser {
has_custom_background?: boolean;
};
mode: ChannelMode;
nsfw: Array<number>;
}
export interface MindsGroup {
......
......@@ -127,10 +127,16 @@ export class BlogViewInfinite {
.setTitle(blog.custom_meta['title'] || blog.title)
.setDescription(description)
//.setAuthor(this.blog.custom_meta['author'] || `@${this.blog.ownerObj.username}`)
.setOgType('article')
.setCanonicalUrl(blog.perma_url)
.setOgUrl(blog.perma_url)
.setOgImage(blog.thumbnail_src)
.setRobots(
blog['thumbs:up:count'] >= MIN_METRIC_FOR_ROBOTS ? 'all' : 'noindex'
);
if (blog.nsfw.length) {
this.metaService.setNsfw(true);
}
}
}
......@@ -96,7 +96,8 @@ describe('ChannelComponent', () => {
{ provide: Client, useValue: clientMock },
{ provide: Upload, useValue: uploadMock },
{ provide: Session, useValue: sessionMock },
{ provide: MetaService, useValue: MockService(MetaService) },
MetaService,
SiteService,
{ provide: ScrollService, useValue: scrollServiceMock },
{ provide: RecentService, useValue: recentServiceMock },
{ provide: ContextService, useValue: contextServiceMock },
......@@ -141,6 +142,7 @@ describe('ChannelComponent', () => {
large: 'thumbs',
master: 'thumbs',
},
nsfw: [],
};
comp.editing = false;
fixture.detectChanges();
......
......@@ -127,21 +127,26 @@ export class ChannelComponent {
private updateMeta(): void {
if (this.user) {
this.metaService.setTitle(`${this.user.name} (@${this.user.username})`);
this.metaService.setDescription(
this.user.briefdescription || `Subscribe to @${this.user.username}`
);
this.metaService.setOgUrl(`/${this.user.username.toLowerCase()}`);
this.metaService.setOgImage(this.user.avatar_url.master, {
width: 2000,
height: 1000,
});
this.metaService.setRobots(
this.user.is_mature ||
const url = `/${this.user.username.toLowerCase()}`;
this.metaService
.setTitle(`${this.user.name} (@${this.user.username})`)
.setDescription(
this.user.briefdescription || `Subscribe to @${this.user.username}`
)
.setOgUrl(url)
.setCanonicalUrl(url)
.setOgImage(this.user.avatar_url.master, {
width: 2000,
height: 1000,
})
.setRobots(
this.user['subscribers_count'] < MIN_METRIC_FOR_ROBOTS
? 'noindex'
: 'all'
);
? 'noindex'
: 'all'
);
if (this.user.is_mature || this.user.nsfw.length) {
this.metaService.setNsfw(true);
}
} else if (this.username) {
this.metaService.setTitle(this.username);
} else {
......
......@@ -91,6 +91,7 @@ describe('ChannelFeed', () => {
impressions: 18200,
pinned_posts: ['a', 'b', 'c'],
mode: ChannelMode.PUBLIC,
nsfw: [],
};
comp.feed = [
{ guid: 'aaaa' },
......
......@@ -191,6 +191,7 @@ describe('ChannelSidebar', () => {
subscribers_count: 182,
impressions: 18200,
mode: ChannelMode.PUBLIC,
nsfw: [],
};
comp.editing = false;
uploadMock.response[`api/v1/channel/avatar`] = {
......
......@@ -5,8 +5,6 @@
[showBottombar]="false"
[forceBackground]="false"
[class.m-homepage__formExperiment]="!!registerForm"
pageTitle="Minds Social Network"
i18n-pageTitle
>
<div class="m-marketing__main m-marketing__section--style-2">
<div class="m-grid m-marketing__wrapper">
......@@ -18,13 +16,11 @@
</h1>
<h2 ngPreserveWhitespaces i18n>
Take back control of your social media
{{ headline }}
</h2>
<p class="m-marketing__description" i18n>
A place to have open conversations and bring people together. Free
your mind and get paid for creating content, driving traffic and
referring friends.
{{ description }}
</p>
<button
......
......@@ -8,6 +8,7 @@ import { RegisterForm } from '../forms/register/register';
import { FeaturesService } from '../../services/features.service';
import { ConfigsService } from '../../common/services/configs.service';
import { OnboardingV2Service } from '../onboarding-v2/service/onboarding.service';
import { MetaService } from '../../common/services/meta.service';
@Component({
selector: 'm-homepage__v2',
......@@ -18,16 +19,19 @@ export class HomepageV2Component {
readonly cdnAssetsUrl: string;
readonly siteUrl: string;
readonly headline = 'Take back control of your social media';
readonly description =
'A place to have open conversations and bring people together. Free your mind and get paid for creating content, driving traffic and referring friends.';
constructor(
public client: Client,
public router: Router,
public navigation: NavigationService,
public session: Session,
private loginReferrer: LoginReferrerService,
private featuresService: FeaturesService,
configs: ConfigsService,
private onboardingService: OnboardingV2Service
private onboardingService: OnboardingV2Service,
private metaService: MetaService
) {
this.cdnAssetsUrl = configs.get('cdn_assets_url');
this.siteUrl = configs.get('site_url');
......@@ -38,6 +42,10 @@ export class HomepageV2Component {
this.router.navigate(['/newsfeed']);
return;
}
this.metaService
.setDescription(`${this.headline}. ${this.description.split('.')[0]}.`)
.setCanonicalUrl('/')
.setOgUrl('/');
}
registered() {
......
......@@ -157,23 +157,38 @@ export class NewsfeedSingleComponent {
private updateMeta(): void {
const activity = this.activity.remind_object || this.activity;
const title: string =
activity.title ||
activity.message ||
`@${activity.ownerObj.username}'s post on Minds`;
let description: string;
if (title.length > 60) {
description = `...${title.substr(57)}`;
} else {
description = activity.blurb || '';
}
description += `. Subscribe to @${activity.ownerObj.username} on Minds`;
this.metaService
.setTitle(`@${activity.ownerObj.username} on Minds`)
.setDescription(
activity.title ||
activity.message ||
`Subscribe to @${activity.ownerObj.username} on Minds`
)
.setTitle(title)
.setDescription(description)
.setOgImage(
activity.custom_type === 'batch'
? activity.custom_data[0]['src']
: activity.thumbnail_src,
{ width: 2000, height: 1000 }
)
.setCanonicalUrl(`/newsfeed/${activity.guid}`)
.setRobots(
activity['thumbs:up:count'] >= MIN_METRIC_FOR_ROBOTS ? 'all' : 'noindex'
);
if (activity.nsfw.length) {
this.metaService.setNsfw(true);
}
if (activity.custom_type === 'video') {
this.metaService.setOgType('video');
this.metaService.setOgImage(activity.custom_data['thumbnail_src']);
......
Please register or to comment