refactor: globalConfigService props to signal

This commit is contained in:
Johannes Millan 2025-08-11 17:40:41 +02:00
parent 384a9ef62b
commit 4b66f93dfa
21 changed files with 170 additions and 167 deletions

15
package-lock.json generated
View file

@ -68,7 +68,6 @@
"@schematics/angular": "^20.1.4",
"@types/electron-localshortcut": "^3.1.3",
"@types/file-saver": "^2.0.5",
"@types/hammerjs": "^2.0.45",
"@types/jasmine": "^3.10.2",
"@types/jasminewd2": "~2.0.13",
"@types/node": "20.12.4",
@ -100,7 +99,6 @@
"fflate": "^0.8.2",
"file-saver": "^2.0.5",
"glob": "^9.3.5",
"hammerjs": "^2.0.8",
"husky": "^4.2.5",
"ical.js": "^2.1.0",
"idb": "^8.0.3",
@ -7477,11 +7475,6 @@
"license": "MIT",
"optional": true
},
"node_modules/@types/hammerjs": {
"version": "2.0.46",
"dev": true,
"license": "MIT"
},
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"dev": true,
@ -14002,14 +13995,6 @@
"license": "MIT",
"optional": true
},
"node_modules/hammerjs": {
"version": "2.0.8",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/handle-thing": {
"version": "2.0.1",
"dev": true,

View file

@ -3,6 +3,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
computed,
effect,
ElementRef,
HostBinding,
@ -162,7 +163,14 @@ export class AppComponent implements OnDestroy, AfterViewInit {
@ViewChild('routeWrapper', { read: ElementRef }) routeWrapper?: ElementRef<HTMLElement>;
@HostBinding('@.disabled') isDisableAnimations = false;
@HostBinding('@.disabled') get isDisableAnimations(): boolean {
return this._isDisableAnimations();
}
private _isDisableAnimations = computed(() => {
const misc = this._globalConfigService.misc();
return misc?.isDisableAnimations ?? false;
});
isRTL: boolean = false;
@ -201,11 +209,6 @@ export class AppComponent implements OnDestroy, AfterViewInit {
}
}),
);
this._subs.add(
this._globalConfigService.misc$.subscribe((misc) => {
this.isDisableAnimations = misc.isDisableAnimations;
}),
);
// init theme and body class handlers
this._globalThemeService.init();
@ -220,7 +223,7 @@ export class AppComponent implements OnDestroy, AfterViewInit {
// init offline banner in lack of a better place for it
this._initOfflineBanner();
if (this._globalConfigService.cfg?.misc.isShowTipLonger) {
if (this._globalConfigService.misc()?.isShowTipLonger) {
this._snackService.open({
ico: 'lightbulb',
config: {
@ -285,13 +288,13 @@ export class AppComponent implements OnDestroy, AfterViewInit {
window.ea.on(IPC.TRANSFER_SETTINGS_REQUESTED, () => {
window.ea.sendAppSettingsToElectron(
this._globalConfigService.cfg as GlobalConfigState,
this._globalConfigService.cfg() as GlobalConfigState,
);
});
} else {
// WEB VERSION
window.addEventListener('beforeunload', (e) => {
const gCfg = this._globalConfigService.cfg;
const gCfg = this._globalConfigService.cfg();
if (!gCfg) {
throw new Error();
}

View file

@ -225,6 +225,6 @@ export class MainHeaderComponent implements OnDestroy {
}
get kb(): KeyboardConfig {
return (this._configService.cfg?.keyboard as KeyboardConfig) || {};
return (this._configService.cfg()?.keyboard as KeyboardConfig) || {};
}
}

View file

@ -141,7 +141,7 @@ export class MobileSidePanelMenuComponent {
{ initialValue: !!this._router.url.match(/tasks$/) },
);
readonly kb: KeyboardConfig = this._globalConfigService.cfg?.keyboard || {};
readonly kb: KeyboardConfig = this._globalConfigService.cfg()?.keyboard || {};
// Plugin-related signals
readonly sidePanelButtons = toSignal(this._pluginBridge.sidePanelButtons$, {

View file

@ -73,7 +73,7 @@ export class ShortcutService {
}
async handleKeyDown(ev: KeyboardEvent): Promise<void> {
const cfg = this._configService.cfg;
const cfg = this._configService.cfg();
if (!cfg) {
throw new Error();
}

View file

@ -149,7 +149,7 @@ export class SideNavComponent implements OnDestroy {
private _cachedIssueUrl?: string;
@HostBinding('class') get cssClass(): string {
return this._globalConfigService.cfg?.misc.isUseMinimalNav ? 'minimal-nav' : '';
return this._globalConfigService.cfg()?.misc.isUseMinimalNav ? 'minimal-nav' : '';
}
constructor() {

View file

@ -1,6 +1,5 @@
import { inject, Injectable } from '@angular/core';
import { computed, inject, Injectable } from '@angular/core';
import { GlobalConfigService } from '../../features/config/global-config.service';
import { map, shareReplay } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
@ -8,53 +7,45 @@ import { map, shareReplay } from 'rxjs/operators';
export class DateTimeFormatService {
private readonly _globalConfigService = inject(GlobalConfigService);
// Cache the current locale for synchronous access
private _currentLocale: string | undefined = undefined;
// Observable for the locale to use
readonly locale$ = this._globalConfigService.lang$.pipe(
map((langConfig) => langConfig.timeLocale || undefined),
shareReplay(1),
// Signal for the locale to use
private readonly _locale = computed(
() => this._globalConfigService.lang()?.timeLocale || undefined,
);
// For detecting if current locale uses 24-hour format (used in schedule component)
get is24HourFormat(): boolean {
readonly is24HourFormat = computed(() => {
const testDate = new Date(2000, 0, 1, 13, 0, 0);
const formatted = testDate.toLocaleTimeString(this._currentLocale, {
const formatted = testDate.toLocaleTimeString(this._locale(), {
hour: 'numeric',
minute: '2-digit',
});
return formatted.includes('13');
}
});
// Get the current locale being used
get currentLocale(): string | undefined {
return this._currentLocale;
return this._locale();
}
constructor() {
// Subscribe to keep the cached locale updated
this.locale$.subscribe((locale) => {
this._currentLocale = locale;
// Debug logging
if (locale) {
const testDate = new Date(2000, 0, 1, 13, 0, 0);
const formatted = testDate.toLocaleTimeString(locale, {
hour: 'numeric',
minute: '2-digit',
});
console.log('[DateTimeFormat] Using locale:', locale);
console.log(' - Test time (13:00):', formatted);
console.log(' - Format:', formatted.includes('13') ? '24-hour' : '12-hour');
} else {
console.log('[DateTimeFormat] Using system default locale');
}
});
// Debug logging when locale changes
const locale = this._locale();
if (locale) {
const testDate = new Date(2000, 0, 1, 13, 0, 0);
const formatted = testDate.toLocaleTimeString(locale, {
hour: 'numeric',
minute: '2-digit',
});
console.log('[DateTimeFormat] Using locale:', locale);
console.log(' - Test time (13:00):', formatted);
console.log(' - Format:', formatted.includes('13') ? '24-hour' : '12-hour');
} else {
console.log('[DateTimeFormat] Using system default locale');
}
}
formatTime(timestamp: number): string {
return new Date(timestamp).toLocaleTimeString(this._currentLocale, {
return new Date(timestamp).toLocaleTimeString(this._locale(), {
hour: 'numeric',
minute: 'numeric',
});

View file

@ -1,4 +1,4 @@
import { Injectable, inject } from '@angular/core';
import { Injectable, inject, effect } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DateAdapter } from '@angular/material/core';
import {
@ -9,7 +9,6 @@ import {
} from '../../app.constants';
import { BehaviorSubject, Observable } from 'rxjs';
import { GlobalConfigService } from 'src/app/features/config/global-config.service';
import { map, startWith } from 'rxjs/operators';
import { DEFAULT_GLOBAL_CONFIG } from 'src/app/features/config/default-global-config.const';
import { Log } from '../log';
@ -55,19 +54,19 @@ export class LanguageService {
}
private _initMonkeyPatchFirstDayOfWeek(): void {
let firstDayOfWeek = DEFAULT_GLOBAL_CONFIG.misc.firstDayOfWeek;
this._globalConfigService.misc$
.pipe(
map((cfg) => cfg.firstDayOfWeek),
startWith(1),
)
.subscribe((_firstDayOfWeek: number) => {
// default should be monday, if we have an invalid value for some reason
firstDayOfWeek =
_firstDayOfWeek === 0 || _firstDayOfWeek > 0 ? _firstDayOfWeek : 1;
});
// overwrites default method to make this configurable
this._dateAdapter.getFirstDayOfWeek = () => firstDayOfWeek;
// Use effect to reactively update firstDayOfWeek when config changes
effect(() => {
const miscConfig = this._globalConfigService.misc();
const firstDayOfWeek =
miscConfig?.firstDayOfWeek ?? DEFAULT_GLOBAL_CONFIG.misc.firstDayOfWeek;
// default should be monday, if we have an invalid value for some reason
const validFirstDayOfWeek =
firstDayOfWeek === 0 || firstDayOfWeek > 0 ? firstDayOfWeek : 1;
// overwrites default method to make this configurable
this._dateAdapter.getFirstDayOfWeek = () => validFirstDayOfWeek;
});
}
private _setFn(lng: LanguageCode): void {

View file

@ -1,4 +1,4 @@
import { inject, Injectable } from '@angular/core';
import { effect, inject, Injectable } from '@angular/core';
import { BodyClass, IS_ELECTRON } from '../../app.constants';
import { IS_MAC } from '../../util/is-mac';
import {
@ -89,8 +89,8 @@ export class GlobalThemeService {
.pipe(skip(1))
.subscribe((darkMode) => localStorage.setItem(LS.DARK_MODE, darkMode));
// Initialize custom theme
this._initCustomTheme();
// Set up reactive custom theme updates
this._setupCustomThemeEffect();
}
private _setDarkTheme(isDarkTheme: boolean): void {
@ -231,8 +231,10 @@ export class GlobalThemeService {
});
}
this._globalConfigService.misc$.subscribe((misc) => {
if (misc.isDisableAnimations) {
// Use effect to reactively update animation class
effect(() => {
const misc = this._globalConfigService.misc();
if (misc?.isDisableAnimations) {
this.document.body.classList.add(BodyClass.isDisableAnimations);
} else {
this.document.body.classList.remove(BodyClass.isDisableAnimations);
@ -295,26 +297,20 @@ export class GlobalThemeService {
this._chartThemeService.setColorschemesOptions(overrides);
}
private _initCustomTheme(): void {
// Load initial theme
this._globalConfigService.misc$
.pipe(
take(1),
map((misc) => misc.customTheme || 'default'),
)
.subscribe((themeId) => {
this._customThemeService.loadTheme(themeId);
});
private _setupCustomThemeEffect(): void {
// Track previous theme to avoid unnecessary reloads
let previousThemeId: string | null = null;
// Watch for theme changes
this._globalConfigService.misc$
.pipe(
map((misc) => misc.customTheme || 'default'),
distinctUntilChanged(),
skip(1),
)
.subscribe((themeId) => {
// Set up effect to reactively update custom theme when config changes
effect(() => {
const misc = this._globalConfigService.misc();
const themeId = misc?.customTheme || 'default';
// Only load theme if it has changed
if (themeId !== previousThemeId) {
this._customThemeService.loadTheme(themeId);
});
previousThemeId = themeId;
}
});
}
}

View file

@ -1,5 +1,6 @@
import { Injectable, inject } from '@angular/core';
import { Injectable, inject, Signal } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { toSignal } from '@angular/core/rxjs-interop';
import { updateGlobalConfigSection } from './store/global-config.actions';
import { Observable } from 'rxjs';
import {
@ -37,7 +38,12 @@ import { distinctUntilChangedObject } from '../../util/distinct-until-changed-ob
export class GlobalConfigService {
private readonly _store = inject<Store<any>>(Store);
cfg$: Observable<GlobalConfigState>;
// Keep observables for backward compatibility
cfg$: Observable<GlobalConfigState> = this._store.pipe(
select(selectConfigFeatureState),
distinctUntilChanged(distinctUntilChangedObject),
shareReplay(1),
);
lang$: Observable<LanguageConfig> = this._store.pipe(
select(selectLanguageConfig),
@ -81,16 +87,38 @@ export class GlobalConfigService {
select(selectTimelineConfig),
);
cfg?: GlobalConfigState;
constructor() {
this.cfg$ = this._store.pipe(
select(selectConfigFeatureState),
distinctUntilChanged(distinctUntilChangedObject),
shareReplay(1),
);
this.cfg$.subscribe((cfg) => (this.cfg = cfg));
}
// Signal versions of the properties
readonly cfg: Signal<GlobalConfigState | undefined> = toSignal(this.cfg$, {
initialValue: undefined,
});
readonly lang: Signal<LanguageConfig | undefined> = toSignal(this.lang$, {
initialValue: undefined,
});
readonly misc: Signal<MiscConfig | undefined> = toSignal(this.misc$, {
initialValue: undefined,
});
readonly shortSyntax: Signal<ShortSyntaxConfig | undefined> = toSignal(
this.shortSyntax$,
{ initialValue: undefined },
);
readonly sound: Signal<SoundConfig | undefined> = toSignal(this.sound$, {
initialValue: undefined,
});
readonly evaluation: Signal<EvaluationConfig | undefined> = toSignal(this.evaluation$, {
initialValue: undefined,
});
readonly idle: Signal<IdleConfig | undefined> = toSignal(this.idle$, {
initialValue: undefined,
});
readonly sync: Signal<SyncConfig | undefined> = toSignal(this.sync$, {
initialValue: undefined,
});
readonly takeABreak: Signal<TakeABreakConfig | undefined> = toSignal(this.takeABreak$, {
initialValue: undefined,
});
readonly timelineCfg: Signal<ScheduleConfig | undefined> = toSignal(this.timelineCfg$, {
initialValue: undefined,
});
updateSection(
sectionKey: GlobalConfigSectionKey,

View file

@ -1,6 +1,7 @@
import {
ChangeDetectionStrategy,
Component,
effect,
HostBinding,
HostListener,
inject,
@ -114,9 +115,13 @@ export class FocusModeMainComponent implements OnDestroy {
private _dragEnterTarget?: HTMLElement;
constructor() {
this._globalConfigService.misc$
.pipe(takeUntil(this._onDestroy$))
.subscribe((misc) => (this.defaultTaskNotes = misc.taskNotesTpl));
// Use effect to reactively update defaultTaskNotes
effect(() => {
const misc = this._globalConfigService.misc();
if (misc) {
this.defaultTaskNotes = misc.taskNotesTpl;
}
});
this.taskService.currentTask$.pipe(takeUntil(this._onDestroy$)).subscribe((task) => {
this.task = task;
});

View file

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { filter, map, take, tap } from 'rxjs/operators';
import { filter, map, tap } from 'rxjs/operators';
import {
addProject,
moveAllProjectBacklogTasksToRegularList,
@ -37,8 +37,8 @@ export class ProjectEffects {
this._timeTrackingService.cleanupDataEverywhereForProject(id);
// we also might need to account for this unlikely but very nasty scenario
const cfg = await this._globalConfigService.cfg$.pipe(take(1)).toPromise();
if (id === cfg.misc.defaultProjectId) {
const cfg = this._globalConfigService.cfg();
if (cfg && id === cfg.misc.defaultProjectId) {
this._globalConfigService.updateSection('misc', { defaultProjectId: null });
}
}),

View file

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, inject, OnDestroy } from '@angular/core';
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import {
MAT_DIALOG_DATA,
MatDialogActions,
@ -13,7 +13,6 @@ import { DEFAULT_GLOBAL_CONFIG } from '../../config/default-global-config.const'
import { ScheduleConfig } from '../../config/global-config.model';
import { T } from '../../../t.const';
import { GlobalConfigService } from '../../config/global-config.service';
import { Subscription } from 'rxjs';
import { LS } from '../../../core/persistence/storage-keys.const';
import { TranslateModule } from '@ngx-translate/core';
@ -38,36 +37,46 @@ import { MatButton } from '@angular/material/button';
MatButton,
],
})
export class DialogTimelineSetupComponent implements OnDestroy {
export class DialogTimelineSetupComponent {
private _matDialogRef =
inject<MatDialogRef<DialogTimelineSetupComponent>>(MatDialogRef);
data = inject<{
isInfoShownInitially: boolean;
}>(MAT_DIALOG_DATA);
private _globalConfigService = inject(GlobalConfigService);
readonly globalConfigService = inject(GlobalConfigService);
T: typeof T = T;
timelineCfg: ScheduleConfig;
formGroup: UntypedFormGroup = new UntypedFormGroup({});
formConfig: FormlyFieldConfig[] = SCHEDULE_FORM_CFG.items as FormlyFieldConfig[];
private _subs = new Subscription();
constructor() {
this.timelineCfg = DEFAULT_GLOBAL_CONFIG.schedule;
this._subs.add(
this._globalConfigService.timelineCfg$.subscribe((v) => (this.timelineCfg = v)),
// Local copy for form editing
private _localTimelineCfg: ScheduleConfig;
get timelineCfg(): ScheduleConfig {
// Return local copy if it exists, otherwise use the signal value or default
return (
this._localTimelineCfg ??
this.globalConfigService.timelineCfg() ??
DEFAULT_GLOBAL_CONFIG.schedule
);
}
ngOnDestroy(): void {
this._subs.unsubscribe();
set timelineCfg(value: ScheduleConfig) {
this._localTimelineCfg = value;
}
formGroup: UntypedFormGroup = new UntypedFormGroup({});
formConfig: FormlyFieldConfig[] = SCHEDULE_FORM_CFG.items as FormlyFieldConfig[];
constructor() {
// Initialize local copy with current config value
this._localTimelineCfg =
this.globalConfigService.timelineCfg() ?? DEFAULT_GLOBAL_CONFIG.schedule;
}
saveAndClose(): void {
localStorage.setItem(LS.WAS_SCHEDULE_INITIAL_DIALOG_SHOWN, 'true');
this._matDialogRef.close();
if (this.timelineCfg) {
this._globalConfigService.updateSection('schedule', this.timelineCfg);
if (this._localTimelineCfg) {
this.globalConfigService.updateSection('schedule', this._localTimelineCfg);
}
}

View file

@ -104,7 +104,7 @@ export class ShortSyntaxEffects {
mergeMap(([{ task, originalAction }, tags, projects, defaultProjectId]) => {
const r = shortSyntax(
task,
this._globalConfigService?.cfg?.shortSyntax ||
this._globalConfigService?.cfg()?.shortSyntax ||
DEFAULT_GLOBAL_CONFIG.shortSyntax,
tags,
projects,

View file

@ -214,7 +214,7 @@ export class TaskContextMenuInnerComponent implements AfterViewInit {
if (IS_TOUCH_PRIMARY || !this.isAdvancedControls()) {
return {} as any;
}
return (this._globalConfigService.cfg?.keyboard as KeyboardConfig) || {};
return (this._globalConfigService.cfg()?.keyboard as KeyboardConfig) || {};
}
quickAccessKeydown(ev: KeyboardEvent): void {

View file

@ -220,12 +220,9 @@ export class TaskDetailPanelComponent implements OnInit, AfterViewInit, OnDestro
return [];
});
// Misc config signal
private _miscCfg = toSignal(this._globalConfigService.misc$);
// Default task notes computed signal
defaultTaskNotes = computed(() => {
const misc = this._miscCfg();
const misc = this._globalConfigService.misc();
return misc?.taskNotesTpl || '';
});

View file

@ -1057,7 +1057,7 @@ export class TaskService {
? { projectId: workContextId }
: {
projectId:
this._globalConfigService.cfg?.misc.defaultProjectId || INBOX_PROJECT.id,
this._globalConfigService.cfg()?.misc.defaultProjectId || INBOX_PROJECT.id,
}),
tagIds:

View file

@ -915,7 +915,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
if (IS_TOUCH_PRIMARY) {
return {} as any;
}
return (this._configService.cfg?.keyboard as KeyboardConfig) || {};
return (this._configService.cfg()?.keyboard as KeyboardConfig) || {};
}
private _handleKeyboardShortcuts(ev: KeyboardEvent): void {
@ -924,7 +924,7 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
}
const t = this.task();
const cfg = this._configService.cfg;
const cfg = this._configService.cfg();
if (!cfg) {
throw new Error();
}

View file

@ -252,13 +252,13 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
todayStart.setHours(0, 0, 0, 0);
this.isIncludeYesterday = Date.now() - todayStart.getTime() <= MAGIC_YESTERDAY_MARGIN;
const cfg = this.configService.cfg();
if (
this.configService.cfg?.dailySummaryNote?.txt &&
this.configService.cfg?.dailySummaryNote?.lastUpdateDayStr !==
this._dateService.todayStr()
cfg?.dailySummaryNote?.txt &&
cfg?.dailySummaryNote?.lastUpdateDayStr !== this._dateService.todayStr()
) {
this.dailySummaryNoteTxt.set(
unToggleCheckboxesInMarkdownTxt(this.configService.cfg.dailySummaryNote.txt),
unToggleCheckboxesInMarkdownTxt(cfg.dailySummaryNote.txt),
);
}
}
@ -393,7 +393,8 @@ export class DailySummaryComponent implements OnInit, OnDestroy, AfterViewInit {
}
private async _finishDayForGood(cb?: any): Promise<void> {
const syncCfg = this.configService.cfg?.sync;
const cfg = this.configService.cfg();
const syncCfg = cfg?.sync;
if (syncCfg?.isEnabled) {
await this._syncWrapperService.sync();
}

View file

@ -1,11 +1,9 @@
@let isTurnOffMarkdownParsing = isTurnOffMarkdownParsing$ | async;
<div
#wrapperEl
[class.isHideOverflow]="isHideOverflow"
class="markdown-wrapper"
>
@if (isShowEdit || isTurnOffMarkdownParsing) {
@if (isShowEdit || isTurnOffMarkdownParsing()) {
<textarea
#textareaEl
(blur)="untoggleShowEdit(); setBlur($event)"
@ -21,7 +19,7 @@
}
<!-- (focus)="clickOrFocusPreview($event, true)"-->
@if (!isTurnOffMarkdownParsing) {
@if (!isTurnOffMarkdownParsing()) {
<div
#previewEl
(click)="clickPreview($event)"
@ -38,7 +36,7 @@
<div class="controls">
<ng-content> </ng-content>
@if (isShowChecklistToggle() && !isTurnOffMarkdownParsing) {
@if (isShowChecklistToggle() && !isTurnOffMarkdownParsing()) {
<!-- -->
<button
[matTooltip]="'Add checklist item'"

View file

@ -2,6 +2,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
computed,
ElementRef,
HostBinding,
inject,
@ -15,8 +16,6 @@ import {
import { fadeInAnimation } from '../animations/fade.ani';
import { MarkdownComponent } from 'ngx-markdown';
import { IS_ELECTRON } from '../../app.constants';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { GlobalConfigService } from '../../features/config/global-config.service';
import { MatDialog } from '@angular/material/dialog';
import { DialogFullscreenMarkdownComponent } from '../dialog-fullscreen-markdown/dialog-fullscreen-markdown.component';
@ -25,7 +24,6 @@ import { FormsModule } from '@angular/forms';
import { MatIconButton } from '@angular/material/button';
import { MatTooltip } from '@angular/material/tooltip';
import { MatIcon } from '@angular/material/icon';
import { AsyncPipe } from '@angular/common';
const HIDE_OVERFLOW_TIMEOUT_DURATION = 300;
@ -35,14 +33,7 @@ const HIDE_OVERFLOW_TIMEOUT_DURATION = 300;
styleUrls: ['./inline-markdown.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [fadeInAnimation],
imports: [
FormsModule,
MarkdownComponent,
MatIconButton,
MatTooltip,
MatIcon,
AsyncPipe,
],
imports: [FormsModule, MarkdownComponent, MatIconButton, MatTooltip, MatIcon],
})
export class InlineMarkdownComponent implements OnInit, OnDestroy {
private _cd = inject(ChangeDetectorRef);
@ -67,10 +58,10 @@ export class InlineMarkdownComponent implements OnInit, OnDestroy {
isShowEdit: boolean = false;
modelCopy: string | undefined;
isTurnOffMarkdownParsing$: Observable<boolean> = this._globalConfigService.misc$.pipe(
map((cfg) => cfg.isTurnOffMarkdown),
startWith(false),
);
isTurnOffMarkdownParsing = computed(() => {
const misc = this._globalConfigService.misc();
return misc?.isTurnOffMarkdown ?? false;
});
private _hideOverFlowTimeout: number | undefined;
constructor() {