From 5a35c098ec5e7e6722e292948f1d07d113954818 Mon Sep 17 00:00:00 2001 From: nivedin Date: Tue, 5 Dec 2023 21:30:58 +0530 Subject: [PATCH] feat: mutations and small refactor in share req --- .../src/components/share/CreateModal.vue | 2 +- .../src/components/share/CustomizeModal.vue | 78 +++++---- .../src/components/share/Modal.vue | 45 ++++++ .../src/components/share/Request.vue | 15 +- .../src/components/share/index.vue | 150 +++++++++++++++++- .../gql/mutations/CreateShortcode.graphql | 4 +- .../mutations/UpdateEmbedProperties.graphql | 8 + .../gql/queries/ResolveShortcode.graphql | 3 +- .../subscriptions/ShortcodeUpdated.graphql | 8 + .../helpers/backend/mutations/Shortcode.ts | 19 ++- .../src/helpers/shortcode/Shortcode.ts | 2 +- .../helpers/shortcode/ShortcodeListAdapter.ts | 31 ++++ 12 files changed, 316 insertions(+), 49 deletions(-) create mode 100644 packages/hoppscotch-common/src/helpers/backend/gql/mutations/UpdateEmbedProperties.graphql create mode 100644 packages/hoppscotch-common/src/helpers/backend/gql/subscriptions/ShortcodeUpdated.graphql diff --git a/packages/hoppscotch-common/src/components/share/CreateModal.vue b/packages/hoppscotch-common/src/components/share/CreateModal.vue index f4639541f..eb79e6c65 100644 --- a/packages/hoppscotch-common/src/components/share/CreateModal.vue +++ b/packages/hoppscotch-common/src/components/share/CreateModal.vue @@ -132,7 +132,7 @@ const embedOption = ref({ { value: "authorization", label: t("tab.authorization"), - enabled: true, + enabled: false, }, ], theme: "system", diff --git a/packages/hoppscotch-common/src/components/share/CustomizeModal.vue b/packages/hoppscotch-common/src/components/share/CustomizeModal.vue index 0df6d9041..e0128ec5c 100644 --- a/packages/hoppscotch-common/src/components/share/CustomizeModal.vue +++ b/packages/hoppscotch-common/src/components/share/CustomizeModal.vue @@ -205,6 +205,35 @@ const props = defineProps({ type: Boolean, default: false, }, + embedOptions: { + type: Object as PropType, + default: () => ({ + selectedTab: "parameters", + tabs: [ + { + value: "parameters", + label: "shared_requests.parameters", + enabled: true, + }, + { + value: "body", + label: "shared_requests.body", + enabled: true, + }, + { + value: "headers", + label: "shared_requests.headers", + enabled: true, + }, + { + value: "authorization", + label: "shared_requests.authorization", + enabled: false, + }, + ], + theme: "system", + }), + }, }) const emit = defineEmits<{ @@ -213,6 +242,7 @@ const emit = defineEmits<{ request: { sharedRequestID: string | undefined content: string | undefined + type: string | undefined } ): void (e: "hide-modal"): void @@ -220,6 +250,7 @@ const emit = defineEmits<{ }>() const selectedWidget = useVModel(props, "modelValue") +const embedOptions = useVModel(props, "embedOptions") type WidgetID = "embed" | "button" | "link" @@ -254,34 +285,6 @@ type EmbedOption = { }[] theme: "light" | "dark" | "system" } - -const embedOptions = ref({ - selectedTab: "parameters", - tabs: [ - { - value: "parameters", - label: t("tab.parameters"), - enabled: true, - }, - { - value: "body", - label: t("tab.body"), - enabled: true, - }, - { - value: "headers", - label: t("tab.headers"), - enabled: true, - }, - { - value: "authorization", - label: t("tab.authorization"), - enabled: true, - }, - ], - theme: "system", -}) - const embedThemeIcon = computed(() => { if (embedOptions.value.theme === "system") { return IconMonitor @@ -355,12 +358,13 @@ const linkVariants: LinkVariant[] = [ const baseURL = import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh" const copyEmbed = () => { - const options = embedOptions.value - const enabledEmbedOptions = options.tabs - .filter((tab) => tab.enabled) - .map((tab) => tab.value) - .toString() - return `` + return `${baseURL}/e/${props.request?.id} ` } const copyButton = ( @@ -410,7 +414,11 @@ const copyContent = ({ } else { content = copyEmbed() } - const copyContent = { sharedRequestID: props.request?.id, content } + const copyContent = { + sharedRequestID: props.request?.id, + content, + type: widget, + } emit("copy-shared-request", copyContent) } diff --git a/packages/hoppscotch-common/src/components/share/Modal.vue b/packages/hoppscotch-common/src/components/share/Modal.vue index 54544c7fb..bff923b34 100644 --- a/packages/hoppscotch-common/src/components/share/Modal.vue +++ b/packages/hoppscotch-common/src/components/share/Modal.vue @@ -21,6 +21,7 @@ , @@ -75,6 +88,35 @@ const props = defineProps({ type: Number, default: 1, }, + embedOptions: { + type: Object as PropType, + default: () => ({ + selectedTab: "parameters", + tabs: [ + { + value: "parameters", + label: "shared_requests.parameters", + enabled: true, + }, + { + value: "body", + label: "shared_requests.body", + enabled: true, + }, + { + value: "headers", + label: "shared_requests.headers", + enabled: true, + }, + { + value: "authorization", + label: "shared_requests.authorization", + enabled: false, + }, + ], + theme: "system", + }), + }, }) type WidgetID = "embed" | "button" | "link" @@ -86,6 +128,7 @@ type Widget = { } const selectedWidget = useVModel(props, "modelValue") +const embedOptions = useVModel(props, "embedOptions") const emit = defineEmits<{ (e: "create-shared-request", request: HoppRESTRequest | null): void @@ -97,6 +140,7 @@ const emit = defineEmits<{ request: { sharedRequestID: string | undefined content: string | undefined + type: string | undefined } ): void }>() @@ -108,6 +152,7 @@ const createSharedRequest = () => { const copySharedRequest = (request: { sharedRequestID: string | undefined content: string | undefined + type: string | undefined }) => { emit("copy-shared-request", request) } diff --git a/packages/hoppscotch-common/src/components/share/Request.vue b/packages/hoppscotch-common/src/components/share/Request.vue index 65ef3f4a0..a81ae8710 100644 --- a/packages/hoppscotch-common/src/components/share/Request.vue +++ b/packages/hoppscotch-common/src/components/share/Request.vue @@ -121,7 +121,12 @@ const props = defineProps<{ }>() const emit = defineEmits<{ - (e: "customize-shared-request", request: HoppRESTRequest, id: string): void + ( + e: "customize-shared-request", + request: HoppRESTRequest, + id: string, + embedProperties?: string | null + ): void (e: "delete-shared-request", codeID: string): void (e: "open-new-tab", request: HoppRESTRequest): void }>() @@ -144,7 +149,13 @@ const openInNewTab = () => { } const customizeSharedRequest = () => { - emit("customize-shared-request", parseRequest.value, props.request.id) + const embedProperties = props.request.properties + emit( + "customize-shared-request", + parseRequest.value, + props.request.id, + embedProperties + ) } const deleteSharedRequest = () => { diff --git a/packages/hoppscotch-common/src/components/share/index.vue b/packages/hoppscotch-common/src/components/share/index.vue index af3861e92..7e2f17f9c 100644 --- a/packages/hoppscotch-common/src/components/share/index.vue +++ b/packages/hoppscotch-common/src/components/share/index.vue @@ -80,11 +80,12 @@ /> @@ -105,6 +106,7 @@ import * as TE from "fp-ts/TaskEither" import { deleteShortcode as backendDeleteShortcode, createShortcode, + updateEmbedProperties, } from "~/helpers/backend/mutations/Shortcode" import { GQLError } from "~/helpers/backend/GQLClient" import { useToast } from "~/composables/toast" @@ -130,6 +132,67 @@ const shareRequestCreatingLoading = ref(false) const requestToShare = ref(null) +const embedOptions = ref({ + selectedTab: "parameters", + tabs: [ + { + value: "parameters", + label: t("tab.parameters"), + enabled: true, + }, + { + value: "body", + label: t("tab.body"), + enabled: true, + }, + { + value: "headers", + label: t("tab.headers"), + enabled: true, + }, + { + value: "authorization", + label: t("tab.authorization"), + enabled: false, + }, + ], + theme: "system", +}) + +const updateEmbedProperty = async ( + shareRequestID: string, + properties: string +) => { + const customizeEmbedResult = await updateEmbedProperties( + shareRequestID, + properties + )() + + if (E.isLeft(customizeEmbedResult)) { + toast.error(`${customizeEmbedResult.left.error}`) + toast.error(t("error.something_went_wrong")) + } else if (E.isRight(customizeEmbedResult)) { + if (customizeEmbedResult.right.updateEmbedProperties) { + if (customizeEmbedResult.right.updateEmbedProperties.properties) { + const parsedEmbedProperties = JSON.parse( + customizeEmbedResult.right.updateEmbedProperties.properties + ) + + embedOptions.value = { + selectedTab: parsedEmbedProperties.options[0], + tabs: embedOptions.value.tabs.map((tab) => { + return { + ...tab, + enabled: parsedEmbedProperties.options.includes(tab.value), + } + }), + theme: parsedEmbedProperties.theme, + } + } + } + } +} + const restTab = useService(RESTTabService) const currentUser = useReadonlyStream( @@ -139,6 +202,18 @@ const currentUser = useReadonlyStream( const step = ref(1) +type EmbedTabs = "parameters" | "body" | "headers" | "authorization" + +type EmbedOption = { + selectedTab: EmbedTabs + tabs: { + value: EmbedTabs + label: string + enabled: boolean + }[] + theme: "light" | "dark" | "system" +} + type WidgetID = "embed" | "button" | "link" type Widget = { @@ -218,15 +293,46 @@ const displayShareRequestModal = (show: boolean) => { showShareRequestModal.value = show step.value = 1 } -const displayCustomizeRequestModal = (show: boolean) => { +const displayCustomizeRequestModal = ( + show: boolean, + embedProperties?: string | null +) => { showShareRequestModal.value = show step.value = 2 + if (!embedProperties) { + selectedWidget.value = { + value: "button", + label: t("shared_requests.button"), + info: t("shared_requests.button_info"), + } + } else { + const parsedEmbedProperties = JSON.parse(embedProperties) + embedOptions.value = { + selectedTab: parsedEmbedProperties.options[0], + tabs: embedOptions.value.tabs.map((tab) => { + return { + ...tab, + enabled: parsedEmbedProperties.options.includes(tab.value), + } + }), + theme: parsedEmbedProperties.theme, + } + } } const createSharedRequest = async (request: HoppRESTRequest | null) => { if (request && selectedWidget.value) { + const properties = { + options: ["parameters", "body", "headers"], + theme: "system", + } shareRequestCreatingLoading.value = true - const sharedRequestResult = await createShortcode(request)() + const sharedRequestResult = await createShortcode( + request, + selectedWidget.value.value === "embed" + ? JSON.stringify(properties) + : undefined + )() platform.analytics?.logEvent({ type: "HOPP_SHORTCODE_CREATED", @@ -243,6 +349,23 @@ const createSharedRequest = async (request: HoppRESTRequest | null) => { id: sharedRequestResult.right.createShortcode.id, } step.value = 2 + + if (sharedRequestResult.right.createShortcode.properties) { + const parsedEmbedProperties = JSON.parse( + sharedRequestResult.right.createShortcode.properties + ) + + embedOptions.value = { + selectedTab: parsedEmbedProperties.options[0], + tabs: embedOptions.value.tabs.map((tab) => { + return { + ...tab, + enabled: parsedEmbedProperties.options.includes(tab.value), + } + }), + theme: parsedEmbedProperties.theme, + } + } } } } @@ -250,22 +373,37 @@ const createSharedRequest = async (request: HoppRESTRequest | null) => { const customizeSharedRequest = ( request: HoppRESTRequest, - shredRequestID: string + shredRequestID: string, + embedProperties?: string | null ) => { requestToShare.value = { ...request, id: shredRequestID, } - displayCustomizeRequestModal(true) + displayCustomizeRequestModal(true, embedProperties) } const copySharedRequest = (request: { sharedRequestID: string | undefined content: string | undefined + type: string | undefined }) => { if (request.content) { copyToClipboard(request.content) toast.success(t("state.copied_to_clipboard")) + if ( + requestToShare.value && + requestToShare.value.id && + request.type === "embed" + ) { + const properties = { + options: embedOptions.value.tabs + .filter((tab) => tab.enabled) + .map((tab) => tab.value), + theme: embedOptions.value.theme, + } + updateEmbedProperty(requestToShare.value.id, JSON.stringify(properties)) + } } } diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/mutations/CreateShortcode.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/CreateShortcode.graphql index 2147dd485..134f14df6 100644 --- a/packages/hoppscotch-common/src/helpers/backend/gql/mutations/CreateShortcode.graphql +++ b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/CreateShortcode.graphql @@ -1,5 +1,5 @@ -mutation CreateShortcode($request: String!) { - createShortcode(request: $request) { +mutation CreateShortcode($request: String!, $properties: String) { + createShortcode(request: $request, properties: $properties) { id request createdOn diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/mutations/UpdateEmbedProperties.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/UpdateEmbedProperties.graphql new file mode 100644 index 000000000..804aac5a6 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/gql/mutations/UpdateEmbedProperties.graphql @@ -0,0 +1,8 @@ +mutation UpdateEmbedProperties($code: ID!, $properties: String!) { + updateEmbedProperties(code: $code, properties: $properties) { + id + request + properties + createdOn + } +} diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/queries/ResolveShortcode.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/queries/ResolveShortcode.graphql index 3aa7f27b1..16af0e204 100644 --- a/packages/hoppscotch-common/src/helpers/backend/gql/queries/ResolveShortcode.graphql +++ b/packages/hoppscotch-common/src/helpers/backend/gql/queries/ResolveShortcode.graphql @@ -2,5 +2,6 @@ query ResolveShortcode($code: ID!) { shortcode(code: $code) { id request + properties } -} \ No newline at end of file +} diff --git a/packages/hoppscotch-common/src/helpers/backend/gql/subscriptions/ShortcodeUpdated.graphql b/packages/hoppscotch-common/src/helpers/backend/gql/subscriptions/ShortcodeUpdated.graphql new file mode 100644 index 000000000..cbbf6af71 --- /dev/null +++ b/packages/hoppscotch-common/src/helpers/backend/gql/subscriptions/ShortcodeUpdated.graphql @@ -0,0 +1,8 @@ +subscription ShortcodeUpdated { + myShortcodesUpdated { + id + request + createdOn + properties + } +} diff --git a/packages/hoppscotch-common/src/helpers/backend/mutations/Shortcode.ts b/packages/hoppscotch-common/src/helpers/backend/mutations/Shortcode.ts index e02759ab9..18d4a5ede 100644 --- a/packages/hoppscotch-common/src/helpers/backend/mutations/Shortcode.ts +++ b/packages/hoppscotch-common/src/helpers/backend/mutations/Shortcode.ts @@ -7,15 +7,22 @@ import { DeleteShortcodeDocument, DeleteShortcodeMutation, DeleteShortcodeMutationVariables, + UpdateEmbedPropertiesDocument, + UpdateEmbedPropertiesMutation, + UpdateEmbedPropertiesMutationVariables, } from "../graphql" type DeleteShortcodeErrors = "shortcode/not_found" -export const createShortcode = (request: HoppRESTRequest) => +export const createShortcode = ( + request: HoppRESTRequest, + properties?: string +) => runMutation( CreateShortcodeDocument, { request: JSON.stringify(request), + properties, } ) @@ -27,3 +34,13 @@ export const deleteShortcode = (code: string) => >(DeleteShortcodeDocument, { code, }) + +export const updateEmbedProperties = (code: string, properties: string) => + runMutation< + UpdateEmbedPropertiesMutation, + UpdateEmbedPropertiesMutationVariables, + "" + >(UpdateEmbedPropertiesDocument, { + code, + properties, + }) diff --git a/packages/hoppscotch-common/src/helpers/shortcode/Shortcode.ts b/packages/hoppscotch-common/src/helpers/shortcode/Shortcode.ts index 6b57c7285..01e4d763c 100644 --- a/packages/hoppscotch-common/src/helpers/shortcode/Shortcode.ts +++ b/packages/hoppscotch-common/src/helpers/shortcode/Shortcode.ts @@ -4,6 +4,6 @@ export interface Shortcode { id: string request: string - properties?: string | null | undefined + properties?: string | null createdOn: Date } diff --git a/packages/hoppscotch-common/src/helpers/shortcode/ShortcodeListAdapter.ts b/packages/hoppscotch-common/src/helpers/shortcode/ShortcodeListAdapter.ts index dbe9e9985..5b02d985d 100644 --- a/packages/hoppscotch-common/src/helpers/shortcode/ShortcodeListAdapter.ts +++ b/packages/hoppscotch-common/src/helpers/shortcode/ShortcodeListAdapter.ts @@ -11,6 +11,7 @@ import { GetUserShortcodesDocument, ShortcodeCreatedDocument, ShortcodeDeletedDocument, + ShortcodeUpdatedDocument, } from "../backend/graphql" import { BACKEND_PAGE_SIZE } from "../backend/helpers" import { Shortcode } from "./Shortcode" @@ -25,9 +26,11 @@ export default class ShortcodeListAdapter { private shortcodeCreated: Subscription | null private shortcodeRevoked: Subscription | null + private shortcodeUpdated: Subscription | null private shortcodeCreatedSub: WSubscription | null private shortcodeRevokedSub: WSubscription | null + private shortcodeUpdatedSub: WSubscription | null constructor(deferInit = false) { this.error$ = new BehaviorSubject | null>(null) @@ -39,8 +42,10 @@ export default class ShortcodeListAdapter { this.isDispose = true this.shortcodeCreated = null this.shortcodeRevoked = null + this.shortcodeUpdated = null this.shortcodeCreatedSub = null this.shortcodeRevokedSub = null + this.shortcodeUpdatedSub = null if (!deferInit) this.initialize() } @@ -48,8 +53,10 @@ export default class ShortcodeListAdapter { unsubscribeSubscriptions() { this.shortcodeCreated?.unsubscribe() this.shortcodeRevoked?.unsubscribe() + this.shortcodeUpdated?.unsubscribe() this.shortcodeCreatedSub?.unsubscribe() this.shortcodeRevokedSub?.unsubscribe() + this.shortcodeUpdatedSub?.unsubscribe() } initialize() { @@ -137,6 +144,14 @@ export default class ShortcodeListAdapter { this.shortcodes$.next(newShortcode) } + private updateSharedRequest(shortcode: Shortcode) { + const newShortcode = this.shortcodes$.value.map((oldShortcode) => + oldShortcode.id === shortcode.id ? shortcode : oldShortcode + ) + + this.shortcodes$.next(newShortcode) + } + private registerSubscriptions() { const [shortcodeCreated$, shortcodeCreatedSub] = runAuthOnlyGQLSubscription( { @@ -169,5 +184,21 @@ export default class ShortcodeListAdapter { this.deleteSharedRequest(result.right.myShortcodesRevoked.id) }) + + const [shortcodeUpdated$, shortcodeUpdatedSub] = runAuthOnlyGQLSubscription( + { + query: ShortcodeUpdatedDocument, + } + ) + + this.shortcodeUpdatedSub = shortcodeUpdatedSub + this.shortcodeUpdated = shortcodeUpdated$.subscribe((result) => { + if (E.isLeft(result)) { + console.error(result.left) + throw new Error(`Shortcode Update Error ${result.left}`) + } + + this.updateSharedRequest(result.right.myShortcodesUpdated) + }) } }