Browse Source

feat: add clipboard block

Ahmad Kholid 3 years ago
parent
commit
69f2f19324

+ 41 - 0
src/background/workflow-engine/blocks-handler/handler-clipboard.js

@@ -0,0 +1,41 @@
+import browser from 'webextension-polyfill';
+import { getBlockConnection } from '../helper';
+
+export default async function ({ data, outputs }) {
+  const nextBlockId = getBlockConnection({ outputs });
+
+  try {
+    const hasPermission = await browser.permissions.contains({
+      permissions: ['clipboardRead'],
+    });
+
+    if (!hasPermission) {
+      throw new Error('no-clipboard-acces');
+    }
+
+    const textarea = document.createElement('textarea');
+    document.body.appendChild(textarea);
+    textarea.focus();
+    document.execCommand('paste');
+
+    const copiedText = textarea.value;
+
+    if (data.assignVariable) {
+      this.referenceData.variables[data.variableName] = copiedText;
+    }
+    if (data.saveData) {
+      this.addDataToColumn(data.dataColumn, copiedText);
+    }
+
+    document.body.removeChild(textarea);
+
+    return {
+      nextBlockId,
+      data: copiedText,
+    };
+  } catch (error) {
+    error.nextBlockId = nextBlockId;
+
+    throw error;
+  }
+}

+ 9 - 0
src/background/workflow-engine/blocks-handler/handler-interaction-block.js

@@ -1,3 +1,4 @@
+import browser from 'webextension-polyfill';
 import { objectHasKey } from '@/utils/helper';
 import { getBlockConnection } from '../helper';
 
@@ -10,6 +11,14 @@ async function checkAccess(blockName) {
     if (hasFileAccess) return true;
 
     throw new Error('no-file-access');
+  } else if (blockName === 'clipboard') {
+    const hasPermission = await browser.permissions.contains({
+      permissions: ['clipboardRead'],
+    });
+
+    if (!hasPermission) {
+      throw new Error('no-clipboard-acces');
+    }
   }
 
   return true;

+ 103 - 0
src/components/newtab/workflow/edit/EditClipboard.vue

@@ -0,0 +1,103 @@
+<template>
+  <div>
+    <ui-textarea
+      :model-value="data.description"
+      class="w-full"
+      :placeholder="t('common.description')"
+      @change="updateData({ description: $event })"
+    />
+    <template v-if="hasPermission">
+      <p class="mt-4">
+        {{ t('workflow.blocks.clipboard.data') }}
+      </p>
+      <ui-input
+        v-if="data.responseType === 'json'"
+        :model-value="data.dataPath"
+        placeholder="path.to.data"
+        label="Data path"
+        class="w-full mt-2"
+        @change="updateData({ dataPath: $event })"
+      />
+      <ui-checkbox
+        :model-value="data.assignVariable"
+        block
+        class="mt-4"
+        @change="updateData({ assignVariable: $event })"
+      >
+        {{ t('workflow.variables.assign') }}
+      </ui-checkbox>
+      <ui-input
+        v-if="data.assignVariable"
+        :model-value="data.variableName"
+        :placeholder="t('workflow.variables.name')"
+        :title="t('workflow.variables.name')"
+        class="mt-2 w-full"
+        @change="updateData({ variableName: $event })"
+      />
+      <ui-checkbox
+        :model-value="data.saveData"
+        block
+        class="mt-4"
+        @change="updateData({ saveData: $event })"
+      >
+        {{ t('workflow.blocks.get-text.checkbox') }}
+      </ui-checkbox>
+      <ui-select
+        v-if="data.saveData"
+        :model-value="data.dataColumn"
+        placeholder="Select column"
+        class="mt-2 w-full"
+        @change="updateData({ dataColumn: $event })"
+      >
+        <option
+          v-for="column in workflow.data.value.table"
+          :key="column.name"
+          :value="column.name"
+        >
+          {{ column.name }}
+        </option>
+      </ui-select>
+    </template>
+    <template v-else>
+      <p class="mt-4">
+        {{ t('workflow.blocks.clipboard.noPermission') }}
+      </p>
+      <ui-button variant="accent" class="mt-2" @click="requestPermission">
+        {{ t('workflow.blocks.clipboard.grantPermission') }}
+      </ui-button>
+    </template>
+  </div>
+</template>
+<script setup>
+import { ref, inject, onMounted } from 'vue';
+import { useI18n } from 'vue-i18n';
+import browser from 'webextension-polyfill';
+
+const props = defineProps({
+  data: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+const emit = defineEmits(['update:data']);
+
+const permission = { permissions: ['clipboardRead'] };
+const { t } = useI18n();
+
+const workflow = inject('workflow');
+const hasPermission = ref(false);
+
+function handlePermission(status) {
+  hasPermission.value = status;
+}
+function requestPermission() {
+  browser.permissions.request(permission).then(handlePermission);
+}
+function updateData(value) {
+  emit('update:data', { ...props.data, ...value });
+}
+
+onMounted(() => {
+  browser.permissions.contains(permission).then(handlePermission);
+});
+</script>

+ 2 - 0
src/lib/v-remixicon.js

@@ -4,6 +4,7 @@ import {
   riH2,
   riLinkM,
   riEarthLine,
+  riClipboardLine,
   riLock2Line,
   riBaseStationLine,
   riKeyboardLine,
@@ -103,6 +104,7 @@ export const icons = {
   riH2,
   riLinkM,
   riEarthLine,
+  riClipboardLine,
   riLock2Line,
   riBaseStationLine,
   riKeyboardLine,

+ 8 - 1
src/locales/en/blocks.json

@@ -28,6 +28,13 @@
           "text": "Multiple"
         }
       },
+      "clipboard": {
+        "name": "Clipboard",
+        "description": "Get the copied text from the clipboard",
+        "data": "Clipboard data",
+        "noPermission": "Don't have permission to access the clipboard",
+        "grantPermission": "Grant permission"
+      },
       "upload-file": {
         "name": "Upload file",
         "description": "Upload file into <input type=\"file\"> element",
@@ -356,7 +363,7 @@
       },
       "webhook": {
         "name": "HTTP request",
-        "description": "make an HTTP request",
+        "description": "Make an HTTP Request",
         "url": "The Post receive URL",
         "contentType": "Content type",
         "method": "Request method",

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

@@ -198,6 +198,7 @@
       "no-file-access": "Automa doesn't have access to the file",
       "no-workflow": "Can't find workflow with \"{workflowId}\" ID",
       "no-match-tab": "Can't find a tab with \"{pattern}\" patterns",
+      "no-clipboard-acces": "Don't have permission to access clipboard",
       "element-not-found": "Can't find an element with \"{selector}\" selector.",
       "not-iframe": "Element with \"{selector}\" selector is not an Iframe element",
       "iframe-not-found": "Can't find an Iframe element with \"{selector}\" selector.",

+ 3 - 0
src/manifest.json

@@ -45,6 +45,9 @@
       "all_frames": false
     }
   ],
+  "optional_permissions": [
+    "clipboardRead"
+  ],
   "permissions": [
     "tabs",
     "proxy",

+ 19 - 0
src/utils/shared.js

@@ -631,6 +631,25 @@ export const tasks = {
       blocks: [],
     },
   },
+  clipboard: {
+    name: 'Clipboard',
+    description: 'Get the copied text from the clipboard',
+    icon: 'riClipboardLine',
+    component: 'BlockBasic',
+    category: 'general',
+    editComponent: 'EditClipboard',
+    inputs: 1,
+    outputs: 1,
+    allowedInputs: true,
+    maxConnection: 1,
+    data: {
+      description: '',
+      assignVariable: false,
+      variableName: '',
+      saveData: true,
+      dataColumn: '',
+    },
+  },
   'switch-to': {
     name: 'Switch frame',
     description: 'Switch between main window and iframe',