Browse Source

feat: add scheduled workflow listener

Ahmad Kholid 2 years ago
parent
commit
52cf7653cc

+ 5 - 0
src/background/BackgroundEventsListeners.js

@@ -1,9 +1,14 @@
 import BackgroundUtils from './BackgroundUtils';
+import BackgroundWorkflowTriggers from './BackgroundWorkflowTriggers';
 
 class BackgroundEventsListeners {
   static onActionClicked() {
     BackgroundUtils.openDashboard();
   }
+
+  static async onAlarms(event) {
+    BackgroundWorkflowTriggers.scheduleWorkflow(event);
+  }
 }
 
 export default BackgroundEventsListeners;

+ 27 - 10
src/background/BackgroundUtils.js

@@ -1,7 +1,8 @@
 import browser from 'webextension-polyfill';
+import { waitTabLoaded } from '@/newtab/utils/workflowEngine/helper';
 
 class BackgroundUtils {
-  static async openDashboard(url) {
+  static async openDashboard(url, updateTab = true) {
     const tabUrl = browser.runtime.getURL(
       `/newtab.html#${typeof url === 'string' ? url : ''}`
     );
@@ -12,27 +13,43 @@ class BackgroundUtils {
       });
 
       if (tab) {
-        await browser.tabs.update(tab.id, { url: tabUrl, active: true });
-        await browser.windows.update(tab.windowId, {
-          focused: true,
-          state: 'maximized',
-        });
+        const tabOptions = { active: true };
+        if (updateTab) tabOptions.url = tabUrl;
+
+        await browser.tabs.update(tab.id, tabOptions);
 
-        if (tabUrl.includes('workflows/')) {
+        if (updateTab) {
+          await browser.windows.update(tab.windowId, {
+            focused: true,
+            state: 'maximized',
+          });
+        }
+        if (updateTab && tabUrl.includes('workflows/')) {
           await browser.tabs.reload(tab.id);
         }
       } else {
-        browser.windows.create({
+        await browser.windows.create({
           url: tabUrl,
-          focused: true,
           type: 'popup',
-          state: 'maximized',
+          focused: updateTab,
         });
       }
     } catch (error) {
       console.error(error);
+      throw error;
     }
   }
+
+  static async sendMessageToDashboard(type, data) {
+    const [tab] = await browser.tabs.query({
+      url: browser.runtime.getURL('/newtab.html'),
+    });
+
+    await waitTabLoaded({ tabId: tab.id });
+    const result = await browser.tabs.sendMessage(tab.id, { type, data });
+
+    return result;
+  }
 }
 
 export default BackgroundUtils;

+ 88 - 5
src/background/BackgroundWorkflowTriggers.js

@@ -1,9 +1,11 @@
-/* eslint-disable class-methods-use-this */
 import browser from 'webextension-polyfill';
-import BackgroundUtils from './BackgroundUtils';
+import dayjs from 'dayjs';
+import { findTriggerBlock, parseJSON } from '@/utils/helper';
+import { registerCronJob, registerSpecificDay } from '@/utils/workflowTrigger';
+import BackgroundWorkflowUtils from './BackgroundWorkflowUtils';
 
 class BackgroundWorkflowTriggers {
-  async visitWebTriggers(tabId, tabUrl) {
+  static async visitWebTriggers(tabId, tabUrl) {
     const { visitWebTriggers } = await browser.storage.local.get(
       'visitWebTriggers'
     );
@@ -22,9 +24,90 @@ class BackgroundWorkflowTriggers {
         workflowId = triggerWorkflowId;
       }
 
-      const workflowData = await BackgroundUtils.getWorkflow(workflowId);
+      const workflowData = await BackgroundWorkflowUtils.getWorkflow(
+        workflowId
+      );
       if (workflowData)
-        BackgroundUtils.executeWorkflow(workflowData, { tabId });
+        BackgroundWorkflowUtils.executeWorkflow(workflowData, { tabId });
+    }
+  }
+
+  static async scheduleWorkflow({ name }) {
+    try {
+      let workflowId = name;
+      let triggerId = null;
+
+      if (name.startsWith('trigger')) {
+        const { 1: triggerWorkflowId, 2: triggerItemId } = name.split(':');
+        triggerId = triggerItemId;
+        workflowId = triggerWorkflowId;
+      }
+
+      const currentWorkflow = await BackgroundWorkflowUtils.getWorkflow(
+        workflowId
+      );
+      if (!currentWorkflow) return;
+
+      let data = currentWorkflow.trigger;
+      if (!data) {
+        const drawflow =
+          typeof currentWorkflow.drawflow === 'string'
+            ? parseJSON(currentWorkflow.drawflow, {})
+            : currentWorkflow.drawflow;
+        const { data: triggerBlockData } = findTriggerBlock(drawflow) || {};
+        data = triggerBlockData;
+      }
+
+      if (triggerId) {
+        data = data.triggers.find((trigger) => trigger.id === triggerId);
+        if (data) data = { ...data, ...data.data };
+      }
+
+      if (data && data.type === 'interval' && data.fixedDelay) {
+        const { workflowStates } = await browser.storage.local.get(
+          'workflowStates'
+        );
+        const workflowState = (workflowStates || []).find(
+          (item) => item.workflowId === workflowId
+        );
+
+        if (workflowState) {
+          let { workflowQueue } = await browser.storage.local.get(
+            'workflowQueue'
+          );
+          workflowQueue = workflowQueue || [];
+
+          if (!workflowQueue.includes(workflowId)) {
+            (workflowQueue = workflowQueue || []).push(workflowId);
+            await browser.storage.local.set({ workflowQueue });
+          }
+
+          return;
+        }
+      } else if (data && data.type === 'date') {
+        const [hour, minute, second] = data.time.split(':');
+        const date = dayjs(data.date)
+          .hour(hour)
+          .minute(minute)
+          .second(second || 0);
+
+        const isAfter = dayjs(Date.now() - 60 * 1000).isAfter(date);
+        if (isAfter) return;
+      }
+
+      BackgroundWorkflowUtils.executeWorkflow(currentWorkflow);
+
+      if (!data) return;
+
+      if (['specific-day', 'cron-job'].includes(data.type)) {
+        if (data.type === 'specific-day') {
+          registerSpecificDay(name, data);
+        } else {
+          registerCronJob(name, data);
+        }
+      }
+    } catch (error) {
+      console.error(error);
     }
   }
 }

+ 55 - 0
src/background/BackgroundWorkflowUtils.js

@@ -0,0 +1,55 @@
+import browser from 'webextension-polyfill';
+import BackgroundUtils from './BackgroundUtils';
+
+const flattenTeamWorkflows = (workflows) =>
+  Object.values(Object.values(workflows)[0]);
+
+class BackgroundWorkflowUtils {
+  static async getWorkflow(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',
+    ]);
+    let findWorkflow = Array.isArray(workflows)
+      ? workflows.find(({ id }) => id === workflowId)
+      : workflows[workflowId];
+
+    if (!findWorkflow) {
+      findWorkflow = Object.values(workflowHosts || {}).find(
+        ({ hostId }) => hostId === workflowId
+      );
+
+      if (findWorkflow) findWorkflow.id = findWorkflow.hostId;
+    }
+
+    return findWorkflow;
+  }
+
+  static async executeWorkflow(workflowData, options) {
+    await BackgroundUtils.openDashboard('', false);
+    const result = await BackgroundUtils.sendMessageToDashboard(
+      'workflow:execute',
+      {
+        data: workflowData,
+        options,
+      }
+    );
+
+    return result;
+  }
+}
+
+export default BackgroundWorkflowUtils;

+ 1 - 69
src/background/index.js

@@ -9,8 +9,6 @@ import convertWorkflowData from '@/utils/convertWorkflowData';
 import getBlockMessage from '@/utils/getBlockMessage';
 import automa from '@business';
 import {
-  registerCronJob,
-  registerSpecificDay,
   registerContextMenu,
   registerWorkflowTrigger,
 } from '../utils/workflowTrigger';
@@ -342,73 +340,7 @@ browser.tabs.onCreated.addListener(async (tab) => {
 
   await browser.storage.local.set({ recording });
 });
-browser.alarms.onAlarm.addListener(async ({ name }) => {
-  let workflowId = name;
-  let triggerId = null;
-
-  if (name.startsWith('trigger')) {
-    const { 1: triggerWorkflowId, 2: triggerItemId } = name.split(':');
-    triggerId = triggerItemId;
-    workflowId = triggerWorkflowId;
-  }
-
-  const currentWorkflow = await workflow.get(workflowId);
-  if (!currentWorkflow) return;
-
-  let data = currentWorkflow.trigger;
-  if (!data) {
-    const drawflow =
-      typeof currentWorkflow.drawflow === 'string'
-        ? parseJSON(currentWorkflow.drawflow, {})
-        : currentWorkflow.drawflow;
-    const { data: triggerBlockData } = findTriggerBlock(drawflow) || {};
-    data = triggerBlockData;
-  }
-
-  if (triggerId) {
-    data = data.triggers.find((trigger) => trigger.id === triggerId);
-    if (data) data = { ...data, ...data.data };
-  }
-
-  if (data && data.type === 'interval' && data.fixedDelay) {
-    const workflowState = await workflow.states.get(
-      (item) => item.workflowId === workflowId
-    );
-
-    if (workflowState) {
-      let { workflowQueue } = await browser.storage.local.get('workflowQueue');
-      workflowQueue = workflowQueue || [];
-
-      if (!workflowQueue.includes(workflowId)) {
-        (workflowQueue = workflowQueue || []).push(workflowId);
-        await browser.storage.local.set({ workflowQueue });
-      }
-
-      return;
-    }
-  } else if (data && data.type === 'date') {
-    const [hour, minute, second] = data.time.split(':');
-    const date = dayjs(data.date)
-      .hour(hour)
-      .minute(minute)
-      .second(second || 0);
-
-    const isAfter = dayjs(Date.now() - 60 * 1000).isAfter(date);
-    if (isAfter) return;
-  }
-
-  workflow.execute(currentWorkflow);
-
-  if (!data) return;
-
-  if (['specific-day', 'cron-job'].includes(data.type)) {
-    if (data.type === 'specific-day') {
-      registerSpecificDay(name, data);
-    } else {
-      registerCronJob(name, data);
-    }
-  }
-});
+browser.alarms.onAlarm.addListener(BackgroundEventsListeners.onAlarms);
 browser.action.onClicked.addListener(BackgroundEventsListeners.onActionClicked);
 
 const contextMenu =

+ 15 - 7
src/newtab/App.vue

@@ -76,6 +76,7 @@ import AppSidebar from '@/components/newtab/app/AppSidebar.vue';
 import dataMigration from '@/utils/dataMigration';
 import iconFirefox from '@/assets/svg/logoFirefox.svg';
 import iconChrome from '@/assets/svg/logo.svg';
+import { executeWorkflow } from './utils/workflowEngine';
 
 let icon;
 if (window.location.protocol === 'moz-extension:') {
@@ -180,13 +181,11 @@ async function syncHostedWorkflows() {
   await hostedWorkflowStore.fetchWorkflows(hostIds);
 }
 
-browser.runtime.onMessage.addListener(({ type, data }) => {
-  if (type === 'refresh-packages') {
+const messageEvents = {
+  'refresh-packages': function () {
     packageStore.loadData(true);
-    return;
-  }
-
-  if (type === 'workflow:added') {
+  },
+  'workflow:added': function (data) {
     if (data.source === 'team') {
       teamWorkflowStore.loadData().then(() => {
         router.push(
@@ -198,7 +197,16 @@ browser.runtime.onMessage.addListener(({ type, data }) => {
         router.push(`/workflows/${data.workflowId}?permission=true`);
       });
     }
-  }
+  },
+  'workflow:execute': function ({ data, options = {} }) {
+    executeWorkflow(data, options);
+  },
+};
+
+browser.runtime.onMessage.addListener(({ type, data }) => {
+  if (!type || !messageEvents[type]) return;
+
+  messageEvents[type](data);
 });
 
 (async () => {

+ 4 - 1
src/newtab/utils/workflowEngine/index.js

@@ -18,7 +18,10 @@ const workflowStateStorage = {
   },
   set(key, value) {
     const workflowStore = useWorkflowStore();
-    workflowStore.updateStates(Object.values(value));
+    const states = Object.values(value);
+
+    browser.storage.local.set({ workflowStates: states });
+    workflowStore.updateStates(states);
   },
 };
 const browserStorage = {