App.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <template v-if="retrieved">
  3. <app-sidebar />
  4. <main class="pl-16">
  5. <router-view />
  6. </main>
  7. <ui-dialog>
  8. <template #auth>
  9. <div class="text-center">
  10. <p class="font-semibold text-xl">Oops!! 😬</p>
  11. <p class="mt-2 text-gray-600 dark:text-gray-200">
  12. {{ t('auth.text') }}
  13. </p>
  14. <ui-button
  15. tag="a"
  16. href="https://www.automa.site/auth"
  17. class="mt-6 w-full block"
  18. variant="accent"
  19. >
  20. {{ t('auth.signIn') }}
  21. </ui-button>
  22. </div>
  23. </template>
  24. </ui-dialog>
  25. <div
  26. v-if="isUpdated"
  27. class="p-4 shadow-2xl z-50 fixed bottom-8 left-1/2 -translate-x-1/2 rounded-lg bg-accent text-white dark:text-gray-900 flex items-center"
  28. >
  29. <v-remixicon name="riInformationLine" class="mr-3" />
  30. <p>
  31. {{ t('updateMessage.text1', { version: currentVersion }) }}
  32. </p>
  33. <a
  34. :href="`https://github.com/AutomaApp/automa/releases/latest`"
  35. target="_blank"
  36. rel="noopener"
  37. class="underline ml-1"
  38. >
  39. {{ t('updateMessage.text2') }}
  40. </a>
  41. <button
  42. class="ml-6 text-gray-200 dark:text-gray-600"
  43. @click="isUpdated = false"
  44. >
  45. <v-remixicon size="20" name="riCloseLine" />
  46. </button>
  47. </div>
  48. </template>
  49. <div v-else class="py-8 text-center">
  50. <ui-spinner color="text-accent" size="28" />
  51. </div>
  52. <app-survey />
  53. </template>
  54. <script setup>
  55. import iconFirefox from '@/assets/svg/logoFirefox.svg';
  56. import iconChrome from '@/assets/svg/logo.svg';
  57. import { ref } from 'vue';
  58. import { useI18n } from 'vue-i18n';
  59. import { useRouter } from 'vue-router';
  60. import { compare } from 'compare-versions';
  61. import browser from 'webextension-polyfill';
  62. import { useStore } from '@/stores/main';
  63. import { useUserStore } from '@/stores/user';
  64. import { useFolderStore } from '@/stores/folder';
  65. import { useWorkflowStore } from '@/stores/workflow';
  66. import { useTeamWorkflowStore } from '@/stores/teamWorkflow';
  67. import { useTheme } from '@/composable/theme';
  68. import { parseJSON } from '@/utils/helper';
  69. import { useHostedWorkflowStore } from '@/stores/hostedWorkflow';
  70. import { useSharedWorkflowStore } from '@/stores/sharedWorkflow';
  71. import { loadLocaleMessages, setI18nLanguage } from '@/lib/vueI18n';
  72. import { getUserWorkflows } from '@/utils/api';
  73. import dbLogs from '@/db/logs';
  74. import dayjs from '@/lib/dayjs';
  75. import AppSurvey from '@/components/newtab/app/AppSurvey.vue';
  76. import AppSidebar from '@/components/newtab/app/AppSidebar.vue';
  77. import dataMigration from '@/utils/dataMigration';
  78. let icon;
  79. if (window.location.protocol === 'moz-extension:') {
  80. icon = iconFirefox;
  81. } else {
  82. icon = iconChrome;
  83. }
  84. const iconElement = document.createElement('link');
  85. iconElement.rel = 'icon';
  86. iconElement.href = icon;
  87. document.head.appendChild(iconElement);
  88. const { t } = useI18n();
  89. const store = useStore();
  90. const theme = useTheme();
  91. const router = useRouter();
  92. const userStore = useUserStore();
  93. const folderStore = useFolderStore();
  94. const workflowStore = useWorkflowStore();
  95. const teamWorkflowStore = useTeamWorkflowStore();
  96. const sharedWorkflowStore = useSharedWorkflowStore();
  97. const hostedWorkflowStore = useHostedWorkflowStore();
  98. theme.init();
  99. const retrieved = ref(false);
  100. const isUpdated = ref(false);
  101. const currentVersion = browser.runtime.getManifest().version;
  102. const prevVersion = localStorage.getItem('ext-version') || '0.0.0';
  103. async function fetchUserData() {
  104. try {
  105. if (!useRouter.user) return;
  106. const { backup, hosted } = await getUserWorkflows();
  107. userStore.hostedWorkflows = hosted || {};
  108. if (backup && backup.length > 0) {
  109. const { lastBackup } = browser.storage.local.get('lastBackup');
  110. if (!lastBackup) {
  111. const backupIds = backup.map(({ id }) => id);
  112. userStore.backupIds = backupIds;
  113. await browser.storage.local.set({
  114. backupIds,
  115. lastBackup: new Date().toISOString(),
  116. });
  117. }
  118. await workflowStore.insertOrUpdate(backup, { checkUpdateDate: true });
  119. }
  120. userStore.retrieved = true;
  121. } catch (error) {
  122. console.error(error);
  123. }
  124. }
  125. /* eslint-disable-next-line */
  126. function autoDeleteLogs() {
  127. const deleteAfter = store.settings.deleteLogAfter;
  128. if (deleteAfter === 'never') return;
  129. const lastCheck =
  130. +localStorage.getItem('checkDeleteLogs') || Date.now() - 8.64e7;
  131. const dayDiff = dayjs().diff(dayjs(lastCheck), 'day');
  132. if (dayDiff < 1) return;
  133. const aDayInMs = 8.64e7;
  134. const maxLogAge = Date.now() - aDayInMs * deleteAfter;
  135. dbLogs.items
  136. .where('endedAt')
  137. .below(maxLogAge)
  138. .toArray()
  139. .then((values) => {
  140. const ids = values.map(({ id }) => id);
  141. dbLogs.items.bulkDelete(ids);
  142. dbLogs.ctxData.where('logId').anyOf(ids).delete();
  143. dbLogs.logsData.where('logId').anyOf(ids).delete();
  144. dbLogs.histories.where('logId').anyOf(ids).delete();
  145. localStorage.setItem('checkDeleteLogs', Date.now());
  146. });
  147. }
  148. async function syncHostedWorkflows() {
  149. const hostIds = [];
  150. const userHosted = userStore.getHostedWorkflows;
  151. const hostedWorkflows = hostedWorkflowStore.workflows;
  152. Object.keys(hostedWorkflows).forEach((hostId) => {
  153. const isItsOwn = userHosted.find((item) => item.hostId === hostId);
  154. if (isItsOwn) return;
  155. hostIds.push({ hostId, updatedAt: hostedWorkflows[hostId].updatedAt });
  156. });
  157. await hostedWorkflowStore.fetchWorkflows(hostIds);
  158. }
  159. window.addEventListener('storage', ({ key, newValue }) => {
  160. if (key !== 'workflowState') return;
  161. const states = parseJSON(newValue, {});
  162. workflowStore.states = Object.values(states).filter(
  163. ({ isDestroyed }) => !isDestroyed
  164. );
  165. });
  166. browser.runtime.onMessage.addListener(({ type, data }) => {
  167. if (type === 'workflow:added') {
  168. if (data.source === 'team') {
  169. teamWorkflowStore.loadData().then(() => {
  170. router.push(
  171. `/teams/${data.teamId}/workflows/${data.workflowId}?permission=true`
  172. );
  173. });
  174. } else {
  175. workflowStore.loadData().then(() => {
  176. router.push(`/workflows/${data.workflowId}?permission=true`);
  177. });
  178. }
  179. }
  180. });
  181. (async () => {
  182. try {
  183. const { isFirstTime } = await browser.storage.local.get('isFirstTime');
  184. isUpdated.value = !isFirstTime && compare(currentVersion, prevVersion, '>');
  185. await Promise.allSettled([
  186. folderStore.load(),
  187. store.loadSettings(),
  188. workflowStore.loadData(),
  189. teamWorkflowStore.loadData(),
  190. hostedWorkflowStore.loadData(),
  191. ]);
  192. await loadLocaleMessages(store.settings.locale, 'newtab');
  193. await setI18nLanguage(store.settings.locale);
  194. await dataMigration();
  195. await userStore.loadUser({ useCache: true });
  196. retrieved.value = true;
  197. await Promise.allSettled([
  198. sharedWorkflowStore.fetchWorkflows(),
  199. fetchUserData(),
  200. syncHostedWorkflows(),
  201. ]);
  202. autoDeleteLogs();
  203. } catch (error) {
  204. retrieved.value = true;
  205. console.error(error);
  206. }
  207. localStorage.setItem('ext-version', currentVersion);
  208. })();
  209. </script>
  210. <style>
  211. html,
  212. body {
  213. @apply bg-gray-50 dark:bg-gray-900 text-black dark:text-gray-100;
  214. }
  215. body {
  216. min-height: 100vh;
  217. }
  218. #app {
  219. height: 100%;
  220. }
  221. h1,
  222. h2,
  223. h3 {
  224. @apply dark:text-white;
  225. }
  226. </style>