Skip to content

Commit

Permalink
fix(hacker-news): improve list and add pipes
Browse files Browse the repository at this point in the history
  • Loading branch information
simplejason committed Sep 15, 2019
1 parent c8fb351 commit b7e5a2f
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 15 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -20,6 +20,7 @@
"@angular/platform-browser-dynamic": "~8.1.0",
"@angular/router": "~8.1.0",
"date-fns": "^2.0.1",
"moment": "^2.24.0",
"ng-zorro-antd": "^8.3.0",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
Expand Down
4 changes: 3 additions & 1 deletion src/app/components/demos/demos.module.ts
Expand Up @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgZorroAntdModule } from 'ng-zorro-antd';
import { PipesModule } from '../../pipes/pipes.module';
import { DemosRoutingModule } from './demos.routing.module';
import { HackerNewsComponent } from './hacker-news/hacker-news.component';
import { TaskDetailComponent } from './todo/task-detail/task-detail.component';
Expand All @@ -28,7 +29,8 @@ const MODAL_COMPONENTS = [
FormsModule,
ReactiveFormsModule,
NgZorroAntdModule,
DemosRoutingModule
DemosRoutingModule,
PipesModule
],
entryComponents: [
...MODAL_COMPONENTS
Expand Down
38 changes: 30 additions & 8 deletions src/app/components/demos/hacker-news/hacker-news.component.html
Expand Up @@ -24,21 +24,46 @@
</nz-header>
<nz-content>
<div class="inner-content">
<nz-list [nzDataSource]="listOfNews" [nzRenderItem]="item" [nzItemLayout]="'horizontal'" [nzLoading]="loading">

<nz-list
[nzDataSource]="listOfNews"
[nzRenderItem]="item"
[nzItemLayout]="'vertical'"
[nzLoading]="loading"
nzSize="small">
<ng-template #item let-item>
<nz-list-item>
<nz-list-item
nzNoFlex="true"
[nzContent]="item.story_text ? content : ''"
[nzActions]="[upAction, hideAction, commentAction]"
>
<ng-template #upAction><i nz-icon nzType="like" nzTheme="outline"></i> upvote</ng-template>
<ng-template #hideAction><i nz-icon nzType="eye-invisible" nzTheme="outline"></i> hide</ng-template>
<ng-template #commentAction><i nz-icon nzType="message" nzTheme="outline"></i> comment</ng-template>
<ng-template #content>
<p [innerHTML]="item.story_text"></p>
</ng-template>
<nz-list-item-meta
[nzTitle]="nzTitle"
[nzDescription]="nzDescription">
<ng-template #nzTitle>
<a [href]="item.url" target="_blank">{{ item.title }}</a>
<a [href]="item.url" target="_blank">{{ item.title }} </a>
<span class="link second-text">
<ng-container *ngIf="item.url">(</ng-container>
{{item.url | hostParse}}
<ng-container *ngIf="item.url">)</ng-container>
</span>
</ng-template>
<ng-template #nzDescription>
<span>
{{item.points}} points by {{item.author}}
{{item.points}} points by <span class="link">{{item.author}}</span> <span class="link"> {{item.created_at | timeAgo}}</span>
</span>
<nz-divider nzType="vertical"></nz-divider>
<span>
<span class="link">
hide
</span>
<nz-divider nzType="vertical"></nz-divider>
<span class="link">
{{item.num_comments}} comments
</span>
</ng-template>
Expand All @@ -48,7 +73,4 @@
</nz-list>
</div>
</nz-content>
<nz-footer>

</nz-footer>
</nz-layout>
24 changes: 22 additions & 2 deletions src/app/components/demos/hacker-news/hacker-news.component.less
Expand Up @@ -67,8 +67,28 @@ nz-layout {
margin-top: 36px;

nz-list {
::ng-deep .ant-list-item-meta-description {
font-size: 12px;
::ng-deep {
.ant-list-item-meta-description {
font-size: 12px;
}
.ant-list-item-action {
li {
&:hover {
color: @text-color;
}
}
}
}

.link {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}

.second-text {
color: @text-color-secondary;
}
}
}
Expand Down
Expand Up @@ -12,11 +12,13 @@ export class HackerNewsComponent implements OnInit {

currentPage = 1;
pageSize = 20;
queryString = '';
sortType: 'search' | 'search_by_date' = 'search';
loading = true;
listOfNews: Hit[] = [];

getStories() {
this.hackerNewsService.getStoriesByAlgolia().subscribe(data => {
this.hackerNewsService.getStoriesByAlgolia(this.queryString, this.currentPage, this.sortType).subscribe(data => {
this.listOfNews = data.hits;
this.loading = false;
this.cdr.markForCheck();
Expand Down
36 changes: 36 additions & 0 deletions src/app/pipes/host-parse.pipe.ts
@@ -0,0 +1,36 @@
// read https://github.com/websanova/js-url to get RegExp

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'hostParse'
})
export class HostParsePipe implements PipeTransform {

regxDomain() {
return new RegExp(/(.*?)\.?([^\.]*?)\.(gl|com|net|org|biz|ws|in|me|co\.uk|co|org\.uk|ltd\.uk|plc\.uk|me\.uk|edu|mil|br\.com|cn\.com|eu\.com|hu\.com|no\.com|qc\.com|sa\.com|se\.com|se\.net|us\.com|uy\.com|ac|co\.ac|gv\.ac|or\.ac|ac\.ac|af|am|as|at|ac\.at|co\.at|gv\.at|or\.at|asn\.au|com\.au|edu\.au|org\.au|net\.au|id\.au|be|ac\.be|adm\.br|adv\.br|am\.br|arq\.br|art\.br|bio\.br|cng\.br|cnt\.br|com\.br|ecn\.br|eng\.br|esp\.br|etc\.br|eti\.br|fm\.br|fot\.br|fst\.br|g12\.br|gov\.br|ind\.br|inf\.br|jor\.br|lel\.br|med\.br|mil\.br|net\.br|nom\.br|ntr\.br|odo\.br|org\.br|ppg\.br|pro\.br|psc\.br|psi\.br|rec\.br|slg\.br|tmp\.br|tur\.br|tv\.br|vet\.br|zlg\.br|br|ab\.ca|bc\.ca|mb\.ca|nb\.ca|nf\.ca|ns\.ca|nt\.ca|on\.ca|pe\.ca|qc\.ca|sk\.ca|yk\.ca|ca|cc|ac\.cn|com\.cn|edu\.cn|gov\.cn|org\.cn|bj\.cn|sh\.cn|tj\.cn|cq\.cn|he\.cn|nm\.cn|ln\.cn|jl\.cn|hl\.cn|js\.cn|zj\.cn|ah\.cn|gd\.cn|gx\.cn|hi\.cn|sc\.cn|gz\.cn|yn\.cn|xz\.cn|sn\.cn|gs\.cn|qh\.cn|nx\.cn|xj\.cn|tw\.cn|hk\.cn|mo\.cn|cn|cx|cz|de|dk|fo|com\.ec|tm\.fr|com\.fr|asso\.fr|presse\.fr|fr|gf|gs|co\.il|net\.il|ac\.il|k12\.il|gov\.il|muni\.il|ac\.in|co\.in|org\.in|ernet\.in|gov\.in|net\.in|res\.in|is|it|ac\.jp|co\.jp|go\.jp|or\.jp|ne\.jp|ac\.kr|co\.kr|go\.kr|ne\.kr|nm\.kr|or\.kr|li|lt|lu|asso\.mc|tm\.mc|com\.mm|org\.mm|net\.mm|edu\.mm|gov\.mm|ms|nl|no|nu|pl|ro|org\.ro|store\.ro|tm\.ro|firm\.ro|www\.ro|arts\.ro|rec\.ro|info\.ro|nom\.ro|nt\.ro|se|si|com\.sg|org\.sg|net\.sg|gov\.sg|sk|st|tf|ac\.th|co\.th|go\.th|mi\.th|net\.th|or\.th|tm|to|com\.tr|edu\.tr|gov\.tr|k12\.tr|net\.tr|org\.tr|com\.tw|org\.tw|net\.tw|ac\.uk|uk\.com|uk\.net|gb\.com|gb\.net|vg|sh|kz|ch|info|ua|gov|name|pro|ie|hk|com\.hk|org\.hk|net\.hk|edu\.hk|us|tk|cd|by|ad|lv|eu\.lv|bz|es|jp|cl|ag|mobi|eu|co\.nz|org\.nz|net\.nz|maori\.nz|iwi\.nz|io|la|md|sc|sg|vc|tw|travel|my|se|tv|pt|com\.pt|edu\.pt|asia|fi|com\.ve|net\.ve|fi|org\.ve|web\.ve|info\.ve|co\.ve|tel|im|gr|ru|net\.ru|org\.ru|hr|com\.hr|ly|xyz)$/);
}

parseDomain(url: string) {
if (!url) {
return '';
}
let hostname = '';
let domain;
const match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i);
if (match != null && match.length > 2 && typeof match[ 2 ] === 'string' && match[ 2 ].length > 0) {
hostname = match[ 2 ];
}

const domainArray = hostname.match(this.regxDomain());
if (domainArray) {
domain = domainArray[ 2 ] ? domainArray[ 2 ] + '.' + domainArray[ 3 ] : '';
}
return domain;
}

transform(value: string, ...args: any[]): any {
return `${this.parseDomain(value)}`;
}

}
15 changes: 15 additions & 0 deletions src/app/pipes/pipes.module.ts
@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { HostParsePipe } from './host-parse.pipe';
import { TimeAgoPipe } from './time-ago.pipe';

@NgModule({
declarations: [
TimeAgoPipe,
HostParsePipe
],
exports: [
TimeAgoPipe,
HostParsePipe
]
})
export class PipesModule {}
16 changes: 16 additions & 0 deletions src/app/pipes/time-ago.pipe.ts
@@ -0,0 +1,16 @@
import { Pipe, PipeTransform } from '@angular/core';
import * as moment from 'moment';

@Pipe({
name: 'timeAgo'
})
export class TimeAgoPipe implements PipeTransform {

transform(value: moment.MomentInput, ...args: any[]): any {
if (value) {
return moment(value).fromNow();
}
return null;
}

}
6 changes: 3 additions & 3 deletions src/app/services/hacker-news.service.ts
Expand Up @@ -43,12 +43,12 @@ export class HackerNewsService {
/**
* =============== algolia.com ==================
*/
getStoriesByAlgolia(query = '', tags = IAvailableTags.STORY): Observable<IHackerNews> {
const params = new HttpParams().append('tags', tags);
getStoriesByAlgolia(query = '', page = 1, sortType: 'search' | 'search_by_date' = 'search', tags = IAvailableTags.STORY): Observable<IHackerNews> {
const params = new HttpParams().append('tags', tags).append('page', page.toString());
if (query) {
params.append('query', query);
}
return this.http.get<IHackerNews>(`${this.BASE_URL_ALGOLIA}/search`, { params, responseType: 'json' });
return this.http.get<IHackerNews>(`${this.BASE_URL_ALGOLIA}/${sortType}`, { params, responseType: 'json' });
}

getStoryByAlgolia(itemId: number): Observable<IStory> {
Expand Down

0 comments on commit b7e5a2f

Please sign in to comment.