diff --git a/client/src/components/ActivityBar/ActivityBar.vue b/client/src/components/ActivityBar/ActivityBar.vue index cde4d7b190ad..51fa15b085e9 100644 --- a/client/src/components/ActivityBar/ActivityBar.vue +++ b/client/src/components/ActivityBar/ActivityBar.vue @@ -132,11 +132,21 @@ function onToggleSidebar(toggle: string = "", to: string | null = null) { userStore.toggleSideBar(toggle); } +const syncActivities = () => { + activityStore.sync(); + if (config.value && config.value.client_mode == "minimal_workflow") { + userStore.untoggleToolbarIfNeeded(); + } +}; + watch( () => hashedUserId.value, - () => { - activityStore.sync(); - } + syncActivities, +); + +watch( + isConfigLoaded, + syncActivities, ); diff --git a/client/src/components/Workflow/List/WorkflowList.vue b/client/src/components/Workflow/List/WorkflowList.vue index 3bcb98b99dcd..bb81681e1275 100644 --- a/client/src/components/Workflow/List/WorkflowList.vue +++ b/client/src/components/Workflow/List/WorkflowList.vue @@ -28,10 +28,14 @@ type WorkflowsList = Record[]; interface Props { activeList?: "my" | "shared_with_me" | "published"; + advancedOptions?: boolean; + initialFilterText?: string; } const props = withDefaults(defineProps(), { activeList: "my", + advancedOptions: true, + initialFilterText: "", }); const router = useRouter(); @@ -194,6 +198,16 @@ watch([filterText, sortBy, sortDesc, showBookmarked], async () => { await load(true); }); +watch( + props, + () => { + if(props.initialFilterText && filterText.value == "") { + filterText.value = props.initialFilterText; + } + }, + { immediate: true }, +); + onMounted(() => { if (router.currentRoute.query.owner) { updateFilterValue("user", `'${router.currentRoute.query.owner}'`); @@ -208,7 +222,7 @@ onMounted(() => {
Workflows - +
@@ -219,6 +233,7 @@ onMounted(() => { @@ -247,7 +262,7 @@ onMounted(() => { - + @@ -10,6 +13,7 @@ + + diff --git a/client/src/stores/activitySetup.ts b/client/src/stores/activitySetup.ts index 17551fc89e2b..1d3109e27b87 100644 --- a/client/src/stores/activitySetup.ts +++ b/client/src/stores/activitySetup.ts @@ -1,22 +1,30 @@ /** * List of built-in activities */ -import { type Activity } from "@/stores/activityStore"; +import { type ClientMode, type Activity, type RawActivity } from "@/stores/activityStore"; import { type EventData } from "@/stores/eventStore"; -export const Activities = [ +function unlessMinimalWorkflow(clientMode: ClientMode): boolean { + return !(clientMode == "minimal_workflow"); +} + +function ifMinimalWorkflow(clientMode: ClientMode): boolean { + return !(clientMode == "minimal_workflow"); +} + +export const ActivitiesRaw: RawActivity[] = [ { anonymous: false, description: "Displays currently running interactive tools (ITs), if these are enabled by the administrator.", icon: "fa-laptop", id: "interactivetools", mutable: false, - optional: false, + optional: ifMinimalWorkflow, panel: false, title: "Interactive Tools", tooltip: "Show active interactive tools", to: "/interactivetool_entry_points/list", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: true, @@ -24,12 +32,12 @@ export const Activities = [ icon: "upload", id: "upload", mutable: false, - optional: false, + optional: ifMinimalWorkflow, panel: false, title: "Upload", to: null, tooltip: "Download from URL or upload files from disk", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: true, @@ -37,12 +45,12 @@ export const Activities = [ icon: "wrench", id: "tools", mutable: false, - optional: false, + optional: ifMinimalWorkflow, panel: true, title: "Tools", - to: null, + to: "/tools", tooltip: "Search and run tools", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: true, @@ -81,7 +89,7 @@ export const Activities = [ title: "Visualization", to: null, tooltip: "Visualize datasets", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: true, @@ -94,7 +102,7 @@ export const Activities = [ title: "Histories", tooltip: "Show all histories", to: "/histories/list", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: false, @@ -107,7 +115,7 @@ export const Activities = [ title: "History Multiview", tooltip: "Select histories to show in History Multiview", to: "/histories/view_multiple", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: false, @@ -120,7 +128,7 @@ export const Activities = [ title: "Datasets", tooltip: "Show all datasets", to: "/datasets/list", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: true, @@ -133,7 +141,7 @@ export const Activities = [ title: "Pages", tooltip: "Show all pages", to: "/pages/list", - visible: true, + visible: unlessMinimalWorkflow, }, { anonymous: false, @@ -150,6 +158,25 @@ export const Activities = [ }, ]; +function resolveActivity(activity: RawActivity, clientMode: ClientMode) : Activity { + let optional = activity.optional; + let visible = activity.visible; + if (typeof optional === 'function') { + optional = optional(clientMode); + } + if (typeof visible === 'function') { + visible = visible(clientMode); + } + return { ...activity, optional, visible}; +} + +export function getActivities(clientMode: ClientMode) { + const resolve = (activity: RawActivity) => { + return resolveActivity(activity, clientMode); + } + return ActivitiesRaw.map(resolve); +} + export function convertDropData(data: EventData): Activity | null { if (data.history_content_type === "dataset") { return { diff --git a/client/src/stores/activityStore.ts b/client/src/stores/activityStore.ts index 377a68e08964..ae31e727fbe6 100644 --- a/client/src/stores/activityStore.ts +++ b/client/src/stores/activityStore.ts @@ -6,8 +6,9 @@ import { defineStore } from "pinia"; import { type Ref } from "vue"; import { useUserLocalStorage } from "@/composables/userLocalStorage"; +import { useConfig } from "@/composables/config"; -import { Activities } from "./activitySetup"; +import { getActivities } from "./activitySetup"; export interface Activity { // determine wether an anonymous user can access this activity @@ -34,6 +35,34 @@ export interface Activity { visible: boolean; } +export type ClientMode = "full" | "minimal_workflow"; + +// config materializes a RawActivity into an Activity +export interface RawActivity { + // determine wether an anonymous user can access this activity + anonymous: boolean; + // description of the activity + description: string; + // unique identifier + id: string; + // icon to be displayed in activity bar + icon: string; + // indicate if this activity can be modified and/or deleted + mutable: boolean; + // indicate wether this activity can be disabled by the user + optional: boolean | ((mode: ClientMode) => boolean); + // specifiy wether this activity utilizes the side panel + panel: boolean; + // title to be displayed in the activity bar + title: string; + // route to be executed upon selecting the activity + to: string | null; + // tooltip to be displayed when hovering above the icon + tooltip: string; + // indicate wether the activity should be visible by default + visible: boolean | ((mode: ClientMode) => boolean); +} + export const useActivityStore = defineStore("activityStore", () => { const activities: Ref> = useUserLocalStorage("activity-store-activities", []); @@ -50,11 +79,18 @@ export const useActivityStore = defineStore("activityStore", () => { * to the user stored activities which are persisted in local cache. */ function sync() { + const { config, isConfigLoaded } = useConfig(); + if (!isConfigLoaded.value) { + return; + } + // create a map of built-in activities const activitiesMap: Record = {}; - Activities.forEach((a) => { + let activityDefs = getActivities(config.value.client_mode); + activityDefs.forEach((a) => { activitiesMap[a.id] = a; }); + // create an updated array of activities const newActivities: Array = []; const foundActivity = new Set(); @@ -76,7 +112,7 @@ export const useActivityStore = defineStore("activityStore", () => { } }); // add new built-in activities - Activities.forEach((a) => { + activityDefs.forEach((a) => { if (!foundActivity.has(a.id)) { newActivities.push({ ...a }); } diff --git a/client/src/stores/userStore.ts b/client/src/stores/userStore.ts index 55a49d491209..9379bcb89634 100644 --- a/client/src/stores/userStore.ts +++ b/client/src/stores/userStore.ts @@ -146,6 +146,12 @@ export const useUserStore = defineStore("userStore", () => { }; } + function untoggleToolbarIfNeeded() { + if (toggledSideBar.value == "tools") { + toggledSideBar.value = ""; + } + } + return { currentUser, currentPreferences, @@ -163,6 +169,7 @@ export const useUserStore = defineStore("userStore", () => { addFavoriteTool, removeFavoriteTool, toggleSideBar, + untoggleToolbarIfNeeded, $reset, }; }); diff --git a/lib/galaxy/config/schemas/config_schema.yml b/lib/galaxy/config/schemas/config_schema.yml index 5098d923d8ca..ad1494e3f950 100644 --- a/lib/galaxy/config/schemas/config_schema.yml +++ b/lib/galaxy/config/schemas/config_schema.yml @@ -3134,6 +3134,24 @@ mapping: When false, the most recently added compatible item in the history will be used for each "Set at Runtime" input, independent of others in the workflow. + client_mode: + type: str + default: 'full' + enum: ['full', 'minimal_workflow'] + required: false + per_host: true + desc: | + Set this to 'minimal_workflow' to turn on the simplified Galaxy workflow + UI. + + simplified_workflow_landing_initial_tags: + type: str + default: null + required: false + per_host: true + desc: | + I will write this in later. + simplified_workflow_run_ui: type: str default: 'prefer' diff --git a/lib/galaxy/managers/configuration.py b/lib/galaxy/managers/configuration.py index d0ccfa8d53c2..61ffbaa504ff 100644 --- a/lib/galaxy/managers/configuration.py +++ b/lib/galaxy/managers/configuration.py @@ -164,6 +164,8 @@ def _config_is_truthy(item, key, **context): "enable_unique_workflow_defaults": _use_config, "enable_beta_markdown_export": _use_config, "enable_beacon_integration": _use_config, + "client_mode": _use_config, + "simplified_workflow_landing_initial_tags": _use_config, "simplified_workflow_run_ui": _use_config, "simplified_workflow_run_ui_target_history": _use_config, "simplified_workflow_run_ui_job_cache": _use_config,