Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/app/settings/settings.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,72 @@ <h2>{{ "TEXT_TO_SPEECH.TEST_SPEAK" | translate }}</h2>
</ion-card-content>
</ion-card>

<ion-card class="margin-top-20">
<ion-card-content>
<ion-card-title>{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_TITLE" | translate }}</ion-card-title>
<ion-item>
<ion-checkbox label-placement='start' (ngModelChange)="saveSettings();"
[(ngModel)]="settings.brew_event_markers_enabled">
<div class='ion-label'>{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_ENABLED" | translate }}</div>
</ion-checkbox>
</ion-item>
@if (settings.brew_event_markers_enabled) {
<ion-item>
<ion-select (ngModelChange)="saveSettings()" [(ngModel)]="settings.brew_event_markers_mode"
cancelText="{{'CANCEL'| translate }}" okText="{{'CHOOSE'| translate }}">
<div slot="label">{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE" | translate }}</div>
<ion-select-option value="line">{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE_LINE" | translate }}</ion-select-option>
<ion-select-option value="region">{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE_REGION" | translate }}</ion-select-option>
</ion-select>
</ion-item>
<ion-accordion-group>
<ion-accordion value="bloomMarker">
<ion-item slot="header" color="light">
<ion-label>{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_BLOOM_COLOR" | translate }}</ion-label>
<ion-button fill="clear" slot="end"
(click)="resetGraphColor('bloomMarker'); $event.stopPropagation(); $event.preventDefault()">
<ion-icon name="refresh-outline" slot="icon-only"></ion-icon>
</ion-button>
</ion-item>
<div class="ion-padding" slot="content">
<ion-item lines="none">
<ion-label position="stacked">{{"GRAPH_ACTIVE_LIGHT_MODE" | translate}}</ion-label>
<ion-input class="margin-top-5" type="color"
[(ngModel)]="settings.graph_colors.bloomMarker.active.light" (ionBlur)="saveSettings()"></ion-input>
</ion-item>
<ion-item lines="none">
<ion-label position="stacked">{{"GRAPH_ACTIVE_DARK_MODE" | translate}}</ion-label>
<ion-input class="margin-top-5" type="color"
[(ngModel)]="settings.graph_colors.bloomMarker.active.dark" (ionBlur)="saveSettings()"></ion-input>
</ion-item>
</div>
</ion-accordion>
<ion-accordion value="firstDripMarker">
<ion-item slot="header" color="light">
<ion-label>{{ "PAGE_SETTINGS_BREW_EVENT_MARKERS_FIRST_DRIP_COLOR" | translate }}</ion-label>
<ion-button fill="clear" slot="end"
(click)="resetGraphColor('firstDripMarker'); $event.stopPropagation(); $event.preventDefault()">
<ion-icon name="refresh-outline" slot="icon-only"></ion-icon>
</ion-button>
</ion-item>
<div class="ion-padding" slot="content">
<ion-item lines="none">
<ion-label position="stacked">{{"GRAPH_ACTIVE_LIGHT_MODE" | translate}}</ion-label>
<ion-input class="margin-top-5" type="color"
[(ngModel)]="settings.graph_colors.firstDripMarker.active.light" (ionBlur)="saveSettings()"></ion-input>
</ion-item>
<ion-item lines="none">
<ion-label position="stacked">{{"GRAPH_ACTIVE_DARK_MODE" | translate}}</ion-label>
<ion-input class="margin-top-5" type="color"
[(ngModel)]="settings.graph_colors.firstDripMarker.active.dark" (ionBlur)="saveSettings()"></ion-input>
</ion-item>
</div>
</ion-accordion>
</ion-accordion-group>
}
</ion-card-content>
</ion-card>

}
@case ('bluetoothscale') {
<ion-card>
Expand Down
7 changes: 7 additions & 0 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1659,6 +1659,13 @@
"GRAPH_REFERENCE_LIGHT_MODE": "Reference (Light)",
"GRAPH_REFERENCE_DARK_MODE": "Reference (Dark)",
"GRAPH_COLORS": "Graph colors",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_TITLE": "Brew event markers",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_ENABLED": "Show bloom and first-drip markers on chart",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE": "Marker style",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE_LINE": "Vertical lines",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_MODE_REGION": "Background regions",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_BLOOM_COLOR": "Bloom marker colour",
"PAGE_SETTINGS_BREW_EVENT_MARKERS_FIRST_DRIP_COLOR": "First drip marker colour",
"PAGE_SETTINGS_LANGUAGE_KOREAN": "Korean",
"PAGE_SETTINGS_LANGUAGE_SLOVAK": "Slovak",
"PAGE_SETTINGS_LANGUAGE_JAPANESE": "Japanese",
Expand Down
110 changes: 110 additions & 0 deletions src/classes/settings/__tests__/settings-event-markers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { DEFAULT_GRAPH_COLORS } from '../../../data/defaultGraphColors';
import { Settings } from '../settings';

describe('Settings — brew event markers', () => {
describe('default construction', () => {
it('should default brew_event_markers_enabled to false and mode to line', () => {
const settings = new Settings();

expect(settings.brew_event_markers_enabled).toBe(false);
expect(settings.brew_event_markers_mode).toBe('line');
});

it('should initialize graph_colors.bloomMarker with all four color variants', () => {
const settings = new Settings();
const marker = settings.graph_colors.bloomMarker;

expect(marker).toBeTruthy();
expect(marker.active.light).toBeTruthy();
expect(marker.active.dark).toBeTruthy();
expect(marker.reference.light).toBeTruthy();
expect(marker.reference.dark).toBeTruthy();
});

it('should initialize graph_colors.firstDripMarker with all four color variants', () => {
const settings = new Settings();
const marker = settings.graph_colors.firstDripMarker;

expect(marker).toBeTruthy();
expect(marker.active.light).toBeTruthy();
expect(marker.active.dark).toBeTruthy();
expect(marker.reference.light).toBeTruthy();
expect(marker.reference.dark).toBeTruthy();
});
});

describe('initializeByObject — all fields present', () => {
it('should preserve brew_event_markers_enabled and mode when serialized object contains them', () => {
const settings = new Settings();
const serialized = JSON.parse(JSON.stringify(settings));
serialized.brew_event_markers_enabled = true;
serialized.brew_event_markers_mode = 'region';

settings.initializeByObject(serialized);

expect(settings.brew_event_markers_enabled).toBe(true);
expect(settings.brew_event_markers_mode).toBe('region');
});

it('should preserve graph_colors.bloomMarker and firstDripMarker when present', () => {
const settings = new Settings();
const serialized = JSON.parse(JSON.stringify(settings));

settings.initializeByObject(serialized);

expect(settings.graph_colors.bloomMarker).toEqual(
DEFAULT_GRAPH_COLORS.bloomMarker,
);
expect(settings.graph_colors.firstDripMarker).toEqual(
DEFAULT_GRAPH_COLORS.firstDripMarker,
);
});
});

describe('initializeByObject — legacy / missing fields', () => {
it('should leave brew_event_markers_enabled as false when missing from legacy payload', () => {
const settings = new Settings();
const legacy = JSON.parse(JSON.stringify(new Settings()));
delete legacy.brew_event_markers_enabled;

expect(() => settings.initializeByObject(legacy)).not.toThrow();
expect(settings.brew_event_markers_enabled).toBe(false);
});

it('should populate bloomMarker from defaults when absent from graph_colors', () => {
const settings = new Settings();
const legacy = JSON.parse(JSON.stringify(new Settings()));
delete legacy.graph_colors.bloomMarker;

expect(() => settings.initializeByObject(legacy)).not.toThrow();
expect(settings.graph_colors.bloomMarker).toEqual(
DEFAULT_GRAPH_COLORS.bloomMarker,
);
});

it('should populate firstDripMarker from defaults when absent from graph_colors', () => {
const settings = new Settings();
const legacy = JSON.parse(JSON.stringify(new Settings()));
delete legacy.graph_colors.firstDripMarker;

expect(() => settings.initializeByObject(legacy)).not.toThrow();
expect(settings.graph_colors.firstDripMarker).toEqual(
DEFAULT_GRAPH_COLORS.firstDripMarker,
);
});

it('should reset graph_colors to full defaults including marker colors when graph_colors is absent', () => {
const settings = new Settings();
const legacy = JSON.parse(JSON.stringify(new Settings()));
delete legacy.graph_colors;

expect(() => settings.initializeByObject(legacy)).not.toThrow();
expect(settings.graph_colors.bloomMarker).toEqual(
DEFAULT_GRAPH_COLORS.bloomMarker,
);
expect(settings.graph_colors.firstDripMarker).toEqual(
DEFAULT_GRAPH_COLORS.firstDripMarker,
);
});
});
});
18 changes: 18 additions & 0 deletions src/classes/settings/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ export class Settings implements ISettings {
public wake_lock: boolean;
public security_check_when_going_back: boolean;

public brew_event_markers_enabled: boolean;
public brew_event_markers_mode: string;

public show_roasting_section: boolean;
public show_water_section: boolean;
public show_cupping_section: boolean;
Expand Down Expand Up @@ -368,6 +371,9 @@ export class Settings implements ISettings {

this.track_caffeine_consumption = false;

this.brew_event_markers_enabled = false;
this.brew_event_markers_mode = 'line';

this.show_roasting_section = false;
this.show_water_section = false;
this.show_cupping_section = false;
Expand Down Expand Up @@ -660,6 +666,18 @@ export class Settings implements ISettings {
JSON.stringify(DEFAULT_GRAPH_COLORS.customTrace),
);
}

if (!this.graph_colors.bloomMarker) {
this.graph_colors.bloomMarker = JSON.parse(
JSON.stringify(DEFAULT_GRAPH_COLORS.bloomMarker),
);
}

if (!this.graph_colors.firstDripMarker) {
this.graph_colors.firstDripMarker = JSON.parse(
JSON.stringify(DEFAULT_GRAPH_COLORS.firstDripMarker),
);
}
// We need to reassign brew order here, else the class would be dismissed.

this.manage_parameters = new ManageBrewParameter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ion-card class='long-card brew-layout' [class.best-brew]="brew.best_brew" [class.favourite-brew]="brew.favourite" style='z-index:-1'>
<ion-card-content class='no-ion-col-vertical-padding ion-padding-top' >
@if (informationContainerWidth) {
<graph-display-card [staticChart]="true" [chartWidth]='informationContainerWidth' [chartHeight]='informationContainerHeight' [flowProfilePath]='brew.flow_profile'></graph-display-card>
<graph-display-card [staticChart]="true" [chartWidth]='informationContainerWidth' [chartHeight]='informationContainerHeight' [flowProfilePath]='brew.flow_profile' [brew]="brew"></graph-display-card>
}
</ion-card-content>
</ion-card>
Expand Down
Loading