feat: introduce updated mutation system and team.vue rewrite

This commit is contained in:
Andrew Bastin
2021-10-01 15:13:48 +05:30
committed by liyasthomas
parent 7bb32ecf7e
commit fb4aab875d
3 changed files with 152 additions and 47 deletions

View File

@@ -14,7 +14,7 @@
:key="`member-${index}`" :key="`member-${index}`"
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:title="member.user.displayName" :title="member.user.displayName"
:src="member.user.photoURL" :src="member.user.photoURL || undefined"
:alt="member.user.displayName" :alt="member.user.displayName"
class="rounded-full h-5 ring-primary ring-2 w-5 inline-block" class="rounded-full h-5 ring-primary ring-2 w-5 inline-block"
/> />
@@ -33,7 +33,7 @@
<SmartItem <SmartItem
v-if="team.myRole === 'OWNER'" v-if="team.myRole === 'OWNER'"
svg="edit" svg="edit"
:label="$t('action.edit')" :label="$t('action.edit').toString()"
@click.native=" @click.native="
() => { () => {
$emit('edit-team') $emit('edit-team')
@@ -45,7 +45,7 @@
v-if="team.myRole === 'OWNER'" v-if="team.myRole === 'OWNER'"
svg="trash-2" svg="trash-2"
color="red" color="red"
:label="$t('action.delete')" :label="$t('action.delete').toString()"
@click.native=" @click.native="
() => { () => {
deleteTeam() deleteTeam()
@@ -56,7 +56,7 @@
<SmartItem <SmartItem
v-if="!(team.myRole === 'OWNER' && team.ownersCount == 1)" v-if="!(team.myRole === 'OWNER' && team.ownersCount == 1)"
svg="trash" svg="trash"
:label="$t('team.exit')" :label="$t('team.exit').toString()"
@click.native=" @click.native="
() => { () => {
exitTeam() exitTeam()
@@ -69,49 +69,79 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { defineComponent } from "@nuxtjs/composition-api" import { useContext } from "@nuxtjs/composition-api"
import * as teamUtils from "~/helpers/teams/utils" import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither"
import {
deleteTeam as backendDeleteTeam,
leaveTeam,
} from "~/helpers/backend/mutations/Team"
import { TeamMemberRole } from "~/helpers/backend/types/TeamMemberRole"
export default defineComponent({ const props = defineProps<{
props: { team: {
team: { type: Object, default: () => {} }, name: string
teamID: { type: String, default: null }, myRole: TeamMemberRole
}, ownersCount: number
methods: { members: Array<{
deleteTeam() { user: {
if (!confirm(this.$t("confirm.remove_team"))) return displayName: string
// Call to the graphql mutation photoURL: string | null
teamUtils }
.deleteTeam(this.$apollo, this.teamID) }>
.then(() => { }
this.$toast.success(this.$t("team.deleted"), { teamID: string
icon: "done", }>()
})
const {
app: { i18n },
$toast,
} = useContext()
const $t = i18n.t.bind(i18n)
const deleteTeam = () => {
if (!confirm($t("confirm.remove_team").toString())) return
pipe(
backendDeleteTeam(props.teamID),
TE.match(
(err) => {
// TODO: Better errors ? We know the possible errors now
$toast.error($t("error.something_went_wrong").toString(), {
icon: "error_outline",
}) })
.catch((e) => { console.error(err)
this.$toast.error(this.$t("error.something_went_wrong"), { },
icon: "error_outline", () => {
}) $toast.success($t("team.deleted").toString(), {
console.error(e) icon: "done",
}) })
}, }
exitTeam() { )
if (!confirm("Are you sure you want to exit this team?")) return )() // Tasks (and TEs) are lazy, so call the function returned
teamUtils }
.exitTeam(this.$apollo, this.teamID)
.then(() => { const exitTeam = () => {
this.$toast.success(this.$t("team.left"), { if (!confirm("Are you sure you want to exit this team?")) return
icon: "done",
}) pipe(
leaveTeam(props.teamID),
TE.match(
(err) => {
// TODO: Better errors ?
$toast.error($t("error.something_went_wrong").toString(), {
icon: "error_outline",
}) })
.catch((e) => { console.error(err)
this.$toast.error(this.$t("error.something_went_wrong"), { },
icon: "error_outline", () => {
}) $toast.success($t("team.left").toString(), {
console.error(e) icon: "done",
}) })
}, }
}, )
}) )() // Tasks (and TEs) are lazy, so call the function returned
}
</script> </script>

View File

@@ -15,7 +15,8 @@ import {
} from "@urql/core" } from "@urql/core"
import { devtoolsExchange } from "@urql/devtools" import { devtoolsExchange } from "@urql/devtools"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { pipe } from "fp-ts/function" import * as TE from "fp-ts/TaskEither"
import { pipe, constVoid } from "fp-ts/function"
import { subscribe } from "wonka" import { subscribe } from "wonka"
import clone from "lodash/clone" import clone from "lodash/clone"
import { getAuthIDToken } from "~/helpers/fb/auth" import { getAuthIDToken } from "~/helpers/fb/auth"
@@ -49,7 +50,7 @@ export type GQLError<T extends string> =
} }
| { | {
type: "gql_error" type: "gql_error"
err: T error: T
} }
const DEFAULT_QUERY_OPTIONS = { const DEFAULT_QUERY_OPTIONS = {
@@ -122,7 +123,7 @@ export function useGQLQuery<
(gqlErr) => (gqlErr) =>
<GQLError<QueryFailType>>{ <GQLError<QueryFailType>>{
type: "gql_error", type: "gql_error",
err: gqlErr as QueryFailType, error: gqlErr as QueryFailType,
}, },
// The right case (it was a GraphQL Error) // The right case (it was a GraphQL Error)
(networkErr) => (networkErr) =>
@@ -163,3 +164,44 @@ export function useGQLQuery<
} }
| { loading: true } | { loading: true }
} }
export const runMutation = <
MutationReturnType = any,
MutationFailType extends string = "",
MutationVariables extends {} = {}
>(
mutation: string | DocumentNode | TypedDocumentNode<any, MutationVariables>,
variables?: MutationVariables
): TE.TaskEither<GQLError<MutationFailType>, NonNullable<MutationReturnType>> =>
pipe(
TE.tryCatch(
() => client.mutation<MutationReturnType>(mutation, variables).toPromise(),
() => constVoid() as never // The mutation function can never fail, so this will never be called ;)
),
TE.chainEitherK((result) =>
pipe(
result.data as MutationReturnType, // If we have the result, then okay
E.fromNullable(
// Result is null
pipe(
result.error?.networkError, // Check for network error
E.fromNullable(result.error?.name), // If it is null, then it is a GQL error
E.match(
// The left case (network error was null)
(gqlErr) =>
<GQLError<MutationFailType>>{
type: "gql_error",
error: gqlErr as MutationFailType,
},
// The right case (it was a GraphQL Error)
(networkErr) =>
<GQLError<MutationFailType>>{
type: "network_error",
error: networkErr,
}
)
)
)
)
)
)

View File

@@ -0,0 +1,33 @@
import gql from "graphql-tag"
import { runMutation } from "../GQLClient"
type DeleteTeamErrors =
| "team/not_required_role"
| "team/invalid_id"
| "team/member_not_found"
type ExitTeamErrors = "team/invalid_id" | "team/member_not_found"
export const deleteTeam = (teamID: string) =>
runMutation<void, DeleteTeamErrors>(
gql`
mutation DeleteTeam($teamID: String!) {
deleteTeam(teamID: $teamID)
}
`,
{
teamID,
}
)
export const leaveTeam = (teamID: string) =>
runMutation<void, ExitTeamErrors>(
gql`
mutation ExitTeam($teamID: String!) {
leaveTeam(teamID: $teamID)
}
`,
{
teamID,
}
)