feat: share and support modals

This commit is contained in:
liyasthomas
2021-08-09 17:55:30 +05:30
parent cbf99d2daf
commit aa8b4231e2
27 changed files with 425 additions and 27 deletions

1
assets/icons/email.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" ><path fill="currentColor" d="M12 .02c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zM18.99 7L12 12.666 5.009 7H18.99zM19 17H5V8.495l7 5.673 7-5.672V17z"/></svg>

After

Width:  |  Height:  |  Size: 224 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" ><path fill="currentColor" d="M22.675 0H1.325C.593 0 0 .593 0 1.325v21.351C0 23.407.593 24 1.325 24H12.82v-9.294H9.692v-3.622h3.128V8.413c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12V24h6.116c.73 0 1.323-.593 1.323-1.325V1.325C24 .593 23.407 0 22.675 0z"/></svg>

After

Width:  |  Height:  |  Size: 393 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" ><path fill="currentColor" d="M19 0H5a5 5 0 00-5 5v14a5 5 0 005 5h14a5 5 0 005-5V5a5 5 0 00-5-5zM8 19H5V8h3v11zM6.5 6.732c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zM20 19h-3v-5.604c0-3.368-4-3.113-4 0V19h-3V8h3v1.765c1.396-2.586 7-2.777 7 2.476V19z"/></svg>

After

Width:  |  Height:  |  Size: 347 B

1
assets/icons/reddit.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="currentColor" d="M24 11.779a2.654 2.654 0 00-4.497-1.899c-1.81-1.191-4.259-1.949-6.971-2.046l1.483-4.669 4.016.941-.006.058a2.17 2.17 0 002.174 2.163c1.198 0 2.172-.97 2.172-2.163a2.171 2.171 0 00-4.193-.785l-4.329-1.015a.37.37 0 00-.44.249L11.755 7.82c-2.838.034-5.409.798-7.3 2.025a2.643 2.643 0 00-1.799-.712A2.654 2.654 0 000 11.779c0 .97.533 1.811 1.317 2.271a4.716 4.716 0 00-.086.857C1.231 18.818 6.039 22 11.95 22s10.72-3.182 10.72-7.093c0-.274-.029-.544-.075-.81A2.633 2.633 0 0024 11.779zM6.776 13.595c0-.868.71-1.575 1.582-1.575.872 0 1.581.707 1.581 1.575s-.709 1.574-1.581 1.574-1.582-.706-1.582-1.574zm9.061 4.669c-.797.793-2.048 1.179-3.824 1.179L12 19.44l-.013.003c-1.777 0-3.028-.386-3.824-1.179a.369.369 0 010-.523.372.372 0 01.526 0c.65.647 1.729.961 3.298.961l.013.003.013-.003c1.569 0 2.648-.315 3.298-.962a.373.373 0 01.526 0 .37.37 0 010 .524zm-.189-3.095a1.58 1.58 0 01-1.581-1.574c0-.868.709-1.575 1.581-1.575s1.581.707 1.581 1.575-.709 1.574-1.581 1.574z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1 +1 @@
<svg clip-rule="evenodd" fill-rule="evenodd" height="1684" stroke-linejoin="round" stroke-miterlimit="2" viewBox="-89.00934757 -46.8841404 643.93723344 446.8841404" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m154.729 400c185.669 0 287.205-153.876 287.205-287.312 0-4.37-.089-8.72-.286-13.052a205.304 205.304 0 0 0 50.352-52.29c-18.087 8.044-37.55 13.458-57.968 15.899 20.841-12.501 36.84-32.278 44.389-55.852a202.42 202.42 0 0 1 -64.098 24.511c-18.42-19.628-44.644-31.904-73.682-31.904-55.744 0-100.948 45.222-100.948 100.965 0 7.925.887 15.631 2.619 23.025-83.895-4.223-158.287-44.405-208.074-105.504a100.739 100.739 0 0 0 -13.668 50.754c0 35.034 17.82 65.961 44.92 84.055a100.172 100.172 0 0 1 -45.716-12.63c-.015.424-.015.837-.015 1.29 0 48.903 34.794 89.734 80.982 98.986a101.036 101.036 0 0 1 -26.617 3.553c-6.493 0-12.821-.639-18.971-1.82 12.851 40.122 50.115 69.319 94.296 70.135-34.549 27.089-78.07 43.224-125.371 43.224a204.9 204.9 0 0 1 -24.078-1.399c44.674 28.645 97.72 45.359 154.734 45.359" fill="#1da1f2" fill-rule="nonzero"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="currentColor" d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 628 B

View File

@@ -111,16 +111,14 @@ a {
@apply py-1 px-2;
@apply shadow;
font-size: var(--body-font-size);
font-size: 88%;
line-height: var(--body-line-height);
kbd {
@apply first:ml-2;
@apply last:-mr-1;
@apply font-sans;
@apply bg-secondaryDark;
@apply text-primaryDark;
@apply rounded;
@apply rounded-sm;
@apply px-1;
@apply ml-1;
}

View File

@@ -58,6 +58,13 @@
blank
@click.native="$refs.options.tippy().hide()"
/>
<SmartItem
:label="$t('app.chat_with_us')"
@click.native="
chatWithUs()
$refs.options.tippy().hide()
"
/>
<hr />
<SmartItem
:label="$t('app.twitter')"
@@ -109,6 +116,7 @@
<script lang="ts">
import { defineComponent, ref } from "@nuxtjs/composition-api"
import { defineActionHandler } from "~/helpers/actions"
import { showChat } from "~/helpers/support"
import { useSetting } from "~/newstore/settings"
export default defineComponent({
@@ -149,6 +157,9 @@ export default defineComponent({
// fallback
}
},
chatWithUs() {
showChat()
},
},
})
</script>

123
components/app/Share.vue Normal file
View File

@@ -0,0 +1,123 @@
<template>
<SmartModal
v-if="show"
:title="$t('app.invite_your_friends')"
@close="$emit('hide-modal')"
>
<template #body>
<p class="text-secondaryLight mb-8 px-2">
{{ $t("app.invite_description") }}
</p>
<div class="flex flex-col space-y-2 px-2">
<div class="grid gap-4 grid-cols-3">
<a
v-for="(platform, index) in platforms"
:key="`platform-${index}`"
:href="platform.link"
target="_blank"
class="share-link"
>
<SmartIcon :name="platform.icon" class="h-6 w-6" />
<span class="mt-3">
{{ platform.name }}
</span>
</a>
<button class="share-link" @click="copyAppLink">
<span class="font-icon h-6 text-xl w-6">{{ copyIcon }}</span>
<span class="font-medium mt-3">
{{ $t("app.copy") }}
</span>
</button>
</div>
</div>
</template>
</SmartModal>
</template>
<script>
import { copyToClipboard } from "~/helpers/utils/clipboard"
export default {
props: {
show: Boolean,
},
data() {
const url = "https://hoppscotch.io"
const text = "Hoppscotch - Open source API development ecosystem."
const description =
"Helps you create requests faster, saving precious time on development."
const subject =
"Checkout Hoppscotch - an open source API development ecosystem"
const summary = `Hi there!%0D%0A%0D%0AI thought youll like this new platform that I joined called Hoppscotch - https://hoppscotch.io.%0D%0AIt is a simple and intuitive interface for creating and managing your APIs. You can build, test, document, and share your APIs.%0D%0A%0D%0AThe best part about Hoppscotch is that it is open source and free to get started.%0D%0A%0D%0A`
const twitter = "hoppscotch_io"
return {
url: "https://hoppscotch.io",
copyIcon: "content_copy",
platforms: [
{
name: "Email",
icon: "email",
link: `mailto:?subject=${subject}&body=${summary}`,
},
{
name: "Twitter",
icon: "twitter",
link: `https://twitter.com/intent/tweet?text=${text} ${description}&url=${url}&via=${twitter}`,
},
{
name: "Facebook",
icon: "facebook",
link: `https://www.facebook.com/sharer/sharer.php?u=${url}`,
},
{
name: "Reddit",
icon: "reddit",
link: `https://www.reddit.com/submit?url=${url}&title=${text}`,
},
{
name: "LinkedIn",
icon: "linkedin",
link: `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
},
],
}
},
methods: {
copyAppLink() {
copyToClipboard(this.url)
this.copyIcon = "done"
this.$toast.success(this.$t("copied_to_clipboard").toString(), {
icon: "done",
})
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>
<style lang="scss" scoped>
.share-link {
@apply border border-dividerLight;
@apply rounded;
@apply flex-col flex;
@apply p-4;
@apply transition;
@apply items-center;
@apply justify-center;
@apply hover:(bg-primaryLight text-secondaryDark);
@apply focus:(border-divider outline-none);
svg {
@apply opacity-80;
}
&:hover {
svg {
@apply opacity-100;
}
}
}
</style>

View File

@@ -86,7 +86,19 @@ export default {
shortcuts: [
{
keys: ["?"],
label: this.$t("shortcut.show_all"),
label: this.$t("shortcut.general.help_menu"),
},
{
keys: ["/"],
label: this.$t("shortcut.general.show_all"),
},
{
keys: [getPlatformSpecialKey(), "K"],
label: this.$t("shortcut.general.command_menu"),
},
{
keys: ["Esc"],
label: this.$t("shortcut.general.close_current_menu"),
},
],
},

View File

@@ -1,7 +1,7 @@
<template>
<aside>
<aside class="flex h-full justify-between md:flex-col">
<nav class="flex flex-nowrap md:flex-col">
<nuxt-link
<NuxtLink
v-for="(navigation, index) in primaryNavigation"
:key="`navigation-${index}`"
:to="localePath(navigation.target)"
@@ -14,13 +14,58 @@
<SmartIcon :name="navigation.svg" class="svg-icons" />
</div>
<span>{{ navigation.title }}</span>
</nuxt-link>
</NuxtLink>
</nav>
<nav
class="
flex flex-nowrap
p-4
items-center
justify-center
md:(flex-col
space-x-0 space-y-2)
"
>
<TabPrimary
v-tippy="{ theme: 'tooltip', placement: 'top' }"
:title="$t('app.invite')"
icon="person_add_alt"
@click.native="showShare = true"
/>
<TabPrimary
v-tippy="{ theme: 'tooltip', placement: 'top' }"
:title="`${$t('support.title')} <kbd>?</kbd>`"
icon="support"
@click.native="showSupport = true"
/>
</nav>
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
<AppShare :show="showShare" @hide-modal="showShare = false" />
</aside>
</template>
<script>
export default {
<script lang="ts">
import { defineComponent, ref } from "@nuxtjs/composition-api"
import { defineActionHandler } from "~/helpers/actions"
export default defineComponent({
setup() {
const showSupport = ref(false)
const showShare = ref(false)
defineActionHandler("modals.support.toggle", () => {
showSupport.value = !showSupport.value
})
defineActionHandler("modals.share.toggle", () => {
showShare.value = !showShare.value
})
return {
showSupport,
showShare,
}
},
data() {
return {
primaryNavigation: [
@@ -52,7 +97,7 @@ export default {
],
}
},
}
})
</script>
<style scoped lang="scss">

View File

@@ -0,0 +1,86 @@
<template>
<SmartModal
v-if="show"
:title="$t('support.title')"
@close="$emit('hide-modal')"
>
<template #body>
<div class="flex flex-col space-y-2 px-2">
<SmartItem
icon="menu_book"
:label="$t('app.documentation')"
to="https://github.com/hoppscotch/hoppscotch/wiki"
:description="$t('support.documentation')"
info-icon="chevron_right"
active
blank
@click.native="hideModal()"
/>
<SmartItem
icon="keyboard"
:label="$t('app.keyboard_shortcuts')"
:description="$t('support.shortcuts')"
info-icon="chevron_right"
active
@click.native="
showShortcuts()
hideModal()
"
/>
<SmartItem
icon="auto_awesome"
:label="$t('app.whats_new')"
to="https://github.com/hoppscotch/hoppscotch/blob/main/CHANGELOG.md"
:description="$t('support.changelog')"
info-icon="chevron_right"
active
blank
@click.native="hideModal()"
/>
<SmartItem
icon="contact_support"
:label="$t('app.chat_with_us')"
:description="$t('support.chat')"
info-icon="chevron_right"
active
@click.native="
chatWithUs()
hideModal()
"
/>
<SmartItem
svg="twitter"
:label="$t('app.twitter')"
to="https://twitter.com/hoppscotch_io"
blank
:description="$t('support.twitter')"
info-icon="chevron_right"
active
@click.native="hideModal()"
/>
</div>
</template>
</SmartModal>
</template>
<script>
import { invokeAction } from "~/helpers/actions"
import { showChat } from "~/helpers/support"
export default {
props: {
show: Boolean,
},
methods: {
chatWithUs() {
showChat()
},
showShortcuts() {
invokeAction("flyouts.keybinds.toggle")
},
hideModal() {
this.$emit("hide-modal")
},
},
}
</script>

View File

@@ -20,6 +20,7 @@
:key="`contentTypeItem-${index}`"
:label="contentTypeItem"
:info-icon="contentTypeItem === contentType ? 'done' : ''"
:active-info-icon="contentTypeItem === contentType"
@click.native="
contentType = contentTypeItem
$refs.contentTypeOptions.tippy().hide()

View File

@@ -41,6 +41,7 @@
:key="`gen-${index}`"
:label="gen.name"
:info-icon="gen.id === codegenType ? 'done' : ''"
:active-info-icon="gen.id === codegenType"
@click.native="
codegenType = gen.id
$refs.options.tippy().hide()

View File

@@ -93,6 +93,7 @@
id="send"
class="rounded-none min-w-20"
:label="!loading ? $t('send') : $t('cancel')"
:shortcut="[getSpecialKey(), 'G']"
@click.native="!loading ? newSendRequest() : cancelRequest()"
/>
<span class="inline-flex">

View File

@@ -18,7 +18,7 @@
{{ $t("shortcut.reset_request") }}
</span>
<span class="flex flex-1 items-center">
{{ $t("shortcut.show_all") }}
{{ $t("shortcut.general.show_all") }}
</span>
</div>
<div class="flex flex-col space-y-4">

View File

@@ -14,7 +14,7 @@
}`"
/>
</template>
<nuxt-link
<NuxtLink
v-for="(locale, index) in $i18n.locales.filter(
({ code }) => code !== $i18n.locale
)"
@@ -23,7 +23,7 @@
@click="$refs.language.tippy().hide()"
>
<SmartItem :label="locale.name" />
</nuxt-link>
</NuxtLink>
</tippy>
</span>
</span>

View File

@@ -17,6 +17,7 @@
:key="`size-${index}`"
:label="size.name"
:info-icon="size.code === active.code ? 'done' : ''"
:active-info-icon="size.code === active.code"
@click.native="
setActiveFont(size)
$refs.fontSize.tippy().hide()

View File

@@ -30,10 +30,17 @@
:disabled="disabled"
:tabindex="loading ? '-1' : '0'"
>
<span v-if="!loading" class="inline-flex items-center">
<span
v-if="!loading"
class="inline-flex items-center"
:class="{ 'self-start': infoIcon }"
>
<i
v-if="icon"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
:class="[
label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : '',
{ 'text-accent': active },
]"
class="material-icons"
>
{{ icon }}
@@ -41,7 +48,10 @@
<SmartIcon
v-if="svg"
:name="svg"
:class="label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : ''"
:class="[
label ? (reverse ? 'ml-4 opacity-75' : 'mr-4 opacity-75') : '',
{ 'text-accent': active },
]"
class="svg-icons"
/>
</span>
@@ -57,7 +67,11 @@
{{ description }}
</p>
</div>
<i v-if="infoIcon" class="text-accent ml-6 self-end material-icons">
<i
v-if="infoIcon"
class="ml-6 self-center material-icons items-center"
:class="{ 'text-accent': activeInfoIcon }"
>
{{ infoIcon }}
</i>
</SmartLink>
@@ -110,6 +124,14 @@ export default {
type: Boolean,
default: false,
},
active: {
type: Boolean,
default: false,
},
activeInfoIcon: {
type: Boolean,
default: false,
},
infoIcon: {
type: String,
default: "",

View File

@@ -162,6 +162,7 @@ export default defineComponent({
}
},
onTransitionLeaveStart() {
this.close()
this.shouldCleanupDomOnUnmount = false
},
$getPortal() {

View File

@@ -17,6 +17,8 @@ export type HoppAction =
| "request.method.put" // Select PUT Method
| "request.method.delete" // Select DELETE Method
| "flyouts.keybinds.toggle" // Shows the keybinds flyout
| "modals.support.toggle" // Shows the support modal
| "modals.share.toggle" // Shows the share modal
type BoundActionList = {
// eslint-disable-next-line no-unused-vars

View File

@@ -17,10 +17,49 @@ let keybindingsEnabled = true
type ModifierKeys = "ctrl" | "alt"
/* eslint-disable prettier/prettier */
type Key = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k'
| 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x'
| 'y' | 'z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
| "up" | "down" | "left" | "right" | "?"
type Key =
| "a"
| "b"
| "c"
| "d"
| "e"
| "f"
| "g"
| "h"
| "i"
| "j"
| "k"
| "l"
| "m"
| "n"
| "o"
| "p"
| "q"
| "r"
| "s"
| "t"
| "u"
| "v"
| "w"
| "x"
| "y"
| "z"
| "0"
| "1"
| "2"
| "3"
| "4"
| "5"
| "6"
| "7"
| "8"
| "9"
| "up"
| "down"
| "left"
| "right"
| "/"
| "?"
/* eslint-enable */
type ModifierBasedShortcutKey = `${ModifierKeys}-${Key}`
@@ -44,7 +83,9 @@ export const bindings: {
"alt-p": "request.method.post",
"alt-u": "request.method.put",
"alt-x": "request.method.delete",
"?": "flyouts.keybinds.toggle",
"/": "flyouts.keybinds.toggle",
"?": "modals.support.toggle",
"ctrl-x": "modals.share.toggle",
}
/**
@@ -115,6 +156,9 @@ function getPressedKey(ev: KeyboardEvent): Key | null {
// Check if question mark
if (val === "?") return "?"
// Check if question mark
if (val === "/") return "/"
// If no other cases match, this is not a valid key
return null
}

4
helpers/support.js Normal file
View File

@@ -0,0 +1,4 @@
export const showChat = () => {
$crisp.push(["do", "chat:show"])
$crisp.push(["do", "chat:open"])
}

View File

@@ -64,17 +64,33 @@
"more": "Hide more",
"preview": "Hide Preview"
},
"support": {
"title": "Support",
"documentation": "Read more about Hoppscotch",
"changelog": "Read more about latest releases",
"twitter": "Follow us on Twitter",
"chat": "Questions? Chat with us!",
"forum": "Ask questions and get answers",
"team": "Get in touch with the team",
"shortcuts": "Become more efficient"
},
"app": {
"name": "Hoppscotch",
"version": "v2.0",
"documentation": "Documentation",
"whats_new": "What's new?",
"chat_with_us": "Chat with us",
"keyboard_shortcuts": "Keyboard shortcuts",
"new_version_found": "New version found. Refresh to update.",
"twitter": "Twitter",
"terms_and_privacy": "Terms and privacy",
"status": "Status",
"help": "Help, feedback and</br>documentation"
"help": "Help, feedback and</br>documentation",
"invite": "Invite",
"invite_your_friends": "Invite your friends",
"invite_description": "In Hoppscotch, we designed a simple and intuitive interface for creating and managing your APIs. Hoppscotch is a tool that helps you build, test, document and share your APIs.",
"share": "Share",
"copy": "Copy"
},
"collection": {
"save_as": "Save as",
@@ -189,7 +205,12 @@
"results": "Results"
},
"shortcut": {
"show_all": "Show all Shortcuts",
"general": {
"help_menu": "Help menu",
"show_all": "Keyboard shortcuts",
"command_menu": "Command menu",
"close_current_menu": "Close current menu"
},
"send_request": "Send Request",
"save_to_collections": "Save to Collections",
"copy_request_link": "Copy Request Link",

View File

@@ -98,6 +98,7 @@ export default {
"~/plugins/vuex-persist",
"~/plugins/vue-rx",
"~/plugins/vue-apollo",
"~/plugins/crisp",
{ src: "~/plugins/web-worker", ssr: false },
],

View File

@@ -375,7 +375,7 @@
{{ $t("shortcut.reset_request") }}
</span>
<span class="flex flex-1 items-center">
{{ $t("shortcut.show_all") }}
{{ $t("shortcut.general.show_all") }}
</span>
</div>
<div class="flex flex-col space-y-4">

View File

@@ -242,6 +242,7 @@
svg="firefox"
label="Firefox"
:info-icon="hasFirefoxExtInstalled ? 'check_circle' : ''"
:active-info-icon="hasFirefoxExtInstalled"
outline
/>
</span>
@@ -252,6 +253,7 @@
svg="chrome"
label="Chrome"
:info-icon="hasChromeExtInstalled ? 'check_circle' : ''"
:active-info-icon="hasChromeExtInstalled"
outline
/>
</span>

18
plugins/crisp.js Normal file
View File

@@ -0,0 +1,18 @@
export default () => {
window.$crisp = []
window.CRISP_WEBSITE_ID = "3ad30257-c192-4773-955d-fb05a4b41af3"
const d = document
const s = d.createElement("script")
s.src = "https://client.crisp.chat/l.js"
s.async = 1
d.getElementsByTagName("head")[0].appendChild(s)
$crisp.push(["do", "chat:hide"])
$crisp.push([
"on",
"chat:closed",
() => {
$crisp.push(["do", "chat:hide"])
},
])
}