Skip to content

Commit

Permalink
feat: copyable invite links (hoppscotch#4153)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 28, 2024
1 parent 0c06f26 commit 2917d50
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 4 deletions.
3 changes: 3 additions & 0 deletions packages/hoppscotch-common/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,10 @@
"success_invites": "Success invites",
"title": "Workspaces",
"we_sent_invite_link": "We sent an invite link to all invitees!",
"invite_sent_smtp_disabled": "Invite links generated",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the workspace.",
"invite_sent_smtp_disabled_description": "Sending invite emails is disabled for this instance of Hoppscotch. Please use the Copy link button to copy and share the invite link manually.",
"copy_invite_link": "Copy Invite Link",
"search_title": "Team Requests"
},
"team_environment": {
Expand Down
85 changes: 81 additions & 4 deletions packages/hoppscotch-common/src/components/teams/Invite.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@
<div class="mb-8 flex max-w-md flex-col items-center justify-center">
<icon-lucide-users class="h-6 w-6 text-accent" />
<h3 class="my-2 text-center text-lg">
{{ t("team.we_sent_invite_link") }}
{{
inviteMethod === "email"
? t("team.we_sent_invite_link")
: t("team.invite_sent_smtp_disabled")
}}
</h3>
<p class="text-center">
{{ t("team.we_sent_invite_link_description") }}
{{
inviteMethod === "email"
? t("team.we_sent_invite_link_description")
: t("team.invite_sent_smtp_disabled_description")
}}
</p>
</div>
<div v-if="successInvites.length">
Expand All @@ -33,6 +41,20 @@
class="svg-icons mr-4 text-green-500"
/>
<span class="truncate">{{ invitee.email }}</span>
<span class="flex items-center gap-1 ml-auto">
<HoppButtonSecondary
outline
filled
:icon="getCopyIcon(invitee.invitationID).value"
class="rounded-md"
:label="t('team.copy_invite_link')"
@click="
() => {
copyInviteLink(invitee.invitationID)
}
"
/>
</span>
</p>
</div>
</div>
Expand Down Expand Up @@ -107,6 +129,20 @@
:value="invitee.inviteeRole"
readonly
/>
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
outline
:icon="getCopyIcon(invitee.id).value"
class="rounded-md"
:title="t('team.copy_invite_link')"
@click="
() => {
copyInviteLink(invitee.id)
}
"
/>
</div>
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
Expand Down Expand Up @@ -352,7 +388,7 @@
</template>
<script setup lang="ts">
import { watch, ref, reactive, computed } from "vue"
import { watch, ref, reactive, computed, Ref, onMounted } from "vue"
import * as T from "fp-ts/Task"
import * as E from "fp-ts/Either"
import * as A from "fp-ts/Array"
Expand Down Expand Up @@ -386,7 +422,24 @@ import IconMailCheck from "~icons/lucide/mail-check"
import IconCircleDot from "~icons/lucide/circle-dot"
import IconCircle from "~icons/lucide/circle"
import IconArrowLeft from "~icons/lucide/arrow-left"
import IconCopy from "~icons/lucide/copy"
import IconCheck from "~icons/lucide/check"
import { TippyComponent } from "vue-tippy"
import { refAutoReset } from "@vueuse/core"
import { copyToClipboard } from "~/helpers/utils/clipboard"
import { platform } from "~/platform"
const copyIcons: Record<string, Ref<typeof IconCopy | typeof IconCheck>> = {}
const getCopyIcon = (id: string) => {
if (!copyIcons[id]) {
copyIcons[id] = refAutoReset<typeof IconCopy | typeof IconCheck>(
IconCopy,
1000
)
}
return copyIcons[id]
}
const t = useI18n()
Expand All @@ -406,6 +459,20 @@ const emit = defineEmits<{
(e: "hide-modal"): void
}>()
const inviteMethod = ref<"email" | "link">("email")
onMounted(async () => {
const getIsSMTPEnabled = platform.infra?.getIsSMTPEnabled
if (getIsSMTPEnabled) {
const res = await getIsSMTPEnabled()
if (E.isRight(res)) {
inviteMethod.value = res.right ? "email" : "link"
}
}
})
const pendingInvites = useGQLQuery<
GetPendingInvitesQuery,
GetPendingInvitesQueryVariables,
Expand Down Expand Up @@ -496,6 +563,14 @@ const removeNewInvitee = (id: number) => {
newInvites.value.splice(id, 1)
}
const copyInviteLink = (invitationID: string) => {
copyToClipboard(
`${import.meta.env.VITE_BASE_URL}/join-team?id=${invitationID}`
)
getCopyIcon(invitationID).value = IconCheck
}
type SendInvitesErrorType =
| {
email: Email
Expand All @@ -505,6 +580,7 @@ type SendInvitesErrorType =
| {
email: Email
status: "success"
invitationID: string
}
const sendInvitesResult = ref<Array<SendInvitesErrorType>>([])
Expand Down Expand Up @@ -555,9 +631,10 @@ const sendInvites = async () => {
email: newInvites.value[i].key as Email,
error: err,
}),
() => ({
(invitation) => ({
status: "success" as const,
email: newInvites.value[i].key as Email,
invitationID: invitation.id,
})
)
)
Expand Down
2 changes: 2 additions & 0 deletions packages/hoppscotch-common/src/platform/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { InspectorsPlatformDef } from "./inspectors"
import { ServiceClassInstance } from "dioc"
import { IOPlatformDef } from "./io"
import { SpotlightPlatformDef } from "./spotlight"
import { InfraPlatformDef } from "./infra"
import { Ref } from "vue"

export type PlatformDef = {
Expand Down Expand Up @@ -52,6 +53,7 @@ export type PlatformDef = {
*/
workspaceSwitcherLogin?: Ref<boolean>
}
infra?: InfraPlatformDef
}

export let platform: PlatformDef
Expand Down
5 changes: 5 additions & 0 deletions packages/hoppscotch-common/src/platform/infra.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as E from "fp-ts/Either"

export type InfraPlatformDef = {
getIsSMTPEnabled?: () => Promise<E.Either<string, boolean>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
query GetSMTPStatus {
isSMTPEnabled
}
2 changes: 2 additions & 0 deletions packages/hoppscotch-selfhost-web/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ExtensionInterceptorService } from "@hoppscotch/common/platform/std/int
import { stdFooterItems } from "@hoppscotch/common/platform/std/ui/footerItem"
import { stdSupportOptionItems } from "@hoppscotch/common/platform/std/ui/supportOptionsItem"
import { browserIODef } from "@hoppscotch/common/platform/std/io"
import { InfraPlatform } from "@platform/infra/infra.platform"

createHoppApp("#app", {
ui: {
Expand Down Expand Up @@ -40,4 +41,5 @@ createHoppApp("#app", {
exportAsGIST: false,
hasTelemetry: false,
},
infra: InfraPlatform,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { runGQLQuery } from "@hoppscotch/common/helpers/backend/GQLClient"
import { InfraPlatformDef } from "@hoppscotch/common/platform/infra"
import { GetSmtpStatusDocument } from "../../api/generated/graphql"
import * as E from "fp-ts/Either"

const getSMTPStatus = () => {
return runGQLQuery({
query: GetSmtpStatusDocument,
variables: {},
})
}

export const InfraPlatform: InfraPlatformDef = {
getIsSMTPEnabled: async () => {
const res = await getSMTPStatus()

if (E.isRight(res)) {
return E.right(res.right.isSMTPEnabled)
}

return E.left("SMTP_STATUS_FETCH_FAILED")
},
}

0 comments on commit 2917d50

Please sign in to comment.