Skip to content

Commit

Permalink
Prevent pushHistoryPatches from using an old location
Browse files Browse the repository at this point in the history
There are situations where the browsers location.href hasn't been
changed yet but the component has already mounted, leaving the diagram
to navigate with a selection query param on the workflow list url
instead of the workflow edit url.

So we store the url in the liveview process and set it as a `data-` attr
on the hook. Since we can't trust the browsers location we have to
track it elsewhere.
  • Loading branch information
stuartc committed Aug 4, 2023
1 parent d297c57 commit 5cf83f6
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 37 deletions.
96 changes: 67 additions & 29 deletions assets/js/workflow-editor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,38 @@ import {
createWorkflowStore,
} from './store';

type WorkflowEditorEntrypoint = PhoenixHook<{
_isMounting: boolean;
_pendingWorker: Promise<void>;
abortController: AbortController | null;
component: ReturnType<typeof mount> | null;
componentModule: Promise<{ mount: typeof mount }>;
getWorkflowParams(): void;
getItem(
id?: string
): Lightning.TriggerNode | Lightning.JobNode | Lightning.Edge | undefined;
handleWorkflowParams(payload: { workflow_params: WorkflowProps }): void;
maybeMountComponent(): void;
onSelectionChange(id?: string): void;
pendingChanges: PendingAction[];
processPendingChanges(): void;
pushPendingChange(
pendingChange: PendingAction,
abortController: AbortController
): Promise<boolean>;
workflowStore: ReturnType<typeof createWorkflowStore>;
}>;
type AttributeMutationRecord = MutationRecord & {
attributeName: string;
oldValue: string;
};

type WorkflowEditorEntrypoint = PhoenixHook<
{
_isMounting: boolean;
_pendingWorker: Promise<void>;
abortController: AbortController | null;
component: ReturnType<typeof mount> | null;
componentModule: Promise<{ mount: typeof mount }>;
getWorkflowParams(): void;
getItem(
id?: string
): Lightning.TriggerNode | Lightning.JobNode | Lightning.Edge | undefined;
handleWorkflowParams(payload: { workflow_params: WorkflowProps }): void;
maybeMountComponent(): void;
onSelectionChange(id?: string): void;
pendingChanges: PendingAction[];
processPendingChanges(): void;
pushPendingChange(
pendingChange: PendingAction,
abortController: AbortController
): Promise<boolean>;
workflowStore: ReturnType<typeof createWorkflowStore>;
observer: MutationObserver | null;
setupObserver(): void;
baseUrl: string | null;
},
{ baseUrl: string | null }
>;

const createNewWorkflow = () => {
const triggers = [
Expand Down Expand Up @@ -61,6 +72,17 @@ const createNewWorkflow = () => {

export default {
mounted(this: WorkflowEditorEntrypoint) {
// Workaround for situations where the hook is mounted before the
// browser has updated window.location.href - it's rare, but it happens.
// Without it, the hook will try to push a history patch to the wrong
// URL.
const { baseUrl } = this.el.dataset;
if (!baseUrl) {
throw new Error('WorkflowEditor requires a data-base-url attribute');
}

this.baseUrl = baseUrl;

console.debug('WorkflowEditor hook mounted');

this._pendingWorker = Promise.resolve();
Expand Down Expand Up @@ -102,6 +124,26 @@ export default {
// between the current state and the server state and send those diffs
// to the server.
},
setupObserver() {
this.observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
const { attributeName, oldValue } = mutation as AttributeMutationRecord;

if (attributeName == 'data-base-url') {
const newValue = this.el.getAttribute(attributeName);

if (oldValue !== newValue) {
this.baseUrl = newValue;
}
}
});
});

this.observer.observe(this.el, {
attributeFilter: ['data-base-url'],
attributeOldValue: true,
});
},
getItem(id?: string) {
if (id) {
const { jobs, triggers, edges } = this.workflowStore.getState();
Expand All @@ -114,7 +156,7 @@ export default {
}
},
onSelectionChange(id?: string) {
const currentUrl = new URL(window.location.href);
const currentUrl = new URL(this.baseUrl!);
const nextUrl = new URL(currentUrl);

const idExists = this.getItem(id);
Expand Down Expand Up @@ -143,13 +185,9 @@ export default {
}
},
destroyed() {
if (this.component) {
this.component.unmount();
}

if (this.abortController) {
this.abortController.abort();
}
this.component?.unmount();
this.abortController?.abort();
this.observer?.disconnect();

console.debug('WorkflowEditor destroyed');
},
Expand Down
13 changes: 5 additions & 8 deletions lib/lightning_web/live/workflow_live/edit.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ defmodule LightningWeb.WorkflowLive.Edit do
phx-hook="WorkflowEditor"
class="grow"
id={"editor-#{@workflow.id}"}
data-base-url={@current_url}
phx-update="ignore"
>
<%!-- Before Editor component has mounted --%>
Expand Down Expand Up @@ -312,16 +313,17 @@ defmodule LightningWeb.WorkflowLive.Edit do
|> authorize()
|> assign(
active_menu_item: :projects,
current_url: nil,
expanded_job: nil,
follow_run_id: nil,
page_title: "",
selected_edge: nil,
selected_job: nil,
selected_trigger: nil,
selection_mode: nil,
selection_params: %{"s" => nil, "m" => nil},
workflow: nil,
workflow_params: %{},
selection_params: %{"s" => nil, "m" => nil}
workflow_params: %{}
)}
end

Expand All @@ -330,7 +332,7 @@ defmodule LightningWeb.WorkflowLive.Edit do
{:noreply,
apply_action(socket, socket.assigns.live_action, params)
|> apply_selection_params(params)
|> assign_url(url)}
|> assign(current_url: url)}
end

def apply_action(socket, :new, _params) do
Expand Down Expand Up @@ -665,11 +667,6 @@ defmodule LightningWeb.WorkflowLive.Edit do
end
end

defp assign_url(socket, url) do
socket
|> assign(url: URI.parse(url))
end

defp build_next_path(socket, workflow) do
%{project: project, selection_params: selection_params} = socket.assigns

Expand Down

0 comments on commit 5cf83f6

Please sign in to comment.