perf(renderer): reuse global task notification indexes
This commit is contained in:
parent
c1ebecf813
commit
8eada276ca
1 changed files with 73 additions and 32 deletions
|
|
@ -8,7 +8,12 @@ import {
|
||||||
} from '@shared/utils/teamTaskState';
|
} from '@shared/utils/teamTaskState';
|
||||||
|
|
||||||
import type { AppConfig } from '@renderer/types/data';
|
import type { AppConfig } from '@renderer/types/data';
|
||||||
import type { GlobalTask, TaskComment, TeamMessageNotificationData, TeamSummary } from '@shared/types';
|
import type {
|
||||||
|
GlobalTask,
|
||||||
|
TaskComment,
|
||||||
|
TeamMessageNotificationData,
|
||||||
|
TeamSummary,
|
||||||
|
} from '@shared/types';
|
||||||
|
|
||||||
const notifiedClarificationTaskKeys = new Set<string>();
|
const notifiedClarificationTaskKeys = new Set<string>();
|
||||||
const notifiedStatusChangeKeys = new Set<string>();
|
const notifiedStatusChangeKeys = new Set<string>();
|
||||||
|
|
@ -19,6 +24,13 @@ const notifiedBlockedTaskKeys = new Set<string>();
|
||||||
|
|
||||||
let isFirstFetchAllTasks = true;
|
let isFirstFetchAllTasks = true;
|
||||||
|
|
||||||
|
interface TaskNotificationIndexes {
|
||||||
|
readonly firstOldTaskByKey: ReadonlyMap<string, GlobalTask>;
|
||||||
|
readonly lastOldTaskByKey: ReadonlyMap<string, GlobalTask>;
|
||||||
|
readonly oldTaskKeys: ReadonlySet<string>;
|
||||||
|
readonly oldTasksByTeam: ReadonlyMap<string, readonly GlobalTask[]>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ProcessGlobalTaskNotificationsParams {
|
export interface ProcessGlobalTaskNotificationsParams {
|
||||||
oldTasks: GlobalTask[];
|
oldTasks: GlobalTask[];
|
||||||
newTasks: GlobalTask[];
|
newTasks: GlobalTask[];
|
||||||
|
|
@ -43,9 +55,7 @@ export function consumeFirstGlobalTasksFetchFlag(): boolean {
|
||||||
return wasFirst;
|
return wasFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function processGlobalTaskNotifications(
|
export function processGlobalTaskNotifications(params: ProcessGlobalTaskNotificationsParams): void {
|
||||||
params: ProcessGlobalTaskNotificationsParams
|
|
||||||
): void {
|
|
||||||
const { oldTasks, newTasks, appConfig, teamByName, isInitialFetch } = params;
|
const { oldTasks, newTasks, appConfig, teamByName, isInitialFetch } = params;
|
||||||
|
|
||||||
if (isInitialFetch) {
|
if (isInitialFetch) {
|
||||||
|
|
@ -54,18 +64,20 @@ export function processGlobalTaskNotifications(
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyOnClarifications = appConfig?.notifications?.notifyOnClarifications ?? true;
|
const notifyOnClarifications = appConfig?.notifications?.notifyOnClarifications ?? true;
|
||||||
detectClarificationNotifications(oldTasks, newTasks, notifyOnClarifications);
|
const oldTaskIndexes = buildTaskNotificationIndexes(oldTasks);
|
||||||
detectBlockedTaskNotifications(oldTasks, newTasks, notifyOnClarifications);
|
|
||||||
detectStatusChangeNotifications(oldTasks, newTasks, appConfig, teamByName);
|
detectClarificationNotifications(oldTaskIndexes, newTasks, notifyOnClarifications);
|
||||||
|
detectBlockedTaskNotifications(oldTaskIndexes, newTasks, notifyOnClarifications);
|
||||||
|
detectStatusChangeNotifications(oldTaskIndexes, newTasks, appConfig, teamByName);
|
||||||
|
|
||||||
const notifyOnTaskComments = appConfig?.notifications?.notifyOnTaskComments ?? true;
|
const notifyOnTaskComments = appConfig?.notifications?.notifyOnTaskComments ?? true;
|
||||||
detectTaskCommentNotifications(oldTasks, newTasks, notifyOnTaskComments);
|
detectTaskCommentNotifications(oldTaskIndexes, newTasks, notifyOnTaskComments);
|
||||||
|
|
||||||
const notifyOnTaskCreated = appConfig?.notifications?.notifyOnTaskCreated ?? true;
|
const notifyOnTaskCreated = appConfig?.notifications?.notifyOnTaskCreated ?? true;
|
||||||
detectTaskCreatedNotifications(oldTasks, newTasks, notifyOnTaskCreated);
|
detectTaskCreatedNotifications(oldTaskIndexes, newTasks, notifyOnTaskCreated);
|
||||||
|
|
||||||
const notifyOnAllCompleted = appConfig?.notifications?.notifyOnAllTasksCompleted ?? true;
|
const notifyOnAllCompleted = appConfig?.notifications?.notifyOnAllTasksCompleted ?? true;
|
||||||
detectAllTasksCompletedNotification(oldTasks, newTasks, notifyOnAllCompleted);
|
detectAllTasksCompletedNotification(oldTaskIndexes, newTasks, notifyOnAllCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
function seedGlobalTaskNotificationState(tasks: readonly GlobalTask[]): void {
|
function seedGlobalTaskNotificationState(tasks: readonly GlobalTask[]): void {
|
||||||
|
|
@ -74,7 +86,9 @@ function seedGlobalTaskNotificationState(tasks: readonly GlobalTask[]): void {
|
||||||
notifiedClarificationTaskKeys.add(`${task.teamName}:${task.id}`);
|
notifiedClarificationTaskKeys.add(`${task.teamName}:${task.id}`);
|
||||||
}
|
}
|
||||||
if ((task.blockedBy?.length ?? 0) > 0) {
|
if ((task.blockedBy?.length ?? 0) > 0) {
|
||||||
notifiedBlockedTaskKeys.add(`${task.teamName}:${task.id}:${(task.blockedBy ?? []).join(',')}`);
|
notifiedBlockedTaskKeys.add(
|
||||||
|
`${task.teamName}:${task.id}:${(task.blockedBy ?? []).join(',')}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
notifiedStatusChangeKeys.add(`${task.teamName}:${task.id}:${task.status}`);
|
notifiedStatusChangeKeys.add(`${task.teamName}:${task.id}:${task.status}`);
|
||||||
if (isTeamTaskNeedsFixActionable(task)) {
|
if (isTeamTaskNeedsFixActionable(task)) {
|
||||||
|
|
@ -105,19 +119,53 @@ function seedGlobalTaskNotificationState(tasks: readonly GlobalTask[]): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTaskNotificationKey(task: Pick<GlobalTask, 'teamName' | 'id'>): string {
|
||||||
|
return `${task.teamName}:${task.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTaskNotificationIndexes(tasks: readonly GlobalTask[]): TaskNotificationIndexes {
|
||||||
|
const firstOldTaskByKey = new Map<string, GlobalTask>();
|
||||||
|
const lastOldTaskByKey = new Map<string, GlobalTask>();
|
||||||
|
const oldTaskKeys = new Set<string>();
|
||||||
|
const oldTasksByTeam = new Map<string, GlobalTask[]>();
|
||||||
|
|
||||||
|
for (const task of tasks) {
|
||||||
|
const key = getTaskNotificationKey(task);
|
||||||
|
if (!firstOldTaskByKey.has(key)) {
|
||||||
|
firstOldTaskByKey.set(key, task);
|
||||||
|
}
|
||||||
|
lastOldTaskByKey.set(key, task);
|
||||||
|
oldTaskKeys.add(key);
|
||||||
|
|
||||||
|
const teamTasks = oldTasksByTeam.get(task.teamName);
|
||||||
|
if (teamTasks) {
|
||||||
|
teamTasks.push(task);
|
||||||
|
} else {
|
||||||
|
oldTasksByTeam.set(task.teamName, [task]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
firstOldTaskByKey,
|
||||||
|
lastOldTaskByKey,
|
||||||
|
oldTaskKeys,
|
||||||
|
oldTasksByTeam,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function showTeamNotification(data: TeamMessageNotificationData): void {
|
function showTeamNotification(data: TeamMessageNotificationData): void {
|
||||||
void api.teams?.showMessageNotification(data).catch(() => undefined);
|
void api.teams?.showMessageNotification(data).catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectClarificationNotifications(
|
function detectClarificationNotifications(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
notifyEnabled: boolean
|
notifyEnabled: boolean
|
||||||
): void {
|
): void {
|
||||||
for (const task of newTasks) {
|
for (const task of newTasks) {
|
||||||
const key = `${task.teamName}:${task.id}`;
|
const key = getTaskNotificationKey(task);
|
||||||
if (task.needsClarification === 'user') {
|
if (task.needsClarification === 'user') {
|
||||||
const oldTask = oldTasks.find((t) => t.teamName === task.teamName && t.id === task.id);
|
const oldTask = oldTaskIndexes.firstOldTaskByKey.get(key);
|
||||||
if (oldTask?.needsClarification !== 'user' && !notifiedClarificationTaskKeys.has(key)) {
|
if (oldTask?.needsClarification !== 'user' && !notifiedClarificationTaskKeys.has(key)) {
|
||||||
notifiedClarificationTaskKeys.add(key);
|
notifiedClarificationTaskKeys.add(key);
|
||||||
showClarificationNotification(task, !notifyEnabled);
|
showClarificationNotification(task, !notifyEnabled);
|
||||||
|
|
@ -155,7 +203,7 @@ function showClarificationNotification(task: GlobalTask, suppressToast: boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectStatusChangeNotifications(
|
function detectStatusChangeNotifications(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
config: AppConfig | null,
|
config: AppConfig | null,
|
||||||
teamByName: Record<string, TeamSummary>
|
teamByName: Record<string, TeamSummary>
|
||||||
|
|
@ -168,7 +216,7 @@ function detectStatusChangeNotifications(
|
||||||
const onlySolo = config?.notifications?.statusChangeOnlySolo ?? true;
|
const onlySolo = config?.notifications?.statusChangeOnlySolo ?? true;
|
||||||
|
|
||||||
for (const task of newTasks) {
|
for (const task of newTasks) {
|
||||||
const oldTask = oldTasks.find((t) => t.teamName === task.teamName && t.id === task.id);
|
const oldTask = oldTaskIndexes.firstOldTaskByKey.get(getTaskNotificationKey(task));
|
||||||
if (!oldTask) continue;
|
if (!oldTask) continue;
|
||||||
|
|
||||||
const taskKanbanColumn = getTeamTaskWorkflowColumn(task);
|
const taskKanbanColumn = getTeamTaskWorkflowColumn(task);
|
||||||
|
|
@ -254,15 +302,12 @@ function showStatusChangeNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectTaskCommentNotifications(
|
function detectTaskCommentNotifications(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
notifyEnabled: boolean
|
notifyEnabled: boolean
|
||||||
): void {
|
): void {
|
||||||
const oldTaskMap = new Map(oldTasks.map((t) => [`${t.teamName}:${t.id}`, t]));
|
|
||||||
|
|
||||||
for (const task of newTasks) {
|
for (const task of newTasks) {
|
||||||
const mapKey = `${task.teamName}:${task.id}`;
|
const oldTask = oldTaskIndexes.lastOldTaskByKey.get(getTaskNotificationKey(task));
|
||||||
const oldTask = oldTaskMap.get(mapKey);
|
|
||||||
const oldCommentCount = oldTask?.comments?.length ?? 0;
|
const oldCommentCount = oldTask?.comments?.length ?? 0;
|
||||||
const newCommentCount = task.comments?.length ?? 0;
|
const newCommentCount = task.comments?.length ?? 0;
|
||||||
|
|
||||||
|
|
@ -346,14 +391,12 @@ function showTaskReviewRequestedNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectBlockedTaskNotifications(
|
function detectBlockedTaskNotifications(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
notifyEnabled: boolean
|
notifyEnabled: boolean
|
||||||
): void {
|
): void {
|
||||||
const oldTaskMap = new Map(oldTasks.map((task) => [`${task.teamName}:${task.id}`, task]));
|
|
||||||
|
|
||||||
for (const task of newTasks) {
|
for (const task of newTasks) {
|
||||||
const oldTask = oldTaskMap.get(`${task.teamName}:${task.id}`);
|
const oldTask = oldTaskIndexes.lastOldTaskByKey.get(getTaskNotificationKey(task));
|
||||||
const oldBlockedBy = new Set(oldTask?.blockedBy?.filter(Boolean) ?? []);
|
const oldBlockedBy = new Set(oldTask?.blockedBy?.filter(Boolean) ?? []);
|
||||||
const newBlockedBy = Array.from(new Set(task.blockedBy?.filter(Boolean) ?? []));
|
const newBlockedBy = Array.from(new Set(task.blockedBy?.filter(Boolean) ?? []));
|
||||||
const taskKeyPrefix = `${task.teamName}:${task.id}:`;
|
const taskKeyPrefix = `${task.teamName}:${task.id}:`;
|
||||||
|
|
@ -407,15 +450,13 @@ function showTaskBlockedNotification(
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectTaskCreatedNotifications(
|
function detectTaskCreatedNotifications(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
notifyEnabled: boolean
|
notifyEnabled: boolean
|
||||||
): void {
|
): void {
|
||||||
const oldTaskKeys = new Set(oldTasks.map((t) => `${t.teamName}:${t.id}`));
|
|
||||||
|
|
||||||
for (const task of newTasks) {
|
for (const task of newTasks) {
|
||||||
const key = `${task.teamName}:${task.id}`;
|
const key = getTaskNotificationKey(task);
|
||||||
if (oldTaskKeys.has(key)) continue;
|
if (oldTaskIndexes.oldTaskKeys.has(key)) continue;
|
||||||
if (notifiedCreatedTaskKeys.has(key)) continue;
|
if (notifiedCreatedTaskKeys.has(key)) continue;
|
||||||
notifiedCreatedTaskKeys.add(key);
|
notifiedCreatedTaskKeys.add(key);
|
||||||
|
|
||||||
|
|
@ -444,7 +485,7 @@ function showTaskCreatedNotification(task: GlobalTask, suppressToast: boolean):
|
||||||
}
|
}
|
||||||
|
|
||||||
function detectAllTasksCompletedNotification(
|
function detectAllTasksCompletedNotification(
|
||||||
oldTasks: GlobalTask[],
|
oldTaskIndexes: TaskNotificationIndexes,
|
||||||
newTasks: GlobalTask[],
|
newTasks: GlobalTask[],
|
||||||
notifyEnabled: boolean
|
notifyEnabled: boolean
|
||||||
): void {
|
): void {
|
||||||
|
|
@ -464,7 +505,7 @@ function detectAllTasksCompletedNotification(
|
||||||
}
|
}
|
||||||
if (notifiedAllCompletedTeams.has(teamName)) continue;
|
if (notifiedAllCompletedTeams.has(teamName)) continue;
|
||||||
|
|
||||||
const oldTeamTasks = oldTasks.filter((t) => t.teamName === teamName);
|
const oldTeamTasks = oldTaskIndexes.oldTasksByTeam.get(teamName) ?? [];
|
||||||
const wasAlreadyAllCompleted =
|
const wasAlreadyAllCompleted =
|
||||||
oldTeamTasks.length > 0 && oldTeamTasks.every(isTeamTaskFinalForCompletionNotification);
|
oldTeamTasks.length > 0 && oldTeamTasks.every(isTeamTaskFinalForCompletionNotification);
|
||||||
if (wasAlreadyAllCompleted) {
|
if (wasAlreadyAllCompleted) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue