Browse Source

feat(background): add workflow state

Ahmad Kholid 3 years ago
parent
commit
651542a947

+ 10 - 6
src/background/blocks-handler.js

@@ -104,12 +104,12 @@ export async function activeTab(block) {
 
 export function interactionHandler(block) {
   return new Promise((resolve, reject) => {
-    if (!this._connectedTab) {
-      reject(new Error("There's no website is opened"));
+    if (!this.connectedTab) {
+      reject(new Error("Can't connect to a tab"));
       return;
     }
 
-    this._connectedTab.postMessage({ isBlock: true, ...block });
+    this.connectedTab.postMessage({ isBlock: true, ...block });
     this._listener({
       name: 'tab-message',
       id: block.name,
@@ -168,10 +168,14 @@ export function exportData(block) {
 }
 
 export function elementExists(block) {
-  return new Promise((resolve) => {
-    if (!this._connectedTab) return;
+  return new Promise((resolve, reject) => {
+    if (!this.connectedTab) {
+      reject(new Error("Can't connect to a tab"));
+
+      return;
+    }
 
-    this._connectedTab.postMessage({ isBlock: true, ...block });
+    this.connectedTab.postMessage({ isBlock: true, ...block });
     this._listener({
       name: 'tab-message',
       id: block.name,

+ 17 - 22
src/background/index.js

@@ -1,20 +1,8 @@
 import browser from 'webextension-polyfill';
-import { nanoid } from 'nanoid';
 import { MessageListener } from '@/utils/message';
-import executingWorkflow from '@/utils/executing-workflow';
+import workflowState from './workflow-state';
 import WorkflowEngine from './workflow-engine';
 
-browser.runtime.onInstalled.addListener((details) => {
-  if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
-    browser.storage.local.set({
-      logs: [],
-      workflows: [],
-      visitWebTriggers: [],
-      executingWorkflow: [],
-    });
-  }
-});
-
 function getWorkflow(workflowId) {
   return new Promise((resolve) => {
     browser.storage.local.get('workflows').then(({ workflows }) => {
@@ -26,15 +14,11 @@ function getWorkflow(workflowId) {
 }
 async function executeWorkflow(workflow) {
   try {
-    const id = nanoid();
-    const engine = new WorkflowEngine(id, workflow);
-
-    executingWorkflow.set(id, {});
+    const engine = new WorkflowEngine(workflow);
 
     engine.init();
     engine.on('destroyed', () => {
       console.log('destroyed...');
-      executingWorkflow.delete(workflow.id);
     });
 
     return true;
@@ -54,11 +38,11 @@ browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
 
       return tab.url.match(isRegex ? new RegExp(url, 'g') : url);
     });
-    const executedWorkflow = await executingWorkflow.find(
-      ({ workflow }) => workflow?.tabId === tabId
+    const runningWorkflow = (await workflowState.get()).find(
+      (item) => item.state.tabId === tabId
     );
-    console.log(executedWorkflow, 'wo');
-    if (trigger && !executedWorkflow) {
+
+    if (trigger && !runningWorkflow) {
       const workflow = await getWorkflow(trigger.id);
 
       executeWorkflow(workflow);
@@ -73,6 +57,17 @@ browser.alarms.onAlarm.addListener(({ name }) => {
   });
 });
 
+browser.runtime.onInstalled.addListener((details) => {
+  if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
+    browser.storage.local.set({
+      logs: [],
+      workflows: [],
+      visitWebTriggers: [],
+      runningWorkflows: [],
+    });
+  }
+});
+
 const message = new MessageListener('background');
 
 message.on('workflow:execute', executeWorkflow);

+ 32 - 30
src/background/workflow-engine.js

@@ -1,9 +1,12 @@
 /* eslint-disable no-underscore-dangle */
 import browser from 'webextension-polyfill';
+import { nanoid } from 'nanoid';
 import { toCamelCase } from '@/utils/helper';
 import { tasks } from '@/utils/shared';
 import * as blocksHandler from './blocks-handler';
-import executingWorkflow from '@/utils/executing-workflow';
+import workflowState from './workflow-state';
+
+// let reloadTimeout;
 
 function tabMessageHandler({ type, data }) {
   const listener = this.tabMessageListeners[type];
@@ -44,7 +47,7 @@ function tabUpdatedHandler(tabId, changeInfo) {
         })
         .then(() => {
           console.log(this.currentBlock);
-          if (this._connectedTab) this._connectTab(this.tabId);
+          if (this.connectedTab) this._connectTab(this.tabId);
 
           this.isInsidePaused = false;
         })
@@ -57,8 +60,8 @@ function tabUpdatedHandler(tabId, changeInfo) {
 }
 
 class WorkflowEngine {
-  constructor(id, workflow) {
-    this.id = id;
+  constructor(workflow) {
+    this.id = nanoid();
     this.workflow = workflow;
     this.data = {};
     this.blocks = {};
@@ -105,7 +108,9 @@ class WorkflowEngine {
     this.blocksArr = blocksArr;
     this.startedTimestamp = Date.now();
 
-    this._blockHandler(triggerBlock);
+    workflowState.add(this.id, this.state).then(() => {
+      this._blockHandler(triggerBlock);
+    });
   }
 
   on(name, listener) {
@@ -114,9 +119,15 @@ class WorkflowEngine {
     );
   }
 
+  pause(pause = true) {
+    this.isPaused = pause;
+
+    workflowState.update(this.tabId, this.state);
+  }
+
   destroy() {
     // save log
-    this._dispatchEvent('destroyed', this.workflow.id);
+    this.dispatchEvent('destroyed', this.workflow.id);
 
     this.eventListeners = {};
     this.tabMessageListeners = {};
@@ -125,11 +136,13 @@ class WorkflowEngine {
     browser.tabs.onRemoved.removeListener(this.tabRemovedHandler);
     browser.tabs.onUpdated.removeListener(this.tabUpdatedHandler);
 
+    workflowState.delete(this.id);
+
     this.isDestroyed = true;
     this.endedTimestamp = Date.now();
   }
 
-  _dispatchEvent(name, params) {
+  dispatchEvent(name, params) {
     const listeners = this.eventListeners[name];
     console.log(name, this.eventListeners);
     if (!listeners) return;
@@ -139,6 +152,16 @@ class WorkflowEngine {
     });
   }
 
+  get state() {
+    const keys = ['tabId', 'isPaused', 'isDestroyed', 'currentBlock'];
+
+    return keys.reduce((acc, key) => {
+      acc[key] = this[key];
+
+      return acc;
+    }, {});
+  }
+
   _blockHandler(block, prevBlockData) {
     if (this.isDestroyed) {
       console.log(
@@ -148,18 +171,7 @@ class WorkflowEngine {
       return;
     }
 
-    const executedWorkflowData = [
-      'data',
-      'isPaused',
-      'isDestroyed',
-      'currentBlock',
-    ].reduce((acc, key) => {
-      acc[key] = this[key];
-
-      return acc;
-    }, {});
-    console.log(executedWorkflowData);
-    executingWorkflow.update(this.id, { workflow: executedWorkflowData });
+    workflowState.update(this.id, this.state);
 
     if (this.isPaused || this.isInsidePaused) {
       setTimeout(() => {
@@ -185,7 +197,7 @@ class WorkflowEngine {
           if (result.nextBlockId) {
             this._blockHandler(this.blocks[result.nextBlockId], result.data);
           } else {
-            this._dispatchEvent('finish');
+            this.dispatchEvent('finish');
             this.destroy();
             console.log('Done', this);
           }
@@ -228,16 +240,6 @@ class WorkflowEngine {
       delete this.tabMessageListeners[id];
     };
   }
-
-  get _connectedTab() {
-    if (!this.connectedTab) {
-      this.destroy();
-
-      return null;
-    }
-
-    return this.connectedTab;
-  }
 }
 
 export default WorkflowEngine;

+ 71 - 0
src/background/workflow-state.js

@@ -0,0 +1,71 @@
+/* eslint-disable  no-param-reassign */
+import browser from 'webextension-polyfill';
+
+async function updater(callback, id) {
+  try {
+    const state = await this.get();
+    const index = id ? state.find((item) => item.id === id) : -1;
+    const items = callback(state, index || -1);
+
+    await browser.storage.local.set({ workflowState: items });
+
+    return items;
+  } catch (error) {
+    console.error(error);
+
+    return [];
+  }
+}
+
+class WorkflowState {
+  static async get() {
+    try {
+      const { workflowState } = await browser.storage.local.get(
+        'workflowState'
+      );
+
+      return workflowState || [];
+    } catch (error) {
+      console.error(error);
+
+      return [];
+    }
+  }
+
+  static add(id, state) {
+    return updater.call(this, (items) => {
+      items.push({ id, state });
+
+      return items;
+    });
+  }
+
+  static update(id, data = {}) {
+    return updater.call(
+      this,
+      (items, index) => {
+        console.log(items, index, items[index]);
+        if (typeof index === 'number' && index !== -1) {
+          items[index].state = { ...items[index].state, ...data };
+        }
+
+        return items;
+      },
+      id
+    );
+  }
+
+  static delete(id) {
+    return updater.call(
+      this,
+      (items, index) => {
+        if (index === -1) items.splice(index, 1);
+
+        return items;
+      },
+      id
+    );
+  }
+}
+
+export default WorkflowState;

+ 0 - 69
src/utils/executing-workflow.js

@@ -1,69 +0,0 @@
-import browser from 'webextension-polyfill';
-
-function getWorkflows() {
-  return new Promise((resolve) => {
-    browser.storage.local
-      .get('executingWorkflow')
-      .then(({ executingWorkflow }) => {
-        console.log(executingWorkflow);
-        resolve(executingWorkflow || []);
-      });
-  });
-}
-async function updater(callback) {
-  try {
-    const executingWorkflow = await getWorkflows();
-    const items = callback(executingWorkflow);
-
-    await browser.storage.local.set({ executingWorkflow: items });
-
-    return items;
-  } catch (error) {
-    console.error(error);
-    return [];
-  }
-}
-function setWorkflow(id, workflow) {
-  return updater((items) => {
-    items.push({ id, workflow });
-
-    return items;
-  });
-}
-async function findWorkflows(callback) {
-  try {
-    const workflows = await getWorkflows();
-
-    return workflows.find(callback);
-  } catch (error) {
-    console.error(error);
-    return [];
-  }
-}
-function deleteWorkflow(id) {
-  return updater((items) => {
-    const index = items.findIndex((item) => item.id === id);
-
-    items.splice(index, 1);
-
-    return items;
-  });
-}
-function update(id, data = {}) {
-  return updater((items) => {
-    const index = items.findIndex((item) => item.id === id);
-
-    /* eslint-disable-next-line */
-    if (index !== -1) items[index] = { ...data, id };
-
-    return items;
-  });
-}
-
-export default {
-  update,
-  set: setWorkflow,
-  get: getWorkflows,
-  find: findWorkflows,
-  delete: deleteWorkflow,
-};