Skip to content

Commit 5e2cdea

Browse files
committed
feat: items alpha via gRPC API
1 parent 707e6f3 commit 5e2cdea

File tree

4 files changed

+279
-79
lines changed

4 files changed

+279
-79
lines changed

src/app/moder/items/alpha/alpha.component.html

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,33 +17,41 @@ <h1 i18n>Alphabetical vehicles list</h1>
1717
</div>
1818

1919
<div class="card card-body">
20-
@for (group of groups; track group) {
21-
<div class="btn-group">
22-
@for (c of group; track c) {
23-
<a
24-
class="btn btn-secondary"
25-
[class.active]="c === char"
26-
[textContent]="c"
27-
routerLink="."
28-
[queryParams]="{char: c, page: null}"
29-
></a>
30-
}
31-
</div>
20+
@if (groups$ | async; as groups) {
21+
@let char = char$ | async;
22+
@for (group of [groups.latin, groups.numbers, groups.other]; track group) {
23+
<div class="btn-group">
24+
@for (c of group; track c) {
25+
<a
26+
class="btn btn-secondary"
27+
[class.active]="c === char"
28+
[textContent]="c"
29+
routerLink="."
30+
[queryParams]="{char: c, page: null}"
31+
></a>
32+
}
33+
</div>
34+
}
35+
} @else {
36+
<span>
37+
<span class="spinner-border" role="status"><span class="visually-hidden" i18n>Loading…</span></span>
38+
</span>
3239
}
3340
</div>
3441

35-
<div [hidden]="!items">
42+
@if (items$ | async; as items) {
3643
<div class="card card-body">
37-
<span [hidden]="loading <= 0">
38-
<span class="spinner-border" role="status"><span class="visually-hidden" i18n>Loading…</span></span>
39-
</span>
40-
@for (item of items; track item) {
44+
@for (item of items.items; track item) {
4145
<div>
4246
<a [routerLink]="['/moder/items/item', item.id]" [innerHTML]="item.nameHtml"></a>
4347
</div>
4448
}
45-
@if (paginator) {
46-
<app-paginator [data]="paginator"></app-paginator>
49+
@if (items.paginator) {
50+
<app-paginator [data]="items.paginator"></app-paginator>
4751
}
4852
</div>
49-
</div>
53+
} @else {
54+
<span>
55+
<span class="spinner-border" role="status"><span class="visually-hidden" i18n>Loading…</span></span>
56+
</span>
57+
}
Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,54 @@
1-
import {Component, inject, OnDestroy, OnInit} from '@angular/core';
1+
import {AsyncPipe} from '@angular/common';
2+
import {Component, inject, OnInit} from '@angular/core';
23
import {ActivatedRoute, RouterLink} from '@angular/router';
3-
import {APIItem, APIItemList, ItemFields, ItemListOptions, ItemsRequest, Pages} from '@grpc/spec.pb';
4+
import {APIItem, APIItemList, ItemFields, ItemListOptions, ItemsRequest} from '@grpc/spec.pb';
45
import {ItemsClient} from '@grpc/spec.pbsc';
5-
import {APIService} from '@services/api.service';
6+
import {Empty} from '@ngx-grpc/well-known-types';
67
import {LanguageService} from '@services/language';
78
import {PageEnvService} from '@services/page-env.service';
8-
import {combineLatest, of, Subscription} from 'rxjs';
9-
import {switchMap} from 'rxjs/operators';
9+
import {combineLatest, of} from 'rxjs';
10+
import {map, shareReplay, switchMap} from 'rxjs/operators';
1011

1112
import {PaginatorComponent} from '../../../paginator/paginator/paginator.component';
1213

13-
export interface APIItemAlphaGetResponse {
14-
groups: string[][];
15-
}
16-
1714
@Component({
18-
imports: [RouterLink, PaginatorComponent],
15+
imports: [RouterLink, PaginatorComponent, AsyncPipe],
1916
selector: 'app-moder-items-alpha',
2017
templateUrl: './alpha.component.html',
2118
})
22-
export class ModerItemsAlphaComponent implements OnDestroy, OnInit {
23-
readonly #api = inject(APIService);
19+
export class ModerItemsAlphaComponent implements OnInit {
2420
readonly #route = inject(ActivatedRoute);
2521
readonly #pageEnv = inject(PageEnvService);
2622
readonly #itemsClient = inject(ItemsClient);
2723
readonly #languageService = inject(LanguageService);
2824

29-
protected char: null | string = null;
30-
#querySub?: Subscription;
31-
protected loading = 0;
32-
protected paginator?: null | Pages = null;
33-
protected groups: string[][] = [];
34-
protected items?: APIItem[];
25+
protected readonly groups$ = this.#itemsClient.getAlpha(new Empty());
26+
27+
protected readonly char$ = this.#route.queryParamMap.pipe(
28+
map((query) => query.get('char')),
29+
shareReplay({bufferSize: 1, refCount: false}),
30+
);
31+
32+
readonly #page$ = this.#route.queryParamMap.pipe(map((query) => parseInt(query.get('page') ?? '', 10)));
33+
34+
protected readonly items$ = combineLatest([this.char$, this.#page$]).pipe(
35+
switchMap(([char, page]) =>
36+
char
37+
? this.#itemsClient.list(
38+
new ItemsRequest({
39+
fields: new ItemFields({nameHtml: true}),
40+
language: this.#languageService.language,
41+
limit: 40,
42+
options: new ItemListOptions({name: char + '%'}),
43+
page,
44+
}),
45+
)
46+
: of({
47+
items: [] as APIItem[],
48+
paginator: undefined,
49+
} as APIItemList),
50+
),
51+
);
3552

3653
ngOnInit(): void {
3754
setTimeout(
@@ -42,46 +59,5 @@ export class ModerItemsAlphaComponent implements OnDestroy, OnInit {
4259
}),
4360
0,
4461
);
45-
46-
this.#querySub = combineLatest([
47-
this.#route.queryParamMap,
48-
this.#api.request$<APIItemAlphaGetResponse>('GET', 'item/alpha'),
49-
])
50-
.pipe(
51-
switchMap(([query, groups]) =>
52-
combineLatest([
53-
of(query.get('char')),
54-
of(groups.groups),
55-
query.get('char')
56-
? this.#itemsClient.list(
57-
new ItemsRequest({
58-
fields: new ItemFields({nameHtml: true}),
59-
language: this.#languageService.language,
60-
limit: 10,
61-
options: new ItemListOptions({
62-
name: query.get('char') + '%',
63-
}),
64-
page: parseInt(query.get('page') ?? '', 10),
65-
}),
66-
)
67-
: of({
68-
items: [] as APIItem[],
69-
paginator: undefined,
70-
} as APIItemList),
71-
]),
72-
),
73-
)
74-
.subscribe(([char, groups, items]) => {
75-
this.char = char;
76-
this.groups = groups;
77-
this.paginator = items.paginator;
78-
this.items = items.items;
79-
});
80-
}
81-
82-
ngOnDestroy(): void {
83-
if (this.#querySub) {
84-
this.#querySub.unsubscribe();
85-
}
8662
}
8763
}

src/grpc/spec.pb.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56185,6 +56185,185 @@ export module PathResponse {
5618556185
}
5618656186
}
5618756187

56188+
/**
56189+
* Message implementation for goautowp.AlphaResponse
56190+
*/
56191+
export class AlphaResponse implements GrpcMessage {
56192+
static id = 'goautowp.AlphaResponse';
56193+
56194+
/**
56195+
* Deserialize binary data to message
56196+
* @param instance message instance
56197+
*/
56198+
static deserializeBinary(bytes: ByteSource) {
56199+
const instance = new AlphaResponse();
56200+
AlphaResponse.deserializeBinaryFromReader(
56201+
instance,
56202+
new BinaryReader(bytes)
56203+
);
56204+
return instance;
56205+
}
56206+
56207+
/**
56208+
* Check all the properties and set default protobuf values if necessary
56209+
* @param _instance message instance
56210+
*/
56211+
static refineValues(_instance: AlphaResponse) {
56212+
_instance.numbers = _instance.numbers || [];
56213+
_instance.latin = _instance.latin || [];
56214+
_instance.other = _instance.other || [];
56215+
}
56216+
56217+
/**
56218+
* Deserializes / reads binary message into message instance using provided binary reader
56219+
* @param _instance message instance
56220+
* @param _reader binary reader instance
56221+
*/
56222+
static deserializeBinaryFromReader(
56223+
_instance: AlphaResponse,
56224+
_reader: BinaryReader
56225+
) {
56226+
while (_reader.nextField()) {
56227+
if (_reader.isEndGroup()) break;
56228+
56229+
switch (_reader.getFieldNumber()) {
56230+
case 1:
56231+
(_instance.numbers = _instance.numbers || []).push(
56232+
_reader.readString()
56233+
);
56234+
break;
56235+
case 2:
56236+
(_instance.latin = _instance.latin || []).push(_reader.readString());
56237+
break;
56238+
case 3:
56239+
(_instance.other = _instance.other || []).push(_reader.readString());
56240+
break;
56241+
default:
56242+
_reader.skipField();
56243+
}
56244+
}
56245+
56246+
AlphaResponse.refineValues(_instance);
56247+
}
56248+
56249+
/**
56250+
* Serializes a message to binary format using provided binary reader
56251+
* @param _instance message instance
56252+
* @param _writer binary writer instance
56253+
*/
56254+
static serializeBinaryToWriter(
56255+
_instance: AlphaResponse,
56256+
_writer: BinaryWriter
56257+
) {
56258+
if (_instance.numbers && _instance.numbers.length) {
56259+
_writer.writeRepeatedString(1, _instance.numbers);
56260+
}
56261+
if (_instance.latin && _instance.latin.length) {
56262+
_writer.writeRepeatedString(2, _instance.latin);
56263+
}
56264+
if (_instance.other && _instance.other.length) {
56265+
_writer.writeRepeatedString(3, _instance.other);
56266+
}
56267+
}
56268+
56269+
private _numbers: string[];
56270+
private _latin: string[];
56271+
private _other: string[];
56272+
56273+
/**
56274+
* Message constructor. Initializes the properties and applies default Protobuf values if necessary
56275+
* @param _value initial values object or instance of AlphaResponse to deeply clone from
56276+
*/
56277+
constructor(_value?: RecursivePartial<AlphaResponse.AsObject>) {
56278+
_value = _value || {};
56279+
this.numbers = (_value.numbers || []).slice();
56280+
this.latin = (_value.latin || []).slice();
56281+
this.other = (_value.other || []).slice();
56282+
AlphaResponse.refineValues(this);
56283+
}
56284+
get numbers(): string[] {
56285+
return this._numbers;
56286+
}
56287+
set numbers(value: string[]) {
56288+
this._numbers = value;
56289+
}
56290+
get latin(): string[] {
56291+
return this._latin;
56292+
}
56293+
set latin(value: string[]) {
56294+
this._latin = value;
56295+
}
56296+
get other(): string[] {
56297+
return this._other;
56298+
}
56299+
set other(value: string[]) {
56300+
this._other = value;
56301+
}
56302+
56303+
/**
56304+
* Serialize message to binary data
56305+
* @param instance message instance
56306+
*/
56307+
serializeBinary() {
56308+
const writer = new BinaryWriter();
56309+
AlphaResponse.serializeBinaryToWriter(this, writer);
56310+
return writer.getResultBuffer();
56311+
}
56312+
56313+
/**
56314+
* Cast message to standard JavaScript object (all non-primitive values are deeply cloned)
56315+
*/
56316+
toObject(): AlphaResponse.AsObject {
56317+
return {
56318+
numbers: (this.numbers || []).slice(),
56319+
latin: (this.latin || []).slice(),
56320+
other: (this.other || []).slice()
56321+
};
56322+
}
56323+
56324+
/**
56325+
* Convenience method to support JSON.stringify(message), replicates the structure of toObject()
56326+
*/
56327+
toJSON() {
56328+
return this.toObject();
56329+
}
56330+
56331+
/**
56332+
* Cast message to JSON using protobuf JSON notation: https://developers.google.com/protocol-buffers/docs/proto3#json
56333+
* Attention: output differs from toObject() e.g. enums are represented as names and not as numbers, Timestamp is an ISO Date string format etc.
56334+
* If the message itself or some of descendant messages is google.protobuf.Any, you MUST provide a message pool as options. If not, the messagePool is not required
56335+
*/
56336+
toProtobufJSON(
56337+
// @ts-ignore
56338+
options?: ToProtobufJSONOptions
56339+
): AlphaResponse.AsProtobufJSON {
56340+
return {
56341+
numbers: (this.numbers || []).slice(),
56342+
latin: (this.latin || []).slice(),
56343+
other: (this.other || []).slice()
56344+
};
56345+
}
56346+
}
56347+
export module AlphaResponse {
56348+
/**
56349+
* Standard JavaScript object representation for AlphaResponse
56350+
*/
56351+
export interface AsObject {
56352+
numbers: string[];
56353+
latin: string[];
56354+
other: string[];
56355+
}
56356+
56357+
/**
56358+
* Protobuf JSON representation for AlphaResponse
56359+
*/
56360+
export interface AsProtobufJSON {
56361+
numbers: string[];
56362+
latin: string[];
56363+
other: string[];
56364+
}
56365+
}
56366+
5618856367
/**
5618956368
* Message implementation for goautowp.PathItem
5619056369
*/

0 commit comments

Comments
 (0)