@@ -365,6 +365,7 @@ interface IData {
365
365
timestamp: number | null
366
366
excerpt: string | null
367
367
category: string | null
368
+ friendlyUrl: string | null
368
369
encrypted: boolean
369
370
deleted: boolean
370
371
tags: Tag []
@@ -387,14 +388,15 @@ interface IData {
387
388
captionHeight? : number
388
389
showShare: boolean
389
390
readingTime: number | null
391
+ wordcount: number | null
392
+ postImages: Array <string >
390
393
realURL: string
391
394
isLeaving: boolean
392
395
showPaywall: boolean
393
396
showSubscriptions: boolean
394
397
enabledTiers: Array <string >
395
398
subscriptionStatus: ` INSUFFICIENT_TIER ` | ` NOT_SUBSCRIBED ` | ` `
396
399
postImageKeys: Array <IPostImageKey >
397
- isMetadataLoading: boolean
398
400
isContentLoading: boolean
399
401
}
400
402
@@ -424,7 +426,6 @@ export default Vue.extend({
424
426
next ()
425
427
},
426
428
layout: ` reader ` ,
427
- // mixins: [markdown],
428
429
data(): IData {
429
430
return {
430
431
title: null ,
@@ -435,6 +436,9 @@ export default Vue.extend({
435
436
category: null ,
436
437
deleted: false ,
437
438
encrypted: false ,
439
+ friendlyUrl: null ,
440
+ postImages: [],
441
+ wordcount: null ,
438
442
tags: [],
439
443
post: null ,
440
444
author: null ,
@@ -463,17 +467,18 @@ export default Vue.extend({
463
467
enabledTiers: [],
464
468
subscriptionStatus: ` ` ,
465
469
postImageKeys: [],
466
- isMetadataLoading: true ,
467
470
isContentLoading: true ,
468
471
}
469
472
},
470
473
head() {
474
+ const canonicalLink = {
475
+ rel: ` canonical ` ,
476
+ // @ts-ignore
477
+ href: this .friendlyUrl ,
478
+ }
471
479
return {
472
480
// @ts-ignore
473
- title: this .title
474
- ? // @ts-ignore
475
- ` ${this .title } by ${this .authorID } on Blogchain `
476
- : ` Loading... ` ,
481
+ title: this .title ?? ` Loading... ` ,
477
482
meta: [
478
483
{
479
484
hid: ` description ` ,
@@ -482,6 +487,10 @@ export default Vue.extend({
482
487
content: this .subtitle ?? this .excerpt ,
483
488
},
484
489
],
490
+ link: [
491
+ // @ts-ignore
492
+ ... (this .friendlyUrl ? [canonicalLink ] : []),
493
+ ],
485
494
}
486
495
},
487
496
beforeDestroy() {
@@ -507,13 +516,22 @@ export default Vue.extend({
507
516
508
517
this .updatePostMetadata (postData )
509
518
this .excerpt = postData .excerpt
519
+ // Get reading time
520
+ this .calculateReadingTime ()
510
521
511
- // Get featured photo
512
- if (postData .featuredPhotoCID ) {
513
- getPhotoFromIPFS (postData .featuredPhotoCID ).then ((p ) => {
514
- this .featuredPhoto = p
522
+ // Change URL to social-friendly link, preserve real for Vue router
523
+ createShareableLink (this .$route .params .post )
524
+ .then ((friendlyUrl ) => {
525
+ this .friendlyUrl = friendlyUrl
526
+ if (! this .isLeaving ) {
527
+ this .realURL = this .$route .fullPath
528
+ history .replaceState (null , ` ` , friendlyUrl )
529
+ }
530
+ })
531
+ .catch ((err ) => {
532
+ // eslint-disable-next-line no-console
533
+ console .log (` Cannot replace state to shareable link: ${err .message } ` )
515
534
})
516
- }
517
535
518
536
// Get author profile
519
537
this .author = createDefaultProfile (postData .authorID )
@@ -529,31 +547,17 @@ export default Vue.extend({
529
547
}
530
548
})
531
549
532
- // Change URL to social-friendly link, preserve real for Vue router
533
- createShareableLink (this .$route .params .post )
534
- .then ((friendlyUrl ) => {
535
- if (! this .isLeaving ) {
536
- this .realURL = this .$route .fullPath
537
- history .replaceState (null , ` ` , friendlyUrl )
538
- }
539
- })
540
- .catch ((err ) => {
541
- // eslint-disable-next-line no-console
542
- console .log (` Cannot replace state to shareable link: ${err .message } ` )
550
+ // Get featured photo
551
+ if (postData .featuredPhotoCID ) {
552
+ getPhotoFromIPFS (postData .featuredPhotoCID ).then ((p ) => {
553
+ this .featuredPhoto = p
543
554
})
555
+ }
544
556
545
557
// Unauthenticated
546
558
if (sessionID === ` ` ) {
547
559
return
548
560
}
549
-
550
- try {
551
- await this .fetchPaymentProfile ({ username: this .authorID })
552
- } catch (err ) {
553
- if (! (err instanceof AxiosError && err .response ?.status === 404 )) {
554
- this .$handleError (err )
555
- }
556
- }
557
561
} catch (err : unknown ) {
558
562
if (! (err instanceof Error )) {
559
563
throw err
@@ -563,8 +567,6 @@ export default Vue.extend({
563
567
}
564
568
565
569
this .$toastError (err .message )
566
- } finally {
567
- this .isMetadataLoading = false
568
570
}
569
571
570
572
// This is a new post
@@ -581,6 +583,8 @@ export default Vue.extend({
581
583
const postCID = this .$route .params .post
582
584
const sessionID = this .$store .state .session .id
583
585
try {
586
+ // Uncomment this to test the loading behaviour/what google sees
587
+ // await new Promise((resolve) => setTimeout(resolve, 1000 * 1000))
584
588
const post = await getPost (postCID )
585
589
verifyPostAuthenticity (post .data , post .sig , post .public_key ).then ((verified ) => {
586
590
if (! verified ) {
@@ -590,6 +594,13 @@ export default Vue.extend({
590
594
591
595
const postData = post .data
592
596
this .updatePostMetadata (postData )
597
+
598
+ this .fetchPaymentProfile ({ username: this .authorID }).catch ((err ) => {
599
+ if (! (err instanceof AxiosError && err .response ?.status === 404 )) {
600
+ this .$handleError (err )
601
+ }
602
+ })
603
+
593
604
// Get featured photo
594
605
if (postData .featuredPhotoCID && ! this .featuredPhoto ) {
595
606
getPhotoFromIPFS (postData .featuredPhotoCID ).then ((p ) => {
@@ -682,6 +693,8 @@ export default Vue.extend({
682
693
category: string
683
694
tags: Tag []
684
695
encrypted? : boolean
696
+ wordCount? : number
697
+ postImages? : Array <string >
685
698
}) {
686
699
this .title = postData .title
687
700
if (postData .subtitle ) {
@@ -693,6 +706,12 @@ export default Vue.extend({
693
706
if (postData .encrypted ) {
694
707
this .encrypted = postData .encrypted
695
708
}
709
+ if (postData .wordCount ) {
710
+ this .wordcount = postData .wordCount
711
+ }
712
+ if (postData .postImages ) {
713
+ this .postImages = postData .postImages
714
+ }
696
715
this .tags = postData .tags
697
716
},
698
717
async getBookmarkStatus() {
@@ -784,14 +803,17 @@ export default Vue.extend({
784
803
})
785
804
},
786
805
calculateReadingTime() {
787
- if (! this .post ) {
788
- throw new Error (` Post can't be null ` )
806
+ let wordcount = this .wordcount
807
+ if (this .content ) {
808
+ wordcount = this .content .split (/ \s + / ).length
809
+ }
810
+ if (! wordcount ) {
811
+ return
789
812
}
790
- const wordcount = this .content .split (/ \s + / ).length
791
813
if (wordcount <= 0 ) {
792
814
throw new Error (` Word count can't be equal or less than zero ` )
793
815
}
794
- this .readingTime = calculateReadingTime (wordcount , this .post . postImages ? .length )
816
+ this .readingTime = calculateReadingTime (wordcount , this .postImages .length )
795
817
},
796
818
toggleSubscription() {
797
819
// Unauth
0 commit comments