From f010cdbe703402a187b6c05fc98d6be8a14de770 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Fri, 3 Jul 2020 19:02:53 +0200 Subject: [PATCH] feat: strict null checks #5 --- src/app/app.component.ts | 4 +- src/app/core-ui/shortcut/shortcut.service.ts | 2 +- src/app/core/compression/lz.worker.ts | 1 + .../config/store/global-config.effects.ts | 4 +- src/app/features/google/google-api.service.ts | 2 +- .../issue/providers/jira/jira-api.service.ts | 2 +- .../evaluation-sheet.component.ts | 49 +++++++++---------- .../improvement-banner.component.ts | 2 +- .../planning-mode/planning-mode.service.ts | 2 +- src/app/features/tag/store/tag.effects.ts | 2 +- .../tag/tag-list/tag-list.component.ts | 14 +++--- src/app/features/tag/tag/tag.component.ts | 4 +- .../dialog-view-task-reminders.component.ts | 10 ++-- .../tasks/store/task-electron.effects.ts | 7 +-- .../tasks/store/task-internal.effects.ts | 29 ++++++----- .../tasks/store/task-related-model.effects.ts | 11 +++-- .../tasks/store/task-reminder.effects.ts | 2 +- .../task-additional-info-item.component.ts | 4 +- .../task-additional-info.component.ts | 2 +- .../task-attachment-link.directive.ts | 2 +- .../dialog-idle/dialog-idle.component.ts | 8 +-- .../features/time-tracking/idle.service.ts | 9 ++-- .../features/ui-helper/ui-helper.service.ts | 2 +- .../work-view/split/split.component.ts | 38 ++++++++------ .../imex/local-backup/local-backup.service.ts | 2 +- 25 files changed, 116 insertions(+), 98 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 4a187d8eb71..a1634b1a74b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -119,12 +119,12 @@ export class AppComponent implements OnDestroy { } if (IS_ELECTRON) { - this._electronService.ipcRenderer.send(IPC.APP_READY); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.APP_READY); this._initElectronErrorHandler(); this._uiHelperService.initElectron(); this._electronService.ipcRenderer.on(IPC.TRANSFER_SETTINGS_REQUESTED, () => { - this._electronService.ipcRenderer.send(IPC.TRANSFER_SETTINGS_TO_ELECTRON, this._configService.cfg); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.TRANSFER_SETTINGS_TO_ELECTRON, this._configService.cfg); }); } else { // WEB VERSION diff --git a/src/app/core-ui/shortcut/shortcut.service.ts b/src/app/core-ui/shortcut/shortcut.service.ts index bec53f7d1c8..99277aea126 100644 --- a/src/app/core-ui/shortcut/shortcut.service.ts +++ b/src/app/core-ui/shortcut/shortcut.service.ts @@ -129,7 +129,7 @@ export class ShortcutService { // special hidden dev tools combo to use them for production if (IS_ELECTRON) { if (checkKeyCombo(ev, 'Ctrl+Shift+J')) { - this._electronService.ipcRenderer.send('TOGGLE_DEV_TOOLS'); + (this._electronService.ipcRenderer as typeof ipcRenderer).send('TOGGLE_DEV_TOOLS'); } else if (checkKeyCombo(ev, keys.zoomIn)) { this._uiHelperService.zoomBy(0.05); } else if (checkKeyCombo(ev, keys.zoomOut)) { diff --git a/src/app/core/compression/lz.worker.ts b/src/app/core/compression/lz.worker.ts index 8e1cbe93a11..250882f5c1a 100644 --- a/src/app/core/compression/lz.worker.ts +++ b/src/app/core/compression/lz.worker.ts @@ -1,4 +1,5 @@ /// +// @ts-ignore import * as LZString from 'lz-string/libs/lz-string'; function handleData(msgData: any) { diff --git a/src/app/features/config/store/global-config.effects.ts b/src/app/features/config/store/global-config.effects.ts index 76e1de63a4e..118c8744f96 100644 --- a/src/app/features/config/store/global-config.effects.ts +++ b/src/app/features/config/store/global-config.effects.ts @@ -53,7 +53,7 @@ export class GlobalConfigEffects { filter((action: UpdateGlobalConfigSection) => IS_ELECTRON && action.payload.sectionKey === 'keyboard'), tap((action: UpdateGlobalConfigSection) => { const keyboardCfg: KeyboardConfig = action.payload.sectionCfg as KeyboardConfig; - this._electronService.ipcRenderer.send(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, keyboardCfg); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, keyboardCfg); }), ); @@ -66,7 +66,7 @@ export class GlobalConfigEffects { tap((action) => { const appDataComplete = action.appDataComplete; const keyboardCfg: KeyboardConfig = (appDataComplete.globalConfig || DEFAULT_GLOBAL_CONFIG).keyboard; - this._electronService.ipcRenderer.send(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, keyboardCfg); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.REGISTER_GLOBAL_SHORTCUTS_EVENT, keyboardCfg); }), ); diff --git a/src/app/features/google/google-api.service.ts b/src/app/features/google/google-api.service.ts index 4238ed50cd4..9410c81acd6 100644 --- a/src/app/features/google/google-api.service.ts +++ b/src/app/features/google/google-api.service.ts @@ -86,7 +86,7 @@ export class GoogleApiService { return new Promise((resolve) => resolve(true)); } - this._electronService.ipcRenderer.send(IPC.TRIGGER_GOOGLE_AUTH, session.refreshToken); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.TRIGGER_GOOGLE_AUTH, session.refreshToken); return new Promise((resolve, reject) => { this._electronService.ipcRenderer.on(IPC.GOOGLE_AUTH_TOKEN, (ev, data: any) => { this._updateSession({ diff --git a/src/app/features/issue/providers/jira/jira-api.service.ts b/src/app/features/issue/providers/jira/jira-api.service.ts index e9dc8ec78f5..a45c6c6e308 100644 --- a/src/app/features/issue/providers/jira/jira-api.service.ts +++ b/src/app/features/issue/providers/jira/jira-api.service.ts @@ -335,7 +335,7 @@ export class JiraApiService { const requestToSend = {requestId, requestInit, url}; if (this._electronService.isElectronApp) { - this._electronService.ipcRenderer.send(IPC.JIRA_MAKE_REQUEST_EVENT, {...requestToSend, jiraCfg}); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.JIRA_MAKE_REQUEST_EVENT, {...requestToSend, jiraCfg}); } else if (this._isExtension) { this._chromeExtensionInterfaceService.dispatchEvent('SP_JIRA_REQUEST', requestToSend); } diff --git a/src/app/features/metric/evaluation-sheet/evaluation-sheet.component.ts b/src/app/features/metric/evaluation-sheet/evaluation-sheet.component.ts index 936fcfef4d5..2d9a6e2c935 100644 --- a/src/app/features/metric/evaluation-sheet/evaluation-sheet.component.ts +++ b/src/app/features/metric/evaluation-sheet/evaluation-sheet.component.ts @@ -8,19 +8,18 @@ import { OnInit, Output } from '@angular/core'; -import {MetricCopy} from '../metric.model'; -import {MetricService} from '../metric.service'; -import {ObstructionService} from '../obstruction/obstruction.service'; -import {ImprovementService} from '../improvement/improvement.service'; -import {BehaviorSubject, Observable, Subscription} from 'rxjs'; -import {NoteService} from '../../note/note.service'; -import {getWorklogStr} from '../../../util/get-work-log-str'; -import {switchMap} from 'rxjs/operators'; -import {ProjectService} from '../../project/project.service'; -import {T} from '../../../t.const'; -import {DialogAddNoteComponent} from '../../note/dialog-add-note/dialog-add-note.component'; -import {MatDialog} from '@angular/material/dialog'; - +import { MetricCopy } from '../metric.model'; +import { MetricService } from '../metric.service'; +import { ObstructionService } from '../obstruction/obstruction.service'; +import { ImprovementService } from '../improvement/improvement.service'; +import { BehaviorSubject, Observable, Subscription } from 'rxjs'; +import { NoteService } from '../../note/note.service'; +import { getWorklogStr } from '../../../util/get-work-log-str'; +import { switchMap } from 'rxjs/operators'; +import { ProjectService } from '../../project/project.service'; +import { T } from '../../../t.const'; +import { DialogAddNoteComponent } from '../../note/dialog-add-note/dialog-add-note.component'; +import { MatDialog } from '@angular/material/dialog'; @Component({ selector: 'evaluation-sheet', @@ -31,7 +30,7 @@ import {MatDialog} from '@angular/material/dialog'; export class EvaluationSheetComponent implements OnDestroy, OnInit { @Output() save: EventEmitter = new EventEmitter(); T: any = T; - metricForDay: MetricCopy; + metricForDay?: MetricCopy; day$: BehaviorSubject = new BehaviorSubject(getWorklogStr()); private _metricForDay$: Observable = this.day$.pipe( switchMap((day) => this._metricService.getMetricForDayOrDefaultWithCheckedImprovements$(day)), @@ -74,43 +73,43 @@ export class EvaluationSheetComponent implements OnDestroy, OnInit { } addObstruction(v: string) { - this._update({obstructions: [...this.metricForDay.obstructions, v]}); + this._update({obstructions: [...(this.metricForDay as MetricCopy).obstructions, v]}); } addNewObstruction(v: string) { const id = this.obstructionService.addObstruction(v); - this._update({obstructions: [...this.metricForDay.obstructions, id]}); + this._update({obstructions: [...(this.metricForDay as MetricCopy).obstructions, id]}); } removeObstruction(idToRemove: string) { - this._update({obstructions: this.metricForDay.obstructions.filter(id => id !== idToRemove)}); + this._update({obstructions: (this.metricForDay as MetricCopy).obstructions.filter(id => id !== idToRemove)}); } addImprovement(v: string) { - this._update({improvements: [...this.metricForDay.improvements, v]}); + this._update({improvements: [...(this.metricForDay as MetricCopy).improvements, v]}); } addNewImprovement(v: string) { const id = this.improvementService.addImprovement(v); - this._update({improvements: [...this.metricForDay.improvements, id]}); + this._update({improvements: [...(this.metricForDay as MetricCopy).improvements, id]}); } removeImprovement(idToRemove: string) { - this._update({improvements: this.metricForDay.improvements.filter(id => id !== idToRemove)}); + this._update({improvements: (this.metricForDay as MetricCopy).improvements.filter(id => id !== idToRemove)}); } addImprovementTomorrow(v: string) { - this._update({improvementsTomorrow: [...this.metricForDay.improvementsTomorrow, v]}); + this._update({improvementsTomorrow: [...(this.metricForDay as MetricCopy).improvementsTomorrow, v]}); } addNewImprovementTomorrow(v: string) { const id = this.improvementService.addImprovement(v); - this._update({improvementsTomorrow: [...this.metricForDay.improvementsTomorrow, id]}); + this._update({improvementsTomorrow: [...(this.metricForDay as MetricCopy).improvementsTomorrow, id]}); } removeImprovementTomorrow(idToRemove: string) { this._update({ - improvementsTomorrow: this.metricForDay.improvementsTomorrow.filter(id => id !== idToRemove), + improvementsTomorrow: (this.metricForDay as MetricCopy).improvementsTomorrow.filter(id => id !== idToRemove), }); // this.improvementService.disableImprovementRepeat(idToRemove); } @@ -125,9 +124,9 @@ export class EvaluationSheetComponent implements OnDestroy, OnInit { private _update(updateData: Partial) { this.metricForDay = { - ...this.metricForDay, + ...(this.metricForDay as MetricCopy), ...updateData, } as MetricCopy; - this._metricService.upsertMetric(this.metricForDay); + this._metricService.upsertMetric((this.metricForDay as MetricCopy)); } } diff --git a/src/app/features/metric/improvement-banner/improvement-banner.component.ts b/src/app/features/metric/improvement-banner/improvement-banner.component.ts index 16e0311e472..710eccc6db6 100644 --- a/src/app/features/metric/improvement-banner/improvement-banner.component.ts +++ b/src/app/features/metric/improvement-banner/improvement-banner.component.ts @@ -15,7 +15,7 @@ import { T } from '../../../t.const'; }) export class ImprovementBannerComponent implements OnDestroy { T: any = T; - improvements: Improvement[]; + improvements?: Improvement[]; private _subs: Subscription = new Subscription(); diff --git a/src/app/features/planning-mode/planning-mode.service.ts b/src/app/features/planning-mode/planning-mode.service.ts index 73fd9a2d292..764c92fcd62 100644 --- a/src/app/features/planning-mode/planning-mode.service.ts +++ b/src/app/features/planning-mode/planning-mode.service.ts @@ -6,7 +6,7 @@ import { WorkContextService } from '../work-context/work-context.service'; @Injectable({providedIn: 'root'}) export class PlanningModeService { private _iPlanningModeEndedUser$: BehaviorSubject = new BehaviorSubject(false); - private _manualTriggerCheck$: BehaviorSubject = new BehaviorSubject(null); + private _manualTriggerCheck$: BehaviorSubject = new BehaviorSubject(null); private _triggerCheck$: Observable = merge( this._manualTriggerCheck$, // TODO fix hacky way of waiting for data to be loaded diff --git a/src/app/features/tag/store/tag.effects.ts b/src/app/features/tag/store/tag.effects.ts index c34b9abb536..21d5c31a731 100644 --- a/src/app/features/tag/store/tag.effects.ts +++ b/src/app/features/tag/store/tag.effects.ts @@ -182,7 +182,7 @@ export class TagEffects { // remove orphaned for archive const taskArchiveState: TaskArchive = await this._persistenceService.taskArchive.loadState() || createEmptyEntity(); const archiveTaskIdsToDelete = (taskArchiveState.ids as string[]).filter((id) => { - const t = taskArchiveState.entities[id]; + const t = taskArchiveState.entities[id] as Task; return isOrphanedParentTask(t); }); await this._persistenceService.taskArchive.execAction(new DeleteMainTasks({taskIds: archiveTaskIdsToDelete})); diff --git a/src/app/features/tag/tag-list/tag-list.component.ts b/src/app/features/tag/tag-list/tag-list.component.ts index 5481979aae8..ad901157d17 100644 --- a/src/app/features/tag/tag-list/tag-list.component.ts +++ b/src/app/features/tag/tag-list/tag-list.component.ts @@ -25,17 +25,17 @@ export class TagListComponent implements OnDestroy { @Output() addedTagsToTask: EventEmitter = new EventEmitter(); @Output() removedTagsFromTask: EventEmitter = new EventEmitter(); @Output() replacedTagForTask: EventEmitter = new EventEmitter(); - projectTag: TagComponentTag; - tags: Tag[]; + projectTag?: TagComponentTag | null; + tags?: Tag[]; private _isShowProjectTagAlways$: BehaviorSubject = new BehaviorSubject(false); - private _projectId$: BehaviorSubject = new BehaviorSubject(null); - projectTag$: Observable = combineLatest([ + private _projectId$: BehaviorSubject = new BehaviorSubject(null); + projectTag$: Observable = combineLatest([ this._workContextService.activeWorkContextTypeAndId$, this._isShowProjectTagAlways$ ]).pipe( switchMap(([{activeType}, isShowAlways]) => isShowAlways || (activeType === WorkContextType.TAG) ? this._projectId$.pipe( - switchMap(id => this._projectService.getByIdOnce$(id)), + switchMap(id => this._projectService.getByIdOnce$(id as string)), map(project => (project && { ...project, icon: 'list' @@ -44,7 +44,7 @@ export class TagListComponent implements OnDestroy { : of(null) ), ); - private _tagIds$: BehaviorSubject = new BehaviorSubject([]); + private _tagIds$: BehaviorSubject = new BehaviorSubject([]); tags$: Observable = combineLatest([ this._tagIds$, this._workContextService.activeWorkContextId$, @@ -71,7 +71,7 @@ export class TagListComponent implements OnDestroy { // NOTE: should normally be enough - private _task: Task; + private _task?: Task; @Input() set task(task: Task) { this._task = task; diff --git a/src/app/features/tag/tag/tag.component.ts b/src/app/features/tag/tag/tag.component.ts index fea3eb7c267..e0705a413f5 100644 --- a/src/app/features/tag/tag/tag.component.ts +++ b/src/app/features/tag/tag/tag.component.ts @@ -14,9 +14,9 @@ export interface TagComponentTag { changeDetection: ChangeDetectionStrategy.OnPush }) export class TagComponent { - tag: TagComponentTag; + tag?: TagComponentTag; // @HostBinding('style.background') - color: string; + color?: string; constructor() { } diff --git a/src/app/features/tasks/dialog-view-task-reminders/dialog-view-task-reminders.component.ts b/src/app/features/tasks/dialog-view-task-reminders/dialog-view-task-reminders.component.ts index bd572d4fa37..8aa337d78cd 100644 --- a/src/app/features/tasks/dialog-view-task-reminders/dialog-view-task-reminders.component.ts +++ b/src/app/features/tasks/dialog-view-task-reminders/dialog-view-task-reminders.component.ts @@ -36,9 +36,9 @@ export class DialogViewTaskRemindersComponent implements OnDestroy { first(), map((tasks: Task[]) => tasks .filter(task => !!task) - .map((task) => ({ + .map((task): TaskWithReminderData => ({ ...task, - reminderData: reminders.find(r => r.relatedId === task.id) + reminderData: reminders.find(r => r.relatedId === task.id) as Reminder })) ) )), @@ -87,14 +87,14 @@ export class DialogViewTaskRemindersComponent implements OnDestroy { reminderId: null, }); this._reminderService.removeReminder(task.reminderData.id); - this._removeFromList(task.reminderId); + this._removeFromList(task.reminderId as string); } snooze(task: TaskWithReminderData, snoozeInMinutes: number) { this._reminderService.updateReminder(task.reminderData.id, { remindAt: Date.now() + (snoozeInMinutes * M) }); - this._removeFromList(task.reminderId); + this._removeFromList(task.reminderId as string); } editReminder(task: TaskWithReminderData, isCloseAfter: boolean = false) { @@ -102,7 +102,7 @@ export class DialogViewTaskRemindersComponent implements OnDestroy { restoreFocus: true, data: {task} as AddTaskReminderInterface }).afterClosed().subscribe(() => { - this._removeFromList(task.reminderId); + this._removeFromList(task.reminderId as string); if (isCloseAfter) { this._close(); } diff --git a/src/app/features/tasks/store/task-electron.effects.ts b/src/app/features/tasks/store/task-electron.effects.ts index 125b6cda56a..bc0f51aeae0 100644 --- a/src/app/features/tasks/store/task-electron.effects.ts +++ b/src/app/features/tasks/store/task-electron.effects.ts @@ -12,6 +12,7 @@ import { IS_ELECTRON } from '../../../app.constants'; import { GlobalConfigState } from '../../config/global-config.model'; import { GlobalConfigService } from '../../config/global-config.service'; import { ElectronService } from '../../../core/electron/electron.service'; +import { ipcRenderer } from 'electron'; // TODO send message to electron when current task changes here @@ -25,7 +26,7 @@ export class TaskElectronEffects { withLatestFrom(this._store$.pipe(select(selectCurrentTask))), tap(([action, current]) => { if (IS_ELECTRON) { - this._electronService.ipcRenderer.send(IPC.CURRENT_TASK_UPDATED, {current}); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.CURRENT_TASK_UPDATED, {current}); } }) ); @@ -38,7 +39,7 @@ export class TaskElectronEffects { filter(() => IS_ELECTRON), tap((act: SetCurrentTask) => { if (!act.payload) { - this._electronService.ipcRenderer.send(IPC.SET_PROGRESS_BAR, {progress: 0}); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.SET_PROGRESS_BAR, {progress: 0}); } }), ); @@ -55,7 +56,7 @@ export class TaskElectronEffects { map(([act]) => act.payload.task), tap((task: Task) => { const progress = task.timeSpent / task.timeEstimate; - this._electronService.ipcRenderer.send(IPC.SET_PROGRESS_BAR, {progress}); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.SET_PROGRESS_BAR, {progress}); }), ); diff --git a/src/app/features/tasks/store/task-internal.effects.ts b/src/app/features/tasks/store/task-internal.effects.ts index f615b5c58ac..92ca6efa75c 100644 --- a/src/app/features/tasks/store/task-internal.effects.ts +++ b/src/app/features/tasks/store/task-internal.effects.ts @@ -5,7 +5,7 @@ import { select, Store } from '@ngrx/store'; import { filter, map, mergeMap, withLatestFrom } from 'rxjs/operators'; import { selectTaskFeatureState } from './task.selectors'; import { selectMiscConfig } from '../../config/store/global-config.reducer'; -import { TaskState } from '../task.model'; +import { Task, TaskState } from '../task.model'; import { EMPTY, of } from 'rxjs'; import { MiscConfig } from '../../config/global-config.model'; import { moveTaskToBacklogList, moveTaskToBacklogListAuto } from '../../work-context/store/work-context-meta.actions'; @@ -23,19 +23,22 @@ export class TaskInternalEffects { this._store$.pipe(select(selectTaskFeatureState)) ), filter(([action, miscCfg, state]: [UpdateTask, MiscConfig, TaskState]) => - miscCfg && miscCfg.isAutMarkParentAsDone && - action.payload.task.changes.isDone && + !!miscCfg && miscCfg.isAutMarkParentAsDone && !!action.payload.task.changes.isDone && + // @ts-ignore !!(state.entities[action.payload.task.id].parentId) ), filter(([action, miscCfg, state]) => { const task = state.entities[action.payload.task.id]; - const parent = state.entities[task.parentId]; - const undoneSubTasks = parent.subTaskIds.filter(id => !state.entities[id].isDone); + if (!task || !task.parentId) { + throw new Error('!task || !task.parentId'); + } + const parent = state.entities[task.parentId] as Task; + const undoneSubTasks = parent.subTaskIds.filter(id => !(state.entities[id] as Task).isDone); return undoneSubTasks.length === 0; }), map(([action, miscCfg, state]) => new UpdateTask({ task: { - id: state.entities[action.payload.task.id].parentId, + id: (state.entities[action.payload.task.id] as Task).parentId as string, changes: {isDone: true}, } })), @@ -100,6 +103,8 @@ export class TaskInternalEffects { nextId = state.currentTaskId; break; } + default: + nextId = null; // NOTE: currently no solution for this, but we're probably fine, as the current task // gets unset every time we go to the finish day view @@ -129,18 +134,18 @@ export class TaskInternalEffects { let nextId = null; const {entities} = state; - const filterUndoneNotCurrent = (id) => !entities[id].isDone && id !== oldCurrentId; + const filterUndoneNotCurrent = (id: string) => !(entities[id] as Task).isDone && id !== oldCurrentId; const flattenToSelectable = (arr: string[]) => arr.reduce((acc: string[], next: string) => { - return entities[next].subTaskIds.length > 0 - ? acc.concat(entities[next].subTaskIds) + return (entities[next] as Task).subTaskIds.length > 0 + ? acc.concat((entities[next] as Task).subTaskIds) : acc.concat(next); }, []); if (oldCurrentId) { const oldCurTask = entities[oldCurrentId]; if (oldCurTask && oldCurTask.parentId) { - entities[oldCurTask.parentId].subTaskIds.some((id) => { - return (id !== oldCurrentId && entities[id].isDone === false) + (entities[oldCurTask.parentId] as Task).subTaskIds.some((id) => { + return (id !== oldCurrentId && !(entities[id] as Task).isDone) ? (nextId = id) && true // assign !!! : false; }); @@ -158,7 +163,7 @@ export class TaskInternalEffects { } } else { - const lastTask = entities[state.lastCurrentTaskId]; + const lastTask = state.lastCurrentTaskId && entities[state.lastCurrentTaskId]; const isLastSelectable = state.lastCurrentTaskId && lastTask && !lastTask.isDone && !lastTask.subTaskIds.length; if (isLastSelectable) { nextId = state.lastCurrentTaskId; diff --git a/src/app/features/tasks/store/task-related-model.effects.ts b/src/app/features/tasks/store/task-related-model.effects.ts index 06892985d85..843a7b9b4c3 100644 --- a/src/app/features/tasks/store/task-related-model.effects.ts +++ b/src/app/features/tasks/store/task-related-model.effects.ts @@ -134,10 +134,10 @@ export class TaskRelatedModelEffects { task: act.payload.task, })) )), - filter(({defaultProjectId, task}) => defaultProjectId && !task.projectId && !task.parentId), + filter(({defaultProjectId, task}) => !!defaultProjectId && !task.projectId && !task.parentId), map(({task, defaultProjectId}) => new MoveToOtherProject({ - task, - targetProjectId: defaultProjectId, + task: task as TaskWithSubTasks, + targetProjectId: defaultProjectId as string, })), ); @@ -162,7 +162,7 @@ export class TaskRelatedModelEffects { const taskIds = [task.id, ...task.subTaskIds]; const currentArchive: TaskArchive = await this._persistenceService.taskArchive.loadState() || createEmptyEntity(); const allIds = currentArchive.ids as string[] || []; - const idsToRemove = []; + const idsToRemove: string[] = []; taskIds.forEach((taskId) => { if (allIds.indexOf(taskId) > -1) { @@ -194,6 +194,9 @@ export class TaskRelatedModelEffects { flatTasks .filter(t => !!t.reminderId) .forEach(t => { + if (!t.reminderId) { + throw new Error('No t.reminderId'); + } this._reminderService.removeReminder(t.reminderId); }); diff --git a/src/app/features/tasks/store/task-reminder.effects.ts b/src/app/features/tasks/store/task-reminder.effects.ts index 9050c95e661..fc3a853c559 100644 --- a/src/app/features/tasks/store/task-reminder.effects.ts +++ b/src/app/features/tasks/store/task-reminder.effects.ts @@ -51,7 +51,7 @@ export class TaskReminderEffects { ...(isMoveToBacklog ? [moveTaskToBacklogListAuto({ taskId: task.id, - workContextId: task.projectId + workContextId: task.projectId as string })] : [] ), diff --git a/src/app/features/tasks/task-additional-info/task-additional-info-item/task-additional-info-item.component.ts b/src/app/features/tasks/task-additional-info/task-additional-info-item/task-additional-info-item.component.ts index 13c2c87a5b4..39bbc224959 100644 --- a/src/app/features/tasks/task-additional-info/task-additional-info-item/task-additional-info-item.component.ts +++ b/src/app/features/tasks/task-additional-info/task-additional-info-item/task-additional-info-item.component.ts @@ -17,8 +17,8 @@ import { }) export class TaskAdditionalInfoItemComponent { @Input() type: 'input' | 'panel' = 'input'; - @Input() expanded: boolean; - @Input() inputIcon: string; + @Input() expanded?: boolean = false; + @Input() inputIcon?: string; @Output() collapseParent: EventEmitter = new EventEmitter(); @Output() keyPress: EventEmitter = new EventEmitter(); diff --git a/src/app/features/tasks/task-additional-info/task-additional-info.component.ts b/src/app/features/tasks/task-additional-info/task-additional-info.component.ts index 5f1b83b7f53..1272fb54f55 100644 --- a/src/app/features/tasks/task-additional-info/task-additional-info.component.ts +++ b/src/app/features/tasks/task-additional-info/task-additional-info.component.ts @@ -164,7 +164,7 @@ export class TaskAdditionalInfoComponent implements AfterViewInit, OnDestroy { switchMap(() => this._projectService.getJiraCfgForProject$(this._taskData.projectId)) ).subscribe((jiraCfg) => { if (jiraCfg.isEnabled) { - this._electronService.ipcRenderer.send(IPC.JIRA_SETUP_IMG_HEADERS, jiraCfg); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.JIRA_SETUP_IMG_HEADERS, jiraCfg); } })); } diff --git a/src/app/features/tasks/task-attachment/task-attachment-link/task-attachment-link.directive.ts b/src/app/features/tasks/task-attachment/task-attachment-link/task-attachment-link.directive.ts index 20ac4a5cd59..69b27e684b8 100644 --- a/src/app/features/tasks/task-attachment/task-attachment-link/task-attachment-link.directive.ts +++ b/src/app/features/tasks/task-attachment/task-attachment-link/task-attachment-link.directive.ts @@ -63,6 +63,6 @@ export class TaskAttachmentLinkDirective { } private _exec(command: string) { - this._electronService.ipcRenderer.send(IPC.EXEC, command); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.EXEC, command); } } diff --git a/src/app/features/time-tracking/dialog-idle/dialog-idle.component.ts b/src/app/features/time-tracking/dialog-idle/dialog-idle.component.ts index dc6be396308..17ec7cbf255 100644 --- a/src/app/features/time-tracking/dialog-idle/dialog-idle.component.ts +++ b/src/app/features/time-tracking/dialog-idle/dialog-idle.component.ts @@ -16,9 +16,9 @@ import { T } from '../../../t.const'; export class DialogIdleComponent implements OnInit { T: any = T; lastCurrentTask$: Observable = this._taskService.getByIdOnce$(this.data.lastCurrentTaskId); - selectedTask: Task; - newTaskTitle: string; - isCreate: boolean; + selectedTask: Task | null = null; + newTaskTitle?: string; + isCreate?: boolean; constructor( public configService: GlobalConfigService, @@ -44,7 +44,7 @@ export class DialogIdleComponent implements OnInit { this.selectedTask = null; } else { this.selectedTask = taskOrTaskTitle as Task; - this.newTaskTitle = null; + this.newTaskTitle = undefined; } } diff --git a/src/app/features/time-tracking/idle.service.ts b/src/app/features/time-tracking/idle.service.ts index 1a6536d04a7..96745937461 100644 --- a/src/app/features/time-tracking/idle.service.ts +++ b/src/app/features/time-tracking/idle.service.ts @@ -13,6 +13,7 @@ import { distinctUntilChanged, shareReplay } from 'rxjs/operators'; import { ElectronService } from '../../core/electron/electron.service'; import { UiHelperService } from '../ui-helper/ui-helper.service'; import { WorkContextService } from '../work-context/work-context.service'; +import { ipcRenderer } from 'electron'; const DEFAULT_MIN_IDLE_TIME = 60000; const IDLE_POLL_INTERVAL = 1000; @@ -33,9 +34,9 @@ export class IdleService { private _triggerResetBreakTimer$: Subject = new Subject(); triggerResetBreakTimer$: Observable = this._triggerResetBreakTimer$.asObservable(); - private lastCurrentTaskId: string; + private lastCurrentTaskId?: string | null; private isIdleDialogOpen: boolean = false; - private idlePollInterval: number; + private idlePollInterval?: number; constructor( private _chromeExtensionInterfaceService: ChromeExtensionInterfaceService, @@ -50,12 +51,12 @@ export class IdleService { init() { if (IS_ELECTRON) { - this._electronService.ipcRenderer.on(IPC.IDLE_TIME, (ev, idleTimeInMs) => { + (this._electronService.ipcRenderer as typeof ipcRenderer).on(IPC.IDLE_TIME, (ev, idleTimeInMs) => { this.handleIdle(idleTimeInMs); }); } this._chromeExtensionInterfaceService.onReady$.subscribe(() => { - this._chromeExtensionInterfaceService.addEventListener(IPC.IDLE_TIME, (ev, idleTimeInMs) => { + this._chromeExtensionInterfaceService.addEventListener(IPC.IDLE_TIME, (ev: any, idleTimeInMs: number) => { this.handleIdle(idleTimeInMs); }); }); diff --git a/src/app/features/ui-helper/ui-helper.service.ts b/src/app/features/ui-helper/ui-helper.service.ts index 33ae23da948..f8c07cdee70 100644 --- a/src/app/features/ui-helper/ui-helper.service.ts +++ b/src/app/features/ui-helper/ui-helper.service.ts @@ -55,7 +55,7 @@ export class UiHelperService { (document.activeElement as HTMLElement).blur(); } - this._electronService.ipcRenderer.send(IPC.SHOW_OR_FOCUS); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.SHOW_OR_FOCUS); } else { console.error('Cannot execute focus app window in browser'); } diff --git a/src/app/features/work-view/split/split.component.ts b/src/app/features/work-view/split/split.component.ts index 7ab984e7c95..6116d5be901 100644 --- a/src/app/features/work-view/split/split.component.ts +++ b/src/app/features/work-view/split/split.component.ts @@ -21,16 +21,16 @@ const ANIMATABLE_CLASS = 'isAnimatable'; changeDetection: ChangeDetectionStrategy.OnPush }) export class SplitComponent implements AfterViewInit { - @Input() splitTopEl: ElementRef; - @Input() splitBottomEl: ElementRef; - @Input() containerEl: HTMLElement; - @Input() counter: ElementRef; - @Input() isAnimateBtn: boolean; + @Input() splitTopEl?: ElementRef; + @Input() splitBottomEl?: ElementRef; + @Input() containerEl?: HTMLElement; + @Input() counter?: ElementRef; + @Input() isAnimateBtn?: boolean; @Output() posChanged: EventEmitter = new EventEmitter(); - pos: number; - eventSubs: Subscription; - @ViewChild('buttonEl', {static: true}) buttonEl: ElementRef; + pos?: number; + eventSubs?: Subscription; + @ViewChild('buttonEl', {static: true}) buttonEl?: ElementRef; private _isDrag: boolean = false; private _isViewInitialized: boolean = false; @@ -59,7 +59,7 @@ export class SplitComponent implements AfterViewInit { this._renderer.addClass(this.splitTopEl, ANIMATABLE_CLASS); this._renderer.addClass(this.splitBottomEl, ANIMATABLE_CLASS); let newPos = 50; - if (this.pos > 45 && this.pos < 55) { + if (typeof this.pos !== 'number' || this.pos > 45 && this.pos < 55) { newPos = 100; } this._updatePos(newPos); @@ -68,11 +68,11 @@ export class SplitComponent implements AfterViewInit { onTouchStart() { this._isDrag = false; const touchend$ = fromEvent(document, 'touchend'); - this.eventSubs = touchend$.subscribe((e: TouchEvent) => this.onMoveEnd(e)); + this.eventSubs = touchend$.subscribe(() => this.onMoveEnd()); const touchmove$ = fromEvent(document, 'touchmove') .pipe(takeUntil(touchend$)) - .subscribe((e: TouchEvent) => this.onMove(e)); + .subscribe((e: Event) => this.onMove(e as TouchEvent)); this.eventSubs.add(touchmove$); } @@ -80,16 +80,16 @@ export class SplitComponent implements AfterViewInit { onMouseDown() { this._isDrag = false; const mouseup$ = fromEvent(document, 'mouseup'); - this.eventSubs = mouseup$.subscribe((e: MouseEvent) => this.onMoveEnd(e)); + this.eventSubs = mouseup$.subscribe(() => this.onMoveEnd()); const mousemove$ = fromEvent(document, 'mousemove') .pipe(takeUntil(mouseup$)) - .subscribe((e: MouseEvent) => this.onMove(e)); + .subscribe((e: Event) => this.onMove(e as MouseEvent)); this.eventSubs.add(mousemove$); } - onMoveEnd(ev: TouchEvent | MouseEvent): void { + onMoveEnd(): void { if (this.eventSubs) { this.eventSubs.unsubscribe(); this.eventSubs = undefined; @@ -101,6 +101,10 @@ export class SplitComponent implements AfterViewInit { } onMove(ev: TouchEvent | MouseEvent) { + if (!this.containerEl) { + throw new Error('No container el'); + } + const clientY = (typeof (ev as MouseEvent).clientY === 'number') ? (ev as MouseEvent).clientY : (ev as TouchEvent).touches[0].clientY; @@ -121,7 +125,11 @@ export class SplitComponent implements AfterViewInit { this._updatePos(percentage); } - private _updatePos(pos: number, isWasOutsideChange: boolean = false) { + private _updatePos(pos: number | undefined, isWasOutsideChange: boolean = false) { + if (typeof pos !== 'number') { + throw new Error('Invalid pos'); + } + this.pos = pos; if (this.splitTopEl && this.splitBottomEl) { this._renderer.setStyle( diff --git a/src/app/imex/local-backup/local-backup.service.ts b/src/app/imex/local-backup/local-backup.service.ts index 912516af9cc..7c9f53cdbb1 100644 --- a/src/app/imex/local-backup/local-backup.service.ts +++ b/src/app/imex/local-backup/local-backup.service.ts @@ -33,6 +33,6 @@ export class LocalBackupService { private async _backup() { const data = await this._dataImportService.getCompleteSyncData(); - this._electronService.ipcRenderer.send(IPC.BACKUP, data); + (this._electronService.ipcRenderer as typeof ipcRenderer).send(IPC.BACKUP, data); } }