Browse Source

Merge pull request #1618 from AutomaApp/dev

Dev
Ahmad Kholid 1 year ago
parent
commit
f6ae6a3c55

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "automa",
-  "version": "1.28.25",
+  "version": "1.28.26",
   "description": "An extension for automating your browser by connecting blocks",
   "repository": {
     "type": "git",

+ 3 - 1
src/components/newtab/logs/LogsVariables.vue

@@ -29,7 +29,9 @@
             readonly
           />
           <ui-input
-            :model-value="JSON.stringify(varValue)"
+            :model-value="
+              typeof varValue === 'string' ? varValue : JSON.stringify(varValue)
+            "
             label="Value"
             class="w-full"
             placeholder="EMPTY"

+ 47 - 28
src/components/newtab/storage/StorageEditTable.vue

@@ -6,7 +6,7 @@
       style="height: 600px"
     >
       <p class="p-4 font-semibold">
-        {{ t('storage.table.add') }}
+        {{ title || t('storage.table.add') }}
       </p>
       <div class="scroll flex-1 overflow-auto px-4 pb-4">
         <ui-input
@@ -27,32 +27,43 @@
         >
           {{ t('message.noData') }}
         </p>
-        <ul class="mt-4 space-y-2">
-          <li
-            v-for="(column, index) in state.columns"
-            :key="column.id"
-            class="flex items-center space-x-2"
-          >
-            <ui-input
-              :model-value="column.name"
-              :placeholder="t('workflow.table.column.name')"
-              class="flex-1"
-              @blur="updateColumnName(index, $event.target)"
-            />
-            <ui-select
-              v-model="column.type"
-              class="flex-1"
-              :placeholder="t('workflow.table.column.type')"
-            >
-              <option v-for="type in dataTypes" :key="type.id" :value="type.id">
-                {{ type.name }}
-              </option>
-            </ui-select>
-            <button @click="deleteColumn(index)">
-              <v-remixicon name="riDeleteBin7Line" />
-            </button>
-          </li>
-        </ul>
+        <draggable
+          v-model="state.columns"
+          tag="ul"
+          handle=".handle"
+          item-key="id"
+          class="mt-4 space-y-2"
+        >
+          <template #item="{ element: column, index }">
+            <li class="flex items-center space-x-2">
+              <span class="handle cursor-move">
+                <v-remixicon name="mdiDrag" />
+              </span>
+              <ui-input
+                :model-value="column.name"
+                :placeholder="t('workflow.table.column.name')"
+                class="flex-1"
+                @blur="updateColumnName(index, $event.target)"
+              />
+              <ui-select
+                v-model="column.type"
+                class="flex-1"
+                :placeholder="t('workflow.table.column.type')"
+              >
+                <option
+                  v-for="type in dataTypes"
+                  :key="type.id"
+                  :value="type.id"
+                >
+                  {{ type.name }}
+                </option>
+              </ui-select>
+              <button @click="deleteColumn(index)">
+                <v-remixicon name="riDeleteBin7Line" />
+              </button>
+            </li>
+          </template>
+        </draggable>
       </div>
       <div class="p-4 text-right">
         <ui-button class="mr-4" @click="clearTempTables(true)">
@@ -73,6 +84,7 @@
 import { reactive, toRaw, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { nanoid } from 'nanoid';
+import draggable from 'vuedraggable';
 import cloneDeep from 'lodash.clonedeep';
 import { dataTypes } from '@/utils/constants/table';
 
@@ -85,6 +97,10 @@ const props = defineProps({
     type: String,
     default: '',
   },
+  title: {
+    type: String,
+    default: '',
+  },
   columns: {
     type: Array,
     default: () => [],
@@ -122,7 +138,10 @@ function updateColumnName(index, target) {
   state.columns[index].name = columnName;
 }
 function saveTable() {
-  const rawState = toRaw(state);
+  const rawState = {
+    ...toRaw(state),
+    columns: state.columns.map(toRaw),
+  };
 
   emit('save', { ...rawState, changes });
 }

+ 10 - 0
src/execute/index.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<meta charset="UTF-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1.0">
+	<title>Execute</title>
+</head>
+<body>
+</body>
+</html>

+ 65 - 0
src/execute/index.js

@@ -0,0 +1,65 @@
+import { parseJSON } from '@/utils/helper';
+import { sendMessage } from '@/utils/message';
+import Browser from 'webextension-polyfill';
+
+function getWorkflowDetail() {
+  let hash = window.location.hash.slice(1);
+  if (!hash.startsWith('/')) hash = `/${hash}`;
+
+  const { pathname, searchParams } = new URL(window.location.origin + hash);
+
+  const variables = {};
+  const { 1: workflowId } = pathname.split('/');
+
+  searchParams.forEach((key, value) => {
+    const varValue = parseJSON(decodeURIComponent(value), '##_empty');
+    if (varValue === '##_empty') return;
+
+    variables[key] = varValue;
+  });
+
+  return { workflowId: workflowId ?? '', variables };
+}
+
+function writeResult(text) {
+  document.body.innerText = text;
+}
+
+(async () => {
+  try {
+    const { workflowId, variables } = getWorkflowDetail();
+    if (!workflowId) {
+      writeResult('Invalid path');
+      return;
+    }
+
+    const { workflows } = await Browser.storage.local.get('workflows');
+
+    let workflow = workflows[workflowId];
+    if (!workflow && Array.isArray(workflows)) {
+      workflow = workflows.find((item) => item.id === workflowId);
+    }
+
+    if (!workflow) {
+      writeResult('Workflow not found');
+      return;
+    }
+
+    const hasVariables = Object.keys(variables).length > 0;
+
+    writeResult('Executing workflow');
+
+    sendMessage(
+      'workflow:execute',
+      {
+        ...workflow,
+        options: { checkParam: !hasVariables, data: { variables } },
+      },
+      'background'
+    ).then(() => {
+      setTimeout(window.close, 1000);
+    });
+  } catch (error) {
+    console.error(error);
+  }
+})();

+ 1 - 0
src/locales/en/newtab.json

@@ -42,6 +42,7 @@
     "title": "Storage",
     "table": {
       "add": "Add table",
+      "edit": "Edit table",
       "createdAt": "Created at",
       "modifiedAt": "Modified at",
       "rowsCount": "Rows count",

+ 4 - 1
src/newtab/pages/settings/SettingsBackup.vue

@@ -336,6 +336,7 @@ async function backupWorkflows() {
 
       return acc;
     }, []);
+
     const payload = {
       isProtected: state.encrypt,
       workflows: JSON.stringify(workflows),
@@ -409,7 +410,9 @@ async function restoreWorkflows() {
       };
 
       if (state.updateIfExists) {
-        return workflowStore.insertOrUpdate(newWorkflows).then(showMessage);
+        return workflowStore
+          .insertOrUpdate(newWorkflows, { duplicateId: true })
+          .then(showMessage);
       }
 
       return workflowStore.insert(newWorkflows).then(showMessage);

+ 1 - 0
src/newtab/pages/storage/Tables.vue

@@ -75,6 +75,7 @@
     </div>
     <storage-edit-table
       v-model="editState.show"
+      :title="t('storage.table.edit')"
       :name="editState.name"
       :columns="editState.columns"
       @save="saveEditedTable"

+ 7 - 3
src/stores/workflow.js

@@ -47,7 +47,7 @@ const defaultWorkflow = (data = null, options = {}) => {
     settings: {
       publicId: '',
       blockDelay: 0,
-      saveLog: false,
+      saveLog: true,
       debugMode: false,
       restartTimes: 3,
       notification: true,
@@ -211,11 +211,15 @@ export const useWorkflowStore = defineStore('workflow', {
 
       return updatedWorkflows;
     },
-    async insertOrUpdate(data = [], { checkUpdateDate = false } = {}) {
+    async insertOrUpdate(
+      data = [],
+      { checkUpdateDate = false, duplicateId = false } = {}
+    ) {
       const insertedData = {};
 
       data.forEach((item) => {
         const currentWorkflow = this.workflows[item.id];
+
         if (currentWorkflow) {
           let insert = true;
           if (checkUpdateDate && currentWorkflow.createdAt && item.updatedAt) {
@@ -229,7 +233,7 @@ export const useWorkflowStore = defineStore('workflow', {
             insertedData[item.id] = mergedData;
           }
         } else {
-          const workflow = defaultWorkflow(item);
+          const workflow = defaultWorkflow(item, { duplicateId });
           this.workflows[workflow.id] = workflow;
           insertedData[workflow.id] = workflow;
         }

+ 11 - 9
src/workflowEngine/blocksHandler/handlerBlockPackage.js

@@ -30,21 +30,23 @@ export default async function (
     const outputsMap = new Set();
 
     data.inputs.forEach((item) => {
-      connections[addBlockPrefix(item.id)] = [
-        {
-          id: addBlockPrefix(item.blockId),
-          targetId: `${addBlockPrefix(block.id)}-input-1`,
-        },
-      ];
+      connections[addBlockPrefix(item.id)] = new Map([
+        [
+          item.id,
+          {
+            id: addBlockPrefix(item.blockId),
+            targetId: `${addBlockPrefix(block.id)}-input-1`,
+          },
+        ],
+      ]);
     });
     data.outputs.forEach((output) => {
-      outputsMap.add(output.handleId);
-
       const connection =
         this.engine.connectionsMap[`${id}-output-${output.id}`];
       if (!connection) return;
 
-      connections[addBlockPrefix(output.handleId)] = [...connection.values()];
+      connections[addBlockPrefix(output.handleId)] = new Map(connection);
+      outputsMap.add(output.handleId);
     });
 
     data.data.nodes.forEach((node) => {

+ 7 - 0
webpack.config.js

@@ -41,6 +41,7 @@ const options = {
   mode: process.env.NODE_ENV || 'development',
   entry: {
     sandbox: path.join(__dirname, 'src', 'sandbox', 'index.js'),
+    execute: path.join(__dirname, 'src', 'execute', 'index.js'),
     newtab: path.join(__dirname, 'src', 'newtab', 'index.js'),
     popup: path.join(__dirname, 'src', 'popup', 'index.js'),
     params: path.join(__dirname, 'src', 'params', 'index.js'),
@@ -200,6 +201,12 @@ const options = {
       chunks: ['sandbox'],
       cache: false,
     }),
+    new HtmlWebpackPlugin({
+      template: path.join(__dirname, 'src', 'execute', 'index.html'),
+      filename: 'execute.html',
+      chunks: ['execute'],
+      cache: false,
+    }),
     new HtmlWebpackPlugin({
       template: path.join(__dirname, 'src', 'popup', 'index.html'),
       filename: 'popup.html',