diff --git a/assets/javascripts/discourse/components/hledger-dashboard.gjs b/assets/javascripts/discourse/components/hledger-dashboard.gjs index d68d0f2..1cbedc5 100644 --- a/assets/javascripts/discourse/components/hledger-dashboard.gjs +++ b/assets/javascripts/discourse/components/hledger-dashboard.gjs @@ -28,6 +28,10 @@ export default class HledgerDashboard extends Component { @tracked beginDate = null; @tracked endDate = null; + #inflight = null; + #lastKey = null; + #requestId = 0; + constructor() { super(...arguments); this.loadReport(); @@ -103,6 +107,16 @@ export default class HledgerDashboard extends Component { return; } + const key = `${this.selectedReport}|${this.beginDate}|${this.endDate}`; + + if (key === this.#lastKey && !this.error) { + return; + } + + this.#lastKey = key; + const requestId = ++this.#requestId; + + this.#inflight?.abort(); this.loading = true; this.error = null; @@ -115,16 +129,27 @@ export default class HledgerDashboard extends Component { } try { - this.report = await ajax( + const promise = ajax( `/hledger/topics/${topicId}/reports/${this.selectedReport}`, { data } ); + this.#inflight = promise; + const report = await promise; + + if (requestId === this.#requestId) { + this.report = report; + } } catch (e) { - this.report = null; - this.error = - e?.jqXHR?.responseJSON?.errors?.[0] || i18n("hledger.errors.generic"); + if (requestId === this.#requestId) { + this.report = null; + this.error = + e?.jqXHR?.responseJSON?.errors?.[0] || i18n("hledger.errors.generic"); + } } finally { - this.loading = false; + if (requestId === this.#requestId) { + this.loading = false; + this.#inflight = null; + } } } diff --git a/test/javascripts/acceptance/hledger-test.js b/test/javascripts/acceptance/hledger-test.js index 300dfe3..65ffa41 100644 --- a/test/javascripts/acceptance/hledger-test.js +++ b/test/javascripts/acceptance/hledger-test.js @@ -260,6 +260,8 @@ const transactionsReport = { acceptance("Hledger plugin", function (needs) { needs.settings({ hledger_enabled: true }); + const requestCounts = { accounts: 0, equity: 0 }; + needs.pretender((server, helper) => { server.get("/t/45.json", () => { const topic = cloneJSON(topicFixtures["/t/28830/1.json"]); @@ -267,12 +269,14 @@ acceptance("Hledger plugin", function (needs) { return helper.response(topic); }); - server.get(`/hledger/topics/${TOPIC_ID}/reports/accounts`, () => - helper.response(accountsReport) - ); - server.get(`/hledger/topics/${TOPIC_ID}/reports/equity`, () => - helper.response(equityReport) - ); + server.get(`/hledger/topics/${TOPIC_ID}/reports/accounts`, () => { + requestCounts.accounts++; + return helper.response(accountsReport); + }); + server.get(`/hledger/topics/${TOPIC_ID}/reports/equity`, () => { + requestCounts.equity++; + return helper.response(equityReport); + }); server.get(`/hledger/topics/${TOPIC_ID}/reports/balance_sheet`, () => helper.response(balanceSheetReport) ); @@ -452,6 +456,24 @@ acceptance("Hledger plugin", function (needs) { .hasText("Server"); }); + test("does not issue duplicate requests for the same report", async function (assert) { + requestCounts.accounts = 0; + requestCounts.equity = 0; + + await visit("/t/-/45"); + + assert.strictEqual(requestCounts.accounts, 1, "loads accounts once"); + + await click(".hledger-dashboard__toolbar .btn:nth-of-type(4)"); + assert.strictEqual(requestCounts.equity, 1, "loads equity once"); + + await click(".hledger-dashboard__toolbar .btn:nth-of-type(4)"); + assert.strictEqual(requestCounts.equity, 1, "ignores the current report"); + + await click(".hledger-dashboard__toolbar .btn:nth-of-type(1)"); + assert.strictEqual(requestCounts.accounts, 2, "reloads on report change"); + }); + test("shows the transaction log", async function (assert) { await visit("/t/-/45");