Browse Source

Feature/persist view mode of holdings tab on home page (#3624)

* Persist view mode of holdings in user settings

* Update changelog
pull/3629/head
Thomas Kaul 6 months ago
committed by GitHub
parent
commit
02db0db733
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 5
      apps/api/src/app/user/update-user-setting.dto.ts
  3. 5
      apps/api/src/app/user/user.service.ts
  4. 40
      apps/client/src/app/components/home-holdings/home-holdings.component.ts
  5. 8
      libs/common/src/lib/interfaces/user-settings.interface.ts
  6. 1
      libs/common/src/lib/types/holding-view-mode.type.ts
  7. 1
      libs/common/src/lib/types/holdings-view-mode.type.ts
  8. 4
      libs/common/src/lib/types/index.ts

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Improved the color assignment in the chart of the holdings tab on the home page (experimental) - Improved the color assignment in the chart of the holdings tab on the home page (experimental)
- Persisted the view mode of the holdings tab on the home page (experimental)
- Improved the language localization for Catalan (`ca`) - Improved the language localization for Catalan (`ca`)
- Improved the language localization for Spanish (`es`) - Improved the language localization for Spanish (`es`)

5
apps/api/src/app/user/update-user-setting.dto.ts

@ -2,6 +2,7 @@ import { IsCurrencyCode } from '@ghostfolio/api/validators/is-currency-code';
import type { import type {
ColorScheme, ColorScheme,
DateRange, DateRange,
HoldingsViewMode,
ViewMode ViewMode
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
@ -66,6 +67,10 @@ export class UpdateUserSettingDto {
@IsOptional() @IsOptional()
'filters.tags'?: string[]; 'filters.tags'?: string[];
@IsIn(<HoldingsViewMode[]>['CHART', 'TABLE'])
@IsOptional()
holdingsViewMode?: HoldingsViewMode;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
isExperimentalFeatures?: boolean; isExperimentalFeatures?: boolean;

5
apps/api/src/app/user/user.service.ts

@ -190,7 +190,7 @@ export class UserService {
(user.Settings.settings as UserSettings).dateRange = (user.Settings.settings as UserSettings).dateRange =
(user.Settings.settings as UserSettings).viewMode === 'ZEN' (user.Settings.settings as UserSettings).viewMode === 'ZEN'
? 'max' ? 'max'
: (user.Settings.settings as UserSettings)?.dateRange ?? 'max'; : ((user.Settings.settings as UserSettings)?.dateRange ?? 'max');
// Set default value for view mode // Set default value for view mode
if (!(user.Settings.settings as UserSettings).viewMode) { if (!(user.Settings.settings as UserSettings).viewMode) {
@ -243,6 +243,9 @@ export class UserService {
// Reset benchmark // Reset benchmark
user.Settings.settings.benchmark = undefined; user.Settings.settings.benchmark = undefined;
// Reset holdings view mode
user.Settings.settings.holdingsViewMode = undefined;
} else if (user.subscription?.type === 'Premium') { } else if (user.subscription?.type === 'Premium') {
currentPermissions.push(permissions.reportDataGlitch); currentPermissions.push(permissions.reportDataGlitch);

40
apps/client/src/app/components/home-holdings/home-holdings.component.ts

@ -9,7 +9,7 @@ import {
import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { import {
HoldingType, HoldingType,
HoldingViewMode, HoldingsViewMode,
ToggleOption ToggleOption
} from '@ghostfolio/common/types'; } from '@ghostfolio/common/types';
@ -18,7 +18,7 @@ import { FormControl } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DeviceDetectorService } from 'ngx-device-detector'; import { DeviceDetectorService } from 'ngx-device-detector';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators'; import { skip, takeUntil } from 'rxjs/operators';
@Component({ @Component({
selector: 'gf-home-holdings', selector: 'gf-home-holdings',
@ -26,6 +26,8 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './home-holdings.html' templateUrl: './home-holdings.html'
}) })
export class HomeHoldingsComponent implements OnDestroy, OnInit { export class HomeHoldingsComponent implements OnDestroy, OnInit {
public static DEFAULT_HOLDINGS_VIEW_MODE: HoldingsViewMode = 'TABLE';
public deviceType: string; public deviceType: string;
public hasImpersonationId: boolean; public hasImpersonationId: boolean;
public hasPermissionToAccessHoldingsChart: boolean; public hasPermissionToAccessHoldingsChart: boolean;
@ -37,7 +39,9 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
{ label: $localize`Closed`, value: 'CLOSED' } { label: $localize`Closed`, value: 'CLOSED' }
]; ];
public user: User; public user: User;
public viewModeFormControl = new FormControl<HoldingViewMode>('TABLE'); public viewModeFormControl = new FormControl<HoldingsViewMode>(
HomeHoldingsComponent.DEFAULT_HOLDINGS_VIEW_MODE
);
private unsubscribeSubject = new Subject<void>(); private unsubscribeSubject = new Subject<void>();
@ -81,6 +85,21 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
this.changeDetectorRef.markForCheck(); this.changeDetectorRef.markForCheck();
} }
}); });
this.viewModeFormControl.valueChanges
.pipe(
// Skip inizialization: "new FormControl"
skip(1),
takeUntil(this.unsubscribeSubject)
)
.subscribe((holdingsViewMode) => {
this.dataService
.putUserSetting({ holdingsViewMode })
.pipe(takeUntil(this.unsubscribeSubject))
.subscribe(() => {
this.userService.remove();
});
});
} }
public onChangeHoldingType(aHoldingType: HoldingType) { public onChangeHoldingType(aHoldingType: HoldingType) {
@ -122,9 +141,20 @@ export class HomeHoldingsComponent implements OnDestroy, OnInit {
this.hasPermissionToAccessHoldingsChart && this.hasPermissionToAccessHoldingsChart &&
this.holdingType === 'ACTIVE' this.holdingType === 'ACTIVE'
) { ) {
this.viewModeFormControl.enable(); this.viewModeFormControl.enable({ emitEvent: false });
this.viewModeFormControl.setValue(
this.deviceType === 'mobile'
? HomeHoldingsComponent.DEFAULT_HOLDINGS_VIEW_MODE
: this.user?.settings?.holdingsViewMode ||
HomeHoldingsComponent.DEFAULT_HOLDINGS_VIEW_MODE,
{ emitEvent: false }
);
} else if (this.holdingType === 'CLOSED') { } else if (this.holdingType === 'CLOSED') {
this.viewModeFormControl.setValue('TABLE'); this.viewModeFormControl.setValue(
HomeHoldingsComponent.DEFAULT_HOLDINGS_VIEW_MODE,
{ emitEvent: false }
);
} }
this.holdings = undefined; this.holdings = undefined;

8
libs/common/src/lib/interfaces/user-settings.interface.ts

@ -1,4 +1,9 @@
import { ColorScheme, DateRange, ViewMode } from '@ghostfolio/common/types'; import {
ColorScheme,
DateRange,
HoldingsViewMode,
ViewMode
} from '@ghostfolio/common/types';
export interface UserSettings { export interface UserSettings {
annualInterestRate?: number; annualInterestRate?: number;
@ -9,6 +14,7 @@ export interface UserSettings {
emergencyFund?: number; emergencyFund?: number;
'filters.accounts'?: string[]; 'filters.accounts'?: string[];
'filters.tags'?: string[]; 'filters.tags'?: string[];
holdingsViewMode?: HoldingsViewMode;
isExperimentalFeatures?: boolean; isExperimentalFeatures?: boolean;
isRestrictedView?: boolean; isRestrictedView?: boolean;
language?: string; language?: string;

1
libs/common/src/lib/types/holding-view-mode.type.ts

@ -1 +0,0 @@
export type HoldingViewMode = 'CHART' | 'TABLE';

1
libs/common/src/lib/types/holdings-view-mode.type.ts

@ -0,0 +1 @@
export type HoldingsViewMode = 'CHART' | 'TABLE';

4
libs/common/src/lib/types/index.ts

@ -8,7 +8,7 @@ import type { DateRange } from './date-range.type';
import type { Granularity } from './granularity.type'; import type { Granularity } from './granularity.type';
import type { GroupBy } from './group-by.type'; import type { GroupBy } from './group-by.type';
import type { HoldingType } from './holding-type.type'; import type { HoldingType } from './holding-type.type';
import type { HoldingViewMode } from './holding-view-mode.type'; import type { HoldingsViewMode } from './holdings-view-mode.type';
import type { MarketAdvanced } from './market-advanced.type'; import type { MarketAdvanced } from './market-advanced.type';
import type { MarketDataPreset } from './market-data-preset.type'; import type { MarketDataPreset } from './market-data-preset.type';
import type { MarketState } from './market-state.type'; import type { MarketState } from './market-state.type';
@ -31,7 +31,7 @@ export type {
Granularity, Granularity,
GroupBy, GroupBy,
HoldingType, HoldingType,
HoldingViewMode, HoldingsViewMode,
Market, Market,
MarketAdvanced, MarketAdvanced,
MarketDataPreset, MarketDataPreset,

Loading…
Cancel
Save