Browse Source

resolve comments

pull/4604/head
KenTandrian 4 months ago
parent
commit
854e774f94
  1. 7
      apps/api/src/app/endpoints/watchlist/watchlist.controller.ts
  2. 4
      apps/api/src/app/endpoints/watchlist/watchlist.service.ts
  3. 25
      apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts
  4. 25
      apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts
  5. 45
      apps/client/src/app/components/home-watchlist/home-watchlist.component.ts
  6. 4
      apps/client/src/app/components/home-watchlist/home-watchlist.module.ts
  7. 3
      apps/client/src/app/pages/home/home-page.component.ts
  8. 6
      apps/client/src/app/services/data.service.ts

7
apps/api/src/app/endpoints/watchlist/watchlist.controller.ts

@ -2,7 +2,6 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorat
import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard';
import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor';
import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor';
import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces';
import { permissions } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions';
import { RequestWithUser } from '@ghostfolio/common/types'; import { RequestWithUser } from '@ghostfolio/common/types';
@ -55,8 +54,8 @@ export class WatchlistController {
) { ) {
const watchlistItem = await this.watchlistService const watchlistItem = await this.watchlistService
.getWatchlistItems(this.request.user.id) .getWatchlistItems(this.request.user.id)
.then((items) => { .then(({ watchlist }) => {
return items.find((item) => { return watchlist.find((item) => {
return item.dataSource === dataSource && item.symbol === symbol; return item.dataSource === dataSource && item.symbol === symbol;
}); });
}); });
@ -79,7 +78,7 @@ export class WatchlistController {
@HasPermission(permissions.readWatchlist) @HasPermission(permissions.readWatchlist)
@UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseGuards(AuthGuard('jwt'), HasPermissionGuard)
@UseInterceptors(TransformDataSourceInResponseInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor)
public async getWatchlistItems(): Promise<AssetProfileIdentifier[]> { public async getWatchlistItems() {
return this.watchlistService.getWatchlistItems(this.request.user.id); return this.watchlistService.getWatchlistItems(this.request.user.id);
} }
} }

4
apps/api/src/app/endpoints/watchlist/watchlist.service.ts

@ -64,7 +64,7 @@ export class WatchlistService {
public async getWatchlistItems( public async getWatchlistItems(
userId: string userId: string
): Promise<AssetProfileIdentifier[]> { ): Promise<{ watchlist: AssetProfileIdentifier[] }> {
const user = await this.prismaService.user.findUnique({ const user = await this.prismaService.user.findUnique({
select: { select: {
watchlist: { watchlist: {
@ -74,6 +74,6 @@ export class WatchlistService {
where: { id: userId } where: { id: userId }
}); });
return user.watchlist ?? []; return { watchlist: user.watchlist ?? [] };
} }
} }

25
apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.component.ts

@ -1,3 +1,6 @@
import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete';
import { CommonModule } from '@angular/common';
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
@ -9,27 +12,39 @@ import {
FormBuilder, FormBuilder,
FormControl, FormControl,
FormGroup, FormGroup,
FormsModule,
ReactiveFormsModule,
ValidationErrors, ValidationErrors,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { MatDialogRef } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'h-100' }, host: { class: 'h-100' },
imports: [
CommonModule,
FormsModule,
GfSymbolAutocompleteComponent,
MatButtonModule,
MatDialogModule,
MatFormFieldModule,
ReactiveFormsModule
],
selector: 'gf-create-watchlist-item-dialog', selector: 'gf-create-watchlist-item-dialog',
styleUrls: ['./create-watchlist-item-dialog.component.scss'], styleUrls: ['./create-watchlist-item-dialog.component.scss'],
templateUrl: 'create-watchlist-item-dialog.html', templateUrl: 'create-watchlist-item-dialog.html'
standalone: false
}) })
export class CreateWatchlistItemDialog implements OnInit, OnDestroy { export class CreateWatchlistItemDialogComponent implements OnInit, OnDestroy {
public createWatchlistItemForm: FormGroup; public createWatchlistItemForm: FormGroup;
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
public constructor( public constructor(
public readonly dialogRef: MatDialogRef<CreateWatchlistItemDialog>, public readonly dialogRef: MatDialogRef<CreateWatchlistItemDialogComponent>,
public readonly formBuilder: FormBuilder public readonly formBuilder: FormBuilder
) {} ) {}

25
apps/client/src/app/components/home-watchlist/create-watchlist-item-dialog/create-watchlist-item-dialog.module.ts

@ -1,25 +0,0 @@
import { GfSymbolAutocompleteComponent } from '@ghostfolio/ui/symbol-autocomplete';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { CreateWatchlistItemDialog } from './create-watchlist-item-dialog.component';
@NgModule({
declarations: [CreateWatchlistItemDialog],
imports: [
CommonModule,
FormsModule,
GfSymbolAutocompleteComponent,
MatButtonModule,
MatDialogModule,
MatFormFieldModule,
ReactiveFormsModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class GfCreateWatchlistItemDialogModule {}

45
apps/client/src/app/components/home-watchlist/home-watchlist.component.ts

@ -12,10 +12,10 @@ import {
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { forkJoin, Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
import { CreateWatchlistItemDialog } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component'; import { CreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component';
import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/interfaces/interfaces'; import { CreateWatchlistItemDialogParams } from './create-watchlist-item-dialog/interfaces/interfaces';
@Component({ @Component({
@ -72,28 +72,21 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit {
} }
private loadWatchlistData() { private loadWatchlistData() {
forkJoin({ this.dataService
watchlistItems: this.dataService.fetchWatchlist(), .fetchWatchlist()
allBenchmarks: this.dataService .pipe(takeUntil(this.unsubscribeSubject))
.fetchBenchmarks() .subscribe(({ watchlist }) => {
.pipe(map((response) => response.benchmarks)) this.watchlist = watchlist.map((item) => ({
}) dataSource: item.dataSource,
.pipe( symbol: item.symbol,
takeUntil(this.unsubscribeSubject), name: item.symbol,
map(({ watchlistItems, allBenchmarks }) => { marketCondition: null,
const watchlistLookup = new Set( performances: null,
watchlistItems.map((item) => `${item.dataSource}:${item.symbol}`) trend200d: 'UNKNOWN',
); trend50d: 'UNKNOWN'
return allBenchmarks.filter((benchmark) => }));
watchlistLookup.has(`${benchmark.dataSource}:${benchmark.symbol}`)
);
})
)
.subscribe({
next: (filteredBenchmarks) => {
this.watchlist = filteredBenchmarks;
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
}
}); });
} }
@ -104,7 +97,7 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit {
.subscribe((user) => { .subscribe((user) => {
this.user = user; this.user = user;
const dialogRef = this.dialog.open(CreateWatchlistItemDialog, { const dialogRef = this.dialog.open(CreateWatchlistItemDialogComponent, {
autoFocus: false, autoFocus: false,
data: { data: {
deviceType: this.deviceType, deviceType: this.deviceType,
@ -119,7 +112,7 @@ export class HomeWatchlistComponent implements OnDestroy, OnInit {
.subscribe(({ dataSource, symbol } = {}) => { .subscribe(({ dataSource, symbol } = {}) => {
if (dataSource && symbol) { if (dataSource && symbol) {
this.dataService this.dataService
.postWatchlist({ dataSource, symbol }) .postWatchlistItem({ dataSource, symbol })
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe({ .subscribe({
next: () => this.loadWatchlistData() next: () => this.loadWatchlistData()

4
apps/client/src/app/components/home-watchlist/home-watchlist.module.ts

@ -5,7 +5,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { GfCreateWatchlistItemDialogModule } from './create-watchlist-item-dialog/create-watchlist-item-dialog.module'; import { CreateWatchlistItemDialogComponent } from './create-watchlist-item-dialog/create-watchlist-item-dialog.component';
import { HomeWatchlistComponent } from './home-watchlist.component'; import { HomeWatchlistComponent } from './home-watchlist.component';
@NgModule({ @NgModule({
@ -14,7 +14,7 @@ import { HomeWatchlistComponent } from './home-watchlist.component';
imports: [ imports: [
CommonModule, CommonModule,
GfBenchmarkComponent, GfBenchmarkComponent,
GfCreateWatchlistItemDialogModule, CreateWatchlistItemDialogComponent,
MatButtonModule, MatButtonModule,
RouterModule RouterModule
], ],

3
apps/client/src/app/pages/home/home-page.component.ts

@ -56,7 +56,8 @@ export class HomePageComponent implements OnDestroy, OnInit {
{ {
iconName: 'star-outline', iconName: 'star-outline',
label: $localize`Watchlist`, label: $localize`Watchlist`,
path: ['/home', 'watchlist'] path: ['/home', 'watchlist'],
showCondition: this.user?.settings?.isExperimentalFeatures
} }
]; ];
this.user = state.user; this.user = state.user;

6
apps/client/src/app/services/data.service.ts

@ -688,7 +688,9 @@ export class DataService {
} }
public fetchWatchlist() { public fetchWatchlist() {
return this.http.get<AssetProfileIdentifier[]>('/api/v1/watchlist'); return this.http.get<{ watchlist: AssetProfileIdentifier[] }>(
'/api/v1/watchlist'
);
} }
public generateAccessToken(aUserId: string) { public generateAccessToken(aUserId: string) {
@ -753,7 +755,7 @@ export class DataService {
return this.http.post<UserItem>('/api/v1/user', {}); return this.http.post<UserItem>('/api/v1/user', {});
} }
public postWatchlist(watchlistItem: CreateWatchlistItemDto) { public postWatchlistItem(watchlistItem: CreateWatchlistItemDto) {
return this.http.post('/api/v1/watchlist', watchlistItem); return this.http.post('/api/v1/watchlist', watchlistItem);
} }

Loading…
Cancel
Save