Skip to content

Commit dee0ffa

Browse files
authored
Fix detail comments after live updates (#92)
Co-authored-by: gprocunier <gprocunier@users.noreply.github.com>
1 parent 41135d1 commit dee0ffa

5 files changed

Lines changed: 527 additions & 48 deletions

File tree

app/data/subscription-issue-store.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
import { debug } from '../utils/logging.js';
55
import { cmpPriorityThenCreated } from './sort.js';
66

7+
/**
8+
* @param {object} obj
9+
* @param {string} key
10+
*/
11+
function hasOwn(obj, key) {
12+
return Object.prototype.hasOwnProperty.call(obj, key);
13+
}
14+
715
/**
816
* Per-subscription issue store. Holds full Issue objects and exposes a
917
* deterministic, read-only snapshot for rendering. Applies snapshot/upsert/
@@ -98,6 +106,12 @@ export function createSubscriptionIssueStore(id, options = {}) {
98106
? /** @type {number} */ (it.updated_at)
99107
: 0;
100108
if (prev_ts <= next_ts) {
109+
const has_incoming_comments = hasOwn(it, 'comments');
110+
const preserve_comments =
111+
!has_incoming_comments && hasOwn(existing, 'comments');
112+
const previous_comments = preserve_comments
113+
? existing.comments
114+
: undefined;
101115
// Mutate existing object to preserve reference
102116
for (const k of Object.keys(existing)) {
103117
if (!(k in it)) {
@@ -109,6 +123,9 @@ export function createSubscriptionIssueStore(id, options = {}) {
109123
// @ts-ignore - dynamic assignment
110124
existing[k] = v;
111125
}
126+
if (preserve_comments) {
127+
existing.comments = previous_comments;
128+
}
112129
} else {
113130
// stale by timestamp; ignore
114131
}

app/data/subscription-issue-store.test.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,82 @@ describe('subscription issue store', () => {
6666
expect(after).toBe(before); // identity preserved
6767
});
6868

69+
test('preserves comments when upsert omits comments', () => {
70+
const store = createSubscriptionIssueStore('s1');
71+
const comments = [{ id: 1, text: 'Existing comment' }];
72+
store.applyPush({
73+
type: 'snapshot',
74+
id: 's1',
75+
revision: 1,
76+
issues: [
77+
{
78+
id: 'X',
79+
title: 'x',
80+
comments,
81+
comment_count: 1,
82+
created_at: 10_000,
83+
updated_at: 10_000,
84+
closed_at: null
85+
}
86+
]
87+
});
88+
89+
store.applyPush({
90+
type: 'upsert',
91+
id: 's1',
92+
revision: 2,
93+
issue: {
94+
id: 'X',
95+
title: 'X!',
96+
comment_count: 1,
97+
created_at: 10_000,
98+
updated_at: 10_060,
99+
closed_at: null
100+
}
101+
});
102+
103+
expect(store.getById('X')?.comments).toBe(comments);
104+
expect(store.getById('X')?.title).toBe('X!');
105+
});
106+
107+
test('accepts explicit incoming comments on upsert', () => {
108+
const store = createSubscriptionIssueStore('s1');
109+
store.applyPush({
110+
type: 'snapshot',
111+
id: 's1',
112+
revision: 1,
113+
issues: [
114+
{
115+
id: 'X',
116+
title: 'x',
117+
comments: [{ id: 1, text: 'Existing comment' }],
118+
comment_count: 1,
119+
created_at: 10_000,
120+
updated_at: 10_000,
121+
closed_at: null
122+
}
123+
]
124+
});
125+
126+
store.applyPush({
127+
type: 'upsert',
128+
id: 's1',
129+
revision: 2,
130+
issue: {
131+
id: 'X',
132+
title: 'X!',
133+
comments: [],
134+
comment_count: 0,
135+
created_at: 10_000,
136+
updated_at: 10_060,
137+
closed_at: null
138+
}
139+
});
140+
141+
expect(store.getById('X')?.comments).toEqual([]);
142+
expect(store.getById('X')?.comment_count).toBe(0);
143+
});
144+
69145
test('ignores stale upsert by revision and timestamp', () => {
70146
const store = createSubscriptionIssueStore('s1');
71147
store.applyPush({

0 commit comments

Comments
 (0)