123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- <template>
- <SmartModal
- v-if="show"
- dialog
- :title="`${t('modal.collections')}`"
- max-width="sm:max-w-md"
- @close="hideModal"
- >
- <template #actions>
- <span>
- <tippy ref="options" interactive trigger="click" theme="popover" arrow>
- <template #trigger>
- <ButtonSecondary
- v-tippy="{ theme: 'tooltip' }"
- :title="t('action.more')"
- svg="more-vertical"
- />
- </template>
- <div class="flex flex-col" role="menu">
- <SmartItem
- icon="assignment_returned"
- :label="t('import.from_gist')"
- @click.native="
- () => {
- readCollectionGist()
- options.tippy().hide()
- }
- "
- />
- <span
- v-tippy="{ theme: 'tooltip' }"
- :title="
- !currentUser
- ? `${t('export.require_github')}`
- : currentUser.provider !== 'github.com'
- ? `${t('export.require_github')}`
- : undefined
- "
- >
- <SmartItem
- :disabled="
- !currentUser
- ? true
- : currentUser.provider !== 'github.com'
- ? true
- : false
- "
- icon="assignment_turned_in"
- :label="t('export.create_secret_gist')"
- @click.native="
- () => {
- createCollectionGist()
- options.tippy().hide()
- }
- "
- />
- </span>
- </div>
- </tippy>
- </span>
- </template>
- <template #body>
- <div class="flex flex-col px-2 space-y-2">
- <SmartItem
- svg="folder-plus"
- :label="t('import.from_json')"
- @click.native="openDialogChooseFileToImportFrom"
- />
- <input
- ref="inputChooseFileToImportFrom"
- class="input"
- type="file"
- accept="application/json"
- @change="importFromJSON"
- />
- <hr />
- <SmartItem
- v-tippy="{ theme: 'tooltip' }"
- :title="t('action.download_file')"
- svg="download"
- :label="t('export.as_json')"
- @click.native="exportJSON"
- />
- </div>
- </template>
- </SmartModal>
- </template>
- <script setup lang="ts">
- import { computed, ref } from "@nuxtjs/composition-api"
- import { currentUser$ } from "~/helpers/fb/auth"
- import {
- useAxios,
- useI18n,
- useReadonlyStream,
- useToast,
- } from "~/helpers/utils/composables"
- import {
- graphqlCollections$,
- setGraphqlCollections,
- appendGraphqlCollections,
- } from "~/newstore/collections"
- defineProps<{
- show: boolean
- }>()
- const emit = defineEmits<{
- (e: "hide-modal"): void
- }>()
- const axios = useAxios()
- const toast = useToast()
- const t = useI18n()
- const collections = useReadonlyStream(graphqlCollections$, [])
- const currentUser = useReadonlyStream(currentUser$, null)
- // Template refs
- const options = ref<any>()
- const inputChooseFileToImportFrom = ref<HTMLInputElement>()
- const collectionJson = computed(() => {
- return JSON.stringify(collections.value, null, 2)
- })
- const createCollectionGist = async () => {
- if (!currentUser.value) {
- toast.error(t("profile.no_permission").toString())
- return
- }
- try {
- const res = await axios.$post(
- "https://api.github.com/gists",
- {
- files: {
- "hoppscotch-collections.json": {
- content: collectionJson.value,
- },
- },
- },
- {
- headers: {
- Authorization: `token ${currentUser.value.accessToken}`,
- Accept: "application/vnd.github.v3+json",
- },
- }
- )
- toast.success(t("export.gist_created").toString())
- window.open(res.html_url)
- } catch (e) {
- toast.error(t("error.something_went_wrong").toString())
- console.error(e)
- }
- }
- const fileImported = () => {
- toast.success(t("state.file_imported").toString())
- }
- const failedImport = () => {
- toast.error(t("import.failed").toString())
- }
- const readCollectionGist = async () => {
- const gist = prompt(t("import.gist_url").toString())
- if (!gist) return
- try {
- const { files } = (await axios.$get(
- `https://api.github.com/gists/${gist.split("/").pop()}`,
- {
- headers: {
- Accept: "application/vnd.github.v3+json",
- },
- }
- )) as {
- files: {
- [fileName: string]: {
- content: any
- }
- }
- }
- const collections = JSON.parse(Object.values(files)[0].content)
- setGraphqlCollections(collections)
- fileImported()
- } catch (e) {
- failedImport()
- console.error(e)
- }
- }
- const hideModal = () => {
- emit("hide-modal")
- }
- const openDialogChooseFileToImportFrom = () => {
- if (inputChooseFileToImportFrom.value)
- inputChooseFileToImportFrom.value.click()
- }
- const importFromJSON = () => {
- if (!inputChooseFileToImportFrom.value) return
- if (
- !inputChooseFileToImportFrom.value.files ||
- inputChooseFileToImportFrom.value.files.length === 0
- ) {
- toast.show(t("action.choose_file").toString())
- return
- }
- const reader = new FileReader()
- reader.onload = ({ target }) => {
- const content = target!.result as string | null
- if (!content) {
- toast.show(t("action.choose_file").toString())
- return
- }
- const collections = JSON.parse(content)
- if (collections[0]) {
- const [name, folders, requests] = Object.keys(collections[0])
- if (name === "name" && folders === "folders" && requests === "requests") {
- // Do nothing
- }
- } else {
- failedImport()
- return
- }
- appendGraphqlCollections(collections)
- fileImported()
- }
- reader.readAsText(inputChooseFileToImportFrom.value.files[0])
- inputChooseFileToImportFrom.value.value = ""
- }
- const exportJSON = () => {
- const dataToWrite = collectionJson.value
- const file = new Blob([dataToWrite], { type: "application/json" })
- const a = document.createElement("a")
- const url = URL.createObjectURL(file)
- a.href = url
- // TODO: get uri from meta
- a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
- document.body.appendChild(a)
- a.click()
- toast.success(t("state.download_started").toString())
- setTimeout(() => {
- document.body.removeChild(a)
- URL.revokeObjectURL(url)
- }, 1000)
- }
- </script>
|