commit 538a7121576cd4d1aade5018620b7ae3398df18a
parent 8d9a3c54902a495d504b7cffa8d83e353b8949b8
Author: MTRNord <mtrnord1@gmail.com>
Date: Tue, 27 Mar 2018 15:22:59 +0200
Do some linkifing and add meta tags dynamically
Diffstat:
9 files changed, 195 insertions(+), 37 deletions(-)
diff --git a/.angular-cli.json b/.angular-cli.json
@@ -1,7 +1,7 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
- "name": "social-networks-news-web2"
+ "name": "social-networks-news-web"
},
"apps": [
{
@@ -12,7 +12,6 @@
"favicon.ico",
"manifest.json"
],
- "serviceWorker": true,
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
diff --git a/package.json b/package.json
@@ -34,6 +34,7 @@
"express": "^4.16.2",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
+ "linkifyjs": "^2.1.6",
"ng-http-loader": "^0.9.0",
"ngx-avatar": "^2.9.0",
"ngx-toastr": "^8.3.0",
diff --git a/src/app/tweet-list/tweet-list.component.html b/src/app/tweet-list/tweet-list.component.html
@@ -8,9 +8,9 @@
</div>
<mat-card-header>
<img src="https://twitter.com/{{item.username}}/profile_image" mat-card-avatar>
- <mat-card-title><a href="{{item.userprofile_link}}">{{item.display_name}}</a></mat-card-title>
+ <mat-card-title><a rel="noopener noreferrer" target="_blank" href="{{item.userprofile_link}}">{{item.display_name}}</a></mat-card-title>
<mat-card-subtitle *ngIf="!item.retweetby_display_name"><i class="fa fa-clock-o"> </i>{{item.created_at.toLocaleString()}}</mat-card-subtitle>
- <mat-card-subtitle *ngIf="item.retweetby_display_name"><i class="fa fa-clock-o"> </i>{{item.created_at.toLocaleString()}} -- Retweeted by: <a href="{{item.retweetby_userprofile_link}}">{{item.retweetby_display_name}}</a></mat-card-subtitle>
+ <mat-card-subtitle *ngIf="item.retweetby_display_name"><i class="fa fa-clock-o"> </i>{{item.created_at.toLocaleString()}} -- Retweeted by: <a rel="noopener noreferrer" target="_blank" href="{{item.retweetby_userprofile_link}}">{{item.retweetby_display_name}}</a></mat-card-subtitle>
</mat-card-header>
<a (click)="lightbox.show(lightboxItem)" >
<img mat-card-image *ngIf="item.image_urls && item.image_urls[0]" [src]="item.image_urls[0]">
@@ -23,7 +23,7 @@
</div>
</a>
<mat-card-content>
- {{item.text}}
+ {{item.text | linkify}}
<br>
<i class="fa fa-heart"> </i>{{item.favorites.toLocaleString()}} <i class="fa fa-retweet"> </i>{{item.retweets.toLocaleString()}}
</mat-card-content>
diff --git a/src/app/tweet-list/tweet-list.component.ts b/src/app/tweet-list/tweet-list.component.ts
@@ -10,7 +10,14 @@ import 'rxjs/add/operator/take';
import 'rxjs/add/operator/concat';
import 'rxjs/add/observable/throw';
import {ToastrService} from 'ngx-toastr';
+import {Meta, Title} from '@angular/platform-browser';
import {Lightbox} from '../utils/lightbox';
+import * as linkify from 'linkifyjs';
+import hashtagL from 'linkifyjs/plugins/hashtag';
+import mentionL from 'linkifyjs/plugins/mention';
+hashtagL(linkify);
+mentionL(linkify);
+
@Component({
selector: 'app-tweet-list',
@@ -22,7 +29,7 @@ export class TweetListComponent implements OnInit {
data: (TweetsEntity)[];
paperData: Paper;
- constructor(private apiService: ApiService, private toastr: ToastrService, private lightbox: Lightbox) {
+ constructor(private apiService: ApiService, private toastr: ToastrService, private lightbox: Lightbox, private metaService: Meta, private titleService: Title) {
Raven.captureBreadcrumb({
message: 'Showing Paper',
category: 'paper',
@@ -33,8 +40,19 @@ export class TweetListComponent implements OnInit {
}
ngOnInit() {
+ // Get API
this.getYesterday();
this.getPaper();
+
+ // Set Meta Tags
+ this.metaService.addTags([
+ { name: 'og:title', content: this.paperData.name },
+ { name: 'og:description', content: this.paperData.description },
+ { name: 'og:image', content: this.paperData.paper_image },
+ { name: 'description', content: this.paperData.description },
+ { name: 'author', content: this.paperData.author.username },
+ ]);
+ this.titleService.setTitle( this.paperData.name );
}
getPaper() {
this.apiService.getPaper(this.uuid)
@@ -96,23 +114,17 @@ export class TweetListComponent implements OnInit {
);
}
- private sort(arr: (TweetsEntity)[]): (TweetsEntity)[] {
- const compare = (a, b): number => {
- if ((a.image_urls && a.image_urls[0]) && !(b.image_urls && b.image_urls[0])) {
- return -1;
- }
- if (!(a.image_urls && a.image_urls[0]) && (b.image_urls && b.image_urls[0])) {
- return 1;
- }
- return 0;
- };
+ // TODO use this
+ private linkifyHash(text: string) {
+ hashtagL.find(text);
+ }
- arr.sort(compare);
- return arr;
+ // TODO use this
+ private linkifyMention(text: string) {
+ hashtagL.find(text);
}
- private chunk(arr: (TweetsEntity)[], size: number): (TweetsEntity)[][] {
- // Todo sort with a point system
+ private sort(arr: (TweetsEntity)[]): (TweetsEntity)[] {
const compare = (a, b): number => {
if ((a.image_urls && a.image_urls[0]) && !(b.image_urls && b.image_urls[0])) {
return -1;
@@ -124,10 +136,6 @@ export class TweetListComponent implements OnInit {
};
arr.sort(compare);
- const newArr = [];
- for (let i = 0; i < arr.length; i += size) {
- newArr.push(arr.slice(i, i + size));
- }
- return newArr;
+ return arr;
}
}
diff --git a/src/app/utils/linkifier.ts b/src/app/utils/linkifier.ts
@@ -0,0 +1,9 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import linkifyStr from 'linkifyjs/string';
+
+@Pipe({name: 'linkify'})
+export class LinkifyPipe implements PipeTransform {
+ transform(str: string): string {
+ return str ? linkifyStr(str, {target: '_blank', attributes: {rel: 'noopener noreferrer'}}) : str;
+ }
+}
diff --git a/src/app/utils/types/linkifyjs.d.ts b/src/app/utils/types/linkifyjs.d.ts
@@ -0,0 +1,63 @@
+// Type definitions for linkifyjs v2.1.3
+// Project: https://github.com/SoapBox/linkifyjs
+// Definitions by: Aleksey Nemiro <https://github.com/alekseynemiro>
+declare namespace LinkifyJs {
+
+ interface ILinkifyOptions {
+
+ /** Object of attributes to add to each new link. Note: the class and target attributes have dedicated options. */
+ attributes?: any | (( href, type ) => void);
+
+ /** class attribute to use for newly created links. */
+ className?: string;
+
+ /** Protocol that should be used in href attributes for URLs without a protocol (e.g., github.com). */
+ defaultProtocol?: string;
+
+ /** Add event listeners to newly created link elements. Takes a hash where each key is an standard event name and the value is an event handler. */
+ events?: any | (( href, type ) => void);
+
+ /** Format the text displayed by a linkified entity. e.g., truncate a long URL. */
+ format?: (( value, type ) => void) | any;
+
+ /** Similar to format, except the result of this function will be used as the href attribute of the new link. */
+ formatHref?: (( href, type ) => void) | any;
+
+ /** Prevent linkify from trying to parse links in the specified tags. This is useful when running linkify on arbitrary HTML. */
+ ignoreTags?: Array<string>;
+
+ /** If true, \n line breaks will automatically be converted to <br> tags. */
+ nl2br?: boolean;
+
+ /** The tag name to use for each link. For cases where you can’t use anchor tags. */
+ tagName?: string | (( href, type ) => void) | any;
+
+ /** target attribute for generated link. */
+ target?: string | (( href, type ) => void) | any;
+
+ /** If option resolves to false, the given value will not show up as a link. */
+ validate?: boolean;
+
+ }
+
+
+ type linkify = (value: string, options?: ILinkifyOptions) => string;
+
+}
+
+declare module 'linkifyjs' {
+
+ export = LinkifyJs;
+
+}
+
+declare module 'linkifyjs/html' {
+
+ let data: LinkifyJs.linkify;
+
+ export default data;
+
+}
+
+declare var linkify: LinkifyJs.linkify;
+declare var linkifyHtml: LinkifyJs.linkify;
diff --git a/src/typings.d.ts b/src/typings.d.ts
@@ -1,5 +0,0 @@
-/* SystemJS module definition */
-declare var module: NodeModule;
-interface NodeModule {
- id: string;
-}
diff --git a/tsconfig.json b/tsconfig.json
@@ -9,12 +9,13 @@
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
- "node_modules/@types"
+ "node_modules/@types",
+ "src/app/utils/types"
],
"lib": [
"es2017",
"dom"
]
},
- "include": ["node_modules/angular-bootstrap-md/**/*.ts", "src/**/*.ts"]
+ "include": ["src/**/*.ts"]
}
diff --git a/yarn.lock b/yarn.lock
@@ -1593,6 +1593,10 @@ copy-webpack-plugin@~4.4.1:
p-limit "^1.0.0"
serialize-javascript "^1.4.0"
+core-js@^1.0.0:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+
core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1:
version "2.5.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
@@ -2098,6 +2102,12 @@ encodeurl@~1.0.1, encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+encoding@^0.1.11:
+ version "0.1.12"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+ dependencies:
+ iconv-lite "~0.4.13"
+
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -2500,6 +2510,18 @@ faye-websocket@~0.11.0:
dependencies:
websocket-driver ">=0.5.1"
+fbjs@^0.8.16:
+ version "0.8.16"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.9"
+
file-loader@^1.1.5:
version "1.1.11"
resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8"
@@ -3217,7 +3239,7 @@ iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
-iconv-lite@0.4.19:
+iconv-lite@0.4.19, iconv-lite@~0.4.13:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
@@ -3562,7 +3584,7 @@ is-retina@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-retina/-/is-retina-1.0.3.tgz#d7401b286bea2ae37f62477588de504d0b8647e3"
-is-stream@^1.1.0:
+is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -3620,6 +3642,13 @@ isobject@^3.0.0, isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+isomorphic-fetch@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+ dependencies:
+ node-fetch "^1.0.1"
+ whatwg-fetch ">=0.10.0"
+
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -3723,6 +3752,10 @@ jasminewd2@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e"
+jquery@^3.3.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
+
js-base64@^2.1.8:
version "2.4.3"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582"
@@ -3996,6 +4029,14 @@ lie@~3.1.0:
dependencies:
immediate "~3.0.5"
+linkifyjs@^2.1.6:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-2.1.6.tgz#f1cc88a86ff8863196615857fd47eb193c0a26cb"
+ optionalDependencies:
+ jquery "^3.3.1"
+ react "^16.2.0"
+ react-dom "^16.2.0"
+
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -4102,7 +4143,7 @@ longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
-loose-envify@^1.0.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -4474,6 +4515,13 @@ no-case@^2.2.0:
dependencies:
lower-case "^1.1.1"
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
node-forge@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
@@ -4690,7 +4738,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@^4.0.1, object-assign@^4.1.0:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5161,6 +5209,14 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
+prop-types@^15.6.0:
+ version "15.6.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.3.1"
+ object-assign "^4.1.1"
+
protractor@^5.1.2:
version "5.3.0"
resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.3.0.tgz#5df98201cbdaeb50826af6d05630ef1945bf9c32"
@@ -5336,6 +5392,24 @@ rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
+react-dom@^16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
+react@^16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba"
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
@@ -5908,7 +5982,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4:
+setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
@@ -6704,6 +6778,10 @@ typedarray@^0.0.6, typedarray@~0.0.5:
version "2.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
+ua-parser-js@^0.7.9:
+ version "0.7.17"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac"
+
uglify-es@^3.3.4:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@@ -7072,6 +7150,10 @@ websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
+whatwg-fetch@>=0.10.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
+
when@^3.7.7:
version "3.7.8"
resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82"