import { IssueProviderKey } from '../issue/issue.model'; import { EntityState } from '@ngrx/entity'; import { TaskAttachment } from './task-attachment/task-attachment.model'; // Import the unified Task type from plugin-api import { Task as PluginTask } from '@super-productivity/plugin-api'; export enum HideSubTasksMode { // Show is undefined HideDone = 1, HideAll = 2, } export enum TaskDetailTargetPanel { Default = 'Default', Attachments = 'Attachments', Notes = 'Notes', DONT_OPEN_PANEL = 'DONT_OPEN_PANEL', } export type DropListModelSource = | 'UNDONE' | 'DONE' | 'BACKLOG' | 'ADD_TASK_PANEL' | 'OVERDUE' | 'LATER_TODAY'; // NOTE: do not change these, as they are used inside task repeat model directly // (new can be added though) export enum TaskReminderOptionId { DoNotRemind = 'DoNotRemind', AtStart = 'AtStart', m5 = 'm5', m10 = 'm10', m15 = 'm15', m30 = 'm30', h1 = 'h1', } export interface TaskReminderOption { value: TaskReminderOptionId; label: string; } export interface TimeSpentOnDayCopy { [key: string]: number; } export interface TaskArchive extends EntityState { ids: string[]; } export type TimeSpentOnDay = Readonly; export interface IssueTaskTimeTracked { [key: string]: number; } export interface IssueFieldsForTask { // NOTE: keep in mind that the issueId is not unique (especially for github) issueId?: string; issueProviderId?: string; issueType?: IssueProviderKey; issueWasUpdated?: boolean; // TODO remove null again issueLastUpdated?: number | null; issueAttachmentNr?: number; issueTimeTracked?: IssueTaskTimeTracked; issuePoints?: number; issueLastSyncedValues?: Record; } // Extend the plugin Task type with app-specific fields // Omit issue fields from PluginTask to avoid conflict with IssueFieldsForTask export interface TaskCopy extends Omit< PluginTask, | 'issueId' | 'issueProviderId' | 'issueType' | 'issueWasUpdated' | 'issueLastUpdated' | 'issueAttachmentNr' | 'issuePoints' >, IssueFieldsForTask { // Override required fields that are optional in plugin type projectId: string; timeSpentOnDay: TimeSpentOnDay; // Additional app-specific fields /** * Scheduled time as Unix timestamp (ms). For tasks scheduled with a specific time. * * IMPORTANT: dueWithTime and dueDay follow a mutual exclusivity pattern: * - When dueWithTime is set, dueDay MUST be undefined/null (not both set) * - When reading, check dueWithTime FIRST (it takes priority over dueDay) * * @see docs/ai/dueDay-dueWithTime-mutual-exclusivity.md */ dueWithTime?: number | null; /** * Scheduled date as ISO date string (YYYY-MM-DD). For tasks scheduled for all-day (no specific time). * * IMPORTANT: dueDay and dueWithTime follow a mutual exclusivity pattern: * - When dueDay is set, dueWithTime should be undefined/null (for new data) * - When dueWithTime is set, dueDay MUST be undefined/null (not both set) * - When reading, check dueWithTime FIRST (it takes priority over dueDay) * - Legacy data may have both fields set; handle via priority pattern * * @see docs/ai/dueDay-dueWithTime-mutual-exclusivity.md */ dueDay?: string | null; hasPlannedTime?: boolean; /** * Deadline date as ISO string (YYYY-MM-DD). For deadlines without a specific time. * Follows mutual exclusivity with deadlineWithTime (same pattern as dueDay/dueWithTime). */ deadlineDay?: string | null; /** * Deadline as Unix timestamp (ms). For deadlines with a specific time. * When set, deadlineDay MUST be cleared. */ deadlineWithTime?: number | null; /** Reminder timestamp for the deadline. */ deadlineRemindAt?: number | null; attachments: TaskAttachment[]; reminderId?: string | null; // Ensure type compatibility for internal fields modified?: number; doneOn?: number; parentId?: string; remindAt?: number; repeatCfgId?: string; _hideSubTasksMode?: HideSubTasksMode; } /** * standard task but with: * * reminder removed, if any * * sub tasks not included (but copied) */ // attachment data saved to it export type ArchiveTask = Readonly; export type Task = Readonly; export interface TaskWithReminderData extends Task { readonly reminderData: { remindAt: number }; readonly parentData?: Task; readonly isDeadlineReminder?: boolean; } export interface TaskWithReminder extends Task { remindAt: number; } export interface TaskWithDueTime extends Task { dueWithTime: number; } export interface TaskWithDueDay extends Task { dueDay: string; } export type TaskPlannedWithDayOrTime = TaskWithDueTime | TaskWithDueDay; export interface TaskWithDeadlineDay extends Task { deadlineDay: string; } export interface TaskWithDeadlineTime extends Task { deadlineWithTime: number; } export type TaskWithDeadline = TaskWithDeadlineDay | TaskWithDeadlineTime; export interface TaskWithoutReminder extends Task { remindAt: undefined; } export interface TaskWithPlannedForDayIndication extends TaskWithoutReminder { plannedForDay: string; } export interface TaskWithSubTasks extends Task { readonly subTasks: Task[]; } // make title required and add optional property for possible related (parent) task export type IssueTask = Partial & { title: string; related_to?: string; }; export const DEFAULT_TASK: Omit = { id: '', subTaskIds: [], timeSpentOnDay: {}, timeSpent: 0, timeEstimate: 0, isDone: false, title: '', tagIds: [], created: Date.now(), attachments: [], }; export interface TaskState extends EntityState { // overwrite entity model to avoid problems with typing ids: string[]; // additional entities state properties currentTaskId: string | null; selectedTaskId: string | null; taskDetailTargetPanel?: TaskDetailTargetPanel | null; lastCurrentTaskId: string | null; isDataLoaded: boolean; } export interface WorklogTask extends Task { dateStr: string; } export type SubmitTrigger = 'blur' | 'escape' | 'enter' | 'modEnter';