Skip to content

Commit

Permalink
Merge branch 'release_24.2' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
mvdbeek committed Dec 20, 2024
2 parents 4c44920 + 962839d commit b88a792
Show file tree
Hide file tree
Showing 26 changed files with 186 additions and 69 deletions.
3 changes: 3 additions & 0 deletions client/src/components/ActivityBar/ActivityBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,15 @@ defineExpose({
v-if="activity.id === 'upload'"
:id="`${activity.id}`"
:key="activity.id"
:activity-bar-id="props.activityBarId"
:icon="activity.icon"
:title="activity.title"
:tooltip="activity.tooltip" />
<InteractiveItem
v-else-if="activity.to && activity.id === 'interactivetools'"
:id="`${activity.id}`"
:key="activity.id"
:activity-bar-id="props.activityBarId"
:icon="activity.icon"
:is-active="isActiveRoute(activity.to)"
:title="activity.title"
Expand Down Expand Up @@ -272,6 +274,7 @@ defineExpose({
<NotificationItem
v-if="isConfigLoaded && config.enable_notification_system"
id="notifications"
:activity-bar-id="props.activityBarId"
:icon="faBell"
:is-active="isActiveSideBar('notifications') || isActiveRoute('/user/notifications')"
title="Notifications"
Expand Down
7 changes: 4 additions & 3 deletions client/src/components/ActivityBar/Items/InteractiveItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,22 @@ const totalCount = computed(() => entryPoints.value.length);
export interface Props {
id: string;
activityBarId: string;
title: string;
icon: IconDefinition;
isActive: boolean;
to: string;
}
defineProps<Props>();
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "click"): void;
}>();
const tooltip = computed(() =>
totalCount.value === 1
? `You currently have 1 active interactive tool`
? "You currently have 1 active interactive tool"
: `You currently have ${totalCount.value} active interactive tools`
);
</script>
Expand All @@ -36,7 +37,7 @@ const tooltip = computed(() =>
<ActivityItem
v-if="totalCount > 0"
:id="id"
:activity-bar-id="id"
:activity-bar-id="props.activityBarId"
:icon="icon"
:indicator="totalCount"
:is-active="isActive"
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/ActivityBar/Items/NotificationItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ const { totalUnreadCount } = storeToRefs(useNotificationsStore());
export interface Props {
id: string;
activityBarId: string;
title: string;
icon: IconDefinition;
isActive: boolean;
}
defineProps<Props>();
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "click"): void;
Expand All @@ -32,7 +33,7 @@ const tooltip = computed(() =>
<template>
<ActivityItem
:id="id"
:activity-bar-id="'notifications'"
:activity-bar-id="props.activityBarId"
:icon="icon"
:indicator="totalUnreadCount"
:is-active="isActive"
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/ActivityBar/Items/UploadItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import ActivityItem from "@/components/ActivityBar/ActivityItem.vue";
export interface Props {
id: string;
activityBarId: string;
title: string;
icon: IconDefinition;
tooltip: string;
}
defineProps<Props>();
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "click"): void;
Expand All @@ -40,7 +41,7 @@ function onUploadModal() {
<template>
<ActivityItem
:id="id"
:activity-bar-id="id"
:activity-bar-id="props.activityBarId"
:title="title"
:tooltip="tooltip"
:icon="icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function onDoNotShowAgain() {
watch(
() => props.querySelectionBreak,
() => {
dismissCountDown.value = showSelectionQueryBreakWarning ? dismissSecs.value : 0;
dismissCountDown.value = showSelectionQueryBreakWarning.value ? dismissSecs.value : 0;
}
);
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,5 +411,6 @@ export default {
<style>
.modal-with-selector {
overflow: initial;
min-height: 300px; /* To make room for the selector */
}
</style>
6 changes: 4 additions & 2 deletions client/src/components/Panels/ActivityPanel.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { BButton } from "bootstrap-vue";
import { BButton, BButtonGroup } from "bootstrap-vue";
import { computed } from "vue";
interface Props {
Expand All @@ -26,7 +26,9 @@ const hasGoToAll = computed(() => props.goToAllTitle && props.href);
<nav unselectable="on" class="activity-panel-header-top">
<h2 id="activity-panel-heading" v-localize class="activity-panel-heading h-sm">{{ props.title }}</h2>

<slot name="header-buttons" />
<BButtonGroup>
<slot name="header-buttons" />
</BButtonGroup>
</nav>

<slot name="header" class="activity-panel-header-description" />
Expand Down
26 changes: 25 additions & 1 deletion client/src/components/Panels/WorkflowPanel.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<script setup lang="ts">
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { useMemoize, watchImmediate } from "@vueuse/core";
import { BButton } from "bootstrap-vue";
import { computed, ref, watch } from "vue";
import { loadWorkflows, type Workflow } from "@/components/Workflow/workflows.services";
Expand All @@ -19,6 +22,7 @@ const props = defineProps<{
const emit = defineEmits<{
(e: "insertWorkflow", id: string, name: string): void;
(e: "insertWorkflowSteps", id: string, stepCount: number): void;
(e: "createWorkflow"): void;
}>();
const scrollable = ref<HTMLDivElement | null>(null);
Expand Down Expand Up @@ -52,7 +56,7 @@ const loadWorkflowsOptions = {
sortBy: "update_time",
sortDesc: true,
limit: 20,
showPublished: true,
showPublished: false,
skipStepCounts: false,
} as const;
Expand Down Expand Up @@ -138,11 +142,26 @@ watch(
function scrollToTop() {
scrollable.value?.scrollTo({ top: 0, behavior: "smooth" });
}
function createNew(event: Event) {
event.preventDefault();
emit("createWorkflow");
}
</script>

<template>
<ActivityPanel title="Workflows">
<template v-slot:header-buttons>
<BButton
v-b-tooltip.hover.top.noninteractive
size="sm"
variant="link"
class="create-button"
title="Create new workflow"
href="/workflows/edit"
@click="createNew">
<FontAwesomeIcon :icon="faPlus" />
</BButton>
<FavoritesButton v-model="showFavorites" tooltip="Show bookmarked" />
</template>

Expand Down Expand Up @@ -189,4 +208,9 @@ function scrollToTop() {
color: $text-light;
margin: 0.5rem;
}
.create-button:hover {
background-color: #c8cfd6;
border-color: #c1c9d0;
}
</style>
7 changes: 1 addition & 6 deletions client/src/components/Panels/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,7 @@
*/
import { orderBy } from "lodash";

import {
type FilterSettings as ToolFilters,
type Tool,
type ToolSection,
type ToolSectionLabel,
} from "@/stores/toolStore";
import type { FilterSettings as ToolFilters, Tool, ToolSection, ToolSectionLabel } from "@/stores/toolStore";
import levenshteinDistance from "@/utils/levenshtein";

const FILTER_KEYS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const props = defineProps<Props>();
library.add(faChartBar);
const label = computed(() => props.data.id);
const label = computed(() => props.data.label);
const viewDetailsIcon = computed(() => "chart-bar");
const emit = defineEmits<{
Expand Down
12 changes: 11 additions & 1 deletion client/src/components/Workflow/Editor/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
v-else-if="isActiveSideBar('workflow-editor-workflows')"
:current-workflow-id="id"
@insertWorkflow="onInsertWorkflow"
@insertWorkflowSteps="onInsertWorkflowSteps" />
@insertWorkflowSteps="onInsertWorkflowSteps"
@createWorkflow="createNewWorkflow" />
<WorkflowAttributes
v-else-if="isActiveSideBar('workflow-editor-attributes')"
:id="id"
Expand Down Expand Up @@ -739,6 +740,10 @@ export default {
onSaveAs() {
this.showSaveAsModal = true;
},
async createNewWorkflow() {
await this.saveOrCreate();
this.$router.push("/workflows/edit");
},
async saveOrCreate() {
if (this.hasInvalidConnections) {
const confirmed = await this.confirm(
Expand Down Expand Up @@ -789,6 +794,10 @@ export default {
if (activityId === "save-workflow-as") {
this.onSaveAs();
}
if (activityId === "workflow-create") {
this.createNewWorkflow();
}
},
onAnnotation(nodeId, newAnnotation) {
this.stepActions.setAnnotation(this.steps[nodeId], newAnnotation);
Expand All @@ -811,6 +820,7 @@ export default {
const { id, name, number_of_steps } = await this.services.createWorkflow(this);
const message = `Created new workflow '${name}' with ${number_of_steps} steps.`;
this.hasChanges = false;
this.$emit("skipNextReload");
await this.routeToWorkflow(id);
Toast.success(message);
} catch (e) {
Expand Down
11 changes: 11 additions & 0 deletions client/src/components/Workflow/Editor/modules/activities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
faMagic,
faPencilAlt,
faPlay,
faPlus,
faRecycle,
faSave,
faSignOutAlt,
Expand Down Expand Up @@ -126,6 +127,16 @@ export const workflowEditorActivities = [
click: true,
optional: true,
},
{
description: "Save this workflow and create a new workflow.",
icon: faPlus,
title: "Create new",
id: "workflow-create",
tooltip: "Save this workflow and create a new one",
visible: true,
click: true,
optional: true,
},
{
description: "Exit the workflow editor and return to the start screen.",
icon: faSignOutAlt,
Expand Down
5 changes: 3 additions & 2 deletions client/src/composables/hashedUserId.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useLocalStorage } from "@vueuse/core";
import { storeToRefs } from "pinia";
import { computed, type Ref, ref, watch } from "vue";

import { type AnyUser } from "@/api";
import { useUserStore } from "@/stores/userStore";

import { usePersistentRef } from "./persistentRef";

async function hash32(value: string): Promise<string> {
const valueUtf8 = new TextEncoder().encode(value);
const hashBuffer = await crypto.subtle.digest("SHA-256", valueUtf8);
Expand Down Expand Up @@ -43,7 +44,7 @@ export function useHashedUserId(user?: Ref<AnyUser>) {
}

// salt the local store, to make a user untraceable by id across different clients
const localStorageSalt = useLocalStorage("local-storage-salt", createSalt());
const localStorageSalt = usePersistentRef("local-storage-salt", createSalt());

watch(
() => currentUser.value,
Expand Down
67 changes: 67 additions & 0 deletions client/src/composables/persistentRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
vueuse local storage has some strange, buggy side-effects,
so were re-implementing just the parts we need here
*/

import { type Ref, ref, watch } from "vue";

import { match } from "@/utils/utils";

function stringify(value: unknown): string {
if (typeof value !== "object") {
return `${value}`;
} else {
return JSON.stringify(value);
}
}

function parse<T>(value: string, type: "string" | "number" | "boolean" | "object"): T {
return match(type, {
string: () => value as T,
number: () => parseFloat(value) as T,
boolean: () => (value.toLowerCase().trim() === "true" ? true : false) as T,
object: () => JSON.parse(value),
});
}

export function syncRefToLocalStorage<T>(key: string, refToSync: Ref<T>) {
const stored = window.localStorage.getItem(key);

const sync = () => {
const stringified = stringify(refToSync.value);
window.localStorage.setItem(key, stringified);
};

if (stored) {
try {
refToSync.value = parse(stored, typeof refToSync.value as "string" | "number" | "boolean" | "object");
} catch (e) {
console.error(`Failed to parse value "${stored}" from local storage key "${key}". Resetting key`);
sync();
}
} else {
sync();
}

watch(
() => refToSync.value,
() => {
sync();
},
{ deep: true }
);
}

export function usePersistentRef(key: string, defaultValue: string): Ref<string>;
export function usePersistentRef(key: string, defaultValue: number): Ref<number>;
export function usePersistentRef<T>(key: string, defaultValue: T): Ref<T>;
export function usePersistentRef<T extends string | number | boolean | object | null>(
key: string,
defaultValue: T
): Ref<T> {
const storageSyncedRef = ref(defaultValue) as Ref<T>;

syncRefToLocalStorage(key, storageSyncedRef);

return storageSyncedRef;
}
Loading

0 comments on commit b88a792

Please sign in to comment.