Skip to content

Commit 6217fa9

Browse files
Jan Kaiserhonzikec
authored andcommitted
wip: PRs
1 parent 078ccc0 commit 6217fa9

15 files changed

Lines changed: 203 additions & 171 deletions

angular-ngrx-scss/src/app/file-viewer/file-explorer/file-explorer.component.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import { Component, OnDestroy, OnInit } from '@angular/core';
1+
import { Component, OnDestroy } from '@angular/core';
22
import { Store } from '@ngrx/store';
33
import { ActivatedRoute } from '@angular/router';
4-
import {
5-
fetchRepository,
6-
RepoContents,
7-
selectedRepository,
8-
} from '../../state/repository';
9-
import { map, takeWhile, tap } from 'rxjs';
4+
import { RepoContents, selectedRepository } from '../../state/repository';
5+
import { map } from 'rxjs';
106

117
@Component({
128
selector: 'app-file-explorer',

angular-ngrx-scss/src/app/pull-requests/components/pull-request-card/pull-request-card.component.html

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,23 @@
3434
<div class="comments">
3535
<span class="icon green" appOcticon="comment" size="16"></span>
3636
<div class="comment-count">
37-
{{ pullRequest.commentCount }}
37+
{{ pullRequest.comments }}
3838
</div>
3939
</div>
4040
</div>
4141

4242
<ng-template #openPRMeta let-pullRequest="pullRequest">
4343
<span class="meta">
4444
#{{ pullRequest.number }} opened
45-
{{ pullRequest.createdAt | relativeTime }} by
46-
<a href="#">{{ pullRequest.login }}</a>
45+
{{ pullRequest.created_at | relativeTime }} by
46+
<a href="#">{{ pullRequest.user.login }}</a>
4747
</span>
4848
</ng-template>
4949

5050
<ng-template #closedPRMeta let-pullRequest="pullRequest">
5151
<span class="meta">
52-
#{{ pullRequest.number }} by <a href="#">{{ pullRequest.login }}</a> was
53-
closed {{ pullRequest.createdAt | relativeTime }}
52+
#{{ pullRequest.number }} by
53+
<a href="#">{{ pullRequest.user.login }}</a> was closed
54+
{{ pullRequest.created_at | relativeTime }}
5455
</span>
5556
</ng-template>

angular-ngrx-scss/src/app/pull-requests/components/pull-request-card/pull-request-card.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2-
import { RepoPullRequest } from '../../../state/repository';
2+
import { PullRequest } from 'src/app/repository/services/repository.interfaces';
33

44
@Component({
55
selector: 'app-pull-request-card',
@@ -8,7 +8,7 @@ import { RepoPullRequest } from '../../../state/repository';
88
changeDetection: ChangeDetectionStrategy.OnPush,
99
})
1010
export class PullRequestCardComponent {
11-
@Input() pullRequest!: RepoPullRequest;
11+
@Input() pullRequest!: PullRequest;
1212

1313
colorMap(color: string): string {
1414
switch (color) {

angular-ngrx-scss/src/app/pull-requests/components/pull-requests-header/pull-requests-header.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
(click)="changeViewState('open')"
77
>
88
<span class="icon" appOcticon="no-entry" size="16"></span>
9-
<span>{{ openPullRequests?.totalCount ?? 0 }} Open</span>
9+
<span>{{ openPullRequests?.total ?? 0 }} Open</span>
1010
</button>
1111
<button
1212
class="tab-button"
1313
[ngClass]="{ active: viewState === 'closed' }"
1414
(click)="changeViewState('closed')"
1515
>
1616
<span class="icon" appOcticon="check" size="16"></span
17-
><span>{{ closedPullRequests?.totalCount ?? 0 }} closed</span>
17+
><span>{{ closedPullRequests?.total ?? 0 }} closed</span>
1818
</button>
1919
</div>
2020
<div class="pull-requests-filters">

angular-ngrx-scss/src/app/pull-requests/components/pull-requests-header/pull-requests-header.component.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
Input,
66
Output,
77
} from '@angular/core';
8-
import { ISSUE_STATE, RepoPullRequests } from '../../../state/repository';
8+
import {
9+
ISSUE_STATE,
10+
RepoPullRequests,
11+
selectLabels,
12+
} from '../../../state/repository';
13+
import { Store } from '@ngrx/store';
14+
import { map } from 'rxjs';
915

1016
@Component({
1117
selector: 'app-pull-requests-header',
@@ -19,6 +25,16 @@ export class PullRequestsHeaderComponent {
1925
@Input() viewState: ISSUE_STATE = 'open';
2026
@Output() viewStateChange = new EventEmitter<ISSUE_STATE>();
2127

28+
labels$ = this.store
29+
.select(selectLabels)
30+
.pipe(
31+
map((labels) =>
32+
labels.map((label) => ({ label: label.name, value: label.name })),
33+
),
34+
);
35+
36+
constructor(private store: Store) {}
37+
2238
changeViewState(state: ISSUE_STATE) {
2339
this.viewState = state;
2440
this.viewStateChange.emit(this.viewState);

angular-ngrx-scss/src/app/pull-requests/pull-requests.component.html

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
<app-pull-requests-header
33
[openPullRequests]="openPullRequests$ | async"
44
[closedPullRequests]="closedPullRequests$ | async"
5-
[(viewState)]="viewState"
5+
[viewState]="viewState"
6+
(viewStateChange)="viewStateChange($event)"
67
></app-pull-requests-header>
78
<app-pull-requests-list
89
[pullRequests]="
@@ -12,4 +13,11 @@
1213
"
1314
></app-pull-requests-list>
1415
</div>
15-
<app-pagination></app-pagination>
16+
<app-pagination
17+
[params]="
18+
viewState === 'open'
19+
? (openPullRequestsPaginationParams$ | async)
20+
: (closedPullRequestsPaginationParams$ | async)
21+
"
22+
(pageChange)="pageChange($event)"
23+
></app-pagination>

angular-ngrx-scss/src/app/pull-requests/pull-requests.component.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import {
55
fetchPullRequests,
66
ISSUE_STATE,
77
selectClosedPullRequests,
8+
selectClosedPullRequestsPaginationParams,
89
selectOpenPullRequests,
10+
selectOpenPullRequestsPaginationParams,
911
} from '../state/repository';
10-
1112
@Component({
1213
selector: 'app-pull-requests',
1314
templateUrl: './pull-requests.component.html',
@@ -20,6 +21,14 @@ export class PullRequestsComponent implements OnInit {
2021
closedPullRequests$ = this.store.select(selectClosedPullRequests);
2122
viewState: ISSUE_STATE = 'open';
2223

24+
openPullRequestsPaginationParams$ = this.store.select(
25+
selectOpenPullRequestsPaginationParams,
26+
);
27+
28+
closedPullRequestsPaginationParams$ = this.store.select(
29+
selectClosedPullRequestsPaginationParams,
30+
);
31+
2332
constructor(private route: ActivatedRoute, private store: Store) {}
2433

2534
ngOnInit() {
@@ -30,16 +39,34 @@ export class PullRequestsComponent implements OnInit {
3039
fetchPullRequests({
3140
owner: this.owner,
3241
repoName: this.repoName,
33-
prState: 'open',
42+
params: {
43+
state: 'open',
44+
},
3445
}),
3546
);
3647

3748
this.store.dispatch(
3849
fetchPullRequests({
3950
owner: this.owner,
4051
repoName: this.repoName,
41-
prState: 'closed',
52+
params: {
53+
state: 'closed',
54+
},
4255
}),
4356
);
4457
}
58+
59+
pageChange(page: number) {
60+
this.store.dispatch(
61+
fetchPullRequests({
62+
owner: this.owner,
63+
repoName: this.repoName,
64+
params: { state: this.viewState, page },
65+
}),
66+
);
67+
}
68+
69+
viewStateChange(viewState: ISSUE_STATE) {
70+
this.viewState = viewState;
71+
}
4572
}

angular-ngrx-scss/src/app/repository/components/repository-details/repository-details.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, OnDestroy, OnInit } from '@angular/core';
2-
import { Subject, takeUntil, takeWhile, tap } from 'rxjs';
2+
import { Subject, takeUntil, tap } from 'rxjs';
33
import { fetchRepository } from '../../../state/repository';
44
import { ActivatedRoute } from '@angular/router';
55
import { Store } from '@ngrx/store';

angular-ngrx-scss/src/app/repository/services/repository.interfaces.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,22 @@ export interface RepositoryIssuesApiParams {
5454
page?: number;
5555
}
5656

57-
export interface PullRequest {
58-
title: string;
59-
number: number;
60-
user: User;
61-
closed_at?: string;
62-
created_at: string;
57+
export interface RepositoryPullsApiParams {
58+
state: 'open' | 'closed' | 'all';
59+
page?: number;
60+
}
61+
62+
export interface PullRequest extends Issue {
63+
merged: boolean;
64+
mergeable: boolean;
65+
merged_by: User;
66+
merged_at: string;
67+
merge_commit_sha: string;
68+
comments: number;
69+
commits: number;
70+
additions: number;
71+
deletions: number;
72+
changed_files: number;
6373
}
6474

6575
export type PullRequests = Array<PullRequest>;

angular-ngrx-scss/src/app/repository/services/repository.service.ts

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import {
88
IssueLabel,
99
Milestone,
1010
PullRequestAPIResponse,
11-
PullRequestLabel,
1211
ReadmeApiResponse,
1312
RepoApiResponse,
1413
RepoContentsApiResponse,
1514
RepoIssues,
15+
RepoPullRequests,
1616
} from 'src/app/state/repository';
1717
import { environment } from 'src/environments/environment';
1818
import {
@@ -21,6 +21,7 @@ import {
2121
PullRequest,
2222
PullRequests,
2323
RepositoryIssuesApiParams,
24+
RepositoryPullsApiParams,
2425
} from './repository.interfaces';
2526

2627
@Injectable({
@@ -50,6 +51,34 @@ export class RepositoryService {
5051
});
5152
}
5253

54+
getRepositoryPullRequestsCount(
55+
repoOwner: string,
56+
repoName: string,
57+
): Observable<number> {
58+
const owner = encodeURIComponent(repoOwner);
59+
const name = encodeURIComponent(repoName);
60+
const url = `${environment.githubUrl}/repos/${owner}/${name}/pulls`;
61+
62+
return this.http
63+
.get<PullRequests>(url, {
64+
observe: 'response',
65+
headers: {
66+
Accept: 'application/vnd.github.v3+json',
67+
},
68+
params: new HttpParams({
69+
fromObject: {
70+
state: 'open',
71+
per_page: 1,
72+
},
73+
}),
74+
})
75+
.pipe(
76+
map((response) =>
77+
this.extractTotalFromLinkHeader(response.headers.get('Link')),
78+
),
79+
);
80+
}
81+
5382
/**
5483
* Gets a list of all the pull requests for the specified repository
5584
* @param repoOwner who the repo belongs to
@@ -59,16 +88,52 @@ export class RepositoryService {
5988
getRepositoryPullRequests(
6089
repoOwner: string,
6190
repoName: string,
62-
): Observable<PullRequests> {
91+
params: RepositoryPullsApiParams,
92+
): Observable<RepoPullRequests> {
93+
const defaultParams = {
94+
state: 'all',
95+
page: 1,
96+
};
97+
6398
const owner = encodeURIComponent(repoOwner);
6499
const name = encodeURIComponent(repoName);
65-
const url = `${environment.githubUrl}/repos/${owner}/${name}/pulls`;
100+
const state = encodeURIComponent(params.state);
101+
const url = `${environment.githubUrl}/search/issues?q=repo:${owner}/${name}+type:pr+state:${state}`;
66102

67-
return this.http.get<PullRequests>(url, {
68-
headers: {
69-
Accept: 'application/vnd.github.v3+json',
70-
},
71-
});
103+
return this.http
104+
.get(url, {
105+
observe: 'response',
106+
headers: {
107+
Accept: 'application/vnd.github.v3+json',
108+
},
109+
params: new HttpParams({
110+
fromObject: { ...Object.assign(defaultParams, params) },
111+
}),
112+
})
113+
.pipe(
114+
map((response) => {
115+
const linkHeader = response.headers.get('Link');
116+
117+
const canNext = !!(linkHeader && linkHeader.includes('rel="next"'));
118+
const canPrev = !!(linkHeader && linkHeader.includes('rel="prev"'));
119+
120+
const data = response.body as PullRequestAPIResponse;
121+
122+
const total = data.total_count;
123+
124+
const page = params?.page || 1;
125+
126+
const paginationParams = {
127+
canNext,
128+
canPrev,
129+
page,
130+
};
131+
132+
const pullRequests: PullRequest[] = data.items;
133+
134+
return { pullRequests, paginationParams, total } as RepoPullRequests;
135+
}),
136+
);
72137
}
73138

74139
/**
@@ -161,31 +226,6 @@ export class RepositoryService {
161226
});
162227
}
163228

164-
/**
165-
* NOTE: This call uses the search URL to find the information, and is a bit of a duplicate of other calls that use the repo URL. Both work fine and are provided currently.
166-
* Gets a list of pull requests matching the provided state
167-
* @param repoOwner who the repo belongs to
168-
* @param repoName name of the repo
169-
* @param prState if the pr is open or closed
170-
* @returns the total count of state-matching pull requests and information for each of those pulls
171-
*/
172-
getPullRequests(
173-
repoOwner: string,
174-
repoName: string,
175-
prState: ISSUE_STATE,
176-
): Observable<PullRequestAPIResponse> {
177-
const owner = encodeURIComponent(repoOwner);
178-
const name = encodeURIComponent(repoName);
179-
const state = encodeURIComponent(prState);
180-
const url = `${environment.githubUrl}/search/issues?q=repo:${owner}/${name}+type:pr+state:${state}`;
181-
182-
return this.http.get<PullRequestAPIResponse>(url, {
183-
headers: {
184-
Accept: 'application/vnd.github.v3+json',
185-
},
186-
});
187-
}
188-
189229
/**
190230
* Get a list of issues for the specified repository
191231
* @param owner who the repo belongs to

0 commit comments

Comments
 (0)