Skip to content

Commit f3ca168

Browse files
committed
Restore subtask creation after helper-board setup.
1 parent 12840ba commit f3ca168

3 files changed

Lines changed: 39 additions & 9 deletions

File tree

client/components/cards/subtasks.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Template.subtasks.events({
2222
event.preventDefault();
2323
const textarea = tpl.find('textarea.js-add-subtask-item');
2424
const title = textarea.value.trim();
25-
const cardId = Template.currentData().cardId;
25+
const cardId = this.cardId;
2626

2727
if (title) {
2828
// Subtask creation is performed server-side by the `addSubtaskCard` Meteor
@@ -32,9 +32,12 @@ Template.subtasks.events({
3232
// (#3868 / #5788 / #2256) and lets multiple subtasks be created reliably
3333
// (#4782), and the method applies the destination board's automatic
3434
// custom fields to the new subtask (#4037 / #3562).
35-
const _id = await Meteor.callAsync('addSubtaskCard', cardId, title);
35+
try {
36+
const _id = await Meteor.callAsync('addSubtaskCard', cardId, title);
3637

37-
if (_id) {
38+
if (!_id) {
39+
throw new Error('The server could not create the subtask.');
40+
}
3841
// In case the filter is active we need to add the newly inserted card in
3942
// the list of exceptions -- cards that are not filtered. Otherwise the
4043
// card will disappear instantly.
@@ -46,10 +49,13 @@ Template.subtasks.events({
4649
.last()
4750
.click();
4851
}, 100);
52+
textarea.value = '';
53+
textarea.focus();
54+
} catch (error) {
55+
alert(error?.reason || error?.message || 'Could not create the subtask.');
56+
textarea.focus();
4957
}
5058
}
51-
textarea.value = '';
52-
textarea.focus();
5359
},
5460
'submit .js-edit-subtask-title'(event, tpl) {
5561
event.preventDefault();

models/boards.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,7 +1895,10 @@ Boards.helpers({
18951895
{ _id: defaultId },
18961896
{ $setOnInsert: this.defaultSwimlaneFields() },
18971897
);
1898-
return ReactiveCache.getSwimlane({ _id: defaultId });
1898+
return (
1899+
(await ReactiveCache.getSwimlane({ _id: defaultId })) ||
1900+
(Meteor.isServer ? await Swimlanes.findOneAsync(defaultId) : undefined)
1901+
);
18991902
},
19001903

19011904
// Fields for an upsert-inserted default swimlane. archived/type must be set
@@ -1910,9 +1913,13 @@ Boards.helpers({
19101913
async getDefaultSwimlineAsync() {
19111914
// Issue #1971: prefer a NON-archived swimlane (see getDefaultSwimline).
19121915
const { pickDefaultSwimlane } = require('./lib/defaultSwimlane');
1913-
let result = pickDefaultSwimlane(
1914-
await ReactiveCache.getSwimlanes({ boardId: this._id }),
1915-
);
1916+
// On the server this can run immediately after creating a helper board and
1917+
// its first swimlane. The reactive cache may still hold the pre-insert
1918+
// empty result, so use the authoritative collection in server methods.
1919+
const swimlanes = Meteor.isServer
1920+
? await Swimlanes.find({ boardId: this._id }).fetchAsync()
1921+
: await ReactiveCache.getSwimlanes({ boardId: this._id });
1922+
let result = pickDefaultSwimlane(swimlanes);
19161923
if (result === undefined && Meteor.isServer && this._id) {
19171924
// Issue #6382: never auto-create swimlanes from the client (see
19181925
// getDefaultSwimline) — only the server may insert the default one.

tests/subtasksDefaultBoard.test.cjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ test('getDefaultSubtasksListAsync creates the landing list with async APIs', ()
5555
assert.ok(fn.includes('Meteor.isServer'));
5656
});
5757

58+
test('async swimlane lookup bypasses a stale server cache after creation', () => {
59+
const fn = extract('getDefaultSwimlineAsync');
60+
assert.ok(
61+
fn.includes("Meteor.isServer\n ? await Swimlanes.find({ boardId: this._id }).fetchAsync()"),
62+
'server creation must read the authoritative collection, not a stale cache',
63+
);
64+
});
65+
66+
test('async default-swimlane self-heal returns the inserted document', () => {
67+
const fn = extract('ensureDefaultSwimlaneIdAsync');
68+
assert.ok(fn.includes('await Swimlanes.upsertAsync('));
69+
assert.ok(
70+
fn.includes('await Swimlanes.findOneAsync(defaultId)'),
71+
'a cache miss immediately after upsert must fall back to the collection',
72+
);
73+
});
74+
5875
// --- The sync getters are pure (no creation) ---------------------------------
5976

6077
test('sync getters no longer create anything', () => {

0 commit comments

Comments
 (0)