Skip to content

Instantly share code, notes, and snippets.

@dannon
Created March 3, 2025 20:30
Show Gist options
  • Save dannon/805146a51511dbf90281979eeae2fe82 to your computer and use it in GitHub Desktop.
Save dannon/805146a51511dbf90281979eeae2fe82 to your computer and use it in GitHub Desktop.
diff --git a/client/src/api/index.ts b/client/src/api/index.ts
index 6f9ce9d021..361c02a8e0 100644
--- a/client/src/api/index.ts
+++ b/client/src/api/index.ts
@@ -266,6 +266,7 @@ export function isRegisteredUser(user: AnyUser | UserModel): user is RegisteredU
}
export function isAnonymousUser(user: AnyUser | UserModel): user is AnonymousUser {
+ console.debug("CHECKING IS ANONYMOUS USER ", user);
return user !== null && !isRegisteredUser(user);
}
diff --git a/client/src/components/Landing/WorkflowLanding.vue b/client/src/components/Landing/WorkflowLanding.vue
index 2677d0a09b..503b9620b2 100644
--- a/client/src/components/Landing/WorkflowLanding.vue
+++ b/client/src/components/Landing/WorkflowLanding.vue
@@ -1,12 +1,8 @@
<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 { onMounted, ref } from "vue";
import { GalaxyApi } from "@/api";
-import { useActivityStore } from "@/stores/activityStore";
-import { useUserStore } from "@/stores/userStore";
import { errorMessageAsString } from "@/utils/simple-error";
import LoadingSpan from "@/components/LoadingSpan.vue";
@@ -27,56 +23,42 @@ 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();
-userStore.loadUser(false);
-const { isAnonymous, currentUser } = storeToRefs(userStore);
+onMounted(async () => {
+ let claim;
+ let claimError;
-const activityStore = useActivityStore("default");
+ 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;
+ }
-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;
- activityStore.closeSideBar();
- 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 }
-);
+ if (claim) {
+ workflowId.value = claim.workflow_id;
+ instance.value = claim.workflow_target_type === "workflow";
+ requestState.value = claim.request_state;
+ } else {
+ errorMessage.value = errorMessageAsString(claimError);
+ }
+ const activityStore = useActivityStore("default");
+ activityStore.closeSideBar();
+});
</script>
<template>
diff --git a/client/src/composables/hashedUserId.ts b/client/src/composables/hashedUserId.ts
index a7f651a3c6..a40529ee86 100644
--- a/client/src/composables/hashedUserId.ts
+++ b/client/src/composables/hashedUserId.ts
@@ -59,6 +59,7 @@ export function useHashedUserId(user?: Ref<AnyUser>) {
);
async function hashUserId(id: string) {
+ console.debug("Hashing user id", id);
if (unhashedId !== id) {
unhashedId = id;
currentHash.value = null;
diff --git a/client/src/composables/userLocalStorage.ts b/client/src/composables/userLocalStorage.ts
index d81dfda55e..2b1270f34d 100644
--- a/client/src/composables/userLocalStorage.ts
+++ b/client/src/composables/userLocalStorage.ts
@@ -1,28 +1,86 @@
+// userLocalStorage.ts
import { watchImmediate } from "@vueuse/core";
-import { type Ref, ref } from "vue";
+import { type Ref, ref, watch } from "vue";
import { type AnyUser } from "@/api";
import { useHashedUserId } from "./hashedUserId";
import { syncRefToLocalStorage } from "./persistentRef";
+interface PendingChange {
+ key: string;
+ value: any; // Use any here
+ type: "string" | "number" | "boolean" | "object";
+}
+
+const pendingChanges: PendingChange[] = [];
+
/**
* Local storage composable specific to current user.
* @param key
* @param initialValue
*/
-export function useUserLocalStorage<T>(key: string, initialValue: T, user?: Ref<AnyUser>) {
+export function useUserLocalStorage<T>(key: string, initialValue: T, user?: Ref<AnyUser>): Ref<T> {
+ //return Ref<T>
const { hashedUserId } = useHashedUserId(user);
-
- const refToSync = ref(initialValue);
+ const refToSync = ref(initialValue) as Ref<T>;
let hasSynced = false;
+ // Function to apply pending changes
+ const applyPendingChanges = (userId: string) => {
+ const changesToApply = pendingChanges.filter((change) => change.key === key);
+ if (changesToApply.length > 0) {
+ console.debug(`Applying ${changesToApply.length} pending changes for key "${key}"`);
+ changesToApply.forEach((change) => {
+ // No try-catch is needed when applying to memory (it's needed when parsing)
+ // No type checking or casting is needed here!
+ refToSync.value = change.value;
+
+ //remove item from pending changes
+ const index = pendingChanges.indexOf(change);
+ if (index > -1) {
+ pendingChanges.splice(index, 1);
+ }
+ });
+
+ // Sync to local storage after applying in-memory changes.
+ syncRefToLocalStorage(`${key}-${userId}`, refToSync);
+ }
+ };
+
+ // Watch for changes to refToSync and store them if hashedUserId is not yet available.
+ watch(
+ refToSync,
+ (newValue) => {
+ if (!hashedUserId.value) {
+ console.debug(`Queueing change for key "${key}" until user ID is available.`);
+ // Check if a change for this key already exists, and update it, or add a new change
+
+ const existingChangeIndex = pendingChanges.findIndex((change) => change.key === key);
+ const type = typeof newValue as "string" | "number" | "boolean" | "object";
+ if (existingChangeIndex > -1) {
+ pendingChanges[existingChangeIndex] = { key, value: newValue, type };
+ } else {
+ pendingChanges.push({ key, value: newValue, type });
+ }
+ } else if (!hasSynced) {
+ //This case handles calls to setItem that might happen *after* hashedUserId is set,
+ // but *before* this particular watchImmediate has run.
+ syncRefToLocalStorage(`${key}-${hashedUserId.value}`, refToSync);
+ }
+ },
+ { deep: true }
+ );
+
watchImmediate(
() => hashedUserId.value,
() => {
if (hashedUserId.value && !hasSynced) {
+ applyPendingChanges(hashedUserId.value); // Apply pending changes first
syncRefToLocalStorage(`${key}-${hashedUserId.value}`, refToSync);
hasSynced = true;
+ } else {
+ console.debug("NO USER -- SKIPPING", key, hashedUserId.value);
}
}
);
diff --git a/client/src/entry/analysis/router.js b/client/src/entry/analysis/router.js
index 7358e84708..f5ca031300 100644
--- a/client/src/entry/analysis/router.js
+++ b/client/src/entry/analysis/router.js
@@ -54,6 +54,8 @@ import AdminRoutes from "entry/analysis/routes/admin-routes";
import LibraryRoutes from "entry/analysis/routes/library-routes";
import StorageDashboardRoutes from "entry/analysis/routes/storageDashboardRoutes";
import { getAppRoot } from "onload/loadConfig";
+import { useActivityStore } from "stores/activityStore";
+import { useUserStore } from "stores/userStore";
import Vue from "vue";
import VueRouter from "vue-router";
diff --git a/client/src/stores/activityStore.ts b/client/src/stores/activityStore.ts
index f7fad52718..b91904aa5c 100644
--- a/client/src/stores/activityStore.ts
+++ b/client/src/stores/activityStore.ts
@@ -69,7 +69,10 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
}
function closeSideBar() {
+ console.debug("CLOSE SIDEBAR");
toggledSideBar.value = "closed";
+ console.debug("AFTER CLOSE SIDEBAR", isSideBarOpen.value);
+ sync();
}
function overrideDefaultActivities(activities: Activity[]) {
@@ -95,6 +98,7 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
* to the user stored activities which are persisted in local cache.
*/
const sync = useDebounceFn(() => {
+ console.trace("sync");
// create a map of built-in activities
const activitiesMap: Record<string, Activity> = {};
@@ -133,8 +137,8 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
});
activities.value = newActivities;
-
// if toggled side-bar does not exist, choose the first option
+ console.debug("IN SYNC< SIDEBAR OPEN IS", isSideBarOpen.value, "value is ", toggledSideBar.value);
if (isSideBarOpen.value) {
const allSideBars = activities.value.flatMap((activity) => {
if (activity.panel) {
@@ -193,7 +197,8 @@ export const useActivityStore = defineScopedStore("activityStore", (scope) => {
watchImmediate(
() => hashedUserId.value,
- () => {
+ (newValue, oldValue) => {
+ console.debug("Sync from watchImmediate", oldValue, newValue);
sync();
}
);
diff --git a/client/src/stores/userStore.ts b/client/src/stores/userStore.ts
index 4e3499e800..f7f385afd7 100644
--- a/client/src/stores/userStore.ts
+++ b/client/src/stores/userStore.ts
@@ -68,7 +68,7 @@ export const useUserStore = defineStore("userStore", () => {
currentUser.value = user;
}
- function loadUser(includeHistories = true) {
+ function loadUser(includeHistories = true): Promise<void> {
if (!loadPromise) {
loadPromise = new Promise<void>((resolve, reject) => {
(async () => {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment