diff --git a/assets/js/phoenix_live_view/view.js b/assets/js/phoenix_live_view/view.js index 33e63a1b8c..77579e1092 100644 --- a/assets/js/phoenix_live_view/view.js +++ b/assets/js/phoenix_live_view/view.js @@ -1122,20 +1122,19 @@ export default class View { } } - pushHookEvent(el, targetCtx, event, payload, onReply){ + pushHookEvent(el, targetCtx, event, payload){ if(!this.isConnected()){ this.log("hook", () => ["unable to push hook event. LiveView not connected", event, payload]) - return false + return Promise.reject(new Error("unable to push hook event. LiveView not connected")) } let [ref, els, opts] = this.putRef([{el, loading: true, lock: true}], event, "hook") - this.pushWithReply(() => [ref, els, opts], "event", { + + return this.pushWithReply(() => [ref, els, opts], "event", { type: "hook", event: event, value: payload, cid: this.closestComponentID(targetCtx) - }).then(({resp: _resp, reply: hookReply}) => onReply(hookReply, ref)) - - return ref + }).then(({resp: _resp, reply}) => ({reply, ref})) } extractMeta(el, meta, value){ diff --git a/assets/js/phoenix_live_view/view_hook.js b/assets/js/phoenix_live_view/view_hook.js index e145ec950d..2e53f8699d 100644 --- a/assets/js/phoenix_live_view/view_hook.js +++ b/assets/js/phoenix_live_view/view_hook.js @@ -253,39 +253,33 @@ export default class ViewHook { } pushEvent(event, payload = {}, onReply){ + const promise = this.__view().pushHookEvent(this.el, null, event, payload) if(onReply === undefined){ - return new Promise((resolve, reject) => { - try { - const ref = this.__view().pushHookEvent(this.el, null, event, payload, (reply, _ref) => resolve(reply)) - if(ref === false){ - reject(new Error("unable to push hook event. LiveView not connected")) - } - } catch (error){ - reject(error) - } - }) - } - return this.__view().pushHookEvent(this.el, null, event, payload, onReply) + return promise.then(({reply}) => reply) + } + promise.then(({reply, ref}) => onReply(reply, ref)).catch(() => {}) + // return nothing (for now) + return } pushEventTo(phxTarget, event, payload = {}, onReply){ if(onReply === undefined){ - return new Promise((resolve, reject) => { - try { - this.__view().withinTargets(phxTarget, (view, targetCtx) => { - const ref = view.pushHookEvent(this.el, targetCtx, event, payload, (reply, _ref) => resolve(reply)) - if(ref === false){ - reject(new Error("unable to push hook event. LiveView not connected")) - } - }) - } catch (error){ - reject(error) - } + const targetPair = [] + this.__view().withinTargets(phxTarget, (view, targetCtx) => { + targetPair.push({view, targetCtx}) }) + const promises = targetPair.map(({view, targetCtx}) => { + return view.pushHookEvent(this.el, targetCtx, event, payload) + }) + return Promise.allSettled(promises) } - return this.__view().withinTargets(phxTarget, (view, targetCtx) => { - return view.pushHookEvent(this.el, targetCtx, event, payload, onReply) + this.__view().withinTargets(phxTarget, (view, targetCtx) => { + return view.pushHookEvent(this.el, targetCtx, event, payload) + .then(({reply, ref}) => onReply(reply, ref)) + .catch(() => {}) }) + // return nothing (for now) + return } handleEvent(event, callback){ diff --git a/assets/test/event_test.js b/assets/test/event_test.js index 0f4c7fa3b1..bf0ae46395 100644 --- a/assets/test/event_test.js +++ b/assets/test/event_test.js @@ -35,6 +35,22 @@ let stubNextChannelReply = (view, replyPayload) => { } } +let stubNextChannelReplyWithError = (view, reason) => { + let oldPush = view.channel.push + view.channel.push = () => { + return { + receives: [], + receive(kind, cb){ + if(kind === "error"){ + cb(reason) + view.channel.push = oldPush + } + return this + } + } + } +} + describe("events", () => { let processedEvents beforeEach(() => { @@ -153,13 +169,12 @@ describe("pushEvent replies", () => { test("reply", (done) => { let view - let pushedRef = null let liveSocket = new LiveSocket("/live", Socket, { hooks: { Gateway: { mounted(){ stubNextChannelReply(view, {transactionID: "1001"}) - pushedRef = this.pushEvent("charge", {amount: 123}, (resp, ref) => { + this.pushEvent("charge", {amount: 123}, (resp, ref) => { processedReplies.push({resp, ref}) view.el.dispatchEvent(new CustomEvent("replied", {detail: {resp, ref}})) }) @@ -176,7 +191,6 @@ describe("pushEvent replies", () => { }, []) view.el.addEventListener("replied", () => { - expect(pushedRef).toEqual(0) expect(processedReplies).toEqual([{resp: {transactionID: "1001"}, ref: 0}]) done() }) @@ -211,15 +225,74 @@ describe("pushEvent replies", () => { }) }) - test("pushEvent without connection noops", () => { + test("rejects with error", (done) => { + let view + let liveSocket = new LiveSocket("/live", Socket, { + hooks: { + Gateway: { + mounted(){ + stubNextChannelReplyWithError(view, "error") + this.pushEvent("charge", {amount: 123}).catch((error) => { + expect(error).toEqual(expect.any(Error)) + done() + }) + } + } + } + }) + view = simulateView(liveSocket, [], "") + view.update({ + s: [` +