feat: add loading state for import actions (#4217)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
HelixY2J
2024-09-23 17:00:48 +05:30
committed by GitHub
parent e9e1366cc8
commit 1701961335
9 changed files with 196 additions and 95 deletions

View File

@@ -19,30 +19,30 @@ import { UrlSource } from "~/helpers/import-export/import/import-sources/UrlSour
import IconFile from "~icons/lucide/file" import IconFile from "~icons/lucide/file"
import { import {
hoppRESTImporter,
hoppInsomniaImporter,
hoppPostmanImporter,
toTeamsImporter,
hoppOpenAPIImporter,
harImporter, harImporter,
hoppInsomniaImporter,
hoppOpenAPIImporter,
hoppPostmanImporter,
hoppRESTImporter,
toTeamsImporter,
} from "~/helpers/import-export/import/importers" } from "~/helpers/import-export/import/importers"
import { defineStep } from "~/composables/step-components" import { defineStep } from "~/composables/step-components"
import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue"
import { useI18n } from "~/composables/i18n" import { useI18n } from "~/composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import { appendRESTCollections, restCollections$ } from "~/newstore/collections" import { appendRESTCollections, restCollections$ } from "~/newstore/collections"
import MyCollectionImport from "~/components/importExport/ImportExportSteps/MyCollectionImport.vue"
import IconFolderPlus from "~icons/lucide/folder-plus"
import IconOpenAPI from "~icons/lucide/file"
import IconPostman from "~icons/hopp/postman"
import IconInsomnia from "~icons/hopp/insomnia" import IconInsomnia from "~icons/hopp/insomnia"
import IconPostman from "~icons/hopp/postman"
import IconOpenAPI from "~icons/lucide/file"
import IconFolderPlus from "~icons/lucide/folder-plus"
import IconGithub from "~icons/lucide/github" import IconGithub from "~icons/lucide/github"
import IconLink from "~icons/lucide/link" import IconLink from "~icons/lucide/link"
import IconUser from "~icons/lucide/user"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
import IconUser from "~icons/lucide/user"
import { getTeamCollectionJSON } from "~/helpers/backend/helpers" import { getTeamCollectionJSON } from "~/helpers/backend/helpers"
@@ -53,10 +53,18 @@ import { gistExporter } from "~/helpers/import-export/export/gist"
import { myCollectionsExporter } from "~/helpers/import-export/export/myCollections" import { myCollectionsExporter } from "~/helpers/import-export/export/myCollections"
import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections" import { teamCollectionsExporter } from "~/helpers/import-export/export/teamCollections"
import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource"
import { ImporterOrExporter } from "~/components/importExport/types" import { ImporterOrExporter } from "~/components/importExport/types"
import { GistSource } from "~/helpers/import-export/import/import-sources/GistSource"
import { TeamWorkspace } from "~/services/workspace.service" import { TeamWorkspace } from "~/services/workspace.service"
const isPostmanImporterInProgress = ref(false)
const isInsomniaImporterInProgress = ref(false)
const isOpenAPIImporterInProgress = ref(false)
const isRESTImporterInProgress = ref(false)
const isPersonalCollectionImporterInProgress = ref(false)
const isHarImporterInProgress = ref(false)
const isGistImporterInProgress = ref(false)
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
@@ -92,7 +100,7 @@ const showImportFailedError = () => {
const handleImportToStore = async (collections: HoppCollection[]) => { const handleImportToStore = async (collections: HoppCollection[]) => {
const importResult = const importResult =
props.collectionsType.type === "my-collections" props.collectionsType.type === "my-collections"
? await importToPersonalWorkspace(collections) ? importToPersonalWorkspace(collections)
: await importToTeamsWorkspace(collections) : await importToTeamsWorkspace(collections)
if (E.isRight(importResult)) { if (E.isRight(importResult)) {
@@ -179,10 +187,11 @@ const HoppRESTImporter: ImporterOrExporter = {
caption: "import.from_file", caption: "import.from_file",
acceptedFileTypes: ".json", acceptedFileTypes: ".json",
onImportFromFile: async (content) => { onImportFromFile: async (content) => {
isRESTImporterInProgress.value = true
const res = await hoppRESTImporter(content)() const res = await hoppRESTImporter(content)()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_IMPORT_COLLECTION", type: "HOPP_IMPORT_COLLECTION",
@@ -193,7 +202,10 @@ const HoppRESTImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isRESTImporterInProgress.value = false
}, },
isLoading: isRESTImporterInProgress,
}), }),
} }
@@ -207,8 +219,11 @@ const HoppMyCollectionImporter: ImporterOrExporter = {
applicableTo: ["team-workspace"], applicableTo: ["team-workspace"],
}, },
component: defineStep("my_collection_import", MyCollectionImport, () => ({ component: defineStep("my_collection_import", MyCollectionImport, () => ({
loading: isPersonalCollectionImporterInProgress.value,
async onImportFromMyCollection(content) { async onImportFromMyCollection(content) {
handleImportToStore([content]) isPersonalCollectionImporterInProgress.value = true
await handleImportToStore([content])
// our analytics consider this as an export event, so keeping compatibility with that // our analytics consider this as an export event, so keeping compatibility with that
platform.analytics?.logEvent({ platform.analytics?.logEvent({
@@ -216,6 +231,8 @@ const HoppMyCollectionImporter: ImporterOrExporter = {
exporter: "import_to_teams", exporter: "import_to_teams",
platform: "rest", platform: "rest",
}) })
isPersonalCollectionImporterInProgress.value = false
}, },
})), })),
} }
@@ -238,10 +255,12 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
caption: "import.from_file", caption: "import.from_file",
acceptedFileTypes: ".json, .yaml, .yml", acceptedFileTypes: ".json, .yaml, .yml",
onImportFromFile: async (content) => { onImportFromFile: async (content) => {
isOpenAPIImporterInProgress.value = true
const res = await hoppOpenAPIImporter(content)() const res = await hoppOpenAPIImporter(content)()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
platform: "rest", platform: "rest",
@@ -252,7 +271,10 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isOpenAPIImporterInProgress.value = false
}, },
isLoading: isOpenAPIImporterInProgress,
}), }),
}, },
{ {
@@ -262,10 +284,12 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
step: UrlSource({ step: UrlSource({
caption: "import.from_url", caption: "import.from_url",
onImportFromURL: async (content) => { onImportFromURL: async (content) => {
isOpenAPIImporterInProgress.value = true
const res = await hoppOpenAPIImporter([content])() const res = await hoppOpenAPIImporter([content])()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
platform: "rest", platform: "rest",
@@ -276,7 +300,10 @@ const HoppOpenAPIImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isOpenAPIImporterInProgress.value = false
}, },
isLoading: isOpenAPIImporterInProgress,
}), }),
}, },
], ],
@@ -295,10 +322,12 @@ const HoppPostmanImporter: ImporterOrExporter = {
caption: "import.from_file", caption: "import.from_file",
acceptedFileTypes: ".json", acceptedFileTypes: ".json",
onImportFromFile: async (content) => { onImportFromFile: async (content) => {
isPostmanImporterInProgress.value = true
const res = await hoppPostmanImporter(content)() const res = await hoppPostmanImporter(content)()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
platform: "rest", platform: "rest",
@@ -309,7 +338,10 @@ const HoppPostmanImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isPostmanImporterInProgress.value = false
}, },
isLoading: isPostmanImporterInProgress,
}), }),
} }
@@ -326,10 +358,12 @@ const HoppInsomniaImporter: ImporterOrExporter = {
caption: "import.from_file", caption: "import.from_file",
acceptedFileTypes: ".json", acceptedFileTypes: ".json",
onImportFromFile: async (content) => { onImportFromFile: async (content) => {
isInsomniaImporterInProgress.value = true
const res = await hoppInsomniaImporter(content)() const res = await hoppInsomniaImporter(content)()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
platform: "rest", platform: "rest",
@@ -340,7 +374,10 @@ const HoppInsomniaImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isInsomniaImporterInProgress.value = false
}, },
isLoading: isInsomniaImporterInProgress,
}), }),
} }
@@ -361,10 +398,12 @@ const HoppGistImporter: ImporterOrExporter = {
return return
} }
isGistImporterInProgress.value = true
const res = await hoppRESTImporter(content.right)() const res = await hoppRESTImporter(content.right)()
if (E.isRight(res)) { if (E.isRight(res)) {
handleImportToStore(res.right) await handleImportToStore(res.right)
platform.analytics?.logEvent({ platform.analytics?.logEvent({
platform: "rest", platform: "rest",
@@ -375,7 +414,10 @@ const HoppGistImporter: ImporterOrExporter = {
} else { } else {
showImportFailedError() showImportFailedError()
} }
isGistImporterInProgress.value = false
}, },
isLoading: isGistImporterInProgress,
}), }),
} }
@@ -528,22 +570,26 @@ const HARImporter: ImporterOrExporter = {
caption: "import.from_file", caption: "import.from_file",
acceptedFileTypes: ".har", acceptedFileTypes: ".har",
onImportFromFile: async (content) => { onImportFromFile: async (content) => {
isHarImporterInProgress.value = true
const res = await harImporter(content) const res = await harImporter(content)
if (E.isLeft(res)) { if (E.isRight(res)) {
await handleImportToStore(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_COLLECTION",
importer: "import.from_har",
platform: "rest",
workspaceType: isTeamWorkspace.value ? "team" : "personal",
})
} else {
showImportFailedError() showImportFailedError()
return
} }
handleImportToStore(res.right) isHarImporterInProgress.value = false
platform.analytics?.logEvent({
type: "HOPP_IMPORT_COLLECTION",
importer: "import.from_har",
platform: "rest",
workspaceType: isTeamWorkspace.value ? "team" : "personal",
})
}, },
isLoading: isHarImporterInProgress,
}), }),
} }

View File

@@ -231,7 +231,7 @@ const showImportFailedError = () => {
toast.error(t("import.failed")) toast.error(t("import.failed"))
} }
const handleImportToStore = async (gqlCollections: HoppCollection[]) => { const handleImportToStore = (gqlCollections: HoppCollection[]) => {
appendGraphqlCollections(gqlCollections) appendGraphqlCollections(gqlCollections)
toast.success(t("state.file_imported")) toast.success(t("state.file_imported"))
} }

View File

@@ -60,6 +60,11 @@ const currentUser = useReadonlyStream(
platform.auth.getCurrentUser() platform.auth.getCurrentUser()
) )
const isPostmanImporterInProgress = ref(false)
const isInsomniaImporterInProgress = ref(false)
const isRESTImporterInProgress = ref(false)
const isGistImporterInProgress = ref(false)
const isEnvironmentGistExportInProgress = ref(false) const isEnvironmentGistExportInProgress = ref(false)
const isTeamEnvironment = computed(() => { const isTeamEnvironment = computed(() => {
@@ -91,23 +96,27 @@ const HoppEnvironmentsImport: ImporterOrExporter = {
acceptedFileTypes: "application/json", acceptedFileTypes: "application/json",
caption: "import.hoppscotch_environment_description", caption: "import.hoppscotch_environment_description",
onImportFromFile: async (environments) => { onImportFromFile: async (environments) => {
isRESTImporterInProgress.value = true
const res = await hoppEnvImporter(environments)() const res = await hoppEnvImporter(environments)()
if (E.isLeft(res)) { if (E.isRight(res)) {
await handleImportToStore(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: isTeamEnvironment.value ? "team" : "personal",
})
emit("hide-modal")
} else {
showImportFailedError() showImportFailedError()
return
} }
handleImportToStore(res.right) isRESTImporterInProgress.value = false
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: workspaceType.value,
})
emit("hide-modal")
}, },
isLoading: isRESTImporterInProgress,
}), }),
} }
@@ -124,23 +133,27 @@ const PostmanEnvironmentsImport: ImporterOrExporter = {
acceptedFileTypes: "application/json", acceptedFileTypes: "application/json",
caption: "import.postman_environment_description", caption: "import.postman_environment_description",
onImportFromFile: async (environments) => { onImportFromFile: async (environments) => {
isPostmanImporterInProgress.value = true
const res = await postmanEnvImporter(environments)() const res = await postmanEnvImporter(environments)()
if (E.isLeft(res)) { if (E.isRight(res)) {
await handleImportToStore(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: isTeamEnvironment.value ? "team" : "personal",
})
emit("hide-modal")
} else {
showImportFailedError() showImportFailedError()
return
} }
handleImportToStore(res.right) isPostmanImporterInProgress.value = false
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: workspaceType.value,
})
emit("hide-modal")
}, },
isLoading: isPostmanImporterInProgress,
}), }),
} }
@@ -157,30 +170,34 @@ const insomniaEnvironmentsImport: ImporterOrExporter = {
acceptedFileTypes: "application/json", acceptedFileTypes: "application/json",
caption: "import.insomnia_environment_description", caption: "import.insomnia_environment_description",
onImportFromFile: async (environments) => { onImportFromFile: async (environments) => {
isInsomniaImporterInProgress.value = true
const res = await insomniaEnvImporter(environments)() const res = await insomniaEnvImporter(environments)()
if (E.isLeft(res)) { if (E.isRight(res)) {
const globalEnvs = res.right.filter(
(env) => env.name === "Base Environment"
)
const otherEnvs = res.right.filter(
(env) => env.name !== "Base Environment"
)
await handleImportToStore(otherEnvs, globalEnvs)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: isTeamEnvironment.value ? "team" : "personal",
})
emit("hide-modal")
} else {
showImportFailedError() showImportFailedError()
return
} }
const globalEnvs = res.right.filter( isInsomniaImporterInProgress.value = false
(env) => env.name === "Base Environment"
)
const otherEnvs = res.right.filter(
(env) => env.name !== "Base Environment"
)
handleImportToStore(otherEnvs, globalEnvs)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: workspaceType.value,
})
emit("hide-modal")
}, },
isLoading: isInsomniaImporterInProgress,
}), }),
} }
@@ -201,21 +218,26 @@ const EnvironmentsImportFromGIST: ImporterOrExporter = {
return return
} }
isGistImporterInProgress.value = true
const res = await hoppEnvImporter(environments.right)() const res = await hoppEnvImporter(environments.right)()
if (E.isLeft(res)) { if (E.isRight(res)) {
await handleImportToStore(res.right)
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: isTeamEnvironment.value ? "team" : "personal",
})
emit("hide-modal")
} else {
showImportFailedError() showImportFailedError()
return
} }
handleImportToStore(res.right) isGistImporterInProgress.value = false
platform.analytics?.logEvent({
type: "HOPP_IMPORT_ENVIRONMENT",
platform: "rest",
workspaceType: workspaceType.value,
})
emit("hide-modal")
}, },
isLoading: isGistImporterInProgress,
}), }),
} }

View File

@@ -45,9 +45,10 @@
</p> </p>
<div> <div>
<HoppButtonPrimary <HoppButtonPrimary
class="w-full" :disabled="disableImportCTA"
:label="t('import.title')" :label="t('import.title')"
:disabled="!hasFile || showFileSizeLimitExceededWarning" :loading="loading"
class="w-full"
@click="emit('importFromFile', fileContent)" @click="emit('importFromFile', fileContent)"
/> />
</div> </div>
@@ -55,14 +56,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { useToast } from "@composables/toast" import { useToast } from "@composables/toast"
import { computed, ref } from "vue"
defineProps<{ const props = withDefaults(
caption: string defineProps<{
acceptedFileTypes: string caption: string
}>() acceptedFileTypes: string
loading?: boolean
}>(),
{
loading: false,
}
)
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
@@ -81,6 +88,12 @@ const emit = defineEmits<{
(e: "importFromFile", content: string[]): void (e: "importFromFile", content: string[]): void
}>() }>()
// Disable the import CTA if no file is selected, the file size limit is exceeded, or during an import action indicated by the `isLoading` prop
const disableImportCTA = computed(
() =>
!hasFile.value || showFileSizeLimitExceededWarning.value || props.loading
)
const onFileChange = async () => { const onFileChange = async () => {
// Reset the state on entering the handler to avoid any stale state // Reset the state on entering the handler to avoid any stale state
if (showFileSizeLimitExceededWarning.value) { if (showFileSizeLimitExceededWarning.value) {

View File

@@ -24,7 +24,8 @@
<HoppButtonPrimary <HoppButtonPrimary
class="w-full" class="w-full"
:label="t('import.title')" :label="t('import.title')"
:disabled="!hasSelectedCollectionID" :loading="loading"
:disabled="!hasSelectedCollectionID || loading"
@click="fetchCollectionFromMyCollections" @click="fetchCollectionFromMyCollections"
/> />
</div> </div>
@@ -39,6 +40,10 @@ import { getRESTCollection, restCollections$ } from "~/newstore/collections"
const t = useI18n() const t = useI18n()
defineProps<{
loading: boolean
}>()
const mySelectedCollectionID = ref<number | undefined>(undefined) const mySelectedCollectionID = ref<number | undefined>(undefined)
const hasSelectedCollectionID = computed(() => { const hasSelectedCollectionID = computed(() => {

View File

@@ -26,8 +26,8 @@
<HoppButtonPrimary <HoppButtonPrimary
class="w-full" class="w-full"
:label="t('import.title')" :label="t('import.title')"
:disabled="!hasURL" :disabled="disableImportCTA"
:loading="isFetchingUrl" :loading="isFetchingUrl || loading"
@click="fetchUrlData" @click="fetchUrlData"
/> />
</div> </div>
@@ -35,7 +35,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from "vue" import { computed, ref, watch } from "vue"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
import axios, { AxiosResponse } from "axios" import axios, { AxiosResponse } from "axios"
@@ -44,10 +44,14 @@ const t = useI18n()
const toast = useToast() const toast = useToast()
const props = defineProps<{ const props = withDefaults(
caption: string defineProps<{
fetchLogic?: (url: string) => Promise<AxiosResponse<any>> caption: string
}>() fetchLogic?: (url: string) => Promise<AxiosResponse<any>>
loading?: boolean
}>(),
{ fetchLogic: undefined, loading: false }
)
const emit = defineEmits<{ const emit = defineEmits<{
(e: "importFromURL", content: unknown): void (e: "importFromURL", content: unknown): void
@@ -62,6 +66,8 @@ watch(inputChooseGistToImportFrom, (url) => {
hasURL.value = !!url hasURL.value = !!url
}) })
const disableImportCTA = computed(() => !hasURL.value || props.loading)
const urlFetchLogic = const urlFetchLogic =
props.fetchLogic ?? props.fetchLogic ??
async function (url: string) { async function (url: string) {

View File

@@ -2,11 +2,13 @@ import FileImportVue from "~/components/importExport/ImportExportSteps/FileImpor
import { defineStep } from "~/composables/step-components" import { defineStep } from "~/composables/step-components"
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
import { Ref } from "vue"
export function FileSource(metadata: { export function FileSource(metadata: {
acceptedFileTypes: string acceptedFileTypes: string
caption: string caption: string
onImportFromFile: (content: string[]) => any | Promise<any> onImportFromFile: (content: string[]) => any | Promise<any>
isLoading?: Ref<boolean>
}) { }) {
const stepID = uuidv4() const stepID = uuidv4()
@@ -14,5 +16,6 @@ export function FileSource(metadata: {
acceptedFileTypes: metadata.acceptedFileTypes, acceptedFileTypes: metadata.acceptedFileTypes,
caption: metadata.caption, caption: metadata.caption,
onImportFromFile: metadata.onImportFromFile, onImportFromFile: metadata.onImportFromFile,
loading: metadata.isLoading?.value,
})) }))
} }

View File

@@ -6,18 +6,20 @@ import * as E from "fp-ts/Either"
import { z } from "zod" import { z } from "zod"
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
import { Ref } from "vue"
export function GistSource(metadata: { export function GistSource(metadata: {
caption: string caption: string
onImportFromGist: ( onImportFromGist: (
importResult: E.Either<string, string[]> importResult: E.Either<string, string[]>
) => any | Promise<any> ) => any | Promise<any>
isLoading?: Ref<boolean>
}) { }) {
const stepID = uuidv4() const stepID = uuidv4()
return defineStep(stepID, UrlImport, () => ({ return defineStep(stepID, UrlImport, () => ({
caption: metadata.caption, caption: metadata.caption,
onImportFromURL: (gistResponse: Record<string, unknown>) => { onImportFromURL: (gistResponse: unknown) => {
const fileSchema = z.object({ const fileSchema = z.object({
files: z.record(z.object({ content: z.string() })), files: z.record(z.object({ content: z.string() })),
}) })
@@ -36,6 +38,7 @@ export function GistSource(metadata: {
metadata.onImportFromGist(E.right(contents)) metadata.onImportFromGist(E.right(contents))
}, },
fetchLogic: fetchGistFromUrl, fetchLogic: fetchGistFromUrl,
loading: metadata.isLoading?.value,
})) }))
} }

View File

@@ -2,11 +2,13 @@ import UrlImport from "~/components/importExport/ImportExportSteps/UrlImport.vue
import { defineStep } from "~/composables/step-components" import { defineStep } from "~/composables/step-components"
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
import { Ref } from "vue"
export function UrlSource(metadata: { export function UrlSource(metadata: {
caption: string caption: string
onImportFromURL: (content: string) => any | Promise<any> onImportFromURL: (content: string) => any | Promise<any>
fetchLogic?: (url: string) => Promise<any> fetchLogic?: (url: string) => Promise<any>
isLoading?: Ref<boolean>
}) { }) {
const stepID = uuidv4() const stepID = uuidv4()
@@ -17,5 +19,6 @@ export function UrlSource(metadata: {
metadata.onImportFromURL(content) metadata.onImportFromURL(content)
} }
}, },
loading: metadata.isLoading?.value,
})) }))
} }