Skip to content

Commit c9eb53e

Browse files
committed
Mark initial project sync as read
1 parent bc1ccd4 commit c9eb53e

5 files changed

Lines changed: 84 additions & 25 deletions

File tree

tools/asana/src/asana.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type SyncState = {
2727
offset: number;
2828
batchNumber: number;
2929
tasksProcessed: number;
30+
initialSync: boolean;
3031
};
3132

3233
/**
@@ -148,13 +149,21 @@ export class Asana extends Tool<Asana> implements ProjectTool {
148149
* Start syncing tasks from an Asana project
149150
*/
150151
async startSync<
151-
TCallback extends (task: NewActivityWithNotes, ...args: any[]) => any
152+
TCallback extends (
153+
task: NewActivityWithNotes,
154+
syncMeta: { initialSync: boolean },
155+
...args: any[]
156+
) => any
152157
>(
153158
authToken: string,
154159
projectId: string,
155160
callback: TCallback,
156161
options?: ProjectSyncOptions,
157-
...extraArgs: TCallback extends (task: any, ...rest: infer R) => any
162+
...extraArgs: TCallback extends (
163+
task: any,
164+
syncMeta: any,
165+
...rest: infer R
166+
) => any
158167
? R
159168
: []
160169
): Promise<void> {
@@ -198,6 +207,7 @@ export class Asana extends Tool<Asana> implements ProjectTool {
198207
offset: 0,
199208
batchNumber: 1,
200209
tasksProcessed: 0,
210+
initialSync: true,
201211
});
202212

203213
// Queue first batch
@@ -268,8 +278,12 @@ export class Asana extends Tool<Asana> implements ProjectTool {
268278
task,
269279
projectId
270280
);
271-
// Execute the callback using the callback token
272-
await this.tools.callbacks.run(callbackToken, activityWithNotes);
281+
// Execute the callback using the callback token with syncMeta
282+
await this.tools.callbacks.run(
283+
callbackToken,
284+
activityWithNotes,
285+
{ initialSync: state.initialSync }
286+
);
273287
}
274288

275289
// Check if more pages by checking if we got a full batch
@@ -280,6 +294,7 @@ export class Asana extends Tool<Asana> implements ProjectTool {
280294
offset: state.offset + batchSize,
281295
batchNumber: state.batchNumber + 1,
282296
tasksProcessed: state.tasksProcessed + tasksResult.data.length,
297+
initialSync: state.initialSync,
283298
});
284299

285300
// Queue next batch
@@ -291,7 +306,7 @@ export class Asana extends Tool<Asana> implements ProjectTool {
291306
);
292307
await this.tools.tasks.runTask(nextBatch);
293308
} else {
294-
// Cleanup sync state
309+
// Initial sync is complete - cleanup sync state
295310
await this.clear(`sync_state_${projectId}`);
296311
}
297312
}
@@ -423,8 +438,8 @@ export class Asana extends Tool<Asana> implements ProjectTool {
423438
projectId
424439
);
425440

426-
// Execute stored callback
427-
await this.run(callbackToken, activityWithNotes);
441+
// Execute stored callback - webhooks are never part of initial sync
442+
await this.tools.callbacks.run(callbackToken, activityWithNotes, { initialSync: false });
428443
} catch (error) {
429444
console.warn("Failed to process Asana task webhook:", error);
430445
}

tools/jira/src/jira.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type SyncState = {
2727
startAt: number;
2828
batchNumber: number;
2929
issuesProcessed: number;
30+
initialSync: boolean;
3031
};
3132

3233
/**
@@ -154,13 +155,21 @@ export class Jira extends Tool<Jira> implements ProjectTool {
154155
* Start syncing issues from a Jira project
155156
*/
156157
async startSync<
157-
TCallback extends (issue: NewActivityWithNotes, ...args: any[]) => any
158+
TCallback extends (
159+
issue: NewActivityWithNotes,
160+
syncMeta: { initialSync: boolean },
161+
...args: any[]
162+
) => any
158163
>(
159164
authToken: string,
160165
projectId: string,
161166
callback: TCallback,
162167
options?: ProjectSyncOptions,
163-
...extraArgs: TCallback extends (issue: any, ...rest: infer R) => any
168+
...extraArgs: TCallback extends (
169+
issue: any,
170+
syncMeta: any,
171+
...rest: infer R
172+
) => any
164173
? R
165174
: []
166175
): Promise<void> {
@@ -204,6 +213,7 @@ export class Jira extends Tool<Jira> implements ProjectTool {
204213
startAt: 0,
205214
batchNumber: 1,
206215
issuesProcessed: 0,
216+
initialSync: true,
207217
});
208218

209219
// Queue first batch
@@ -269,8 +279,12 @@ export class Jira extends Tool<Jira> implements ProjectTool {
269279
issue,
270280
projectId
271281
);
272-
// Execute the callback using the callback token
273-
await this.tools.callbacks.run(callbackToken, activityWithNotes);
282+
// Execute the callback using the callback token with syncMeta
283+
await this.tools.callbacks.run(
284+
callbackToken,
285+
activityWithNotes,
286+
{ initialSync: state.initialSync }
287+
);
274288
}
275289

276290
// Check if more pages
@@ -282,6 +296,7 @@ export class Jira extends Tool<Jira> implements ProjectTool {
282296
startAt: nextStartAt,
283297
batchNumber: state.batchNumber + 1,
284298
issuesProcessed: state.issuesProcessed + (searchResult.issues?.length || 0),
299+
initialSync: state.initialSync,
285300
});
286301

287302
// Queue next batch
@@ -293,7 +308,7 @@ export class Jira extends Tool<Jira> implements ProjectTool {
293308
);
294309
await this.tools.tasks.runTask(nextBatch);
295310
} else {
296-
// Cleanup sync state
311+
// Initial sync is complete - cleanup sync state
297312
await this.clear(`sync_state_${projectId}`);
298313
}
299314
}
@@ -402,8 +417,8 @@ export class Jira extends Tool<Jira> implements ProjectTool {
402417
projectId
403418
);
404419

405-
// Execute stored callback
406-
await this.run(callbackToken, activityWithNotes);
420+
// Execute stored callback - webhooks are never part of initial sync
421+
await this.tools.callbacks.run(callbackToken, activityWithNotes, { initialSync: false });
407422
}
408423
}
409424

tools/linear/src/linear.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type SyncState = {
2727
after: string | null;
2828
batchNumber: number;
2929
issuesProcessed: number;
30+
initialSync: boolean;
3031
};
3132

3233
/**
@@ -133,13 +134,21 @@ export class Linear extends Tool<Linear> implements ProjectTool {
133134
* Start syncing issues from a Linear team
134135
*/
135136
async startSync<
136-
TCallback extends (issue: NewActivityWithNotes, ...args: any[]) => any
137+
TCallback extends (
138+
issue: NewActivityWithNotes,
139+
syncMeta: { initialSync: boolean },
140+
...args: any[]
141+
) => any
137142
>(
138143
authToken: string,
139144
projectId: string,
140145
callback: TCallback,
141146
options?: ProjectSyncOptions,
142-
...extraArgs: TCallback extends (issue: any, ...rest: infer R) => any
147+
...extraArgs: TCallback extends (
148+
issue: any,
149+
syncMeta: any,
150+
...rest: infer R
151+
) => any
143152
? R
144153
: []
145154
): Promise<void> {
@@ -218,6 +227,7 @@ export class Linear extends Tool<Linear> implements ProjectTool {
218227
after: null,
219228
batchNumber: 1,
220229
issuesProcessed: 0,
230+
initialSync: true,
221231
});
222232

223233
// Queue first batch
@@ -272,8 +282,12 @@ export class Linear extends Tool<Linear> implements ProjectTool {
272282
issue,
273283
projectId
274284
);
275-
// Execute the callback using the callback token
276-
await this.tools.callbacks.run(callbackToken, activityWithNotes);
285+
// Execute the callback using the callback token with syncMeta
286+
await this.tools.callbacks.run(
287+
callbackToken,
288+
activityWithNotes,
289+
{ initialSync: state.initialSync }
290+
);
277291
}
278292

279293
// Check if more pages
@@ -282,6 +296,7 @@ export class Linear extends Tool<Linear> implements ProjectTool {
282296
after: issuesConnection.pageInfo.endCursor,
283297
batchNumber: state.batchNumber + 1,
284298
issuesProcessed: state.issuesProcessed + issuesConnection.nodes.length,
299+
initialSync: state.initialSync,
285300
});
286301

287302
// Queue next batch
@@ -293,7 +308,7 @@ export class Linear extends Tool<Linear> implements ProjectTool {
293308
);
294309
await this.tools.tasks.runTask(nextBatch);
295310
} else {
296-
// Cleanup sync state
311+
// Initial sync is complete - cleanup sync state
297312
await this.clear(`sync_state_${projectId}`);
298313
}
299314
}
@@ -430,8 +445,8 @@ export class Linear extends Tool<Linear> implements ProjectTool {
430445
projectId
431446
);
432447

433-
// Execute stored callback
434-
await this.run(callbackToken, activityWithNotes);
448+
// Execute stored callback - webhooks are never part of initial sync
449+
await this.tools.callbacks.run(callbackToken, activityWithNotes, { initialSync: false });
435450
}
436451
}
437452

twister/src/common/projects.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,29 @@ export interface ProjectTool {
8282
*
8383
* @param authToken - Authorization token for access
8484
* @param projectId - ID of the project to sync
85-
* @param callback - Function receiving (issue, ...extraArgs) for each synced issue
85+
* @param callback - Function receiving (issue, syncMeta, ...extraArgs) for each synced issue.
86+
* The syncMeta parameter contains { initialSync: boolean } to indicate if this is
87+
* part of the initial sync or an incremental update.
8688
* @param options - Optional configuration for limiting the sync scope (e.g., time range)
8789
* @param extraArgs - Additional arguments to pass to the callback (type-checked)
8890
* @returns Promise that resolves when sync setup is complete
8991
*/
9092
startSync<
91-
TCallback extends (issue: NewActivityWithNotes, ...args: any[]) => any
93+
TCallback extends (
94+
issue: NewActivityWithNotes,
95+
syncMeta: { initialSync: boolean },
96+
...args: any[]
97+
) => any
9298
>(
9399
authToken: string,
94100
projectId: string,
95101
callback: TCallback,
96102
options?: ProjectSyncOptions,
97-
...extraArgs: TCallback extends (issue: any, ...rest: infer R) => any
103+
...extraArgs: TCallback extends (
104+
issue: any,
105+
syncMeta: any,
106+
...rest: infer R
107+
) => any
98108
? R
99109
: []
100110
): Promise<void>;

twists/project-sync/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export default class ProjectSync extends Twist<ProjectSync> {
229229
*/
230230
async onIssue(
231231
issue: NewActivityWithNotes,
232+
syncMeta: { initialSync: boolean },
232233
provider: ProjectProvider,
233234
projectId: string
234235
) {
@@ -256,7 +257,10 @@ export default class ProjectSync extends Twist<ProjectSync> {
256257
}
257258

258259
// Create new activity for new issue (new thread with initial note)
259-
await this.tools.plot.createActivity(issue);
260+
// Mark existing issues as read during initial sync to avoid overwhelming the user
261+
await this.tools.plot.createActivity(issue, {
262+
unread: !syncMeta.initialSync,
263+
});
260264
}
261265

262266
/**

0 commit comments

Comments
 (0)