feat: mutations and small refactor in share req

This commit is contained in:
nivedin
2023-12-05 21:30:58 +05:30
committed by Andrew Bastin
parent ab7c29d228
commit 5a35c098ec
12 changed files with 316 additions and 49 deletions

View File

@@ -132,7 +132,7 @@ const embedOption = ref<EmbedOption>({
{ {
value: "authorization", value: "authorization",
label: t("tab.authorization"), label: t("tab.authorization"),
enabled: true, enabled: false,
}, },
], ],
theme: "system", theme: "system",

View File

@@ -205,6 +205,35 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, default: false,
}, },
embedOptions: {
type: Object as PropType<EmbedOption>,
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<{ const emit = defineEmits<{
@@ -213,6 +242,7 @@ const emit = defineEmits<{
request: { request: {
sharedRequestID: string | undefined sharedRequestID: string | undefined
content: string | undefined content: string | undefined
type: string | undefined
} }
): void ): void
(e: "hide-modal"): void (e: "hide-modal"): void
@@ -220,6 +250,7 @@ const emit = defineEmits<{
}>() }>()
const selectedWidget = useVModel(props, "modelValue") const selectedWidget = useVModel(props, "modelValue")
const embedOptions = useVModel(props, "embedOptions")
type WidgetID = "embed" | "button" | "link" type WidgetID = "embed" | "button" | "link"
@@ -254,34 +285,6 @@ type EmbedOption = {
}[] }[]
theme: "light" | "dark" | "system" theme: "light" | "dark" | "system"
} }
const embedOptions = ref<EmbedOption>({
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(() => { const embedThemeIcon = computed(() => {
if (embedOptions.value.theme === "system") { if (embedOptions.value.theme === "system") {
return IconMonitor return IconMonitor
@@ -355,12 +358,13 @@ const linkVariants: LinkVariant[] = [
const baseURL = import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh" const baseURL = import.meta.env.VITE_SHORTCODE_BASE_URL ?? "https://hopp.sh"
const copyEmbed = () => { const copyEmbed = () => {
const options = embedOptions.value //const options = embedOptions.value
const enabledEmbedOptions = options.tabs // const enabledEmbedOptions = options.tabs
.filter((tab) => tab.enabled) // .filter((tab) => tab.enabled)
.map((tab) => tab.value) // .map((tab) => tab.value)
.toString() // .toString()
return `<iframe src="${baseURL}/e/${props.request?.id}/${enabledEmbedOptions}' style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'></iframe>` //return `<iframe src="${baseURL}/e/${props.request?.id}' style='width: 100%; height: 500px; border: 0; border-radius: 4px; overflow: hidden;'></iframe>`
return `${baseURL}/e/${props.request?.id} `
} }
const copyButton = ( const copyButton = (
@@ -410,7 +414,11 @@ const copyContent = ({
} else { } else {
content = copyEmbed() content = copyEmbed()
} }
const copyContent = { sharedRequestID: props.request?.id, content } const copyContent = {
sharedRequestID: props.request?.id,
content,
type: widget,
}
emit("copy-shared-request", copyContent) emit("copy-shared-request", copyContent)
} }

View File

@@ -21,6 +21,7 @@
<ShareCustomizeModal <ShareCustomizeModal
v-else-if="step === 2" v-else-if="step === 2"
v-model="selectedWidget" v-model="selectedWidget"
v-model:embed-options="embedOptions"
:request="request" :request="request"
:loading="loading" :loading="loading"
@copy-shared-request="copySharedRequest" @copy-shared-request="copySharedRequest"
@@ -53,6 +54,18 @@ import { useI18n } from "~/composables/i18n"
const t = useI18n() const t = useI18n()
type EmbedTabs = "parameters" | "body" | "headers" | "authorization"
type EmbedOption = {
selectedTab: EmbedTabs
tabs: {
value: EmbedTabs
label: string
enabled: boolean
}[]
theme: "light" | "dark" | "system"
}
const props = defineProps({ const props = defineProps({
request: { request: {
type: Object as PropType<HoppRESTRequest | null>, type: Object as PropType<HoppRESTRequest | null>,
@@ -75,6 +88,35 @@ const props = defineProps({
type: Number, type: Number,
default: 1, default: 1,
}, },
embedOptions: {
type: Object as PropType<EmbedOption>,
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" type WidgetID = "embed" | "button" | "link"
@@ -86,6 +128,7 @@ type Widget = {
} }
const selectedWidget = useVModel(props, "modelValue") const selectedWidget = useVModel(props, "modelValue")
const embedOptions = useVModel(props, "embedOptions")
const emit = defineEmits<{ const emit = defineEmits<{
(e: "create-shared-request", request: HoppRESTRequest | null): void (e: "create-shared-request", request: HoppRESTRequest | null): void
@@ -97,6 +140,7 @@ const emit = defineEmits<{
request: { request: {
sharedRequestID: string | undefined sharedRequestID: string | undefined
content: string | undefined content: string | undefined
type: string | undefined
} }
): void ): void
}>() }>()
@@ -108,6 +152,7 @@ const createSharedRequest = () => {
const copySharedRequest = (request: { const copySharedRequest = (request: {
sharedRequestID: string | undefined sharedRequestID: string | undefined
content: string | undefined content: string | undefined
type: string | undefined
}) => { }) => {
emit("copy-shared-request", request) emit("copy-shared-request", request)
} }

View File

@@ -121,7 +121,12 @@ const props = defineProps<{
}>() }>()
const emit = defineEmits<{ 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: "delete-shared-request", codeID: string): void
(e: "open-new-tab", request: HoppRESTRequest): void (e: "open-new-tab", request: HoppRESTRequest): void
}>() }>()
@@ -144,7 +149,13 @@ const openInNewTab = () => {
} }
const customizeSharedRequest = () => { 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 = () => { const deleteSharedRequest = () => {

View File

@@ -80,11 +80,12 @@
/> />
<ShareModal <ShareModal
v-model="selectedWidget" v-model="selectedWidget"
v-model:embed-options="embedOptions"
:step="step"
:request="requestToShare" :request="requestToShare"
:show="showShareRequestModal" :show="showShareRequestModal"
:loading="shareRequestCreatingLoading" :loading="shareRequestCreatingLoading"
:step="step" @hide-modal="displayCustomizeRequestModal(false, null)"
@hide-modal="displayCustomizeRequestModal(false)"
@copy-shared-request="copySharedRequest" @copy-shared-request="copySharedRequest"
@create-shared-request="createSharedRequest" @create-shared-request="createSharedRequest"
/> />
@@ -105,6 +106,7 @@ import * as TE from "fp-ts/TaskEither"
import { import {
deleteShortcode as backendDeleteShortcode, deleteShortcode as backendDeleteShortcode,
createShortcode, createShortcode,
updateEmbedProperties,
} from "~/helpers/backend/mutations/Shortcode" } from "~/helpers/backend/mutations/Shortcode"
import { GQLError } from "~/helpers/backend/GQLClient" import { GQLError } from "~/helpers/backend/GQLClient"
import { useToast } from "~/composables/toast" import { useToast } from "~/composables/toast"
@@ -130,6 +132,67 @@ const shareRequestCreatingLoading = ref(false)
const requestToShare = ref<HoppRESTRequest | null>(null) const requestToShare = ref<HoppRESTRequest | null>(null)
const embedOptions = ref<EmbedOption>({
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 restTab = useService(RESTTabService)
const currentUser = useReadonlyStream( const currentUser = useReadonlyStream(
@@ -139,6 +202,18 @@ const currentUser = useReadonlyStream(
const step = ref(1) 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 WidgetID = "embed" | "button" | "link"
type Widget = { type Widget = {
@@ -218,15 +293,46 @@ const displayShareRequestModal = (show: boolean) => {
showShareRequestModal.value = show showShareRequestModal.value = show
step.value = 1 step.value = 1
} }
const displayCustomizeRequestModal = (show: boolean) => { const displayCustomizeRequestModal = (
show: boolean,
embedProperties?: string | null
) => {
showShareRequestModal.value = show showShareRequestModal.value = show
step.value = 2 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) => { const createSharedRequest = async (request: HoppRESTRequest | null) => {
if (request && selectedWidget.value) { if (request && selectedWidget.value) {
const properties = {
options: ["parameters", "body", "headers"],
theme: "system",
}
shareRequestCreatingLoading.value = true shareRequestCreatingLoading.value = true
const sharedRequestResult = await createShortcode(request)() const sharedRequestResult = await createShortcode(
request,
selectedWidget.value.value === "embed"
? JSON.stringify(properties)
: undefined
)()
platform.analytics?.logEvent({ platform.analytics?.logEvent({
type: "HOPP_SHORTCODE_CREATED", type: "HOPP_SHORTCODE_CREATED",
@@ -243,6 +349,23 @@ const createSharedRequest = async (request: HoppRESTRequest | null) => {
id: sharedRequestResult.right.createShortcode.id, id: sharedRequestResult.right.createShortcode.id,
} }
step.value = 2 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 = ( const customizeSharedRequest = (
request: HoppRESTRequest, request: HoppRESTRequest,
shredRequestID: string shredRequestID: string,
embedProperties?: string | null
) => { ) => {
requestToShare.value = { requestToShare.value = {
...request, ...request,
id: shredRequestID, id: shredRequestID,
} }
displayCustomizeRequestModal(true) displayCustomizeRequestModal(true, embedProperties)
} }
const copySharedRequest = (request: { const copySharedRequest = (request: {
sharedRequestID: string | undefined sharedRequestID: string | undefined
content: string | undefined content: string | undefined
type: string | undefined
}) => { }) => {
if (request.content) { if (request.content) {
copyToClipboard(request.content) copyToClipboard(request.content)
toast.success(t("state.copied_to_clipboard")) 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))
}
} }
} }

View File

@@ -1,5 +1,5 @@
mutation CreateShortcode($request: String!) { mutation CreateShortcode($request: String!, $properties: String) {
createShortcode(request: $request) { createShortcode(request: $request, properties: $properties) {
id id
request request
createdOn createdOn

View File

@@ -0,0 +1,8 @@
mutation UpdateEmbedProperties($code: ID!, $properties: String!) {
updateEmbedProperties(code: $code, properties: $properties) {
id
request
properties
createdOn
}
}

View File

@@ -2,5 +2,6 @@ query ResolveShortcode($code: ID!) {
shortcode(code: $code) { shortcode(code: $code) {
id id
request request
properties
} }
} }

View File

@@ -0,0 +1,8 @@
subscription ShortcodeUpdated {
myShortcodesUpdated {
id
request
createdOn
properties
}
}

View File

@@ -7,15 +7,22 @@ import {
DeleteShortcodeDocument, DeleteShortcodeDocument,
DeleteShortcodeMutation, DeleteShortcodeMutation,
DeleteShortcodeMutationVariables, DeleteShortcodeMutationVariables,
UpdateEmbedPropertiesDocument,
UpdateEmbedPropertiesMutation,
UpdateEmbedPropertiesMutationVariables,
} from "../graphql" } from "../graphql"
type DeleteShortcodeErrors = "shortcode/not_found" type DeleteShortcodeErrors = "shortcode/not_found"
export const createShortcode = (request: HoppRESTRequest) => export const createShortcode = (
request: HoppRESTRequest,
properties?: string
) =>
runMutation<CreateShortcodeMutation, CreateShortcodeMutationVariables, "">( runMutation<CreateShortcodeMutation, CreateShortcodeMutationVariables, "">(
CreateShortcodeDocument, CreateShortcodeDocument,
{ {
request: JSON.stringify(request), request: JSON.stringify(request),
properties,
} }
) )
@@ -27,3 +34,13 @@ export const deleteShortcode = (code: string) =>
>(DeleteShortcodeDocument, { >(DeleteShortcodeDocument, {
code, code,
}) })
export const updateEmbedProperties = (code: string, properties: string) =>
runMutation<
UpdateEmbedPropertiesMutation,
UpdateEmbedPropertiesMutationVariables,
""
>(UpdateEmbedPropertiesDocument, {
code,
properties,
})

View File

@@ -4,6 +4,6 @@
export interface Shortcode { export interface Shortcode {
id: string id: string
request: string request: string
properties?: string | null | undefined properties?: string | null
createdOn: Date createdOn: Date
} }

View File

@@ -11,6 +11,7 @@ import {
GetUserShortcodesDocument, GetUserShortcodesDocument,
ShortcodeCreatedDocument, ShortcodeCreatedDocument,
ShortcodeDeletedDocument, ShortcodeDeletedDocument,
ShortcodeUpdatedDocument,
} from "../backend/graphql" } from "../backend/graphql"
import { BACKEND_PAGE_SIZE } from "../backend/helpers" import { BACKEND_PAGE_SIZE } from "../backend/helpers"
import { Shortcode } from "./Shortcode" import { Shortcode } from "./Shortcode"
@@ -25,9 +26,11 @@ export default class ShortcodeListAdapter {
private shortcodeCreated: Subscription | null private shortcodeCreated: Subscription | null
private shortcodeRevoked: Subscription | null private shortcodeRevoked: Subscription | null
private shortcodeUpdated: Subscription | null
private shortcodeCreatedSub: WSubscription | null private shortcodeCreatedSub: WSubscription | null
private shortcodeRevokedSub: WSubscription | null private shortcodeRevokedSub: WSubscription | null
private shortcodeUpdatedSub: WSubscription | null
constructor(deferInit = false) { constructor(deferInit = false) {
this.error$ = new BehaviorSubject<GQLError<string> | null>(null) this.error$ = new BehaviorSubject<GQLError<string> | null>(null)
@@ -39,8 +42,10 @@ export default class ShortcodeListAdapter {
this.isDispose = true this.isDispose = true
this.shortcodeCreated = null this.shortcodeCreated = null
this.shortcodeRevoked = null this.shortcodeRevoked = null
this.shortcodeUpdated = null
this.shortcodeCreatedSub = null this.shortcodeCreatedSub = null
this.shortcodeRevokedSub = null this.shortcodeRevokedSub = null
this.shortcodeUpdatedSub = null
if (!deferInit) this.initialize() if (!deferInit) this.initialize()
} }
@@ -48,8 +53,10 @@ export default class ShortcodeListAdapter {
unsubscribeSubscriptions() { unsubscribeSubscriptions() {
this.shortcodeCreated?.unsubscribe() this.shortcodeCreated?.unsubscribe()
this.shortcodeRevoked?.unsubscribe() this.shortcodeRevoked?.unsubscribe()
this.shortcodeUpdated?.unsubscribe()
this.shortcodeCreatedSub?.unsubscribe() this.shortcodeCreatedSub?.unsubscribe()
this.shortcodeRevokedSub?.unsubscribe() this.shortcodeRevokedSub?.unsubscribe()
this.shortcodeUpdatedSub?.unsubscribe()
} }
initialize() { initialize() {
@@ -137,6 +144,14 @@ export default class ShortcodeListAdapter {
this.shortcodes$.next(newShortcode) 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() { private registerSubscriptions() {
const [shortcodeCreated$, shortcodeCreatedSub] = runAuthOnlyGQLSubscription( const [shortcodeCreated$, shortcodeCreatedSub] = runAuthOnlyGQLSubscription(
{ {
@@ -169,5 +184,21 @@ export default class ShortcodeListAdapter {
this.deleteSharedRequest(result.right.myShortcodesRevoked.id) 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)
})
} }
} }