浏览代码

feat: register team workflows trigger

Ahmad Kholid 2 年之前
父节点
当前提交
fd43a8db7a

+ 36 - 4
src/background/index.js

@@ -18,6 +18,9 @@ import blocksHandler from './workflowEngine/blocksHandler';
 import WorkflowLogger from './WorkflowLogger';
 
 const validateUrl = (str) => str?.startsWith('http');
+const flattenTeamWorkflows = (workflows) =>
+  Object.values(Object.values(workflows)[0]);
+
 const browserStorage = {
   async get(key) {
     try {
@@ -53,6 +56,19 @@ const workflow = {
   states: new WorkflowState({ storage: localStateStorage }),
   logger: new WorkflowLogger({ storage: browserStorage }),
   async get(workflowId) {
+    if (!workflowId) return null;
+
+    if (workflowId.startsWith('team_')) {
+      const { teamWorkflows } = await browser.storage.local.get(
+        'teamWorkflows'
+      );
+      if (!teamWorkflows) return null;
+
+      const workflows = flattenTeamWorkflows(teamWorkflows);
+
+      return workflows.find((item) => item.id === workflowId);
+    }
+
     const { workflows, workflowHosts } = await browser.storage.local.get([
       'workflows',
       'workflowHosts',
@@ -419,10 +435,23 @@ browser.runtime.onInstalled.addListener(async ({ reason }) => {
   }
 });
 browser.runtime.onStartup.addListener(async () => {
-  const { workflows } = await browser.storage.local.get('workflows');
-  const workflowsArr = Array.isArray(workflows)
-    ? workflows
-    : Object.values(workflows);
+  const { workflows, workflowHosts, teamWorkflows } =
+    await browser.storage.local.get([
+      'workflows',
+      'workflowHosts',
+      'teamWorkflows',
+    ]);
+  const convertToArr = (value) =>
+    Array.isArray(value) ? value : Object.values(value);
+
+  const workflowsArr = convertToArr(workflows);
+
+  if (workflowHosts) {
+    workflowsArr.push(...convertToArr(workflowHosts));
+  }
+  if (teamWorkflows) {
+    workflowsArr.push(...flattenTeamWorkflows(teamWorkflows));
+  }
 
   for (const currWorkflow of workflowsArr) {
     let triggerBlock = currWorkflow.trigger;
@@ -560,5 +589,8 @@ message.on('workflow:added', ({ workflowId, teamId, source = 'community' }) => {
       }
     });
 });
+message.on('workflow:register', ({ triggerBlock, workflowId }) => {
+  registerWorkflowTrigger(workflowId, triggerBlock);
+});
 
 browser.runtime.onMessage.addListener(message.listener());

+ 3 - 1
src/background/workflowEngine/engine.js

@@ -189,6 +189,7 @@ class WorkflowEngine {
         state: this.state,
         workflowId: this.workflow.id,
         parentState: this.parentWorkflow,
+        teamId: this.workflow.teamId || null,
       });
       this.addWorker({ blockId: triggerBlock.id });
     } catch (error) {
@@ -317,12 +318,13 @@ class WorkflowEngine {
       this.executeQueue();
 
       if (!this.workflow.isTesting) {
-        const { name, id } = this.workflow;
+        const { name, id, teamId } = this.workflow;
 
         await this.logger.add({
           detail: {
             name,
             status,
+            teamId,
             message,
             id: this.id,
             workflowId: id,

+ 12 - 1
src/components/newtab/logs/LogsHistory.vue

@@ -81,8 +81,9 @@
             />
           </router-link>
           <router-link
+            v-if="getBlockPath(item.blockId)"
             v-show="currentLog.workflowId && item.blockId"
-            :to="`/workflows/${currentLog.workflowId}?blockId=${item.blockId}`"
+            :to="getBlockPath(item.blockId)"
           >
             <v-remixicon
               name="riExternalLinkLine"
@@ -215,4 +216,14 @@ function translateLog(log) {
 
   return copyLog;
 }
+function getBlockPath(blockId) {
+  const { workflowId, teamId } = props.currentLog;
+  let path = `/workflows/${workflowId}`;
+
+  if (workflowId.startsWith('team') && !teamId) return null;
+
+  path = `/teams/${teamId}/workflows/${workflowId}`;
+
+  return `${path}?blockId=${blockId}`;
+}
 </script>

+ 11 - 0
src/components/newtab/workflow/WorkflowShareTeam.vue

@@ -147,6 +147,7 @@ import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
 import { workflowCategories } from '@/utils/shared';
 import { parseJSON, debounce } from '@/utils/helper';
 import { convertWorkflow } from '@/utils/workflowData';
+import { registerWorkflowTrigger } from '@/utils/workflowTrigger';
 import SharedWysiwyg from '@/components/newtab/shared/SharedWysiwyg.vue';
 
 const props = defineProps({
@@ -198,12 +199,22 @@ async function publishWorkflow() {
     }
 
     workflow.id = result.id;
+    workflow.teamId = result.teamId;
     workflow.createdAt = Date.now();
     workflow.drawflow = props.workflow.drawflow;
 
     await teamWorkflowStore.insert(state.activeTeam, cloneDeep(workflow));
     state.isPublishing = false;
 
+    const triggerBlock = workflow.drawflow.nodes?.find(
+      (node) => node.label === 'trigger'
+    );
+    if (triggerBlock) {
+      await registerWorkflowTrigger(workflow.id, triggerBlock);
+    }
+
+    toast('Successfully share the workflow with your team');
+
     emit('publish');
   } catch (error) {
     let errorMessage = t('message.somethingWrong');

+ 1 - 1
src/components/newtab/workflow/editor/EditorLocalActions.vue

@@ -384,7 +384,7 @@ const renameState = reactive({
 const shared = computed(() => sharedWorkflowStore.getById(props.workflow.id));
 const hosted = computed(() => userStore.hostedWorkflows[props.workflow.id]);
 const userDontHaveTeamsAccess = computed(() => {
-  if (props.isTeam || !userStore.user.teams) return false;
+  if (props.isTeam || !userStore.user.teams) return true;
 
   return !userStore.user.teams.some((team) =>
     team.access.some((item) => ['owner', 'create'].includes(item))

+ 31 - 2
src/content/services/webService.js

@@ -1,7 +1,7 @@
 import { openDB } from 'idb';
 import { nanoid } from 'nanoid';
 import browser from 'webextension-polyfill';
-import cloneDeep from 'lodash.clonedeep';
+import deepmerge from 'lodash.merge';
 import { sendMessage } from '@/utils/message';
 import { objectHasKey, parseJSON } from '@/utils/helper';
 
@@ -45,6 +45,11 @@ window.addEventListener('DOMContentLoaded', async () => {
     await browser.storage.local.set({ session });
 
     const webListener = initWebListener();
+    webListener.on('open-dashboard', ({ path }) => {
+      if (!path) return;
+
+      sendMessage('open:dashboard', path, 'background');
+    });
     webListener.on('open-workflow', ({ workflowId }) => {
       if (!workflowId) return;
 
@@ -102,11 +107,23 @@ window.addEventListener('DOMContentLoaded', async () => {
       const workflowToMerge =
         teamWorkflows[workflowData.teamId][workflow.id] || null;
       if (workflowToMerge) {
-        workflowData = cloneDeep(workflowToMerge, workflowData);
+        workflowData = deepmerge(workflowToMerge, workflowData);
       }
 
       teamWorkflows[workflowData.teamId][workflow.id] = workflowData;
       await browser.storage.local.set({ teamWorkflows });
+
+      const triggerBlock = workflowData.drawflow.nodes?.find(
+        (node) => node.label === 'trigger'
+      );
+      if (triggerBlock) {
+        await sendMessage(
+          'workflow:register',
+          { triggerBlock, workflowId: workflowData.id },
+          'background'
+        );
+      }
+
       sendMessage(
         'workflow:added',
         {
@@ -117,6 +134,18 @@ window.addEventListener('DOMContentLoaded', async () => {
         'background'
       );
     });
+    webListener.on('check-team-workflow', async ({ teamId, workflowId }) => {
+      const { teamWorkflows } = await browser.storage.local.get(
+        'teamWorkflows'
+      );
+      const workflowExist = Boolean(teamWorkflows?.[teamId]?.[workflowId]);
+
+      window.dispatchEvent(
+        new CustomEvent('__automa-team-workflow__', {
+          detail: { exists: workflowExist },
+        })
+      );
+    });
   } catch (error) {
     console.error(error);
   }

+ 12 - 1
src/newtab/pages/logs/Running.vue

@@ -56,7 +56,8 @@
               {{ t(`workflow.blocks.${block.name}.name`) }}
             </p>
             <router-link
-              :to="`/workflows/${running.workflowId}?block=${block.id}`"
+              v-if="getBlockPath(block.id)"
+              :to="getBlockPath(block.id)"
               title="Go to block"
               class="invisible group-hover:visible"
             >
@@ -101,6 +102,16 @@ const running = computed(() =>
 function stopWorkflow() {
   sendMessage('workflow:stop', running.value.id, 'background');
 }
+function getBlockPath(blockId) {
+  const { workflowId, teamId } = running.value;
+  let path = `/workflows/${workflowId}`;
+
+  if (workflowId.startsWith('team') && !teamId) return null;
+
+  path = `/teams/${teamId}/workflows/${workflowId}`;
+
+  return `${path}?blockId=${blockId}`;
+}
 
 watch(
   running,

+ 3 - 0
src/stores/teamWorkflow.js

@@ -1,6 +1,7 @@
 import { defineStore } from 'pinia';
 import browser from 'webextension-polyfill';
 import lodashDeepmerge from 'lodash.merge';
+import { cleanWorkflowTriggers } from '@/utils/workflowTrigger';
 
 export const useTeamWorkflowStore = defineStore('team-workflows', {
   storageMap: {
@@ -58,7 +59,9 @@ export const useTeamWorkflowStore = defineStore('team-workflows', {
       if (!this.workflows[teamId]) return;
 
       delete this.workflows[teamId][id];
+
       await this.saveToStorage('workflows');
+      await cleanWorkflowTriggers(id);
     },
     async loadData() {
       const { teamWorkflows } = await browser.storage.local.get(

+ 2 - 1
src/utils/shared.js

@@ -684,12 +684,13 @@ export const tasks = {
     allowedInputs: true,
     maxConnection: 1,
     refDataKeys: [
+      'maxLoop',
       'loopData',
       'variableName',
       'referenceKey',
       'elementSelector',
     ],
-    autocomplete: ['variableName', 'loopId', 'maxLoop'],
+    autocomplete: ['variableName', 'loopId'],
     data: {
       disableBlock: false,
       loopId: '',