diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index e510880ed..24b060ac1 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -1,7 +1,8 @@ import type { ColorScheme, DateRange, - ViewMode + ViewMode, + UnknownMode } from '@ghostfolio/common/types'; import { IsBoolean, @@ -68,4 +69,8 @@ export class UpdateUserSettingDto { @IsIn(['DEFAULT', 'ZEN']) @IsOptional() viewMode?: ViewMode; + + @IsIn(['DEFAULT', 'OFF']) + @IsOptional() + unknownMode?: UnknownMode; } diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 1630a74aa..256fc5a96 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -161,6 +161,10 @@ export class UserService { (user.Settings.settings as UserSettings).viewMode = 'DEFAULT'; } + if (!(user.Settings.settings as UserSettings).unknownMode) { + (user.Settings.settings as UserSettings).unknownMode = 'DEFAULT'; + } + let currentPermissions = getPermissions(user.role); if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 6a8349f89..35142594e 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -118,7 +118,7 @@ export class YahooFinanceService implements DataProviderInterface { }); response.symbol = aSymbol; - if (assetSubClass === AssetSubClass.MUTUALFUND) { + if (assetSubClass === AssetSubClass.MUTUALFUND || assetSubClass === AssetSubClass.ETF) { response.sectors = []; for (const sectorWeighting of assetProfile.topHoldings diff --git a/apps/client/src/app/pages/account/account-page.component.ts b/apps/client/src/app/pages/account/account-page.component.ts index 56e43c755..30e3e26a5 100644 --- a/apps/client/src/app/pages/account/account-page.component.ts +++ b/apps/client/src/app/pages/account/account-page.component.ts @@ -53,6 +53,7 @@ export class AccountPageComponent implements OnDestroy, OnInit { public hasPermissionToCreateAccess: boolean; public hasPermissionToDeleteAccess: boolean; public hasPermissionToUpdateViewMode: boolean; + public hasPermissionToUpdateUnknownMode: boolean; public hasPermissionToUpdateUserSettings: boolean; public language = document.documentElement.lang; public locales = [ @@ -138,6 +139,11 @@ export class AccountPageComponent implements OnDestroy, OnInit { permissions.updateViewMode ); + this.hasPermissionToUpdateUnknownMode = hasPermission( + this.user.permissions, + permissions.updateUnknownMode + ); + this.locales.push(this.user.settings.locale); this.locales = uniq(this.locales.sort()); @@ -330,6 +336,22 @@ export class AccountPageComponent implements OnDestroy, OnInit { }); }); } + public onUnknownChange(aEvent: MatSlideToggleChange) { + this.dataService + .putUserSetting({ unknownMode: aEvent.checked === true ? 'DEFAULT' : 'OFF'}) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.userService.remove(); + + this.userService + .get() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((user) => { + this.user = user; + this.changeDetectorRef.markForCheck(); + }); + }); + } public ngOnDestroy() { this.unsubscribeSubject.next(); diff --git a/apps/client/src/app/pages/account/account-page.html b/apps/client/src/app/pages/account/account-page.html index 749a3d7be..589bc755f 100644 --- a/apps/client/src/app/pages/account/account-page.html +++ b/apps/client/src/app/pages/account/account-page.html @@ -225,6 +225,22 @@ > +
+
+
Unknown Mode
+
+ Show Unknown categories in Portfolio Allocation View +
+
+
+ +
+
Sign in with fingerprint
diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index b7a631625..27dd6f843 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -309,7 +309,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; } } - } else { + } else if (this.user.settings.unknownMode === "DEFAULT") { this.continents[UNKNOWN_KEY].value += this.portfolioDetails.holdings[symbol].value; @@ -330,7 +330,7 @@ export class AllocationsPageComponent implements OnDestroy, OnInit { }; } } - } else { + } else if (this.user.settings.unknownMode === "DEFAULT") { this.sectors[UNKNOWN_KEY].value += this.portfolioDetails.holdings[symbol].value; } diff --git a/apps/client/src/app/pages/public/public-page.component.ts b/apps/client/src/app/pages/public/public-page.component.ts index f25c69dae..5940c5f9d 100644 --- a/apps/client/src/app/pages/public/public-page.component.ts +++ b/apps/client/src/app/pages/public/public-page.component.ts @@ -5,7 +5,7 @@ import { UNKNOWN_KEY } from '@ghostfolio/common/config'; import { prettifySymbol } from '@ghostfolio/common/helper'; import { PortfolioPosition, - PortfolioPublicDetails + PortfolioPublicDetails, User } from '@ghostfolio/common/interfaces'; import { Market } from '@ghostfolio/common/types'; import { StatusCodes } from 'http-status-codes'; @@ -13,6 +13,7 @@ import { isNumber } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; import { EMPTY, Subject } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; @Component({ host: { class: 'page' }, @@ -47,23 +48,34 @@ export class PublicPageComponent implements OnInit { }; private id: string; + + private user: User; + private unsubscribeSubject = new Subject(); public constructor( private activatedRoute: ActivatedRoute, private changeDetectorRef: ChangeDetectorRef, private dataService: DataService, + private userService: UserService, private deviceService: DeviceDetectorService, private router: Router ) { this.activatedRoute.params.subscribe((params) => { this.id = params['id']; }); + this.userService.stateChanged + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((state) => { + if (state?.user) { + this.user = state.user; + this.initializeAnalysisData() + } + }); } public ngOnInit() { this.deviceType = this.deviceService.getDeviceInfo().deviceType; - this.dataService .fetchPortfolioPublic(this.id) .pipe( @@ -191,7 +203,7 @@ export class PublicPageComponent implements OnInit { }; } } - } else { + } else if (this.user.settings.unknownMode === "DEFAULT") { this.sectors[UNKNOWN_KEY].value += this.portfolioPublicDetails.holdings[symbol].value; } diff --git a/libs/common/src/lib/interfaces/user-settings.interface.ts b/libs/common/src/lib/interfaces/user-settings.interface.ts index d3864ab64..3196f299d 100644 --- a/libs/common/src/lib/interfaces/user-settings.interface.ts +++ b/libs/common/src/lib/interfaces/user-settings.interface.ts @@ -1,4 +1,4 @@ -import { ColorScheme, DateRange, ViewMode } from '@ghostfolio/common/types'; +import {ColorScheme, DateRange, UnknownMode, ViewMode} from '@ghostfolio/common/types'; export interface UserSettings { annualInterestRate?: number; @@ -15,4 +15,5 @@ export interface UserSettings { retirementDate?: string; savingsRate?: number; viewMode?: ViewMode; + unknownMode?: UnknownMode; } diff --git a/libs/common/src/lib/permissions.ts b/libs/common/src/lib/permissions.ts index b9dc6806a..75e571528 100644 --- a/libs/common/src/lib/permissions.ts +++ b/libs/common/src/lib/permissions.ts @@ -27,7 +27,8 @@ export const permissions = { updateAuthDevice: 'updateAuthDevice', updateOrder: 'updateOrder', updateUserSettings: 'updateUserSettings', - updateViewMode: 'updateViewMode' + updateViewMode: 'updateViewMode', + updateUnknownMode: 'updateUnknownMode' }; export function getPermissions(aRole: Role): string[] { @@ -47,7 +48,8 @@ export function getPermissions(aRole: Role): string[] { permissions.updateAuthDevice, permissions.updateOrder, permissions.updateUserSettings, - permissions.updateViewMode + permissions.updateViewMode, + permissions.updateUnknownMode ]; case 'DEMO': @@ -66,7 +68,8 @@ export function getPermissions(aRole: Role): string[] { permissions.updateAuthDevice, permissions.updateOrder, permissions.updateUserSettings, - permissions.updateViewMode + permissions.updateViewMode, + permissions.updateUnknownMode ]; default: diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts index 12bee4132..e9072ed9d 100644 --- a/libs/common/src/lib/types/index.ts +++ b/libs/common/src/lib/types/index.ts @@ -10,7 +10,7 @@ import { Market } from './market.type'; import type { OrderWithAccount } from './order-with-account.type'; import type { RequestWithUser } from './request-with-user.type'; import { ToggleOption } from './toggle-option.type'; -import type { ViewMode } from './view-mode.type'; +import type { ViewMode, UnknownMode } from './view-mode.type'; export type { AccessWithGranteeUser, @@ -25,5 +25,6 @@ export type { OrderWithAccount, RequestWithUser, ToggleOption, - ViewMode + ViewMode, + UnknownMode }; diff --git a/libs/common/src/lib/types/view-mode.type.ts b/libs/common/src/lib/types/view-mode.type.ts index ad38adb0a..4222deb13 100644 --- a/libs/common/src/lib/types/view-mode.type.ts +++ b/libs/common/src/lib/types/view-mode.type.ts @@ -1 +1,2 @@ export type ViewMode = 'DEFAULT' | 'ZEN'; +export type UnknownMode = 'DEFAULT' | 'OFF'; diff --git a/prisma/migrations/20230212232240_added_unknown_mode/migration.sql b/prisma/migrations/20230212232240_added_unknown_mode/migration.sql new file mode 100644 index 000000000..5de1899b0 --- /dev/null +++ b/prisma/migrations/20230212232240_added_unknown_mode/migration.sql @@ -0,0 +1,2 @@ +-- CreateEnum +CREATE TYPE "UnknownMode" AS ENUM ('DEFAULT', 'OFF'); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index dd46e52b6..c8ad24226 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -219,6 +219,11 @@ enum ViewMode { ZEN } +enum UnknownMode { + DEFAULT + OFF +} + enum Provider { ANONYMOUS GOOGLE