123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- <template>
- <div class="flex flex-col items-center justify-between min-h-screen">
- <div
- v-if="invalidLink"
- class="flex flex-col items-center justify-center flex-1"
- >
- <i class="pb-2 opacity-75 material-icons">error_outline</i>
- <h1 class="text-center heading">
- {{ $t("team.invalid_invite_link") }}
- </h1>
- <p class="mt-2 text-center">
- {{ $t("team.invalid_invite_link_description") }}
- </p>
- </div>
- <div
- v-else-if="loadingCurrentUser"
- class="flex flex-col items-center justify-center flex-1 p-4"
- >
- <SmartSpinner />
- </div>
- <div
- v-else-if="currentUser === null"
- class="flex flex-col items-center justify-center flex-1 p-4"
- >
- <h1 class="heading">{{ $t("team.login_to_continue") }}</h1>
- <p class="mt-2">{{ $t("team.login_to_continue_description") }}</p>
- <ButtonPrimary
- :label="$t('auth.login_to_hoppscotch')"
- class="mt-8"
- @click.native="showLogin = true"
- />
- </div>
- <div v-else class="flex flex-col items-center justify-center flex-1 p-4">
- <div
- v-if="inviteDetails.loading"
- class="flex flex-col items-center justify-center flex-1 p-4"
- >
- <SmartSpinner />
- </div>
- <div v-else>
- <div
- v-if="!inviteDetails.loading && E.isLeft(inviteDetails.data)"
- class="flex flex-col items-center p-4"
- >
- <i class="mb-4 material-icons">error_outline</i>
- <p>
- {{ getErrorMessage(inviteDetails.data.left) }}
- </p>
- <p
- class="flex flex-col items-center p-4 mt-8 border rounded border-dividerLight"
- >
- <span class="mb-4">
- {{ $t("team.logout_and_try_again") }}
- </span>
- <span class="flex">
- <FirebaseLogout
- v-if="inviteDetails.data.left.type === 'gql_error'"
- outline
- />
- </span>
- </p>
- </div>
- <div
- v-if="
- !inviteDetails.loading &&
- E.isRight(inviteDetails.data) &&
- !joinTeamSuccess
- "
- class="flex flex-col items-center justify-center flex-1 p-4"
- >
- <h1 class="heading">
- {{
- $t("team.join_team", {
- team: inviteDetails.data.right.teamInvitation.team.name,
- })
- }}
- </h1>
- <p class="mt-2 text-secondaryLight">
- {{
- $t("team.invited_to_team", {
- owner:
- inviteDetails.data.right.teamInvitation.creator.displayName,
- team: inviteDetails.data.right.teamInvitation.team.name,
- })
- }}
- </p>
- <div class="mt-8">
- <ButtonPrimary
- :label="
- $t('team.join_team', {
- team: inviteDetails.data.right.teamInvitation.team.name,
- })
- "
- :loading="loading"
- :disabled="revokedLink"
- @click.native="joinTeam"
- />
- </div>
- </div>
- <div
- v-if="
- !inviteDetails.loading &&
- E.isRight(inviteDetails.data) &&
- joinTeamSuccess
- "
- class="flex flex-col items-center justify-center flex-1 p-4"
- >
- <h1 class="heading">
- {{
- $t("team.joined_team", {
- team: inviteDetails.data.right.teamInvitation.team.name,
- })
- }}
- </h1>
- <p class="mt-2 text-secondaryLight">
- {{
- $t("team.joined_team_description", {
- team: inviteDetails.data.right.teamInvitation.team.name,
- })
- }}
- </p>
- <div class="mt-8">
- <ButtonSecondary to="/" svg="home" filled :label="$t('app.home')" />
- </div>
- </div>
- </div>
- </div>
- <div class="p-4">
- <ButtonSecondary
- class="tracking-wide !font-bold !text-secondaryDark"
- label="HOPPSCOTCH"
- to="/"
- />
- </div>
- <FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
- </div>
- </template>
- <script lang="ts">
- import { computed, defineComponent, useRoute } from "@nuxtjs/composition-api"
- import * as E from "fp-ts/Either"
- import * as TE from "fp-ts/TaskEither"
- import { pipe } from "fp-ts/function"
- import { GQLError, useGQLQuery } from "~/helpers/backend/GQLClient"
- import {
- GetInviteDetailsDocument,
- GetInviteDetailsQuery,
- GetInviteDetailsQueryVariables,
- } from "~/helpers/backend/graphql"
- import { acceptTeamInvitation } from "~/helpers/backend/mutations/TeamInvitation"
- import { initializeFirebase } from "~/helpers/fb"
- import { currentUser$, onLoggedIn, probableUser$ } from "~/helpers/fb/auth"
- import { useReadonlyStream } from "~/helpers/utils/composables"
- type GetInviteDetailsError =
- | "team_invite/not_valid_viewer"
- | "team_invite/not_found"
- | "team_invite/no_invite_found"
- | "team_invite/email_do_not_match"
- | "team_invite/already_member"
- export default defineComponent({
- layout: "empty",
- setup() {
- const route = useRoute()
- const inviteDetails = useGQLQuery<
- GetInviteDetailsQuery,
- GetInviteDetailsQueryVariables,
- GetInviteDetailsError
- >({
- query: GetInviteDetailsDocument,
- variables: {
- inviteID: route.value.query.id as string,
- },
- defer: true,
- })
- onLoggedIn(() => {
- if (typeof route.value.query.id === "string") {
- inviteDetails.execute({
- inviteID: route.value.query.id,
- })
- }
- })
- const probableUser = useReadonlyStream(probableUser$, null)
- const currentUser = useReadonlyStream(currentUser$, null)
- const loadingCurrentUser = computed(() => {
- if (!probableUser.value) return false
- else if (!currentUser.value) return true
- else return false
- })
- return {
- E,
- inviteDetails,
- loadingCurrentUser,
- currentUser,
- }
- },
- data() {
- return {
- invalidLink: false,
- showLogin: false,
- loading: false,
- revokedLink: false,
- inviteID: "",
- joinTeamSuccess: false,
- }
- },
- beforeMount() {
- initializeFirebase()
- },
- mounted() {
- if (typeof this.$route.query.id === "string") {
- this.inviteID = this.$route.query.id
- }
- this.invalidLink = !this.inviteID
- // TODO: check revokeTeamInvitation
- // TODO: check login user already a member
- },
- methods: {
- joinTeam() {
- this.loading = true
- pipe(
- acceptTeamInvitation(this.inviteID),
- TE.matchW(
- () => {
- this.loading = false
- this.$toast.error(`${this.$t("error.something_went_wrong")}`)
- },
- () => {
- this.joinTeamSuccess = true
- this.loading = false
- }
- )
- )()
- },
- getErrorMessage(error: GQLError<GetInviteDetailsError>) {
- if (error.type === "network_error") {
- return this.$t("error.network_error")
- } else {
- switch (error.error) {
- case "team_invite/not_valid_viewer":
- return this.$t("team.not_valid_viewer")
- case "team_invite/not_found":
- return this.$t("team.not_found")
- case "team_invite/no_invite_found":
- return this.$t("team.no_invite_found")
- case "team_invite/already_member":
- return this.$t("team.already_member")
- case "team_invite/email_do_not_match":
- return this.$t("team.email_do_not_match")
- default:
- return this.$t("error.something_went_wrong")
- }
- }
- },
- },
- })
- </script>
|