fix: error when using global add task shortcut

Closes #4859
This commit is contained in:
Johannes Millan 2025-07-28 19:35:37 +02:00
parent f2e959adeb
commit fb7f925e92
7 changed files with 127 additions and 12 deletions

View file

@ -155,7 +155,7 @@ export const initIpcInterfaces = (): void => {
actionFn = () => {
showOrFocus(mainWin);
// NOTE: delay slightly to make sure app is ready
mainWin.webContents.send(IPC.ADD_TASK);
mainWin.webContents.send(IPC.SHOW_ADD_TASK_BAR);
};
break;

View file

@ -40,7 +40,7 @@ export const processProtocolUrl = (url: string, mainWin: BrowserWindow | null):
// Send IPC message to create task
if (mainWin && mainWin.webContents) {
mainWin.webContents.send(IPC.ADD_TASK, { title: taskTitle });
mainWin.webContents.send(IPC.ADD_TASK_FROM_APP_URI, { title: taskTitle });
}
}
break;

View file

@ -22,7 +22,8 @@ export enum IPC {
TASK_MARK_AS_DONE = 'TASK_MARK_AS_DONE',
TASK_START = 'TASK_START',
TASK_TOGGLE_START = 'TASK_TOGGLE_START',
ADD_TASK = 'ADD_TASK',
SHOW_ADD_TASK_BAR = 'SHOW_ADD_TASK_BAR',
ADD_TASK_FROM_APP_URI = 'ADD_TASK_FROM_APP_URI',
ADD_NOTE = 'ADD_NOTE',
TASK_PAUSE = 'TASK_PAUSE',

View file

@ -57,7 +57,7 @@ export class ShortcutService {
window.ea.on(IPC.TASK_TOGGLE_START, () => {
this._taskService.toggleStartTask();
});
window.ea.on(IPC.ADD_TASK, () => {
window.ea.on(IPC.SHOW_ADD_TASK_BAR, () => {
this._layoutService.showAddTaskBar();
});
window.ea.on(IPC.ADD_NOTE, () => {

View file

@ -25,6 +25,12 @@ export const ipcSuspend$: Observable<unknown> = IS_ELECTRON
? ipcEvent$(IPC.SUSPEND).pipe()
: EMPTY;
export const ipcAddTask$: Observable<{ title: string }> = IS_ELECTRON
? ipcEvent$(IPC.ADD_TASK).pipe(map(([ev, data]: any) => data as { title: string }))
export const ipcShowAddTaskBar$: Observable<unknown> = IS_ELECTRON
? ipcEvent$(IPC.SHOW_ADD_TASK_BAR).pipe()
: EMPTY;
export const ipcAddTaskFromAppUri$: Observable<{ title: string }> = IS_ELECTRON
? ipcEvent$(IPC.ADD_TASK_FROM_APP_URI).pipe(
map(([ev, data]: any) => data as { title: string }),
)
: EMPTY;

View file

@ -0,0 +1,110 @@
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Observable, of, Subject } from 'rxjs';
import { TaskElectronEffects } from './task-electron.effects';
import { TaskService } from '../task.service';
import { provideMockStore } from '@ngrx/store/testing';
import { GlobalConfigService } from '../../config/global-config.service';
import { PomodoroService } from '../../pomodoro/pomodoro.service';
import { FocusModeService } from '../../focus-mode/focus-mode.service';
import { tap } from 'rxjs/operators';
describe('TaskElectronEffects', () => {
let effects: TaskElectronEffects;
let actions$: Observable<any>;
let taskService: jasmine.SpyObj<TaskService>;
let mockIpcAddTaskFromAppUri$: Subject<{ title: string }>;
beforeEach(() => {
const taskServiceSpy = jasmine.createSpyObj('TaskService', ['add']);
const globalConfigServiceSpy = jasmine.createSpyObj('GlobalConfigService', [], {
cfg$: of({}),
});
const pomodoroServiceSpy = jasmine.createSpyObj('PomodoroService', [], {
isEnabled$: of(false),
currentSessionTime$: of(0),
});
const focusModeServiceSpy = jasmine.createSpyObj('FocusModeService', [], {
currentSessionTime$: of(0),
});
// Mock window.ea
(window as any).ea = {
on: jasmine.createSpy('on'),
updateCurrentTask: jasmine.createSpy('updateCurrentTask'),
setProgressBar: jasmine.createSpy('setProgressBar'),
};
actions$ = new Subject<any>();
mockIpcAddTaskFromAppUri$ = new Subject<{ title: string }>();
TestBed.configureTestingModule({
providers: [
{
provide: TaskElectronEffects,
useFactory: (
taskServiceInj: TaskService,
// Other dependencies could be injected here if needed
) => {
const effectsInstance = new TaskElectronEffects();
// Manually inject dependencies that are used in the effect
(effectsInstance as any)._taskService = taskServiceInj;
// Override the effect with our mock observable
effectsInstance.handleAddTaskFromProtocol$ = mockIpcAddTaskFromAppUri$.pipe(
tap((data) => {
taskServiceInj.add(data.title);
}),
) as any;
return effectsInstance;
},
deps: [TaskService],
},
provideMockActions(() => actions$),
provideMockStore(),
{ provide: TaskService, useValue: taskServiceSpy },
{ provide: GlobalConfigService, useValue: globalConfigServiceSpy },
{ provide: PomodoroService, useValue: pomodoroServiceSpy },
{ provide: FocusModeService, useValue: focusModeServiceSpy },
],
});
effects = TestBed.inject(TaskElectronEffects);
taskService = TestBed.inject(TaskService) as jasmine.SpyObj<TaskService>;
});
describe('handleAddTaskFromProtocol$', () => {
it('should add task when receiving data with title', (done) => {
const mockData = { title: 'Test Task' };
// Subscribe to the effect
effects.handleAddTaskFromProtocol$.subscribe(() => {
expect(taskService.add).toHaveBeenCalledWith('Test Task');
done();
});
// Emit data through the mocked observable
mockIpcAddTaskFromAppUri$.next(mockData);
});
it('should handle multiple tasks', (done) => {
let callCount = 0;
const expectedCalls = 2;
effects.handleAddTaskFromProtocol$.subscribe(() => {
callCount++;
if (callCount === expectedCalls) {
expect(taskService.add).toHaveBeenCalledTimes(2);
expect(taskService.add).toHaveBeenCalledWith('Task 1');
expect(taskService.add).toHaveBeenCalledWith('Task 2');
done();
}
});
// Emit multiple tasks
mockIpcAddTaskFromAppUri$.next({ title: 'Task 1' });
mockIpcAddTaskFromAppUri$.next({ title: 'Task 2' });
});
});
});

View file

@ -19,7 +19,7 @@ import {
unPauseFocusSession,
} from '../../focus-mode/store/focus-mode.actions';
import { IPC } from '../../../../../electron/shared-with-frontend/ipc-events.const';
import { ipcAddTask$ } from '../../../core/ipc-events';
import { ipcAddTaskFromAppUri$ } from '../../../core/ipc-events';
import { TaskService } from '../task.service';
// TODO send message to electron when current task changes here
@ -159,11 +159,9 @@ export class TaskElectronEffects {
handleAddTaskFromProtocol$ = createEffect(
() =>
ipcAddTask$.pipe(
tap(({ title }) => {
if (title) {
this._taskService.add(title);
}
ipcAddTaskFromAppUri$.pipe(
tap((data) => {
this._taskService.add(data.title);
}),
),
{ dispatch: false },