refactor: move download file out of common into platform
This commit is contained in:
@@ -258,7 +258,7 @@ const importFromJSON = () => {
|
|||||||
inputChooseFileToImportFrom.value.value = ""
|
inputChooseFileToImportFrom.value.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportJSON = () => {
|
const exportJSON = async () => {
|
||||||
const dataToWrite = collectionJson.value
|
const dataToWrite = collectionJson.value
|
||||||
|
|
||||||
const parsedCollections = JSON.parse(dataToWrite)
|
const parsedCollections = JSON.parse(dataToWrite)
|
||||||
@@ -268,24 +268,32 @@ const exportJSON = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
const url = URL.createObjectURL(file)
|
||||||
a.href = url
|
|
||||||
|
|
||||||
platform?.analytics?.logEvent({
|
const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
||||||
type: "HOPP_EXPORT_COLLECTION",
|
|
||||||
exporter: "json",
|
URL.revokeObjectURL(url)
|
||||||
platform: "gql",
|
|
||||||
|
const result = await platform.io.saveFileWithDialog({
|
||||||
|
data: dataToWrite,
|
||||||
|
contentType: "application/json",
|
||||||
|
suggestedFilename: filename,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "Hoppscotch Collection JSON file",
|
||||||
|
extensions: ["json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: get uri from meta
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
platform?.analytics?.logEvent({
|
||||||
document.body.appendChild(a)
|
type: "HOPP_EXPORT_COLLECTION",
|
||||||
a.click()
|
exporter: "json",
|
||||||
toast.success(t("state.download_started").toString())
|
platform: "gql",
|
||||||
setTimeout(() => {
|
})
|
||||||
document.body.removeChild(a)
|
|
||||||
URL.revokeObjectURL(url)
|
toast.success(t("state.download_started").toString())
|
||||||
}, 1000)
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1866,28 +1866,25 @@ const getJSONCollection = async () => {
|
|||||||
* @param collectionJSON - JSON string of the collection
|
* @param collectionJSON - JSON string of the collection
|
||||||
* @param name - Name of the collection set as the file name
|
* @param name - Name of the collection set as the file name
|
||||||
*/
|
*/
|
||||||
const initializeDownloadCollection = (
|
const initializeDownloadCollection = async (
|
||||||
collectionJSON: string,
|
collectionJSON: string,
|
||||||
name: string | null
|
name: string | null
|
||||||
) => {
|
) => {
|
||||||
const file = new Blob([collectionJSON], { type: "application/json" })
|
const result = await platform.io.saveFileWithDialog({
|
||||||
const a = document.createElement("a")
|
data: collectionJSON,
|
||||||
const url = URL.createObjectURL(file)
|
contentType: "application/json",
|
||||||
a.href = url
|
suggestedFilename: `${name ?? "collection"}.json`,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "Hoppscotch Collection JSON file",
|
||||||
|
extensions: ["json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
if (name) {
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
a.download = `${name}.json`
|
toast.success(t("state.download_started").toString())
|
||||||
} else {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1916,11 +1913,14 @@ const exportData = async (
|
|||||||
exportLoading.value = false
|
exportLoading.value = false
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
(coll) => {
|
async (coll) => {
|
||||||
const hoppColl = teamCollToHoppRESTColl(coll)
|
const hoppColl = teamCollToHoppRESTColl(coll)
|
||||||
const collectionJSONString = JSON.stringify(hoppColl)
|
const collectionJSONString = JSON.stringify(hoppColl)
|
||||||
|
|
||||||
initializeDownloadCollection(collectionJSONString, hoppColl.name)
|
await initializeDownloadCollection(
|
||||||
|
collectionJSONString,
|
||||||
|
hoppColl.name
|
||||||
|
)
|
||||||
exportLoading.value = false
|
exportLoading.value = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ const importFromPostman = ({
|
|||||||
importFromHoppscotch(environments)
|
importFromHoppscotch(environments)
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportJSON = () => {
|
const exportJSON = async () => {
|
||||||
const dataToWrite = environmentJson.value
|
const dataToWrite = environmentJson.value
|
||||||
|
|
||||||
const parsedCollections = JSON.parse(dataToWrite)
|
const parsedCollections = JSON.parse(dataToWrite)
|
||||||
@@ -385,19 +385,27 @@ const exportJSON = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
const url = URL.createObjectURL(file)
|
||||||
a.href = url
|
|
||||||
|
|
||||||
// TODO: get uri from meta
|
const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
|
||||||
document.body.appendChild(a)
|
URL.revokeObjectURL(url)
|
||||||
a.click()
|
|
||||||
toast.success(t("state.download_started").toString())
|
const result = await platform.io.saveFileWithDialog({
|
||||||
setTimeout(() => {
|
data: dataToWrite,
|
||||||
document.body.removeChild(a)
|
contentType: "application/json",
|
||||||
URL.revokeObjectURL(url)
|
suggestedFilename: filename,
|
||||||
}, 1000)
|
filters: [
|
||||||
|
{
|
||||||
|
name: "JSON file",
|
||||||
|
extensions: ["json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
|
toast.success(t("state.download_started").toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getErrorMessage = (err: GQLError<string>) => {
|
const getErrorMessage = (err: GQLError<string>) => {
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import { useToast } from "@composables/toast"
|
|||||||
import { defineActionHandler } from "~/helpers/actions"
|
import { defineActionHandler } from "~/helpers/actions"
|
||||||
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
import { getPlatformSpecialKey as getSpecialKey } from "~/helpers/platformutils"
|
||||||
import { GQLResponseEvent } from "~/helpers/graphql/connection"
|
import { GQLResponseEvent } from "~/helpers/graphql/connection"
|
||||||
|
import { platform } from "~/platform"
|
||||||
|
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -111,21 +112,31 @@ const copyResponse = (str: string) => {
|
|||||||
toast.success(`${t("state.copied_to_clipboard")}`)
|
toast.success(`${t("state.copied_to_clipboard")}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadResponse = (str: string) => {
|
const downloadResponse = async (str: string) => {
|
||||||
const dataToWrite = str
|
const dataToWrite = str
|
||||||
const file = new Blob([dataToWrite!], { type: "application/json" })
|
const file = new Blob([dataToWrite!], { type: "application/json" })
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
const url = URL.createObjectURL(file)
|
||||||
a.href = url
|
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}`
|
const filename = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
||||||
document.body.appendChild(a)
|
|
||||||
a.click()
|
URL.revokeObjectURL(url)
|
||||||
downloadResponseIcon.value = IconCheck
|
|
||||||
toast.success(`${t("state.download_started")}`)
|
const result = await platform.io.saveFileWithDialog({
|
||||||
setTimeout(() => {
|
data: dataToWrite,
|
||||||
document.body.removeChild(a)
|
contentType: "application/json",
|
||||||
URL.revokeObjectURL(url)
|
suggestedFilename: filename,
|
||||||
}, 1000)
|
filters: [
|
||||||
|
{
|
||||||
|
name: "JSON file",
|
||||||
|
extensions: ["json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
|
downloadResponseIcon.value = IconCheck
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineActionHandler(
|
defineActionHandler(
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ import {
|
|||||||
schemaString,
|
schemaString,
|
||||||
subscriptionFields,
|
subscriptionFields,
|
||||||
} from "~/helpers/graphql/connection"
|
} from "~/helpers/graphql/connection"
|
||||||
|
import { platform } from "~/platform"
|
||||||
|
|
||||||
type NavigationTabs = "history" | "collection" | "docs" | "schema"
|
type NavigationTabs = "history" | "collection" | "docs" | "schema"
|
||||||
type GqlTabs = "queries" | "mutations" | "subscriptions" | "types"
|
type GqlTabs = "queries" | "mutations" | "subscriptions" | "types"
|
||||||
@@ -372,21 +373,33 @@ useCodemirror(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const downloadSchema = () => {
|
const downloadSchema = async () => {
|
||||||
const dataToWrite = JSON.stringify(schemaString.value, null, 2)
|
const dataToWrite = schemaString.value
|
||||||
const file = new Blob([dataToWrite], { type: "application/graphql" })
|
const file = new Blob([dataToWrite], { type: "application/graphql" })
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
const url = URL.createObjectURL(file)
|
||||||
a.href = url
|
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.graphql`
|
const filename = `${
|
||||||
document.body.appendChild(a)
|
url.split("/").pop()!.split("#")[0].split("?")[0]
|
||||||
a.click()
|
}.graphql`
|
||||||
downloadSchemaIcon.value = IconCheck
|
|
||||||
toast.success(`${t("state.download_started")}`)
|
URL.revokeObjectURL(url)
|
||||||
setTimeout(() => {
|
|
||||||
document.body.removeChild(a)
|
const result = await platform.io.saveFileWithDialog({
|
||||||
URL.revokeObjectURL(url)
|
data: dataToWrite,
|
||||||
}, 1000)
|
contentType: "application/graphql",
|
||||||
|
suggestedFilename: filename,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "GraphQL Schema File",
|
||||||
|
extensions: ["graphql"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
|
downloadSchemaIcon.value = IconCheck
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const copySchema = () => {
|
const copySchema = () => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useI18n } from "./i18n"
|
|||||||
import { refAutoReset } from "@vueuse/core"
|
import { refAutoReset } from "@vueuse/core"
|
||||||
import { copyToClipboard } from "@helpers/utils/clipboard"
|
import { copyToClipboard } from "@helpers/utils/clipboard"
|
||||||
import { HoppRESTResponse } from "@helpers/types/HoppRESTResponse"
|
import { HoppRESTResponse } from "@helpers/types/HoppRESTResponse"
|
||||||
|
import { platform } from "~/platform"
|
||||||
|
|
||||||
export function useCopyResponse(responseBodyText: Ref<any>) {
|
export function useCopyResponse(responseBodyText: Ref<any>) {
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
@@ -40,15 +41,14 @@ export function useDownloadResponse(
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const t = useI18n()
|
const t = useI18n()
|
||||||
|
|
||||||
const downloadResponse = () => {
|
const downloadResponse = async () => {
|
||||||
const dataToWrite = responseBody.value
|
const dataToWrite = responseBody.value
|
||||||
const file = new Blob([dataToWrite], { type: contentType })
|
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
a.href = url
|
|
||||||
|
|
||||||
// TODO: get uri from meta
|
// Guess extension and filename
|
||||||
a.download = pipe(
|
const file = new Blob([dataToWrite], { type: contentType })
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
|
||||||
|
const filename = pipe(
|
||||||
url,
|
url,
|
||||||
S.split("/"),
|
S.split("/"),
|
||||||
RNEA.last,
|
RNEA.last,
|
||||||
@@ -58,15 +58,24 @@ export function useDownloadResponse(
|
|||||||
RNEA.head
|
RNEA.head
|
||||||
)
|
)
|
||||||
|
|
||||||
document.body.appendChild(a)
|
URL.revokeObjectURL(url)
|
||||||
a.click()
|
|
||||||
downloadIcon.value = IconCheck
|
console.log(filename)
|
||||||
toast.success(`${t("state.download_started")}`)
|
|
||||||
setTimeout(() => {
|
// TODO: Look at the mime type and determine extension ?
|
||||||
document.body.removeChild(a)
|
const result = await platform.io.saveFileWithDialog({
|
||||||
URL.revokeObjectURL(url)
|
data: dataToWrite,
|
||||||
}, 1000)
|
contentType: contentType,
|
||||||
|
suggestedFilename: filename,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Assume success if unknown as we cannot determine
|
||||||
|
if (result.type === "unknown" || result.type === "saved") {
|
||||||
|
downloadIcon.value = IconCheck
|
||||||
|
toast.success(`${t("state.download_started")}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
downloadIcon,
|
downloadIcon,
|
||||||
downloadResponse,
|
downloadResponse,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Environment } from "@hoppscotch/data"
|
import { Environment } from "@hoppscotch/data"
|
||||||
import { TeamEnvironment } from "~/helpers/teams/TeamEnvironment"
|
import { TeamEnvironment } from "~/helpers/teams/TeamEnvironment"
|
||||||
import { cloneDeep } from "lodash-es"
|
import { cloneDeep } from "lodash-es"
|
||||||
|
import { platform } from "~/platform"
|
||||||
|
|
||||||
const getEnvironmentJson = (
|
const getEnvironmentJson = (
|
||||||
environmentObj: TeamEnvironment | Environment,
|
environmentObj: TeamEnvironment | Environment,
|
||||||
@@ -32,17 +33,24 @@ export const exportAsJSON = (
|
|||||||
if (!dataToWrite) return false
|
if (!dataToWrite) return false
|
||||||
|
|
||||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||||
const a = document.createElement("a")
|
|
||||||
const url = URL.createObjectURL(file)
|
const url = URL.createObjectURL(file)
|
||||||
a.href = url
|
|
||||||
|
|
||||||
// Extracts the path from url, removes fragment identifier and query parameters if any, appends the ".json" extension, and assigns it
|
URL.revokeObjectURL(url)
|
||||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}.json`
|
|
||||||
document.body.appendChild(a)
|
platform.io.saveFileWithDialog({
|
||||||
a.click()
|
data: dataToWrite,
|
||||||
setTimeout(() => {
|
contentType: "application/json",
|
||||||
document.body.removeChild(a)
|
// Extracts the path from url, removes fragment identifier and query parameters if any, appends the ".json" extension, and assigns it
|
||||||
window.URL.revokeObjectURL(url)
|
suggestedFilename: `${
|
||||||
}, 0)
|
url.split("/").pop()!.split("#")[0].split("?")[0]
|
||||||
|
}.json`,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "JSON file",
|
||||||
|
extensions: ["json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ import { AnalyticsPlatformDef } from "./analytics"
|
|||||||
import { InterceptorsPlatformDef } from "./interceptors"
|
import { InterceptorsPlatformDef } from "./interceptors"
|
||||||
import { HoppModule } from "~/modules"
|
import { HoppModule } from "~/modules"
|
||||||
import { InspectorsPlatformDef } from "./inspectors"
|
import { InspectorsPlatformDef } from "./inspectors"
|
||||||
|
import { IOPlatformDef } from "./io"
|
||||||
|
|
||||||
export type PlatformDef = {
|
export type PlatformDef = {
|
||||||
ui?: UIPlatformDef
|
ui?: UIPlatformDef
|
||||||
addedHoppModules?: HoppModule[]
|
addedHoppModules?: HoppModule[]
|
||||||
auth: AuthPlatformDef
|
auth: AuthPlatformDef
|
||||||
analytics?: AnalyticsPlatformDef
|
analytics?: AnalyticsPlatformDef
|
||||||
|
io: IOPlatformDef
|
||||||
sync: {
|
sync: {
|
||||||
environments: EnvironmentsPlatformDef
|
environments: EnvironmentsPlatformDef
|
||||||
collections: CollectionsPlatformDef
|
collections: CollectionsPlatformDef
|
||||||
|
|||||||
84
packages/hoppscotch-common/src/platform/io.ts
Normal file
84
packages/hoppscotch-common/src/platform/io.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Defines how to save a file to the user's filesystem.
|
||||||
|
*/
|
||||||
|
export type SaveFileWithDialogOptions = {
|
||||||
|
/**
|
||||||
|
* The data to be saved
|
||||||
|
*/
|
||||||
|
data: string | ArrayBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The suggested filename for the file. This name will be shown in the
|
||||||
|
* save dialog by default when a save is initiated.
|
||||||
|
*/
|
||||||
|
suggestedFilename: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The content type mime type of the data to be saved.
|
||||||
|
*
|
||||||
|
* NOTE: The usage of this data might be platform dependent.
|
||||||
|
* For example, this field is used in the web, but not in the desktop app.
|
||||||
|
*/
|
||||||
|
contentType: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the filters (like in Windows, on the right side, where you can
|
||||||
|
* select the file type) for the file dialog.
|
||||||
|
*
|
||||||
|
* NOTE: The usage of this data might be platform dependent.
|
||||||
|
* For example, this field is used in the web, but not in the desktop app.
|
||||||
|
*/
|
||||||
|
filters?: Array<{
|
||||||
|
/**
|
||||||
|
* The name of the filter (in Windows, if the filter looks
|
||||||
|
* like "Images (*.png, *.jpg)", the name would be "Images")
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array of extensions that are supported, without the dot.
|
||||||
|
*/
|
||||||
|
extensions: string[]
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SaveFileResponse =
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The implementation was unable to determine the status of the save operation.
|
||||||
|
* This cannot be considered a success or a failure and should be handled as an uncertainity.
|
||||||
|
* The browser standard implementation (std) returns this value as there is no way to
|
||||||
|
* check if the user downloaded the file or not.
|
||||||
|
*/
|
||||||
|
type: "unknown"
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The result is known and the user cancelled the save.
|
||||||
|
*/
|
||||||
|
type: "cancelled"
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The result is known and the user saved the file.
|
||||||
|
*/
|
||||||
|
type: "saved"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The full path of where the file was saved
|
||||||
|
*/
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform definitions for how to handle IO operations.
|
||||||
|
*/
|
||||||
|
export type IOPlatformDef = {
|
||||||
|
/**
|
||||||
|
* Defines how to save a file to the user's filesystem.
|
||||||
|
* The expected behaviour is for the browser to show a prompt to save the file.
|
||||||
|
*/
|
||||||
|
saveFileWithDialog: (
|
||||||
|
opts: SaveFileWithDialogOptions
|
||||||
|
) => Promise<SaveFileResponse>
|
||||||
|
}
|
||||||
37
packages/hoppscotch-common/src/platform/std/io.ts
Normal file
37
packages/hoppscotch-common/src/platform/std/io.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { IOPlatformDef } from "../io"
|
||||||
|
import { pipe } from "fp-ts/function"
|
||||||
|
import * as S from "fp-ts/string"
|
||||||
|
import * as RNEA from "fp-ts/ReadonlyNonEmptyArray"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation for how to handle IO operations in the browser.
|
||||||
|
*/
|
||||||
|
export const browserIODef: IOPlatformDef = {
|
||||||
|
saveFileWithDialog(opts) {
|
||||||
|
const file = new Blob([opts.data], { type: opts.contentType })
|
||||||
|
const a = document.createElement("a")
|
||||||
|
const url = URL.createObjectURL(file)
|
||||||
|
|
||||||
|
a.href = url
|
||||||
|
a.download = pipe(
|
||||||
|
url,
|
||||||
|
S.split("/"),
|
||||||
|
RNEA.last,
|
||||||
|
S.split("#"),
|
||||||
|
RNEA.head,
|
||||||
|
S.split("?"),
|
||||||
|
RNEA.head
|
||||||
|
)
|
||||||
|
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(a)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
// Browsers provide no way for us to know the save went successfully.
|
||||||
|
return Promise.resolve({ type: "unknown" })
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1896,6 +1896,17 @@ dependencies = [
|
|||||||
"objc_exception",
|
"objc_exception",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc-foundation"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"objc",
|
||||||
|
"objc_id",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc-sys"
|
name = "objc-sys"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -2510,6 +2521,30 @@ dependencies = [
|
|||||||
"winreg 0.50.0",
|
"winreg 0.50.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfd"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea"
|
||||||
|
dependencies = [
|
||||||
|
"block",
|
||||||
|
"dispatch",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"gtk-sys",
|
||||||
|
"js-sys",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"objc-foundation",
|
||||||
|
"objc_id",
|
||||||
|
"raw-window-handle",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"windows 0.37.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
@@ -3059,6 +3094,7 @@ dependencies = [
|
|||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rfd",
|
||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -3888,6 +3924,19 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc 0.37.0",
|
||||||
|
"windows_i686_gnu 0.37.0",
|
||||||
|
"windows_i686_msvc 0.37.0",
|
||||||
|
"windows_x86_64_gnu 0.37.0",
|
||||||
|
"windows_x86_64_msvc 0.37.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -4037,6 +4086,12 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -4055,6 +4110,12 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -4073,6 +4134,12 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -4091,6 +4158,12 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@@ -4121,6 +4194,12 @@ version = "0.48.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ edition = "2021"
|
|||||||
tauri-build = { version = "1.4.0", features = [] }
|
tauri-build = { version = "1.4.0", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "1.4.1", features = ["http-all", "os-all", "shell-open", "window-start-dragging", "http-multipart"] }
|
tauri = { version = "1.4.1", features = [ "dialog-save", "fs-write-file", "http-all", "os-all", "shell-open", "window-start-dragging", "http-multipart"] }
|
||||||
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||||
tauri-plugin-deep-link = { git = "https://github.com/FabianLars/tauri-plugin-deep-link", branch = "main" }
|
tauri-plugin-deep-link = { git = "https://github.com/FabianLars/tauri-plugin-deep-link", branch = "main" }
|
||||||
tauri-plugin-window-state = "0.1.0"
|
tauri-plugin-window-state = "0.1.0"
|
||||||
|
|||||||
@@ -20,14 +20,16 @@
|
|||||||
"os": {
|
"os": {
|
||||||
"all": true
|
"all": true
|
||||||
},
|
},
|
||||||
|
"fs": {
|
||||||
|
"writeFile": true
|
||||||
|
},
|
||||||
|
"dialog": {
|
||||||
|
"save": true
|
||||||
|
},
|
||||||
"http": {
|
"http": {
|
||||||
"all": true,
|
"all": true,
|
||||||
"request": true,
|
"request": true,
|
||||||
"scope": [
|
"scope": ["http://*", "https://*", "wss://*"]
|
||||||
"http://*",
|
|
||||||
"https://*",
|
|
||||||
"wss://*"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"window": {
|
"window": {
|
||||||
"startDragging": true
|
"startDragging": true
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { appWindow } from "@tauri-apps/api/window"
|
|||||||
import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem"
|
import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem"
|
||||||
import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem"
|
import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem"
|
||||||
import { useMousePressed } from "@vueuse/core"
|
import { useMousePressed } from "@vueuse/core"
|
||||||
|
import { ioDef } from "./platform/io"
|
||||||
|
|
||||||
const headerPaddingLeft = ref("0px")
|
const headerPaddingLeft = ref("0px")
|
||||||
const headerPaddingTop = ref("0px")
|
const headerPaddingTop = ref("0px")
|
||||||
@@ -29,6 +30,7 @@ createHoppApp("#app", {
|
|||||||
paddingTop: headerPaddingTop,
|
paddingTop: headerPaddingTop,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
io: ioDef,
|
||||||
auth: authDef,
|
auth: authDef,
|
||||||
sync: {
|
sync: {
|
||||||
environments: environmentsDef,
|
environments: environmentsDef,
|
||||||
|
|||||||
24
packages/hoppscotch-selfhost-desktop/src/platform/io.ts
Normal file
24
packages/hoppscotch-selfhost-desktop/src/platform/io.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { IOPlatformDef } from "@hoppscotch/common/platform/io"
|
||||||
|
import { save } from "@tauri-apps/api/dialog"
|
||||||
|
import { writeBinaryFile, writeTextFile } from "@tauri-apps/api/fs"
|
||||||
|
|
||||||
|
export const ioDef: IOPlatformDef = {
|
||||||
|
async saveFileWithDialog(opts) {
|
||||||
|
const path = await save({
|
||||||
|
filters: opts.filters,
|
||||||
|
defaultPath: opts.suggestedFilename,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (path === null) {
|
||||||
|
return { type: "cancelled" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof opts.data === "string") {
|
||||||
|
await writeTextFile(path, opts.data)
|
||||||
|
} else {
|
||||||
|
await writeBinaryFile(path, opts.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { type: "saved", path }
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import { ExtensionInspectorService } from "@hoppscotch/common/platform/std/inspe
|
|||||||
import { ExtensionInterceptorService } from "@hoppscotch/common/platform/std/interceptors/extension"
|
import { ExtensionInterceptorService } from "@hoppscotch/common/platform/std/interceptors/extension"
|
||||||
import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem"
|
import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem"
|
||||||
import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem"
|
import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem"
|
||||||
|
import { browserIODef } from "@hoppscotch/common/platform/std/io"
|
||||||
|
|
||||||
createHoppApp("#app", {
|
createHoppApp("#app", {
|
||||||
ui: {
|
ui: {
|
||||||
@@ -18,6 +19,7 @@ createHoppApp("#app", {
|
|||||||
additionalSupportOptionsMenuItems: stdSupportOptionItems,
|
additionalSupportOptionsMenuItems: stdSupportOptionItems,
|
||||||
},
|
},
|
||||||
auth: authDef,
|
auth: authDef,
|
||||||
|
io: browserIODef,
|
||||||
sync: {
|
sync: {
|
||||||
environments: environmentsDef,
|
environments: environmentsDef,
|
||||||
collections: collectionsDef,
|
collections: collectionsDef,
|
||||||
|
|||||||
Reference in New Issue
Block a user