From 40b76bdfcf721a3271cda0622da1895fc59359f1 Mon Sep 17 00:00:00 2001 From: Thomas <4159106+dtslvr@users.noreply.github.com> Date: Fri, 15 Apr 2022 10:28:41 +0200 Subject: [PATCH] Refactoring --- .../position-detail-dialog.component.ts | 11 ++- .../transactions-page.component.ts | 75 +++++-------------- .../src/app/services/ics/ics.service.ts | 59 +++++++++++++++ libs/common/src/lib/helper.ts | 26 ++++--- 4 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 apps/client/src/app/services/ics/ics.service.ts diff --git a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts index 3b3cd8da6..55efc0249 100644 --- a/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts +++ b/apps/client/src/app/components/position/position-detail-dialog/position-detail-dialog.component.ts @@ -211,15 +211,14 @@ export class PositionDetailDialog implements OnDestroy, OnInit { ) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((data) => { - downloadAsFile( - data, - `ghostfolio-export-${this.SymbolProfile?.symbol}-${format( + downloadAsFile({ + content: data, + fileName: `ghostfolio-export-${this.SymbolProfile?.symbol}-${format( parseISO(data.meta.date), 'yyyyMMddHHmm' )}.json`, - 'text/plain', - 'json' - ); + format: 'json' + }); }); } diff --git a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts index fcfe7b8dd..61683fd75 100644 --- a/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts +++ b/apps/client/src/app/pages/portfolio/transactions/transactions-page.component.ts @@ -7,15 +7,16 @@ import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interf import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; import { PositionDetailDialog } from '@ghostfolio/client/components/position/position-detail-dialog/position-detail-dialog.component'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { IcsService } from '@ghostfolio/client/services/ics/ics.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { ImportTransactionsService } from '@ghostfolio/client/services/import-transactions.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; import { downloadAsFile } from '@ghostfolio/common/helper'; -import { Export, User } from '@ghostfolio/common/interfaces'; +import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { DataSource, Order as OrderModel, Type } from '@prisma/client'; +import { DataSource, Order as OrderModel } from '@prisma/client'; import { format, parseISO } from 'date-fns'; -import { capitalize, isArray } from 'lodash'; +import { isArray } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; import { Subject, Subscription } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -50,6 +51,7 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { private dataService: DataService, private deviceService: DeviceDetectorService, private dialog: MatDialog, + private icsService: IcsService, private impersonationStorageService: ImpersonationStorageService, private importTransactionsService: ImportTransactionsService, private route: ActivatedRoute, @@ -156,15 +158,14 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { delete activity.id; } - downloadAsFile( - data, - `ghostfolio-export-${format( + downloadAsFile({ + content: data, + fileName: `ghostfolio-export-${format( parseISO(data.meta.date), 'yyyyMMddHHmm' )}.json`, - 'text/plain', - 'json' - ); + format: 'json' + }); }); } @@ -173,15 +174,16 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { .fetchExport(activityIds) .pipe(takeUntil(this.unsubscribeSubject)) .subscribe((data) => { - downloadAsFile( - this.getIcsContent(data.activities), - `ghostfolio-drafts-${format( + downloadAsFile({ + content: this.icsService.transformActivitiesToIcsContent( + data.activities + ), + fileName: `ghostfolio-drafts-${format( parseISO(data.meta.date), 'yyyyMMddHHmm' )}.ics`, - 'text/plain', - 'string' - ); + format: 'string' + }); }); } @@ -315,49 +317,6 @@ export class TransactionsPageComponent implements OnDestroy, OnInit { this.unsubscribeSubject.complete(); } - private getIcsContent(aActivities: Export['activities']) { - const header = [ - 'BEGIN:VCALENDAR', - 'VERSION:2.0', - 'PRODID:-//Ghostfolio//NONSGML v1.0//EN' - ]; - const events = aActivities.map((activity) => { - return this.getEvent({ - date: parseISO(activity.date), - id: activity.id, - symbol: activity.symbol, - type: activity.type - }); - }); - const footer = ['END:VCALENDAR']; - - return [...header, ...events, ...footer].join('\n'); - } - - private getEvent({ - date, - id, - symbol, - type - }: { - date: Date; - id: string; - symbol: string; - type: Type; - }) { - const today = format(new Date(), 'yyyyMMdd'); - - return [ - 'BEGIN:VEVENT', - `UID:${id}`, - `DTSTAMP:${today}T000000`, - `DTSTART;VALUE=DATE:${format(date, 'yyyyMMdd')}`, - `DTEND;VALUE=DATE:${format(date, 'yyyyMMdd')}`, - `SUMMARY:${capitalize(type)} ${symbol}`, - 'END:VEVENT' - ].join('\n'); - } - private handleImportError({ activities, error diff --git a/apps/client/src/app/services/ics/ics.service.ts b/apps/client/src/app/services/ics/ics.service.ts new file mode 100644 index 000000000..7329ea663 --- /dev/null +++ b/apps/client/src/app/services/ics/ics.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@angular/core'; +import { capitalize } from '@ghostfolio/common/helper'; +import { Export } from '@ghostfolio/common/interfaces'; +import { Type } from '@prisma/client'; +import { format, parseISO } from 'date-fns'; + +@Injectable({ + providedIn: 'root' +}) +export class IcsService { + private readonly ICS_DATE_FORMAT = 'yyyyMMdd'; + + public constructor() {} + + public transformActivitiesToIcsContent( + aActivities: Export['activities'] + ): string { + const header = [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Ghostfolio//NONSGML v1.0//EN' + ]; + const events = aActivities.map((activity) => { + return this.getEvent({ + date: parseISO(activity.date), + id: activity.id, + symbol: activity.symbol, + type: activity.type + }); + }); + const footer = ['END:VCALENDAR']; + + return [...header, ...events, ...footer].join('\n'); + } + + private getEvent({ + date, + id, + symbol, + type + }: { + date: Date; + id: string; + symbol: string; + type: Type; + }) { + const today = format(new Date(), this.ICS_DATE_FORMAT); + + return [ + 'BEGIN:VEVENT', + `UID:${id}`, + `DTSTAMP:${today}T000000`, + `DTSTART;VALUE=DATE:${format(date, this.ICS_DATE_FORMAT)}`, + `DTEND;VALUE=DATE:${format(date, this.ICS_DATE_FORMAT)}`, + `SUMMARY:${capitalize(type)} ${symbol}`, + 'END:VEVENT' + ].join('\n'); + } +} diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 9406b4df2..ad47abfdd 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -12,24 +12,28 @@ export function decodeDataSource(encodedDataSource: string) { return Buffer.from(encodedDataSource, 'hex').toString(); } -export function downloadAsFile( - aContent: unknown, - aFileName: string, - aContentType: string, - aType: 'json' | 'string' -) { +export function downloadAsFile({ + content, + contentType = 'text/plain', + fileName, + format +}: { + content: unknown; + contentType?: string; + fileName: string; + format: 'json' | 'string'; +}) { const a = document.createElement('a'); - let content = aContent; - if (aType === 'json') { - content = JSON.stringify(aContent, undefined, ' '); + if (format === 'json') { + content = JSON.stringify(content, undefined, ' '); } const file = new Blob([content], { - type: aContentType + type: contentType }); a.href = URL.createObjectURL(file); - a.download = aFileName; + a.download = fileName; a.click(); }