App.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <div class="w-full flex flex-col h-full max-w-lg mx-auto dark:text-gray-100">
  3. <nav class="flex items-center w-full p-4 border-b mb-4">
  4. <span class="p-1 rounded-full bg-box-transparent dark:bg-none">
  5. <img src="@/assets/svg/logo.svg" class="w-10" />
  6. </span>
  7. <p class="font-semibold text-lg ml-4">Automa</p>
  8. </nav>
  9. <div class="px-4 pb-4 flex-1 overflow-auto scroll">
  10. <p class="text-gray-600 my-4 dark:text-gray-200">
  11. Input these workflows parameters before it runs.
  12. </p>
  13. <ui-expand
  14. v-for="(workflow, index) in sortedWorkflows"
  15. :key="index"
  16. :model-value="true"
  17. append-icon
  18. header-class="flex items-center text-left p-4 w-full rounded-lg"
  19. class="bg-white mb-4 dark:bg-gray-800 rounded-lg"
  20. >
  21. <template #header>
  22. <ui-img
  23. v-if="workflow.data.icon?.startsWith('http')"
  24. :src="workflow.data.icon"
  25. class="overflow-hidden rounded-lg"
  26. style="height: 40px; width: 40px"
  27. alt="Can not display"
  28. />
  29. <span v-else class="p-2 rounded-lg bg-box-transparent">
  30. <v-remixicon :name="workflow.data.icon" />
  31. </span>
  32. <div class="ml-4 flex-1 ml-2 overflow-hidden">
  33. <p
  34. class="text-overflow leading-tight mr-4 text-gray-600 dark:text-gray-200"
  35. >
  36. {{ workflow.data.name }}
  37. </p>
  38. <p class="leading-tight text-overflow">
  39. {{ workflow.data.description }}
  40. </p>
  41. </div>
  42. </template>
  43. <div class="px-4 pb-4">
  44. <ul class="space-y-2">
  45. <li v-for="(param, paramIdx) in workflow.params" :key="paramIdx">
  46. <component
  47. :is="workflowParameters[param.type].valueComp"
  48. v-if="workflowParameters[param.type]"
  49. v-model="param.value"
  50. :label="param.name"
  51. :param-data="param"
  52. class="w-full"
  53. />
  54. <ui-input
  55. v-else
  56. v-model="param.value"
  57. :type="param.inputType"
  58. :label="param.name"
  59. :placeholder="param.placeholder"
  60. class="w-full"
  61. @keyup.enter="runWorkflow(index, workflow)"
  62. />
  63. </li>
  64. </ul>
  65. <div class="flex items-center mt-6">
  66. <p>{{ dayjs(workflow.addedDate).fromNow() }}</p>
  67. <div class="flex-grow" />
  68. <ui-button class="mr-4" @click="deleteWorkflow(index)">
  69. Cancel
  70. </ui-button>
  71. <ui-button variant="accent" @click="runWorkflow(index, workflow)">
  72. <v-remixicon name="riPlayLine" class="mr-2 -ml-1" />
  73. Run
  74. </ui-button>
  75. </div>
  76. </div>
  77. </ui-expand>
  78. </div>
  79. </div>
  80. </template>
  81. <script setup>
  82. import { onMounted, ref, computed } from 'vue';
  83. import browser from 'webextension-polyfill';
  84. import { workflowParameters } from '@business';
  85. import dayjs from '@/lib/dayjs';
  86. import { useTheme } from '@/composable/theme';
  87. const theme = useTheme();
  88. theme.init();
  89. const workflows = ref([]);
  90. const sortedWorkflows = computed(() =>
  91. workflows.value.slice().sort((a, b) => b.addedDate - a.addedDate)
  92. );
  93. const flattenTeamWorkflows = (items) => Object.values(Object.values(items)[0]);
  94. async function findWorkflow(workflowId) {
  95. if (!workflowId) return null;
  96. if (workflowId.startsWith('team')) {
  97. const { teamWorkflows } = await browser.storage.local.get('teamWorkflows');
  98. if (!teamWorkflows) return null;
  99. const teamWorkflowsArr = flattenTeamWorkflows(teamWorkflows);
  100. return teamWorkflowsArr.find((item) => item.id === workflowId);
  101. }
  102. const { workflows: localWorkflows, workflowHosts } =
  103. await browser.storage.local.get(['workflows', 'workflowHosts']);
  104. let workflow = Array.isArray(localWorkflows)
  105. ? localWorkflows.find(({ id }) => id === workflowId)
  106. : localWorkflows[workflowId];
  107. if (!workflow) {
  108. workflow = Object.values(workflowHosts || {}).find(
  109. ({ hostId }) => hostId === workflowId
  110. );
  111. if (workflow) workflow.id = workflow.hostId;
  112. }
  113. return workflow;
  114. }
  115. function deleteWorkflow(index) {
  116. workflows.value.splice(index, 1);
  117. if (workflows.value.length === 0) {
  118. window.close();
  119. }
  120. }
  121. async function addWorkflow(workflowId) {
  122. try {
  123. const workflow =
  124. typeof workflowId === 'string'
  125. ? await findWorkflow(workflowId)
  126. : workflowId;
  127. const triggerBlock = workflow.drawflow.nodes.find(
  128. (node) => node.label === 'trigger'
  129. );
  130. if (!triggerBlock) return;
  131. const params = triggerBlock.data.parameters.map((param) => ({
  132. ...param,
  133. value: '',
  134. inputType: param.type === 'string' ? 'text' : 'number',
  135. }));
  136. workflows.value.push({
  137. params,
  138. data: workflow,
  139. addedDate: Date.now(),
  140. });
  141. } catch (error) {
  142. console.error(error);
  143. }
  144. }
  145. function runWorkflow(index, { data, params }) {
  146. const getParamVal = {
  147. string: (str) => str,
  148. number: (num) => (Number.isNaN(+num) ? 0 : +num),
  149. default: (value) => value,
  150. };
  151. const variables = params.reduce((acc, param) => {
  152. const valueFunc =
  153. getParamVal[param.type] ||
  154. workflowParameters[param.type]?.getValue ||
  155. getParamVal.default;
  156. const value = valueFunc(param.value || param.defaultValue);
  157. acc[param.name] = value;
  158. return acc;
  159. }, {});
  160. const payload = {
  161. name: 'background--workflow:execute',
  162. data: {
  163. ...data,
  164. options: {
  165. checkParams: false,
  166. data: { variables },
  167. },
  168. },
  169. };
  170. const isFirefox = BROWSER_TYPE === 'firefox';
  171. browser.runtime
  172. .sendMessage(isFirefox ? JSON.stringify(payload) : payload)
  173. .then(() => {
  174. deleteWorkflow(index);
  175. });
  176. }
  177. browser.runtime.onMessage.addListener(({ name, data }) => {
  178. if (name !== 'workflow:params') return;
  179. addWorkflow(data);
  180. });
  181. onMounted(() => {
  182. const query = new URLSearchParams(window.location.search);
  183. const workflowId = query.get('workflowId');
  184. if (workflowId) addWorkflow(workflowId);
  185. });
  186. </script>