Compare commits

...

8 Commits

Author SHA1 Message Date
nivedin
7411e36880 chore: remove readonly type 2024-04-24 21:13:58 +05:30
nivedin
05ad84f372 chore: add fallback url and add reqIndex for graphql saving 2024-04-24 21:13:55 +05:30
nivedin
0c993d0e90 chore: add i18n and readonly to envinput 2024-04-24 21:12:28 +05:30
nivedin
10cb900bd7 fix: revert text update in envinput for empty state 2024-04-24 21:12:28 +05:30
nivedin
8fd6b2ffb0 chore: update default tab to have empty endpoint 2024-04-24 21:12:28 +05:30
nivedin
e6e300ca86 chore: add fallback url if endpoint is empty while saving and sharing req 2024-04-24 21:12:28 +05:30
nivedin
5bac6222a0 chore: update URL input 2024-04-24 21:12:28 +05:30
nivedin
cf37fbd610 feat: hover placeholder transition for smartenvinput 2024-04-24 21:12:27 +05:30
14 changed files with 190 additions and 44 deletions

View File

@@ -568,7 +568,9 @@
"generated_code": "Generated code",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"graphql_placeholder": "Enter a URL",
"header_list": "Header List",
"http_placeholder":"Enter a URL or cURL command",
"invalid_name": "Please provide a name for the request",
"method": "Method",
"moved": "Request moved",

View File

@@ -86,6 +86,8 @@ import { platform } from "~/platform"
import { useService } from "dioc/vue"
import { RESTTabService } from "~/services/tab/rest"
import { GQLTabService } from "~/services/tab/graphql"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
const t = useI18n()
const toast = useToast()
@@ -221,6 +223,15 @@ const saveRequestAs = async () => {
requestUpdated.name = requestName.value
if (props.mode === "rest") {
;(requestUpdated as HoppRESTRequest).endpoint =
(requestUpdated as HoppRESTRequest).endpoint ||
getDefaultRESTRequest().endpoint
} else {
;(requestUpdated as HoppGQLRequest).url =
(requestUpdated as HoppGQLRequest).url || getDefaultGQLRequest().url
}
if (picked.value.pickedType === "my-collection") {
if (!isHoppRESTRequest(requestUpdated))
throw new Error("requestUpdated is not a REST Request")

View File

@@ -193,6 +193,7 @@ import { PersistedOAuthConfig } from "~/services/oauth/oauth.service"
import { GQLOptionTabs } from "~/components/graphql/RequestOptions.vue"
import { EditingProperties } from "../Properties.vue"
import { defineActionHandler } from "~/helpers/actions"
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
const t = useI18n()
const toast = useToast()
@@ -380,32 +381,26 @@ const editCollection = (
displayModalEdit(true)
}
const onAddRequest = ({
name,
path,
index,
}: {
name: string
path: string
index: number
}) => {
const onAddRequest = ({ name, path }: { name: string; path: string }) => {
const newRequest = {
...tabs.currentActiveTab.value.document.request,
name,
url:
tabs.currentActiveTab.value.document.request.url ||
getDefaultGQLRequest().url,
}
saveGraphqlRequestAs(path, newRequest)
const insertionIndex = saveGraphqlRequestAs(path, newRequest)
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
path,
"graphql"
)
tabs.createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: index,
requestIndex: insertionIndex,
},
request: newRequest,
isDirty: false,

View File

@@ -254,6 +254,7 @@ import { PersistenceService } from "~/services/persistence"
import { PersistedOAuthConfig } from "~/services/oauth/oauth.service"
import { RESTOptionTabs } from "../http/RequestOptions.vue"
import { EditingProperties } from "./Properties.vue"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
const t = useI18n()
const toast = useToast()
@@ -790,6 +791,9 @@ const onAddRequest = (requestName: string) => {
const newRequest = {
...cloneDeep(tabs.currentActiveTab.value.document.request),
name: requestName,
endpoint:
tabs.currentActiveTab.value.document.request.endpoint ||
getDefaultRESTRequest().endpoint,
}
const path = editingFolderPath.value

View File

@@ -3,16 +3,13 @@
class="sticky top-0 z-10 flex flex-shrink-0 space-x-2 overflow-x-auto bg-primary p-4"
>
<div class="inline-flex flex-1 space-x-2">
<input
id="url"
<SmartEnvInput
v-model="url"
type="url"
autocomplete="off"
spellcheck="false"
class="w-full rounded border border-divider bg-primaryLight px-4 py-2 text-secondaryDark"
:placeholder="`${t('request.url')}`"
:disabled="connected"
@keyup.enter="onConnectClick"
:placeholder="getDefaultGQLRequest().url"
:placeholder-hover-string="t('request.graphql_placeholder')"
:readonly="connected"
class="rounded border border-divider bg-primaryLight"
@enter="onConnectClick"
/>
<HoppButtonPrimary
id="get"
@@ -72,6 +69,7 @@ import { InterceptorService } from "~/services/interceptor.service"
import { useService } from "dioc/vue"
import { defineActionHandler } from "~/helpers/actions"
import { GQLTabService } from "~/services/tab/graphql"
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
const t = useI18n()
const tabs = useService(GQLTabService)
@@ -98,7 +96,10 @@ const onConnectClick = () => {
}
const gqlConnect = () => {
connect(url.value, tabs.currentActiveTab.value?.document.request.headers)
connect(
url.value || getDefaultGQLRequest().url,
tabs.currentActiveTab.value?.document.request.headers
)
platform.analytics?.logEvent({
type: "HOPP_REQUEST_RUN",
@@ -118,7 +119,9 @@ watch(
tabs.currentActiveTab,
(newVal) => {
if (newVal) {
lastTwoUrls.value.push(newVal.document.request.url)
lastTwoUrls.value.push(
newVal.document.request.url ?? getDefaultGQLRequest().url
)
if (lastTwoUrls.value.length > 2) {
lastTwoUrls.value.shift()
}

View File

@@ -76,6 +76,7 @@ import { InterceptorService } from "~/services/interceptor.service"
import { editGraphqlRequest } from "~/newstore/collections"
import { GQLTabService } from "~/services/tab/graphql"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { getDefaultGQLRequest } from "~/helpers/graphql/default"
const VALID_GQL_OPERATIONS = [
"query",
@@ -119,7 +120,9 @@ const request = useVModel(props, "modelValue", emit)
const url = computedWithControl(
() => tabs.currentActiveTab.value,
() => tabs.currentActiveTab.value.document.request.url
() =>
tabs.currentActiveTab.value.document.request.url ||
getDefaultGQLRequest().url
)
const activeGQLHeadersCount = computed(
@@ -247,10 +250,16 @@ const saveRequest = () => {
tabs.currentActiveTab.value.document.saveContext.originLocation ===
"user-collection"
) {
const finalRequest = {
...tabs.currentActiveTab.value.document.request,
url:
tabs.currentActiveTab.value.document.request.url ||
getDefaultGQLRequest().url,
}
editGraphqlRequest(
tabs.currentActiveTab.value.document.saveContext.folderPath,
tabs.currentActiveTab.value.document.saveContext.requestIndex,
tabs.currentActiveTab.value.document.request
finalRequest
)
tabs.currentActiveTab.value.document.isDirty = false

View File

@@ -54,9 +54,10 @@
>
<SmartEnvInput
v-model="tab.document.request.endpoint"
:placeholder="`${t('request.url')}`"
:placeholder="getDefaultRESTRequest().endpoint"
:auto-complete-source="userHistories"
:auto-complete-env="true"
:placeholder-hover-string="t('request.http_placeholder')"
:inspection-results="tabResults"
@paste="onPasteUrl($event)"
@enter="newSendRequest"
@@ -331,12 +332,12 @@ const tabs = useService(RESTTabService)
const workspaceService = useService(WorkspaceService)
const newSendRequest = async () => {
if (newEndpoint.value === "" || /^\s+$/.test(newEndpoint.value)) {
if (/^\s+$/.test(newEndpoint.value)) {
toast.error(`${t("empty.endpoint")}`)
return
}
ensureMethodInEndpoint()
if (newEndpoint.value) ensureMethodInEndpoint()
loading.value = true
@@ -348,7 +349,20 @@ const newSendRequest = async () => {
workspaceType: workspaceService.currentWorkspace.value.type,
})
const [cancel, streamPromise] = runRESTRequest$(tab)
const finalTab = ref({
...tab.value,
document: {
...tab.value.document,
request: {
...tab.value.document.request,
endpoint:
tab.value.document.request.endpoint ||
getDefaultRESTRequest().endpoint,
},
},
})
const [cancel, streamPromise] = runRESTRequest$(finalTab)
const streamResult = await streamPromise
requestCancelFunc.value = cancel
@@ -472,8 +486,13 @@ const fetchingShareLink = ref(false)
const shareRequest = () => {
if (currentUser.value) {
const finalRequest = {
...tab.value.document.request,
endpoint:
tab.value.document.request.endpoint || getDefaultRESTRequest().endpoint,
}
invokeAction("share.request", {
request: tab.value.document.request,
request: finalRequest,
})
} else {
invokeAction("modals.login.toggle")
@@ -513,11 +532,17 @@ const saveRequest = () => {
showSaveRequestModal.value = true
return
}
if (saveCtx.originLocation === "user-collection") {
const req = tab.value.document.request
const req = tab.value.document.request
const finalRequest = {
...req,
endpoint: req.endpoint.trim() || getDefaultRESTRequest().endpoint,
}
if (saveCtx.originLocation === "user-collection") {
try {
editRESTRequest(saveCtx.folderPath, saveCtx.requestIndex, req)
editRESTRequest(saveCtx.folderPath, saveCtx.requestIndex, finalRequest)
tab.value.document.isDirty = false
@@ -534,8 +559,6 @@ const saveRequest = () => {
saveRequest()
}
} else if (saveCtx.originLocation === "team-collection") {
const req = tab.value.document.request
// TODO: handle error case (NOTE: overwriteRequestTeams is async)
try {
platform.analytics?.logEvent({
@@ -549,7 +572,7 @@ const saveRequest = () => {
requestID: saveCtx.requestID,
data: {
title: req.name,
request: JSON.stringify(req),
request: JSON.stringify(finalRequest),
},
})().then((result) => {
if (E.isLeft(result)) {

View File

@@ -134,6 +134,7 @@ import * as E from "fp-ts/Either"
import { RESTTabService } from "~/services/tab/rest"
import { useService } from "dioc/vue"
import { watch } from "vue"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
const t = useI18n()
const colorMode = useColorMode()
@@ -511,7 +512,10 @@ const openRequestInNewTab = (request: HoppRESTRequest) => {
}
defineActionHandler("share.request", ({ request }) => {
requestToShare.value = request
requestToShare.value = {
...request,
endpoint: request.endpoint || getDefaultRESTRequest().endpoint,
}
displayShareRequestModal(true)
})
</script>

View File

@@ -73,7 +73,12 @@ import {
keymap,
tooltips,
} from "@codemirror/view"
import { EditorSelection, EditorState, Extension } from "@codemirror/state"
import {
Compartment,
EditorSelection,
EditorState,
Extension,
} from "@codemirror/state"
import { clone } from "lodash-es"
import { history, historyKeymap } from "@codemirror/commands"
import { inputTheme } from "~/helpers/editor/themes/baseTheme"
@@ -109,6 +114,7 @@ const props = withDefaults(
contextMenuEnabled?: boolean
secret?: boolean
autoCompleteEnv?: boolean
placeholderHoverString: string
}>(),
{
modelValue: "",
@@ -124,6 +130,7 @@ const props = withDefaults(
contextMenuEnabled: true,
secret: false,
autoCompleteEnvSource: false,
placeholderHoverString: "",
}
)
@@ -137,6 +144,8 @@ const emit = defineEmits<{
(e: "click", ev: any): void
}>()
const placeholderString = ref(props.placeholder)
const cachedValue = ref(props.modelValue)
const view = ref<EditorView>()
@@ -441,6 +450,9 @@ function handleTextSelection() {
}
}
const placeholderCompt = new Compartment()
const readOnlyCompt = new Compartment()
// Debounce to prevent double click from selecting the word
const debouncedTextSelection = (time: number) =>
useDebounceFn(() => {
@@ -475,6 +487,7 @@ const getExtensions = (readonly: boolean): Extension => {
}),
EditorState.changeFilter.of(() => !readonly),
inputTheme,
readOnlyCompt.of(EditorState.readOnly.of(readonly)),
readonly
? EditorView.theme({
".cm-content": {
@@ -490,7 +503,8 @@ const getExtensions = (readonly: boolean): Extension => {
position: "absolute",
}),
props.environmentHighlights ? envTooltipPlugin : [],
placeholderExt(props.placeholder),
placeholderCompt.of(placeholderExt(props.placeholder)),
EditorView.domEventHandlers({
paste(ev) {
clipboardEv = ev
@@ -505,6 +519,27 @@ const getExtensions = (readonly: boolean): Extension => {
debouncedTextSelection(30)()
}
},
mouseenter() {
//change placeholder to hover string if provided
if (props.placeholderHoverString && !props.readonly) {
placeholderString.value = props.placeholderHoverString
view.value?.dispatch({
effects: placeholderCompt.reconfigure(
placeholderExt(props.placeholderHoverString)
),
})
}
},
mouseleave() {
//change placeholder back to original string
if (props.placeholderHoverString && !props.readonly) {
view.value?.dispatch({
effects: placeholderCompt.reconfigure(
placeholderExt(props.placeholder)
),
})
}
},
}),
props.autoCompleteEnv
? autocompletion({
@@ -568,6 +603,38 @@ const getExtensions = (readonly: boolean): Extension => {
return extensions
}
watch(
() => props.readonly,
(readonly) => {
if (readonly) {
view.value!.dispatch({
effects: [
readOnlyCompt.reconfigure([
EditorState.readOnly.of(readonly),
EditorView.theme({
".cm-content": {
caretColor: "var(--secondary-dark-color)",
color: "var(--secondary-dark-color)",
backgroundColor: "var(--divider-color)",
opacity: 0.25,
},
}),
]),
],
})
} else {
view.value!.dispatch({
effects: [
readOnlyCompt.reconfigure([
EditorState.readOnly.of(readonly),
EditorView.theme({}),
]),
],
})
}
}
)
const triggerTextSelection = () => {
nextTick(() => {
view.value?.focus()

View File

@@ -1393,6 +1393,14 @@ export function editGraphqlRequest(
}
export function saveGraphqlRequestAs(path: string, request: HoppGQLRequest) {
// For calculating the insertion request index
const targetLocation = navigateToFolderWithIndexPath(
graphqlCollectionStore.value.state,
path.split("/").map((x) => parseInt(x))
)
const insertionIndex = targetLocation!.requests.length
graphqlCollectionStore.dispatch({
dispatcher: "saveRequestAs",
payload: {
@@ -1400,6 +1408,8 @@ export function saveGraphqlRequestAs(path: string, request: HoppGQLRequest) {
request,
},
})
return insertionIndex
}
export function removeGraphqlRequest(

View File

@@ -112,7 +112,10 @@ const activeTabs = tabs.getActiveTabs()
const addNewTab = () => {
const tab = tabs.createNewTab({
request: getDefaultGQLRequest(),
request: {
...getDefaultGQLRequest(),
url: "",
},
isDirty: false,
})

View File

@@ -178,7 +178,10 @@ const onTabUpdate = (tab: HoppTab<HoppRESTDocument>) => {
const addNewTab = () => {
const tab = tabs.createNewTab({
request: getDefaultRESTRequest(),
request: {
...getDefaultRESTRequest(),
endpoint: "",
},
isDirty: false,
})
@@ -295,8 +298,14 @@ const shareTabRequest = (tabID: string) => {
const tab = tabs.getTabRef(tabID)
if (tab.value) {
if (currentUser.value) {
const finalRequest = {
...tab.value.document.request,
endpoint:
tab.value.document.request.endpoint ||
getDefaultRESTRequest().endpoint,
}
invokeAction("share.request", {
request: tab.value.document.request,
request: finalRequest,
})
} else {
invokeAction("modals.login.toggle")

View File

@@ -13,7 +13,10 @@ export class GQLTabService extends TabService<HoppGQLDocument> {
this.tabMap.set("test", {
id: "test",
document: {
request: getDefaultGQLRequest(),
request: {
...getDefaultGQLRequest(),
url: "",
},
isDirty: false,
optionTabPreference: "query",
},

View File

@@ -13,7 +13,10 @@ export class RESTTabService extends TabService<HoppRESTDocument> {
this.tabMap.set("test", {
id: "test",
document: {
request: getDefaultRESTRequest(),
request: {
...getDefaultRESTRequest(),
endpoint: "",
},
isDirty: false,
optionTabPreference: "params",
},