Decompose userLocalStorage, watchHistory, etc... to avoid circular dependencies.

This commit is contained in:
John Chilton
2025-04-10 11:34:12 -04:00
parent ea221fec48
commit f5baee47e8
12 changed files with 88 additions and 46 deletions
@@ -14,7 +14,7 @@ import { useHistoryStore } from "@/stores/historyStore";
import { useUserStore } from "@/stores/userStore";
import { type Alias, getOperatorForAlias } from "@/utils/filtering";
import { setItemDragstart } from "@/utils/setDrag";
import { startWatchingHistory } from "@/watch/watchHistory";
import { startWatchingHistory } from "@/watch/watchHistoryProvided";
import { useHistoryDragDrop } from "../../../composables/historyDragDrop";
@@ -6,7 +6,7 @@ import Backbone from "backbone";
import { createDatasetCollection } from "components/History/model/queries";
import { useHistoryItemsStore } from "stores/historyItemsStore";
import { useHistoryStore } from "stores/historyStore";
import { startWatchingHistory } from "watch/watchHistory";
import { startWatchingHistory } from "watch/watchHistoryProvided";
import { buildRuleCollectionModal } from "./buildCollectionModal";
@@ -593,7 +593,7 @@ import _l from "utils/localization";
import Vue from "vue";
import { errorMessageAsString } from "@/utils/simple-error";
import { startWatchingHistory } from "@/watch/watchHistory";
import { startWatchingHistory } from "@/watch/watchHistoryProvided";
import TooltipOnHover from "components/TooltipOnHover.vue";
+1 -1
View File
@@ -122,7 +122,7 @@ import { canMutateHistory } from "@/api";
import { useConfigStore } from "@/stores/configurationStore";
import { useHistoryStore } from "@/stores/historyStore";
import { useUserStore } from "@/stores/userStore";
import { startWatchingHistory } from "@/watch/watchHistory";
import { startWatchingHistory } from "@/watch/watchHistoryProvided";
import ToolRecommendation from "../ToolRecommendation";
import { getToolFormData, submitJob, updateToolFormData } from "./services";
@@ -4,7 +4,7 @@ import { onMounted } from "vue";
import type { WorkflowInvocation } from "@/api/invocations";
import { useHistoryStore } from "@/stores/historyStore";
import Webhooks from "@/utils/webhooks";
import { startWatchingHistory } from "@/watch/watchHistory";
import { startWatchingHistory } from "@/watch/watchHistoryProvided";
import GridInvocation from "@/components/Grid/GridInvocation.vue";
import WorkflowInvocationState from "@/components/WorkflowInvocationState/WorkflowInvocationState.vue";
+10 -7
View File
@@ -1,4 +1,4 @@
export type WatchResourceHandler = () => Promise<void>;
export type WatchResourceHandler<T = unknown> = (app?: T) => Promise<void>;
export interface WatchOptions {
/**
@@ -29,7 +29,10 @@ const DEFAULT_WATCH_OPTIONS: WatchOptions = {
* @param watchHandler The handler function that watches the resource by querying the server.
* @param options Options to customize the polling interval.
*/
export function useResourceWatcher(watchHandler: WatchResourceHandler, options: WatchOptions = DEFAULT_WATCH_OPTIONS) {
export function useResourceWatcher<T = unknown>(
watchHandler: WatchResourceHandler,
options: WatchOptions = DEFAULT_WATCH_OPTIONS
) {
const { shortPollingInterval, longPollingInterval, enableBackgroundPolling } = {
...DEFAULT_WATCH_OPTIONS,
...options,
@@ -41,9 +44,9 @@ export function useResourceWatcher(watchHandler: WatchResourceHandler, options:
/**
* Starts watching the resource by polling the server continuously.
*/
function startWatchingResource() {
function startWatchingResource(app?: T) {
stopWatcher();
tryWatchResource();
tryWatchResource(app);
}
/**
@@ -60,15 +63,15 @@ export function useResourceWatcher(watchHandler: WatchResourceHandler, options:
}
}
async function tryWatchResource() {
async function tryWatchResource(app?: T) {
try {
await watchHandler();
await watchHandler(app);
} catch (error) {
console.warn(error);
} finally {
if (currentPollingInterval) {
watchTimeout = setTimeout(() => {
tryWatchResource();
tryWatchResource(app);
}, currentPollingInterval);
}
}
+4 -19
View File
@@ -1,37 +1,22 @@
import { watchImmediate } from "@vueuse/core";
import { type Ref, ref } from "vue";
import { type Ref, type UnwrapRef } from "vue";
import { type AnyUser } from "@/api";
import { useHashedUserId } from "./hashedUserId";
import { useHashedUserId as useHashedUserIdFromStore } from "./hashedUserIdFromUserStore";
import { syncRefToLocalStorage } from "./persistentRef";
import { useUserLocalStorageFromHashId } from "./userLocalStorageFromHashedId";
/**
* 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<UnwrapRef<T>> {
let hashedUserId;
if (user) {
hashedUserId = useHashedUserId(user).hashedUserId;
} else {
hashedUserId = useHashedUserIdFromStore().hashedUserId;
}
const refToSync = ref(initialValue);
let hasSynced = false;
watchImmediate(
() => hashedUserId.value,
() => {
if (hashedUserId.value && !hasSynced) {
syncRefToLocalStorage(`${key}-${hashedUserId.value}`, refToSync);
hasSynced = true;
}
}
);
return refToSync;
return useUserLocalStorageFromHashId<T>(key, initialValue, hashedUserId);
}
@@ -0,0 +1,30 @@
import { watchImmediate } from "@vueuse/core";
import { type Ref, ref, type UnwrapRef } from "vue";
import { syncRefToLocalStorage } from "./persistentRef";
/**
* Local storage composable specific to current user.
* @param key
* @param initialValue
*/
export function useUserLocalStorageFromHashId<T>(
key: string,
initialValue: T,
hashedUserId: Ref<string | null>
): Ref<UnwrapRef<T>> {
const refToSync = ref(initialValue);
let hasSynced = false;
watchImmediate(
() => hashedUserId.value,
() => {
if (hashedUserId.value && !hasSynced) {
syncRefToLocalStorage(`${key}-${hashedUserId.value}`, refToSync);
hasSynced = true;
}
}
);
return refToSync;
}
+6 -4
View File
@@ -2,7 +2,8 @@ import { defineStore } from "pinia";
import { computed, ref } from "vue";
import { type AnyUser, isAdminUser, isAnonymousUser, isRegisteredUser, type RegisteredUser } from "@/api";
import { useUserLocalStorage } from "@/composables/userLocalStorage";
import { useHashedUserId } from "@/composables/hashedUserId";
import { useUserLocalStorageFromHashId } from "@/composables/userLocalStorageFromHashedId";
import { useHistoryStore } from "@/stores/historyStore";
import {
addFavoriteToolQuery,
@@ -28,14 +29,15 @@ type UserListViewPreferences = Record<string, ListViewMode>;
export const useUserStore = defineStore("userStore", () => {
const currentUser = ref<AnyUser>(null);
const currentPreferences = ref<Preferences | null>(null);
const { hashedUserId } = useHashedUserId(currentUser);
const currentListViewPreferences = useUserLocalStorage<UserListViewPreferences>(
const currentListViewPreferences = useUserLocalStorageFromHashId<UserListViewPreferences>(
"user-store-list-view-preferences",
{},
currentUser
hashedUserId
);
const hasSeenUploadHelp = useUserLocalStorage("user-store-seen-upload-help", false, currentUser);
const hasSeenUploadHelp = useUserLocalStorageFromHashId("user-store-seen-upload-help", false, hashedUserId);
let loadPromise: Promise<void> | null = null;
+3 -1
View File
@@ -89,6 +89,8 @@ function _mountSelectionDialog(clazz, options) {
* TODO: This should live somewhere else.
*/
export function create(options) {
const galaxy = getGalaxyInstance();
async function getHistory() {
if (!options.history_id) {
return getCurrentGalaxyHistory();
@@ -98,7 +100,7 @@ export function create(options) {
getHistory().then((history_id) => {
uploadSubmit({
success: (response) => {
startWatchingHistory();
startWatchingHistory(galaxy);
if (options.success) {
options.success(response);
}
+9 -10
View File
@@ -5,7 +5,6 @@
* submitted, delayed only by the throttle period and the request response time.
*/
import { getGalaxyInstance } from "app";
import { storeToRefs } from "pinia";
import { useHistoryItemsStore } from "stores/historyItemsStore";
import { useHistoryStore } from "stores/historyStore";
@@ -18,8 +17,8 @@ import { useDatasetStore } from "@/stores/datasetStore";
const limit = 1000;
const ACTIVE_POLLING_INTERVAL = 3000;
const INACTIVE_POLLING_INTERVAL = 60000;
export const ACTIVE_POLLING_INTERVAL = 3000;
export const INACTIVE_POLLING_INTERVAL = 60000;
// last time the history has changed
let lastUpdateTime = null;
@@ -34,11 +33,12 @@ const { startWatchingResource: startWatchingHistory } = useResourceWatcher(watch
export { startWatchingHistory };
async function watchHistory() {
export async function watchHistory(app) {
// GalaxyApp
const { isWatching } = storeToRefs(useHistoryItemsStore());
try {
isWatching.value = true;
await watchHistoryOnce();
await watchHistoryOnce(app);
} catch (error) {
// error alerting the user that watch history failed
console.warn(error);
@@ -46,7 +46,7 @@ async function watchHistory() {
}
}
export async function watchHistoryOnce() {
export async function watchHistoryOnce(app) {
const historyStore = useHistoryStore();
const historyItemsStore = useHistoryItemsStore();
const datasetStore = useDatasetStore();
@@ -91,10 +91,9 @@ export async function watchHistoryOnce() {
historyItemsStore.saveHistoryItems(historyId, payload);
collectionElementsStore.saveCollections(payload);
// trigger changes in legacy handler
const Galaxy = getGalaxyInstance();
if (Galaxy) {
Galaxy.user.fetch({
url: `${Galaxy.user.urlRoot()}/${Galaxy.user.id || "current"}`,
if (app) {
app.user.fetch({
url: `${app.user.urlRoot()}/${app.user.id || "current"}`,
});
}
}
+21
View File
@@ -0,0 +1,21 @@
import { getGalaxyInstance } from "app";
import { useResourceWatcher } from "@/composables/resourceWatcher";
import {
ACTIVE_POLLING_INTERVAL,
INACTIVE_POLLING_INTERVAL,
watchHistory as watchHistorySuppliedApp,
} from "./watchHistory";
function watchHistory() {
const app = getGalaxyInstance();
return watchHistorySuppliedApp(app);
}
const { startWatchingResource: startWatchingHistory } = useResourceWatcher(watchHistory, {
shortPollingInterval: ACTIVE_POLLING_INTERVAL,
longPollingInterval: INACTIVE_POLLING_INTERVAL,
});
export { startWatchingHistory };