refactor: move download file out of common into platform

This commit is contained in:
Andrew Bastin
2023-11-07 02:36:27 +05:30
parent 2757722fd0
commit 7811f3b53e
16 changed files with 392 additions and 103 deletions

View File

@@ -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>

View File

@@ -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
} }
) )

View File

@@ -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>) => {

View File

@@ -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(

View File

@@ -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 = () => {

View File

@@ -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,

View File

@@ -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
} }

View File

@@ -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

View 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>
}

View 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" })
},
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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,

View 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 }
},
}

View File

@@ -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,