mobilizon.chapril.org-mobil.../js/src/components/Participation/ParticipationWithoutAccount...

278 lines
8.3 KiB
Vue

<template>
<section class="container mx-auto">
<div class="" v-if="event">
<form @submit.prevent="joinEvent" v-if="!formSent">
<p>
{{
$t(
"This Mobilizon instance and this event organizer allows anonymous participations, but requires validation through email confirmation."
)
}}
</p>
<o-notification variant="info">
{{
$t(
"Your email will only be used to confirm that you're a real person and send you eventual updates for this event. It will NOT be transmitted to other instances or to the event organizer."
)
}}
</o-notification>
<o-notification variant="danger" v-if="error">{{
error
}}</o-notification>
<o-field
:label="$t('Email address')"
labelFor="anonymousParticipationEmail"
>
<o-input
type="email"
id="anonymousParticipationEmail"
v-model="anonymousParticipation.email"
:placeholder="$t('Your email')"
required
/>
</o-field>
<p v-if="event.joinOptions === EventJoinOptions.RESTRICTED">
{{
$t(
"The event organizer manually approves participations. Since you've chosen to participate without an account, please explain why you want to participate to this event."
)
}}
</p>
<p v-else>
{{
$t(
"If you want, you may send a message to the event organizer here."
)
}}
</p>
<o-field
:label="$t('Message')"
labelFor="anonymousParticipationMessage"
>
<o-input
id="anonymousParticipationMessage"
type="textarea"
v-model="anonymousParticipation.message"
minlength="10"
:required="event.joinOptions === EventJoinOptions.RESTRICTED"
/>
</o-field>
<o-field>
<o-checkbox v-model="anonymousParticipation.saveParticipation">
<b>{{ $t("Remember my participation in this browser") }}</b>
<p>
{{
$t(
"Will allow to display and manage your participation status on the event page when using this device. Uncheck if you're using a public device."
)
}}
</p>
</o-checkbox>
</o-field>
<div class="flex gap-2 my-2">
<o-button
:disabled="sendingForm"
variant="primary"
native-type="submit"
>{{ $t("Send email") }}</o-button
>
<o-button
native-type="button"
variant="text"
@click="$router.go(-1)"
>{{ $t("Back to previous page") }}</o-button
>
</div>
</form>
<div v-else>
<h1 class="title">
{{ $t("Request for participation confirmation sent") }}
</h1>
<p class="prose dark:prose-invert">
<span>{{ $t("Check your inbox (and your junk mail folder).") }}</span>
<span
class="details"
v-if="event.joinOptions === EventJoinOptions.RESTRICTED"
>
{{
$t(
"Your participation will be validated once you click the confirmation link into the email, and after the organizer manually validates your participation."
)
}} </span
><span class="details" v-else>{{
$t(
"Your participation will be validated once you click the confirmation link into the email."
)
}}</span>
</p>
<o-notification variant="warning" v-if="error">{{
error
}}</o-notification>
<p class="prose dark:prose-invert">
<i18n-t
keypath="You may now close this window, or {return_to_event}."
>
<template #return_to_event>
<router-link
:to="{ name: RouteName.EVENT, params: { uuid: event.uuid } }"
>{{ $t("return to the event's page") }}</router-link
>
</template>
</i18n-t>
</p>
</div>
</div>
<o-notification variant="danger" v-else-if="!loading"
>{{
$t(
"Unable to load event for participation. The error details are provided below:"
)
}}
<details>
<pre>{{ error }}</pre>
</details>
</o-notification>
</section>
</template>
<script lang="ts" setup>
import { IEvent } from "@/types/event.model";
import { FETCH_EVENT_BASIC, JOIN_EVENT } from "@/graphql/event";
import { addLocalUnconfirmedAnonymousParticipation } from "@/services/AnonymousParticipationStorage";
import { EventJoinOptions, ParticipantRole } from "@/types/enums";
import RouteName from "@/router/name";
import { IParticipant } from "../../types/participant.model";
import { ApolloCache, FetchResult } from "@apollo/client/core";
import { useFetchEventBasic } from "@/composition/apollo/event";
import { useAnonymousActorId } from "@/composition/apollo/config";
import { computed, reactive, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useHead } from "@vueuse/head";
import { useMutation } from "@vue/apollo-composable";
const error = ref<boolean | string>(false);
const { anonymousActorId } = useAnonymousActorId();
const props = defineProps<{
uuid: string;
}>();
const { event, loading } = useFetchEventBasic(props.uuid);
const { t, locale } = useI18n({ useScope: "global" });
useHead({
title: computed(() => t("Participation without account")),
meta: [{ name: "robots", content: "noindex" }],
});
const anonymousParticipation = reactive<{
email: string;
message: string;
saveParticipation: boolean;
}>({
email: "",
message: "",
saveParticipation: true,
});
const formSent = ref(false);
const sendingForm = ref(false);
const {
mutate: joinEventMutation,
onDone: joinEventDone,
onError: joinEventError,
} = useMutation<{
joinEvent: IParticipant;
}>(JOIN_EVENT, () => ({
update: (
store: ApolloCache<{ joinEvent: IParticipant }>,
{ data: updateData }: FetchResult
) => {
if (updateData == null) {
console.error(
"Cannot update event participant cache, because of data null value."
);
return;
}
const cachedData = store.readQuery<{ event: IEvent }>({
query: FETCH_EVENT_BASIC,
variables: { uuid: event.value?.uuid },
});
if (cachedData == null) {
console.error(
"Cannot update event participant cache, because of cached null value."
);
return;
}
const participantStats = { ...cachedData.event.participantStats };
if (updateData.joinEvent.role === ParticipantRole.NOT_CONFIRMED) {
participantStats.notConfirmed += 1;
} else {
participantStats.going += 1;
participantStats.participant += 1;
}
store.writeQuery({
query: FETCH_EVENT_BASIC,
variables: { uuid: event.value?.uuid },
data: {
event: {
...cachedData.event,
participantStats,
},
},
});
},
}));
joinEventDone(async ({ data }) => {
sendingForm.value = false;
formSent.value = true;
if (
data?.joinEvent.metadata.cancellationToken &&
anonymousParticipation.saveParticipation &&
event.value
) {
try {
await addLocalUnconfirmedAnonymousParticipation(
event.value,
data.joinEvent.metadata.cancellationToken
);
} catch (e: any) {
if (
["TextEncoder is not defined", "crypto.subtle is undefined"].includes(
e.message
)
) {
error.value = t("Unable to save your participation in this browser.");
}
}
}
});
joinEventError((e) => {
if (e.graphQLErrors && e.graphQLErrors.length > 0) {
error.value = e.graphQLErrors[0].message;
} else if (e.networkError) {
error.value = e.networkError.message;
}
});
const joinEvent = async (): Promise<void> => {
error.value = false;
sendingForm.value = true;
joinEventMutation({
eventId: event.value?.id,
actorId: anonymousActorId.value,
email: anonymousParticipation.email,
message: anonymousParticipation.message,
locale: locale,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
};
</script>