Skip to content

Commit

Permalink
Merge pull request #19652 from dannon/workflow-landing-tweaks
Browse files Browse the repository at this point in the history
Workflow landing request - collapse activity bar by default.
  • Loading branch information
ahmedhamidawan authored Feb 23, 2025
2 parents 60c080b + ef0bec7 commit e4c3590
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 85 deletions.
3 changes: 1 addition & 2 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const emit = defineEmits<{
}>();
// activities from store
const { activities } = storeToRefs(activityStore);
const { activities, isSideBarOpen } = storeToRefs(activityStore);
// drag references
const dragTarget: Ref<EventTarget | null> = ref(null);
Expand All @@ -100,7 +100,6 @@ const isDragging = ref(false);
// computed values
const canDrag = computed(() => isActiveSideBar("settings"));
const isSideBarOpen = computed(() => activityStore.toggledSideBar !== "");
/**
* Checks if the route of an activity is currently being visited and panels are collapsed
Expand Down
76 changes: 16 additions & 60 deletions client/src/components/Landing/WorkflowLanding.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { ref, watch } from "vue";
import { useRouter } from "vue-router/composables";
import { GalaxyApi } from "@/api";
import { useUserStore } from "@/stores/userStore";
import { errorMessageAsString } from "@/utils/simple-error";
import { useActivityStore } from "@/stores/activityStore";
import { useWorkflowLandingStore } from "@/stores/workflowLandingStore";
import LoadingSpan from "@/components/LoadingSpan.vue";
import WorkflowRun from "@/components/Workflow/Run/WorkflowRun.vue";
Expand All @@ -22,75 +19,34 @@ const props = withDefaults(defineProps<Props>(), {
public: false,
});
const workflowId = ref<string | null>(null);
const errorMessage = ref<string | null>(null);
const requestState = ref<Record<string, never> | null>(null);
const instance = ref<boolean>(false);
const userStore = useUserStore();
const router = useRouter();
const store = useWorkflowLandingStore();
const { claimWorkflow } = store;
const { claimState } = storeToRefs(store);
userStore.loadUser(false);
const { isAnonymous, currentUser } = storeToRefs(userStore);
const activityStore = useActivityStore("default");
watch(
currentUser,
async () => {
if (isAnonymous.value) {
router.push(
`/login/start?redirect=/workflow_landings/${props.uuid}?public=${props.public}&client_secret=${props.secret}`
);
} else if (currentUser.value) {
let claim;
let claimError;
if (props.public) {
const { data, error } = await GalaxyApi().GET("/api/workflow_landings/{uuid}", {
params: {
path: { uuid: props.uuid },
},
});
claim = data;
claimError = error;
} else {
const { data, error } = await GalaxyApi().POST("/api/workflow_landings/{uuid}/claim", {
params: {
path: { uuid: props.uuid },
},
body: {
client_secret: props.secret,
},
});
claim = data;
claimError = error;
}
if (claim) {
workflowId.value = claim.workflow_id;
instance.value = claim.workflow_target_type === "workflow";
requestState.value = claim.request_state;
} else {
errorMessage.value = errorMessageAsString(claimError);
}
}
},
{ immediate: true }
);
// Start claim immediately
claimWorkflow(props.uuid, props.public, props.secret).then(() => {
activityStore.closeSideBar();
});
</script>

<template>
<div>
<div v-if="errorMessage">
<div v-if="claimState.errorMessage">
<BAlert variant="danger" show>
{{ errorMessage }}
{{ claimState.errorMessage }}
</BAlert>
</div>
<div v-else-if="!workflowId">
<div v-else-if="!claimState.workflowId">
<LoadingSpan message="Loading workflow parameters" />
</div>
<div v-else>
<WorkflowRun
:workflow-id="workflowId"
:workflow-id="claimState.workflowId"
:prefer-simple-form="true"
:request-state="requestState"
:instance="instance" />
:request-state="claimState.requestState"
:instance="claimState.instance" />
</div>
</div>
</template>
2 changes: 2 additions & 0 deletions client/src/entry/analysis/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import CreateFileSourceInstance from "@/components/FileSources/Instances/CreateI
import GridHistory from "@/components/Grid/GridHistory";
import GridPage from "@/components/Grid/GridPage";
import CreateObjectStoreInstance from "@/components/ObjectStore/Instances/CreateInstance";
import { requireAuth } from "@/router/guards";
import { parseBool } from "@/utils/utils";

import { patchRouterPush } from "./router-push";
Expand Down Expand Up @@ -523,6 +524,7 @@ export function getRouter(Galaxy) {
public: route.query.public.toLowerCase() === "true",
secret: route.query.client_secret,
}),
beforeEnter: requireAuth,
},
{
path: "user",
Expand Down
19 changes: 19 additions & 0 deletions client/src/router/guards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NavigationGuardNext, Route } from "vue-router";

import { useUserStore } from "@/stores/userStore";

export async function requireAuth(to: Route, from: Route, next: NavigationGuardNext) {
const userStore = useUserStore();
await userStore.loadUser(false);

if (userStore.isAnonymous) {
next({
path: "/login/start",
query: {
redirect: to.fullPath,
},
});
return;
}
next();
}
9 changes: 8 additions & 1 deletion client/src/stores/activityStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,18 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {

const customDefaultActivities = ref<Activity[] | null>(null);
const currentDefaultActivities = computed(() => customDefaultActivities.value ?? defaultActivities);
const isSideBarOpen = computed(() => toggledSideBar.value !== "" && toggledSideBar.value !== "closed");

const toggledSideBar = useUserLocalStorage(`activity-store-current-side-bar-${scope}`, "tools");

function toggleSideBar(currentOpen = "") {
toggledSideBar.value = toggledSideBar.value === currentOpen ? "" : currentOpen;
}

function closeSideBar() {
toggledSideBar.value = "closed";
}

function overrideDefaultActivities(activities: Activity[]) {
customDefaultActivities.value = activities;
sync();
Expand Down Expand Up @@ -130,7 +135,7 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
activities.value = newActivities;

// if toggled side-bar does not exist, choose the first option
if (toggledSideBar.value !== "") {
if (isSideBarOpen.value) {
const allSideBars = activities.value.flatMap((activity) => {
if (activity.panel) {
return [activity.id];
Expand Down Expand Up @@ -196,6 +201,8 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
return {
toggledSideBar,
toggleSideBar,
closeSideBar,
isSideBarOpen,
activities,
activityMeta,
metaForId,
Expand Down
51 changes: 29 additions & 22 deletions client/src/stores/userStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,37 @@ export const useUserStore = defineStore("userStore", () => {

function loadUser(includeHistories = true) {
if (!loadPromise) {
loadPromise = getCurrentUser()
.then(async (user) => {
if (isRegisteredUser(user)) {
currentUser.value = user;
currentPreferences.value = processUserPreferences(user);
} else if (isAnonymousUser(user)) {
currentUser.value = user;
} else if (user === null) {
currentUser.value = null;
loadPromise = new Promise<void>((resolve, reject) => {
(async () => {
console.debug("Loading once");
try {
const user = await getCurrentUser();

if (isRegisteredUser(user)) {
currentUser.value = user;
currentPreferences.value = processUserPreferences(user);
} else if (isAnonymousUser(user)) {
currentUser.value = user;
} else if (user === null) {
currentUser.value = null;
}
if (includeHistories) {
const historyStore = useHistoryStore();
await historyStore.loadHistories();
}
resolve(); // Resolve the promise after successful load
} catch (e) {
console.error("Failed to load user", e);
reject(e); // Reject the promise on error
} finally {
//Don't clear the loadPromise, we still want multiple callers to await.
//Instead we must clear it upon $reset
// loadPromise = null;
}

if (includeHistories) {
const historyStore = useHistoryStore();
// load first few histories for user to start pagination
await historyStore.loadHistories();
}
})
.catch((e) => {
console.error("Failed to load user", e);
})
.finally(() => {
loadPromise = null;
});
})();
});
}
return loadPromise; // Return the shared promise
}

async function setCurrentTheme(theme: string) {
Expand Down
71 changes: 71 additions & 0 deletions client/src/stores/workflowLandingStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { defineStore } from "pinia";
import { ref } from "vue";

import { GalaxyApi } from "@/api";
import { errorMessageAsString } from "@/utils/simple-error";

interface ClaimState {
workflowId: string | null;
instance: boolean;
requestState: Record<string, never> | null;
errorMessage: string | null;
}

export const useWorkflowLandingStore = defineStore("workflowLanding", () => {
const claimState = ref<ClaimState>({
workflowId: null,
instance: false,
requestState: null,
errorMessage: null,
});

async function claimWorkflow(uuid: string, isPublic: boolean, secret?: string) {
let claim;
let claimError;

console.debug("Claiming workflow");
if (isPublic) {
const { data, error } = await GalaxyApi().GET("/api/workflow_landings/{uuid}", {
params: {
path: { uuid },
},
});
claim = data;
claimError = error;
} else {
const { data, error } = await GalaxyApi().POST("/api/workflow_landings/{uuid}/claim", {
params: {
path: { uuid },
},
body: {
client_secret: secret,
},
});
claim = data;
claimError = error;
}

if (claim) {
console.debug("CLaim!", claim);
claimState.value = {
workflowId: claim.workflow_id,
instance: claim.workflow_target_type === "workflow",
requestState: claim.request_state,
errorMessage: null,
};
} else {
console.debug("Claim error", claimError);
claimState.value = {
workflowId: null,
instance: false,
requestState: null,
errorMessage: errorMessageAsString(claimError),
};
}
}

return {
claimState,
claimWorkflow,
};
});

0 comments on commit e4c3590

Please sign in to comment.