Fix lint issues
And disable eslint when building in prod mode Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -51,7 +51,9 @@
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.GLOSSARY }">{{ $t("Glossary") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.GLOSSARY }">{{
|
||||
$t("Glossary")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
@@ -79,9 +81,13 @@
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="title">{{ $t("Find another instance") }}</h2>
|
||||
<b-button type="is-secondary" size="is-large" tag="a" href="https://mobilizon.org">{{
|
||||
$t("Pick an instance")
|
||||
}}</b-button>
|
||||
<b-button
|
||||
type="is-secondary"
|
||||
size="is-large"
|
||||
tag="a"
|
||||
href="https://mobilizon.org"
|
||||
>{{ $t("Pick an instance") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,9 +26,12 @@
|
||||
<div class="column contact">
|
||||
<h4>{{ $t("Contact") }}</h4>
|
||||
<p>
|
||||
<a :title="config.contact" v-if="generateConfigLink()" :href="generateConfigLink().uri">{{
|
||||
generateConfigLink().text
|
||||
}}</a>
|
||||
<a
|
||||
:title="config.contact"
|
||||
v-if="generateConfigLink()"
|
||||
:href="generateConfigLink().uri"
|
||||
>{{ generateConfigLink().text }}</a
|
||||
>
|
||||
<span v-else-if="config.contact">{{ config.contact }}</span>
|
||||
<span v-else>{{ $t("contact uninformed") }}</span>
|
||||
</p>
|
||||
@@ -44,7 +47,9 @@
|
||||
<table class="table is-fullwidth">
|
||||
<tr>
|
||||
<td>{{ $t("Instance languages") }}</td>
|
||||
<td :title="this.config.languages.join(', ')">{{ formattedLanguageList }}</td>
|
||||
<td :title="this.config.languages.join(', ')">
|
||||
{{ formattedLanguageList }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t("Mobilizon version") }}</td>
|
||||
@@ -67,7 +72,9 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t("Anonymous participations") }}</td>
|
||||
<td v-if="config.anonymous.participation.allowed">{{ $t("If allowed by organizer") }}</td>
|
||||
<td v-if="config.anonymous.participation.allowed">
|
||||
{{ $t("If allowed by organizer") }}
|
||||
</td>
|
||||
<td v-else>{{ $t("Disabled") }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -132,12 +139,17 @@ export default class AboutInstance extends Vue {
|
||||
generateConfigLink(): { uri: string; text: string } | null {
|
||||
if (!this.config.contact) return null;
|
||||
if (this.isContactEmail) {
|
||||
return { uri: `mailto:${this.config.contact}`, text: this.config.contact };
|
||||
return {
|
||||
uri: `mailto:${this.config.contact}`,
|
||||
text: this.config.contact,
|
||||
};
|
||||
}
|
||||
if (this.isContactURL) {
|
||||
return {
|
||||
uri: this.config.contact,
|
||||
text: AboutInstance.urlToHostname(this.config.contact) || (this.$t("Contact") as string),
|
||||
text:
|
||||
AboutInstance.urlToHostname(this.config.contact) ||
|
||||
(this.$t("Contact") as string),
|
||||
};
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -19,7 +19,12 @@
|
||||
:src="identity.avatar.url"
|
||||
alt=""
|
||||
/>
|
||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||
<b-icon
|
||||
class="media-left"
|
||||
v-else
|
||||
size="is-large"
|
||||
icon="account-circle"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<h3>@{{ identity.preferredUsername }}</h3>
|
||||
<small>{{ identity.name }}</small>
|
||||
|
||||
@@ -3,13 +3,20 @@
|
||||
<div
|
||||
v-if="inline"
|
||||
class="inline box"
|
||||
:class="{ 'has-background-grey-lighter': masked, 'no-other-identity': !hasOtherIdentities }"
|
||||
:class="{
|
||||
'has-background-grey-lighter': masked,
|
||||
'no-other-identity': !hasOtherIdentities,
|
||||
}"
|
||||
@click="activateModal"
|
||||
>
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48" v-if="currentIdentity.avatar">
|
||||
<img class="image is-rounded" :src="currentIdentity.avatar.url" alt="" />
|
||||
<img
|
||||
class="image is-rounded"
|
||||
:src="currentIdentity.avatar.url"
|
||||
alt=""
|
||||
/>
|
||||
</figure>
|
||||
<b-icon v-else size="is-large" icon="account-circle" />
|
||||
</div>
|
||||
@@ -23,7 +30,11 @@
|
||||
<div class="media-content" v-else>
|
||||
{{ `@${currentIdentity.preferredUsername}` }}
|
||||
</div>
|
||||
<b-button type="is-text" v-if="identities.length > 1" @click="activateModal">
|
||||
<b-button
|
||||
type="is-text"
|
||||
v-if="identities.length > 1"
|
||||
@click="activateModal"
|
||||
>
|
||||
{{ $t("Change") }}
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,12 @@
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
|
||||
<b-dropdown hoverable has-link aria-role="list" v-if="person.feedTokens.length > 0">
|
||||
<b-dropdown
|
||||
hoverable
|
||||
has-link
|
||||
aria-role="list"
|
||||
v-if="person.feedTokens.length > 0"
|
||||
>
|
||||
<button class="button is-info" slot="trigger">
|
||||
{{ $t("Private feeds") }}
|
||||
<b-icon icon="menu-down"></b-icon>
|
||||
@@ -57,7 +62,11 @@
|
||||
</a>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
<a class="button" v-if="currentActor.id === person.id" @click="createToken">
|
||||
<a
|
||||
class="button"
|
||||
v-if="currentActor.id === person.id"
|
||||
@click="createToken"
|
||||
>
|
||||
{{ $t("Create token") }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
{{ $t("Congratulations, your account is now created!") }}
|
||||
</h1>
|
||||
<h1 class="title" v-else>
|
||||
{{ $t("Register an account on {instanceName}!", { instanceName: config.name }) }}
|
||||
{{
|
||||
$t("Register an account on {instanceName}!", {
|
||||
instanceName: config.name,
|
||||
})
|
||||
}}
|
||||
</h1>
|
||||
<p class="content" v-if="userAlreadyActivated">
|
||||
{{ $t("Now, create your first profile:") }}
|
||||
@@ -28,7 +32,9 @@
|
||||
>
|
||||
<b-field
|
||||
:message="
|
||||
$t('Only alphanumeric lowercased characters and underscores are supported.')
|
||||
$t(
|
||||
'Only alphanumeric lowercased characters and underscores are supported.'
|
||||
)
|
||||
"
|
||||
>
|
||||
<b-input
|
||||
@@ -38,7 +44,9 @@
|
||||
v-model="identity.preferredUsername"
|
||||
:validation-message="
|
||||
identity.preferredUsername
|
||||
? $t('Only alphanumeric lowercased characters and underscores are supported.')
|
||||
? $t(
|
||||
'Only alphanumeric lowercased characters and underscores are supported.'
|
||||
)
|
||||
: null
|
||||
"
|
||||
/>
|
||||
@@ -48,11 +56,20 @@
|
||||
</b-field>
|
||||
</b-field>
|
||||
<p class="description">
|
||||
{{ $t("This identifier is unique to your profile. It allows others to find you.") }}
|
||||
{{
|
||||
$t(
|
||||
"This identifier is unique to your profile. It allows others to find you."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
|
||||
<b-field :label="$t('Short bio')">
|
||||
<b-input type="textarea" maxlength="100" rows="2" v-model="identity.summary" />
|
||||
<b-input
|
||||
type="textarea"
|
||||
maxlength="100"
|
||||
rows="2"
|
||||
v-model="identity.summary"
|
||||
/>
|
||||
</b-field>
|
||||
|
||||
<p class="content">
|
||||
@@ -165,7 +182,10 @@ export default class Register extends mixins(identityEditionMixin) {
|
||||
window.localStorage.setItem("new-registered-user", "yes");
|
||||
|
||||
if (this.userAlreadyActivated) {
|
||||
await changeIdentity(this.$apollo.provider.defaultClient, data.registerPerson);
|
||||
await changeIdentity(
|
||||
this.$apollo.provider.defaultClient,
|
||||
data.registerPerson
|
||||
);
|
||||
|
||||
await this.$router.push({ name: RouteName.HOME });
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.IDENTITIES }">{{ $t("Profiles") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.IDENTITIES }">{{
|
||||
$t("Profiles")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active" v-if="isUpdate && identity">
|
||||
<router-link
|
||||
@@ -27,7 +29,11 @@
|
||||
<span v-else>{{ $t("I create an identity") }}</span>
|
||||
</h1>
|
||||
|
||||
<picture-upload v-model="avatarFile" :defaultImage="identity.avatar" class="picture-upload" />
|
||||
<picture-upload
|
||||
v-model="avatarFile"
|
||||
:defaultImage="identity.avatar"
|
||||
class="picture-upload"
|
||||
/>
|
||||
|
||||
<b-field horizontal :label="$t('Display name')">
|
||||
<b-input
|
||||
@@ -62,7 +68,11 @@
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal :label="$t('Description')">
|
||||
<b-input type="textarea" aria-required="false" v-model="identity.summary" />
|
||||
<b-input
|
||||
type="textarea"
|
||||
aria-required="false"
|
||||
v-model="identity.summary"
|
||||
/>
|
||||
</b-field>
|
||||
|
||||
<b-notification
|
||||
@@ -84,7 +94,9 @@
|
||||
</b-field>
|
||||
|
||||
<div class="delete-identity" v-if="isUpdate">
|
||||
<span @click="openDeleteIdentityConfirmation()">{{ $t("Delete this identity") }}</span>
|
||||
<span @click="openDeleteIdentityConfirmation()">{{
|
||||
$t("Delete this identity")
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -229,7 +241,9 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
});
|
||||
|
||||
if (data) {
|
||||
data.identities = data.identities.filter((i) => i.id !== this.identity.id);
|
||||
data.identities = data.identities.filter(
|
||||
(i) => i.id !== this.identity.id
|
||||
);
|
||||
|
||||
store.writeQuery({ query: IDENTITIES, data });
|
||||
}
|
||||
@@ -242,7 +256,8 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
}) as string
|
||||
);
|
||||
/**
|
||||
* If we just deleted the current identity, we need to change it to the next one
|
||||
* If we just deleted the current identity,
|
||||
* we need to change it to the next one
|
||||
*/
|
||||
const data = this.$apollo.provider.defaultClient.readQuery<{
|
||||
identities: IPerson[];
|
||||
@@ -270,7 +285,9 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
});
|
||||
|
||||
if (data) {
|
||||
const index = data.identities.findIndex((i) => i.id === this.identity.id);
|
||||
const index = data.identities.findIndex(
|
||||
(i) => i.id === this.identity.id
|
||||
);
|
||||
|
||||
this.$set(data.identities, index, updatePerson);
|
||||
this.maybeUpdateCurrentActorCache(updatePerson);
|
||||
@@ -351,9 +368,12 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
"Otherwise this identity will just be removed from the group administrators."
|
||||
)}
|
||||
<br /><br />
|
||||
${this.$t('To confirm, type your identity username "{preferredUsername}"', {
|
||||
preferredUsername: this.identity.preferredUsername,
|
||||
})}`,
|
||||
${this.$t(
|
||||
'To confirm, type your identity username "{preferredUsername}"',
|
||||
{
|
||||
preferredUsername: this.identity.preferredUsername,
|
||||
}
|
||||
)}`,
|
||||
confirmText: this.$t("Delete {preferredUsername}", {
|
||||
preferredUsername: this.identity.preferredUsername,
|
||||
}) as string,
|
||||
@@ -406,7 +426,9 @@ export default class EditIdentity extends mixins(identityEditionMixin) {
|
||||
|
||||
private async maybeUpdateCurrentActorCache(identity: IPerson) {
|
||||
if (this.currentActor) {
|
||||
if (this.currentActor.preferredUsername === this.identity.preferredUsername) {
|
||||
if (
|
||||
this.currentActor.preferredUsername === this.identity.preferredUsername
|
||||
) {
|
||||
await changeIdentity(this.$apollo.provider.defaultClient, identity);
|
||||
}
|
||||
this.currentActor = identity;
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
@@ -26,9 +28,17 @@
|
||||
</nav>
|
||||
<div class="actor-card">
|
||||
<router-link
|
||||
:to="{ name: RouteName.GROUP, params: { preferredUsername: usernameWithDomain(group) } }"
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(group) },
|
||||
}"
|
||||
>
|
||||
<actor-card :actor="group" :full="true" :popover="false" :limit="false" />
|
||||
<actor-card
|
||||
:actor="group"
|
||||
:full="true"
|
||||
:popover="false"
|
||||
:limit="false"
|
||||
/>
|
||||
</router-link>
|
||||
</div>
|
||||
<table v-if="metadata.length > 0" class="table is-fullwidth">
|
||||
@@ -45,15 +55,25 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<b-button @click="confirmSuspendProfile" v-if="!group.suspended" type="is-primary">{{
|
||||
$t("Suspend")
|
||||
}}</b-button>
|
||||
<b-button @click="unsuspendProfile" v-if="group.suspended" type="is-primary">{{
|
||||
$t("Unsuspend")
|
||||
}}</b-button>
|
||||
<b-button @click="refreshProfile" v-if="group.domain" type="is-primary" outlined>{{
|
||||
$t("Refresh profile")
|
||||
}}</b-button>
|
||||
<b-button
|
||||
@click="confirmSuspendProfile"
|
||||
v-if="!group.suspended"
|
||||
type="is-primary"
|
||||
>{{ $t("Suspend") }}</b-button
|
||||
>
|
||||
<b-button
|
||||
@click="unsuspendProfile"
|
||||
v-if="group.suspended"
|
||||
type="is-primary"
|
||||
>{{ $t("Unsuspend") }}</b-button
|
||||
>
|
||||
<b-button
|
||||
@click="refreshProfile"
|
||||
v-if="group.domain"
|
||||
type="is-primary"
|
||||
outlined
|
||||
>{{ $t("Refresh profile") }}</b-button
|
||||
>
|
||||
</div>
|
||||
<section>
|
||||
<h2 class="subtitle">
|
||||
@@ -72,15 +92,33 @@
|
||||
:per-page="EVENTS_PER_PAGE"
|
||||
@page-change="onMembersPageChange"
|
||||
>
|
||||
<b-table-column field="actor.preferredUsername" :label="$t('Member')" v-slot="props">
|
||||
<b-table-column
|
||||
field="actor.preferredUsername"
|
||||
:label="$t('Member')"
|
||||
v-slot="props"
|
||||
>
|
||||
<article class="media">
|
||||
<figure class="media-left image is-48x48" v-if="props.row.actor.avatar">
|
||||
<img class="is-rounded" :src="props.row.actor.avatar.url" alt="" />
|
||||
<figure
|
||||
class="media-left image is-48x48"
|
||||
v-if="props.row.actor.avatar"
|
||||
>
|
||||
<img
|
||||
class="is-rounded"
|
||||
:src="props.row.actor.avatar.url"
|
||||
alt=""
|
||||
/>
|
||||
</figure>
|
||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||
<b-icon
|
||||
class="media-left"
|
||||
v-else
|
||||
size="is-large"
|
||||
icon="account-circle"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<span v-if="props.row.actor.name">{{ props.row.actor.name }}</span
|
||||
<span v-if="props.row.actor.name">{{
|
||||
props.row.actor.name
|
||||
}}</span
|
||||
><br />
|
||||
<span class="is-size-7 has-text-grey"
|
||||
>@{{ usernameWithDomain(props.row.actor) }}</span
|
||||
@@ -90,22 +128,37 @@
|
||||
</article>
|
||||
</b-table-column>
|
||||
<b-table-column field="role" :label="$t('Role')" v-slot="props">
|
||||
<b-tag type="is-primary" v-if="props.row.role === MemberRole.ADMINISTRATOR">
|
||||
<b-tag
|
||||
type="is-primary"
|
||||
v-if="props.row.role === MemberRole.ADMINISTRATOR"
|
||||
>
|
||||
{{ $t("Administrator") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-primary" v-else-if="props.row.role === MemberRole.MODERATOR">
|
||||
<b-tag
|
||||
type="is-primary"
|
||||
v-else-if="props.row.role === MemberRole.MODERATOR"
|
||||
>
|
||||
{{ $t("Moderator") }}
|
||||
</b-tag>
|
||||
<b-tag v-else-if="props.row.role === MemberRole.MEMBER">
|
||||
{{ $t("Member") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-warning" v-else-if="props.row.role === MemberRole.NOT_APPROVED">
|
||||
<b-tag
|
||||
type="is-warning"
|
||||
v-else-if="props.row.role === MemberRole.NOT_APPROVED"
|
||||
>
|
||||
{{ $t("Not approved") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-danger" v-else-if="props.row.role === MemberRole.REJECTED">
|
||||
<b-tag
|
||||
type="is-danger"
|
||||
v-else-if="props.row.role === MemberRole.REJECTED"
|
||||
>
|
||||
{{ $t("Rejected") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-danger" v-else-if="props.row.role === MemberRole.INVITED">
|
||||
<b-tag
|
||||
type="is-danger"
|
||||
v-else-if="props.row.role === MemberRole.INVITED"
|
||||
>
|
||||
{{ $t("Invited") }}
|
||||
</b-tag>
|
||||
</b-table-column>
|
||||
@@ -143,11 +196,17 @@
|
||||
@page-change="onOrganizedEventsPageChange"
|
||||
>
|
||||
<b-table-column field="title" :label="$t('Title')" v-slot="props">
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }"
|
||||
>
|
||||
{{ props.row.title }}
|
||||
</router-link>
|
||||
</b-table-column>
|
||||
<b-table-column field="beginsOn" :label="$t('Begins on')" v-slot="props">
|
||||
<b-table-column
|
||||
field="beginsOn"
|
||||
:label="$t('Begins on')"
|
||||
v-slot="props"
|
||||
>
|
||||
{{ props.row.beginsOn | formatDateTimeString }}
|
||||
</b-table-column>
|
||||
<template slot="empty">
|
||||
@@ -177,11 +236,17 @@
|
||||
@page-change="onPostsPageChange"
|
||||
>
|
||||
<b-table-column field="title" :label="$t('Title')" v-slot="props">
|
||||
<router-link :to="{ name: RouteName.POST, params: { slug: props.row.slug } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.POST, params: { slug: props.row.slug } }"
|
||||
>
|
||||
{{ props.row.title }}
|
||||
</router-link>
|
||||
</b-table-column>
|
||||
<b-table-column field="publishAt" :label="$t('Publication date')" v-slot="props">
|
||||
<b-table-column
|
||||
field="publishAt"
|
||||
:label="$t('Publication date')"
|
||||
v-slot="props"
|
||||
>
|
||||
{{ props.row.publishAt | formatDateTimeString }}
|
||||
</b-table-column>
|
||||
<template slot="empty">
|
||||
@@ -254,11 +319,15 @@ export default class AdminGroupProfile extends Vue {
|
||||
const res: Record<string, string>[] = [
|
||||
{
|
||||
key: this.$t("Status") as string,
|
||||
value: (this.group.suspended ? this.$t("Suspended") : this.$t("Active")) as string,
|
||||
value: (this.group.suspended
|
||||
? this.$t("Suspended")
|
||||
: this.$t("Active")) as string,
|
||||
},
|
||||
{
|
||||
key: this.$t("Domain") as string,
|
||||
value: (this.group.domain ? this.group.domain : this.$t("Local")) as string,
|
||||
value: (this.group.domain
|
||||
? this.group.domain
|
||||
: this.$t("Local")) as string,
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Uploaded media size") as string,
|
||||
@@ -360,14 +429,18 @@ export default class AdminGroupProfile extends Vue {
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
if (!fetchMoreResult) return previousResult;
|
||||
const newOrganizedEvents = fetchMoreResult.group.organizedEvents.elements;
|
||||
const newOrganizedEvents =
|
||||
fetchMoreResult.group.organizedEvents.elements;
|
||||
return {
|
||||
group: {
|
||||
...previousResult.group,
|
||||
organizedEvents: {
|
||||
__typename: previousResult.group.organizedEvents.__typename,
|
||||
total: previousResult.group.organizedEvents.total,
|
||||
elements: [...previousResult.group.organizedEvents.elements, ...newOrganizedEvents],
|
||||
elements: [
|
||||
...previousResult.group.organizedEvents.elements,
|
||||
...newOrganizedEvents,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -392,7 +465,10 @@ export default class AdminGroupProfile extends Vue {
|
||||
members: {
|
||||
__typename: previousResult.group.members.__typename,
|
||||
total: previousResult.group.members.total,
|
||||
elements: [...previousResult.group.members.elements, ...newMembers],
|
||||
elements: [
|
||||
...previousResult.group.members.elements,
|
||||
...newMembers,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
@@ -25,7 +27,12 @@
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="actor-card">
|
||||
<actor-card :actor="person" :full="true" :popover="false" :limit="false" />
|
||||
<actor-card
|
||||
:actor="person"
|
||||
:full="true"
|
||||
:popover="false"
|
||||
:limit="false"
|
||||
/>
|
||||
</div>
|
||||
<table v-if="metadata.length > 0" class="table is-fullwidth">
|
||||
<tbody>
|
||||
@@ -71,11 +78,17 @@
|
||||
:per-page="EVENTS_PER_PAGE"
|
||||
@page-change="onOrganizedEventsPageChange"
|
||||
>
|
||||
<b-table-column field="beginsOn" :label="$t('Begins on')" v-slot="props">
|
||||
<b-table-column
|
||||
field="beginsOn"
|
||||
:label="$t('Begins on')"
|
||||
v-slot="props"
|
||||
>
|
||||
{{ props.row.beginsOn | formatDateTimeString }}
|
||||
</b-table-column>
|
||||
<b-table-column field="title" :label="$t('Title')" v-slot="props">
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }"
|
||||
>
|
||||
{{ props.row.title }}
|
||||
</router-link>
|
||||
</b-table-column>
|
||||
@@ -97,7 +110,11 @@
|
||||
}}
|
||||
</h2>
|
||||
<b-table
|
||||
:data="person.participations.elements.map((participation) => participation.event)"
|
||||
:data="
|
||||
person.participations.elements.map(
|
||||
(participation) => participation.event
|
||||
)
|
||||
"
|
||||
:loading="$apollo.queries.person.loading"
|
||||
paginated
|
||||
backend-pagination
|
||||
@@ -105,11 +122,17 @@
|
||||
:per-page="EVENTS_PER_PAGE"
|
||||
@page-change="onParticipationsPageChange"
|
||||
>
|
||||
<b-table-column field="beginsOn" :label="$t('Begins on')" v-slot="props">
|
||||
<b-table-column
|
||||
field="beginsOn"
|
||||
:label="$t('Begins on')"
|
||||
v-slot="props"
|
||||
>
|
||||
{{ props.row.beginsOn | formatDateTimeString }}
|
||||
</b-table-column>
|
||||
<b-table-column field="title" :label="$t('Title')" v-slot="props">
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.EVENT, params: { uuid: props.row.uuid } }"
|
||||
>
|
||||
{{ props.row.title }}
|
||||
</router-link>
|
||||
</b-table-column>
|
||||
@@ -127,7 +150,11 @@
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||
import { formatBytes } from "@/utils/datetime";
|
||||
import { GET_PERSON, SUSPEND_PROFILE, UNSUSPEND_PROFILE } from "../../graphql/actor";
|
||||
import {
|
||||
GET_PERSON,
|
||||
SUSPEND_PROFILE,
|
||||
UNSUSPEND_PROFILE,
|
||||
} from "../../graphql/actor";
|
||||
import { IPerson } from "../../types/actor";
|
||||
import { usernameWithDomain } from "../../types/actor/actor.model";
|
||||
import RouteName from "../../router/name";
|
||||
@@ -190,7 +217,10 @@ export default class AdminProfile extends Vue {
|
||||
if (!this.person.domain && this.person.user) {
|
||||
res.push({
|
||||
key: this.$t("User") as string,
|
||||
link: { name: RouteName.ADMIN_USER_PROFILE, params: { id: this.person.user.id } },
|
||||
link: {
|
||||
name: RouteName.ADMIN_USER_PROFILE,
|
||||
params: { id: this.person.user.id },
|
||||
},
|
||||
value: this.person.user.email,
|
||||
});
|
||||
}
|
||||
@@ -263,14 +293,18 @@ export default class AdminProfile extends Vue {
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
if (!fetchMoreResult) return previousResult;
|
||||
const newOrganizedEvents = fetchMoreResult.person.organizedEvents.elements;
|
||||
const newOrganizedEvents =
|
||||
fetchMoreResult.person.organizedEvents.elements;
|
||||
return {
|
||||
person: {
|
||||
...previousResult.person,
|
||||
organizedEvents: {
|
||||
__typename: previousResult.person.organizedEvents.__typename,
|
||||
total: previousResult.person.organizedEvents.total,
|
||||
elements: [...previousResult.person.organizedEvents.elements, ...newOrganizedEvents],
|
||||
elements: [
|
||||
...previousResult.person.organizedEvents.elements,
|
||||
...newOrganizedEvents,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -288,14 +322,18 @@ export default class AdminProfile extends Vue {
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||
if (!fetchMoreResult) return previousResult;
|
||||
const newParticipations = fetchMoreResult.person.participations.elements;
|
||||
const newParticipations =
|
||||
fetchMoreResult.person.participations.elements;
|
||||
return {
|
||||
person: {
|
||||
...previousResult.person,
|
||||
participations: {
|
||||
__typename: previousResult.person.participations.__typename,
|
||||
total: previousResult.person.participations.total,
|
||||
elements: [...previousResult.person.participations.elements, ...newParticipations],
|
||||
elements: [
|
||||
...previousResult.person.participations.elements,
|
||||
...newParticipations,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
@@ -29,10 +31,15 @@
|
||||
<tr v-for="{ key, value, link, elements, type } in metadata" :key="key">
|
||||
<td>{{ key }}</td>
|
||||
<td v-if="elements && elements.length > 0">
|
||||
<ul v-for="{ value, link: elementLink, active } in elements" :key="value">
|
||||
<ul
|
||||
v-for="{ value, link: elementLink, active } in elements"
|
||||
:key="value"
|
||||
>
|
||||
<li>
|
||||
<router-link :to="elementLink">
|
||||
<span v-if="active">{{ $t("{profile} (by default)", { profile: value }) }}</span>
|
||||
<span v-if="active">{{
|
||||
$t("{profile} (by default)", { profile: value })
|
||||
}}</span>
|
||||
<span v-else>{{ value }}</span>
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -54,9 +61,12 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<b-button @click="deleteAccount" v-if="!user.disabled" type="is-primary">{{
|
||||
$t("Suspend")
|
||||
}}</b-button>
|
||||
<b-button
|
||||
@click="deleteAccount"
|
||||
v-if="!user.disabled"
|
||||
type="is-primary"
|
||||
>{{ $t("Suspend") }}</b-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -113,7 +123,9 @@ export default class AdminUserProfile extends Vue {
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Login status"),
|
||||
value: this.user.disabled ? this.$i18n.t("Disabled") : this.$t("Activated"),
|
||||
value: this.user.disabled
|
||||
? this.$i18n.t("Disabled")
|
||||
: this.$t("Activated"),
|
||||
},
|
||||
{
|
||||
key: this.$i18n.t("Profiles"),
|
||||
@@ -123,7 +135,9 @@ export default class AdminUserProfile extends Vue {
|
||||
value: actor.name
|
||||
? `${actor.name} (${actor.preferredUsername})`
|
||||
: actor.preferredUsername,
|
||||
active: this.user.defaultActor ? actor.id === this.user.defaultActor.id : false,
|
||||
active: this.user.defaultActor
|
||||
? actor.id === this.user.defaultActor.id
|
||||
: false,
|
||||
};
|
||||
}),
|
||||
},
|
||||
@@ -138,7 +152,9 @@ export default class AdminUserProfile extends Vue {
|
||||
key: this.$i18n.t("Last sign-in"),
|
||||
value:
|
||||
this.$options.filters && this.user.currentSignInAt
|
||||
? this.$options.filters.formatDateTimeString(this.user.currentSignInAt)
|
||||
? this.$options.filters.formatDateTimeString(
|
||||
this.user.currentSignInAt
|
||||
)
|
||||
: this.$t("Unknown"),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.ADMIN_DASHBOARD }">{{ $t("Dashboard") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN_DASHBOARD }">{{
|
||||
$t("Dashboard")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -24,7 +28,8 @@
|
||||
'Published events with <b>{comments}</b> comments and <b>{participations}</b> confirmed participations',
|
||||
{
|
||||
comments: dashboard.numberOfComments,
|
||||
participations: dashboard.numberOfConfirmedParticipationsToLocalEvents,
|
||||
participations:
|
||||
dashboard.numberOfConfirmedParticipationsToLocalEvents,
|
||||
}
|
||||
)
|
||||
"
|
||||
@@ -46,7 +51,9 @@
|
||||
</article>
|
||||
<article class="tile is-child box">
|
||||
<router-link :to="{ name: RouteName.RELAY_FOLLOWERS }">
|
||||
<p class="dashboard-number">{{ dashboard.numberOfFollowers }}</p>
|
||||
<p class="dashboard-number">
|
||||
{{ dashboard.numberOfFollowers }}
|
||||
</p>
|
||||
<p>{{ $t("Instances following you") }}</p>
|
||||
</router-link>
|
||||
</article>
|
||||
@@ -54,20 +61,27 @@
|
||||
<div class="tile is-parent is-vertical">
|
||||
<article class="tile is-child box">
|
||||
<router-link :to="{ name: RouteName.REPORTS }">
|
||||
<p class="dashboard-number">{{ dashboard.numberOfReports }}</p>
|
||||
<p class="dashboard-number">
|
||||
{{ dashboard.numberOfReports }}
|
||||
</p>
|
||||
<p>{{ $t("Opened reports") }}</p>
|
||||
</router-link>
|
||||
</article>
|
||||
<article class="tile is-child box">
|
||||
<router-link :to="{ name: RouteName.RELAY_FOLLOWINGS }">
|
||||
<p class="dashboard-number">{{ dashboard.numberOfFollowings }}</p>
|
||||
<p class="dashboard-number">
|
||||
{{ dashboard.numberOfFollowings }}
|
||||
</p>
|
||||
<p>{{ $t("Instances you follow") }}</p>
|
||||
</router-link>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile">
|
||||
<div class="tile is-parent is-vertical is-6" v-if="dashboard.lastPublicEventPublished">
|
||||
<div
|
||||
class="tile is-parent is-vertical is-6"
|
||||
v-if="dashboard.lastPublicEventPublished"
|
||||
>
|
||||
<article class="tile is-child box">
|
||||
<router-link
|
||||
:to="{
|
||||
@@ -76,19 +90,33 @@
|
||||
}"
|
||||
>
|
||||
<p>{{ $t("Last published event") }}</p>
|
||||
<p class="subtitle">{{ dashboard.lastPublicEventPublished.title }}</p>
|
||||
<figure class="image is-4by3" v-if="dashboard.lastPublicEventPublished.picture">
|
||||
<img :src="dashboard.lastPublicEventPublished.picture.url" />
|
||||
<p class="subtitle">
|
||||
{{ dashboard.lastPublicEventPublished.title }}
|
||||
</p>
|
||||
<figure
|
||||
class="image is-4by3"
|
||||
v-if="dashboard.lastPublicEventPublished.picture"
|
||||
>
|
||||
<img
|
||||
:src="dashboard.lastPublicEventPublished.picture.url"
|
||||
/>
|
||||
</figure>
|
||||
</router-link>
|
||||
</article>
|
||||
</div>
|
||||
<div class="tile is-parent is-vertical" v-if="dashboard.lastGroupCreated">
|
||||
<div
|
||||
class="tile is-parent is-vertical"
|
||||
v-if="dashboard.lastGroupCreated"
|
||||
>
|
||||
<article class="tile is-child box">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(dashboard.lastGroupCreated) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(
|
||||
dashboard.lastGroupCreated
|
||||
),
|
||||
},
|
||||
}"
|
||||
>
|
||||
<p>{{ $t("Last group created") }}</p>
|
||||
@@ -98,7 +126,10 @@
|
||||
dashboard.lastGroupCreated.preferredUsername
|
||||
}}
|
||||
</p>
|
||||
<figure class="image is-4by3" v-if="dashboard.lastGroupCreated.avatar">
|
||||
<figure
|
||||
class="image is-4by3"
|
||||
v-if="dashboard.lastGroupCreated.avatar"
|
||||
>
|
||||
<img :src="dashboard.lastGroupCreated.avatar.url" />
|
||||
</figure>
|
||||
</router-link>
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.RELAYS }">{{ $t("Federation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.RELAYS }">{{
|
||||
$t("Federation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWINGS">
|
||||
<router-link :to="{ name: RouteName.RELAY_FOLLOWINGS }">{{
|
||||
@@ -14,7 +18,9 @@
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active" v-if="$route.name == RouteName.RELAY_FOLLOWERS">
|
||||
<router-link :to="{ name: RouteName.RELAY_FOLLOWERS }">{{ $t("Followers") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.RELAY_FOLLOWERS }">{{
|
||||
$t("Followers")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.PROFILES }">{{ $t("Groups") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.PROFILES }">{{
|
||||
$t("Groups")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -24,7 +28,11 @@
|
||||
@page-change="onPageChange"
|
||||
@filters-change="onFiltersChange"
|
||||
>
|
||||
<b-table-column field="preferredUsername" :label="$t('Username')" searchable>
|
||||
<b-table-column
|
||||
field="preferredUsername"
|
||||
:label="$t('Username')"
|
||||
searchable
|
||||
>
|
||||
<template slot="searchable" slot-scope="props">
|
||||
<b-input
|
||||
v-model="props.filters.preferredUsername"
|
||||
@@ -36,7 +44,10 @@
|
||||
<template v-slot:default="props">
|
||||
<router-link
|
||||
class="profile"
|
||||
:to="{ name: RouteName.ADMIN_GROUP_PROFILE, params: { id: props.row.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_GROUP_PROFILE,
|
||||
params: { id: props.row.id },
|
||||
}"
|
||||
>
|
||||
<article class="media">
|
||||
<figure class="media-left" v-if="props.row.avatar">
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.PROFILES }">{{ $t("Profiles") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.PROFILES }">{{
|
||||
$t("Profiles")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -24,7 +28,11 @@
|
||||
@page-change="onPageChange"
|
||||
@filters-change="onFiltersChange"
|
||||
>
|
||||
<b-table-column field="preferredUsername" :label="$t('Username')" searchable>
|
||||
<b-table-column
|
||||
field="preferredUsername"
|
||||
:label="$t('Username')"
|
||||
searchable
|
||||
>
|
||||
<template slot="searchable" slot-scope="props">
|
||||
<b-input
|
||||
v-model="props.filters.preferredUsername"
|
||||
@@ -36,7 +44,10 @@
|
||||
<template v-slot:default="props">
|
||||
<router-link
|
||||
class="profile"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: props.row.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: props.row.id },
|
||||
}"
|
||||
>
|
||||
<article class="media">
|
||||
<figure class="media-left" v-if="props.row.avatar">
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{ $t("Admin") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ADMIN }">{{
|
||||
$t("Admin")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.ADMIN_SETTINGS }">{{
|
||||
@@ -18,7 +20,9 @@
|
||||
<b-input v-model="adminSettings.instanceName" />
|
||||
</b-field>
|
||||
<div class="field">
|
||||
<label class="label has-help">{{ $t("Instance Short Description") }}</label>
|
||||
<label class="label has-help">{{
|
||||
$t("Instance Short Description")
|
||||
}}</label>
|
||||
<small>
|
||||
{{
|
||||
$t(
|
||||
@@ -26,7 +30,11 @@
|
||||
)
|
||||
}}
|
||||
</small>
|
||||
<b-input type="textarea" v-model="adminSettings.instanceDescription" rows="2" />
|
||||
<b-input
|
||||
type="textarea"
|
||||
v-model="adminSettings.instanceDescription"
|
||||
rows="2"
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label has-help">{{ $t("Instance Slogan") }}</label>
|
||||
@@ -76,7 +84,9 @@
|
||||
</b-taginput>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label has-help">{{ $t("Instance Long Description") }}</label>
|
||||
<label class="label has-help">{{
|
||||
$t("Instance Long Description")
|
||||
}}</label>
|
||||
<small>
|
||||
{{
|
||||
$t(
|
||||
@@ -84,13 +94,19 @@
|
||||
)
|
||||
}}
|
||||
</small>
|
||||
<b-input type="textarea" v-model="adminSettings.instanceLongDescription" rows="4" />
|
||||
<b-input
|
||||
type="textarea"
|
||||
v-model="adminSettings.instanceLongDescription"
|
||||
rows="4"
|
||||
/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label has-help">{{ $t("Instance Rules") }}</label>
|
||||
<small>
|
||||
{{
|
||||
$t("A place for your code of conduct, rules or guidelines. You can use HTML tags.")
|
||||
$t(
|
||||
"A place for your code of conduct, rules or guidelines. You can use HTML tags."
|
||||
)
|
||||
}}
|
||||
</small>
|
||||
<b-input type="textarea" v-model="adminSettings.instanceRules" />
|
||||
@@ -126,7 +142,9 @@
|
||||
<div class="column">
|
||||
<div
|
||||
class="notification"
|
||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.DEFAULT"
|
||||
v-if="
|
||||
adminSettings.instanceTermsType === InstanceTermsType.DEFAULT
|
||||
"
|
||||
>
|
||||
<b>{{ $t("Default") }}</b>
|
||||
<i18n
|
||||
@@ -153,11 +171,15 @@
|
||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.URL"
|
||||
>
|
||||
<b>{{ $t("URL") }}</b>
|
||||
<p class="content">{{ $t("Set an URL to a page with your own terms.") }}</p>
|
||||
<p class="content">
|
||||
{{ $t("Set an URL to a page with your own terms.") }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="notification"
|
||||
v-if="adminSettings.instanceTermsType === InstanceTermsType.CUSTOM"
|
||||
v-if="
|
||||
adminSettings.instanceTermsType === InstanceTermsType.CUSTOM
|
||||
"
|
||||
>
|
||||
<b>{{ $t("Custom") }}</b>
|
||||
<i18n
|
||||
@@ -221,7 +243,10 @@
|
||||
<div class="column">
|
||||
<div
|
||||
class="notification"
|
||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.DEFAULT"
|
||||
v-if="
|
||||
adminSettings.instancePrivacyPolicyType ===
|
||||
InstancePrivacyType.DEFAULT
|
||||
"
|
||||
>
|
||||
<b>{{ $t("Default") }}</b>
|
||||
<i18n
|
||||
@@ -240,7 +265,10 @@
|
||||
</div>
|
||||
<div
|
||||
class="notification"
|
||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
||||
v-if="
|
||||
adminSettings.instancePrivacyPolicyType ===
|
||||
InstancePrivacyType.URL
|
||||
"
|
||||
>
|
||||
<b>{{ $t("URL") }}</b>
|
||||
<p class="content">
|
||||
@@ -249,7 +277,10 @@
|
||||
</div>
|
||||
<div
|
||||
class="notification"
|
||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
||||
v-if="
|
||||
adminSettings.instancePrivacyPolicyType ===
|
||||
InstancePrivacyType.CUSTOM
|
||||
"
|
||||
>
|
||||
<b>{{ $t("Custom") }}</b>
|
||||
<i18n
|
||||
@@ -272,24 +303,41 @@
|
||||
</b-field>
|
||||
<b-field
|
||||
:label="$t('Instance Privacy Policy URL')"
|
||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL"
|
||||
v-if="
|
||||
adminSettings.instancePrivacyPolicyType === InstancePrivacyType.URL
|
||||
"
|
||||
>
|
||||
<b-input type="URL" v-model="adminSettings.instancePrivacyPolicyUrl" />
|
||||
<b-input
|
||||
type="URL"
|
||||
v-model="adminSettings.instancePrivacyPolicyUrl"
|
||||
/>
|
||||
</b-field>
|
||||
<b-field
|
||||
:label="$t('Instance Privacy Policy')"
|
||||
v-if="adminSettings.instancePrivacyPolicyType === InstancePrivacyType.CUSTOM"
|
||||
v-if="
|
||||
adminSettings.instancePrivacyPolicyType ===
|
||||
InstancePrivacyType.CUSTOM
|
||||
"
|
||||
>
|
||||
<b-input type="textarea" v-model="adminSettings.instancePrivacyPolicy" />
|
||||
<b-input
|
||||
type="textarea"
|
||||
v-model="adminSettings.instancePrivacyPolicy"
|
||||
/>
|
||||
</b-field>
|
||||
<b-button native-type="submit" type="is-primary">{{ $t("Save") }}</b-button>
|
||||
<b-button native-type="submit" type="is-primary">{{
|
||||
$t("Save")
|
||||
}}</b-button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Component, Vue, Watch } from "vue-property-decorator";
|
||||
import { ADMIN_SETTINGS, SAVE_ADMIN_SETTINGS, LANGUAGES } from "@/graphql/admin";
|
||||
import {
|
||||
ADMIN_SETTINGS,
|
||||
SAVE_ADMIN_SETTINGS,
|
||||
LANGUAGES,
|
||||
} from "@/graphql/admin";
|
||||
import { InstancePrivacyType, InstanceTermsType } from "@/types/enums";
|
||||
import { IAdminSettings, ILanguage } from "../../types/admin.model";
|
||||
import RouteName from "../../router/name";
|
||||
@@ -326,15 +374,19 @@ export default class Settings extends Vue {
|
||||
const variables = { ...this.adminSettings };
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
variables.instanceLanguages = variables.instanceLanguages.map((language) => {
|
||||
return this.codeForLanguage(language);
|
||||
});
|
||||
variables.instanceLanguages = variables.instanceLanguages.map(
|
||||
(language) => {
|
||||
return this.codeForLanguage(language);
|
||||
}
|
||||
);
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: SAVE_ADMIN_SETTINGS,
|
||||
variables,
|
||||
});
|
||||
this.$notifier.success(this.$t("Admin settings successfully saved.") as string);
|
||||
this.$notifier.success(
|
||||
this.$t("Admin settings successfully saved.") as string
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.$notifier.error(this.$t("Failed to save admin settings") as string);
|
||||
@@ -345,7 +397,12 @@ export default class Settings extends Vue {
|
||||
this.filteredLanguages = this.languages
|
||||
? this.languages
|
||||
.filter((language: ILanguage) => {
|
||||
return language.name.toString().toLowerCase().indexOf(text.toLowerCase()) >= 0;
|
||||
return (
|
||||
language.name
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.indexOf(text.toLowerCase()) >= 0
|
||||
);
|
||||
})
|
||||
.map(({ name }) => name)
|
||||
: [];
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.USERS }">{{ $t("Users") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.USERS }">{{
|
||||
$t("Users")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -40,7 +44,10 @@
|
||||
<template v-slot:default="props">
|
||||
<router-link
|
||||
class="user-profile"
|
||||
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: props.row.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_USER_PROFILE,
|
||||
params: { id: props.row.id },
|
||||
}"
|
||||
:class="{ disabled: props.row.disabled }"
|
||||
>
|
||||
{{ props.row.email }}
|
||||
@@ -60,7 +67,12 @@
|
||||
{{ $t("Not confirmed") }}
|
||||
</template>
|
||||
</b-table-column>
|
||||
<b-table-column field="locale" :label="$t('Language')" :centered="true" v-slot="props">
|
||||
<b-table-column
|
||||
field="locale"
|
||||
:label="$t('Language')"
|
||||
:centered="true"
|
||||
v-slot="props"
|
||||
>
|
||||
{{ props.row.locale }}
|
||||
</b-table-column>
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
<editor v-model="discussion.text" />
|
||||
</b-field>
|
||||
|
||||
<button class="button is-primary" type="submit">{{ $t("Create the discussion") }}</button>
|
||||
<button class="button is-primary" type="submit">
|
||||
{{ $t("Create the discussion") }}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</template>
|
||||
@@ -26,7 +28,8 @@ import RouteName from "../../router/name";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
editor: () => import(/* webpackChunkName: "editor" */ "@/components/Editor.vue"),
|
||||
editor: () =>
|
||||
import(/* webpackChunkName: "editor" */ "@/components/Editor.vue"),
|
||||
},
|
||||
apollo: {
|
||||
currentActor: CURRENT_ACTOR_CLIENT,
|
||||
|
||||
@@ -3,14 +3,18 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ $t("My groups") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{
|
||||
$t("My groups")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
v-if="discussion.actor"
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(discussion.actor) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(discussion.actor),
|
||||
},
|
||||
}"
|
||||
>{{ discussion.actor.name }}</router-link
|
||||
>
|
||||
@@ -21,16 +25,19 @@
|
||||
v-if="discussion.actor"
|
||||
:to="{
|
||||
name: RouteName.DISCUSSION_LIST,
|
||||
params: { preferredUsername: usernameWithDomain(discussion.actor) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(discussion.actor),
|
||||
},
|
||||
}"
|
||||
>{{ $t("Discussions") }}</router-link
|
||||
>
|
||||
<b-skeleton animated v-else />
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.DISCUSSION, params: { id: discussion.id } }">{{
|
||||
discussion.title
|
||||
}}</router-link>
|
||||
<router-link
|
||||
:to="{ name: RouteName.DISCUSSION, params: { id: discussion.id } }"
|
||||
>{{ discussion.title }}</router-link
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -39,7 +46,10 @@
|
||||
<h2 class="title" v-if="discussion.title && !editTitleMode">
|
||||
{{ discussion.title }}
|
||||
<span
|
||||
v-if="currentActor.id === discussion.creator.id || isCurrentActorAGroupModerator"
|
||||
v-if="
|
||||
currentActor.id === discussion.creator.id ||
|
||||
isCurrentActorAGroupModerator
|
||||
"
|
||||
@click="
|
||||
() => {
|
||||
newTitle = discussion.title;
|
||||
@@ -54,7 +64,11 @@
|
||||
<form v-else @submit.prevent="updateDiscussion" class="title-edit">
|
||||
<b-input :value="discussion.title" v-model="newTitle" />
|
||||
<div class="buttons">
|
||||
<b-button type="is-primary" native-type="submit" icon-right="check" />
|
||||
<b-button
|
||||
type="is-primary"
|
||||
native-type="submit"
|
||||
icon-right="check"
|
||||
/>
|
||||
<b-button
|
||||
@click="
|
||||
() => {
|
||||
@@ -151,7 +165,8 @@ import { IComment } from "../../types/comment.model";
|
||||
if (
|
||||
!previousDiscussion.comments.elements.find(
|
||||
(comment: IComment) =>
|
||||
comment.id === subscriptionData.data.discussionCommentChanged.lastComment.id
|
||||
comment.id ===
|
||||
subscriptionData.data.discussionCommentChanged.lastComment.id
|
||||
)
|
||||
) {
|
||||
previousDiscussion.lastComment =
|
||||
@@ -169,7 +184,8 @@ import { IComment } from "../../types/comment.model";
|
||||
},
|
||||
components: {
|
||||
DiscussionComment,
|
||||
editor: () => import(/* webpackChunkName: "editor" */ "@/components/Editor.vue"),
|
||||
editor: () =>
|
||||
import(/* webpackChunkName: "editor" */ "@/components/Editor.vue"),
|
||||
},
|
||||
metaInfo() {
|
||||
return {
|
||||
@@ -382,7 +398,9 @@ export default class discussion extends mixins(GroupMixin) {
|
||||
if (this.discussion.actor) {
|
||||
this.$router.push({
|
||||
name: RouteName.DISCUSSION_LIST,
|
||||
params: { preferredUsername: usernameWithDomain(this.discussion.actor) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(this.discussion.actor),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -403,12 +421,15 @@ export default class discussion extends mixins(GroupMixin) {
|
||||
|
||||
handleScroll(): void {
|
||||
const scrollTop =
|
||||
(document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
|
||||
(document.documentElement && document.documentElement.scrollTop) ||
|
||||
document.body.scrollTop;
|
||||
const scrollHeight =
|
||||
(document.documentElement && document.documentElement.scrollHeight) ||
|
||||
document.body.scrollHeight;
|
||||
const clientHeight = document.documentElement.clientHeight || window.innerHeight;
|
||||
const scrolledToBottom = Math.ceil(scrollTop + clientHeight + 800) >= scrollHeight;
|
||||
const clientHeight =
|
||||
document.documentElement.clientHeight || window.innerHeight;
|
||||
const scrolledToBottom =
|
||||
Math.ceil(scrollTop + clientHeight + 800) >= scrollHeight;
|
||||
if (scrolledToBottom) {
|
||||
this.loadMoreComments();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ $t("My groups") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{
|
||||
$t("My groups")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
@@ -27,7 +29,11 @@
|
||||
</nav>
|
||||
<section>
|
||||
<p>
|
||||
{{ $t("Keep the entire conversation about a specific topic together on a single page.") }}
|
||||
{{
|
||||
$t(
|
||||
"Keep the entire conversation about a specific topic together on a single page."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<b-button
|
||||
tag="router-link"
|
||||
|
||||
@@ -16,8 +16,17 @@
|
||||
:defaultImage="event.picture"
|
||||
/>
|
||||
|
||||
<b-field :label="$t('Title')" :type="checkTitleLength[0]" :message="checkTitleLength[1]">
|
||||
<b-input size="is-large" aria-required="true" required v-model="event.title" />
|
||||
<b-field
|
||||
:label="$t('Title')"
|
||||
:type="checkTitleLength[0]"
|
||||
:message="checkTitleLength[1]"
|
||||
>
|
||||
<b-input
|
||||
size="is-large"
|
||||
aria-required="true"
|
||||
required
|
||||
v-model="event.title"
|
||||
/>
|
||||
</b-field>
|
||||
|
||||
<tag-input v-model="event.tags" :data="tags" path="title" />
|
||||
@@ -41,7 +50,12 @@
|
||||
</div>
|
||||
|
||||
<b-field :label="$t('Website / URL')">
|
||||
<b-input icon="link" type="url" v-model="event.onlineAddress" placeholder="URL" />
|
||||
<b-input
|
||||
icon="link"
|
||||
type="url"
|
||||
v-model="event.onlineAddress"
|
||||
placeholder="URL"
|
||||
/>
|
||||
</b-field>
|
||||
|
||||
<subtitle>{{ $t("Organizers") }}</subtitle>
|
||||
@@ -55,18 +69,28 @@
|
||||
/>
|
||||
</b-field>
|
||||
<p v-if="!event.attributedTo.id || attributedToEqualToOrganizerActor">
|
||||
{{ $t("The event will show as attributed to your personal profile.") }}
|
||||
{{
|
||||
$t("The event will show as attributed to your personal profile.")
|
||||
}}
|
||||
</p>
|
||||
<p v-else>
|
||||
<span>{{ $t("The event will show as attributed to this group.") }}</span>
|
||||
<span>{{
|
||||
$t("The event will show as attributed to this group.")
|
||||
}}</span>
|
||||
<span
|
||||
v-if="event.contacts && event.contacts.length"
|
||||
v-html="
|
||||
$tc('<b>{contact}</b> will be displayed as contact.', event.contacts.length, {
|
||||
contact: formatList(
|
||||
event.contacts.map((contact) => displayNameAndUsername(contact))
|
||||
),
|
||||
})
|
||||
$tc(
|
||||
'<b>{contact}</b> will be displayed as contact.',
|
||||
event.contacts.length,
|
||||
{
|
||||
contact: formatList(
|
||||
event.contacts.map((contact) =>
|
||||
displayNameAndUsername(contact)
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
"
|
||||
/>
|
||||
</p>
|
||||
@@ -96,11 +120,21 @@
|
||||
</b-radio>
|
||||
</div>-->
|
||||
|
||||
<div class="field" v-if="config && config.anonymous.participation.allowed">
|
||||
<div
|
||||
class="field"
|
||||
v-if="config && config.anonymous.participation.allowed"
|
||||
>
|
||||
<label class="label">{{ $t("Anonymous participations") }}</label>
|
||||
<b-switch v-model="event.options.anonymousParticipation">
|
||||
{{ $t("I want to allow people to participate without an account.") }}
|
||||
<small v-if="config.anonymous.participation.validation.email.confirmationRequired">
|
||||
{{
|
||||
$t("I want to allow people to participate without an account.")
|
||||
}}
|
||||
<small
|
||||
v-if="
|
||||
config.anonymous.participation.validation.email
|
||||
.confirmationRequired
|
||||
"
|
||||
>
|
||||
<br />
|
||||
{{
|
||||
$t(
|
||||
@@ -120,7 +154,9 @@
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ $t("Number of places") }}</label>
|
||||
<b-switch v-model="limitedPlaces">{{ $t("Limited number of places") }}</b-switch>
|
||||
<b-switch v-model="limitedPlaces">{{
|
||||
$t("Limited number of places")
|
||||
}}</b-switch>
|
||||
</div>
|
||||
|
||||
<div class="box" v-if="limitedPlaces">
|
||||
@@ -225,7 +261,11 @@
|
||||
</b-field>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button" type="button" @click="dateSettingsIsOpen = false">
|
||||
<button
|
||||
class="button"
|
||||
type="button"
|
||||
@click="dateSettingsIsOpen = false"
|
||||
>
|
||||
{{ $t("OK") }}
|
||||
</button>
|
||||
</footer>
|
||||
@@ -243,11 +283,15 @@
|
||||
<div class="container">
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<span class="navbar-item" v-if="isEventModified">{{ $t("Unsaved changes") }}</span>
|
||||
<span class="navbar-item" v-if="isEventModified">{{
|
||||
$t("Unsaved changes")
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<span class="navbar-item">
|
||||
<b-button type="is-text" @click="confirmGoBack">{{ $t("Cancel") }}</b-button>
|
||||
<b-button type="is-text" @click="confirmGoBack">{{
|
||||
$t("Cancel")
|
||||
}}</b-button>
|
||||
</span>
|
||||
<!-- If an event has been published we can't make it draft anymore -->
|
||||
<span class="navbar-item" v-if="event.draft === true">
|
||||
@@ -266,8 +310,12 @@
|
||||
@click="createOrUpdatePublish"
|
||||
@keyup.enter="createOrUpdatePublish"
|
||||
>
|
||||
<span v-if="isUpdate === false">{{ $t("Create my event") }}</span>
|
||||
<span v-else-if="event.draft === true">{{ $t("Publish") }}</span>
|
||||
<span v-if="isUpdate === false">{{
|
||||
$t("Create my event")
|
||||
}}</span>
|
||||
<span v-else-if="event.draft === true">{{
|
||||
$t("Publish")
|
||||
}}</span>
|
||||
<span v-else>{{ $t("Update my event") }}</span>
|
||||
</b-button>
|
||||
</span>
|
||||
@@ -376,7 +424,11 @@ import {
|
||||
import { IPerson, Person, displayNameAndUsername } from "../../types/actor";
|
||||
import { TAGS } from "../../graphql/tags";
|
||||
import { ITag } from "../../types/tag.model";
|
||||
import { buildFileFromIMedia, buildFileVariable, readFileAsync } from "../../utils/image";
|
||||
import {
|
||||
buildFileFromIMedia,
|
||||
buildFileVariable,
|
||||
readFileAsync,
|
||||
} from "../../utils/image";
|
||||
import RouteName from "../../router/name";
|
||||
import "intersection-observer";
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
@@ -419,7 +471,9 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
|
||||
// if no subcomponents specify a metaInfo.title, this title will be used
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
title: (this.isUpdate ? this.$t("Event edition") : this.$t("Event creation")) as string,
|
||||
title: (this.isUpdate
|
||||
? this.$t("Event edition")
|
||||
: this.$t("Event creation")) as string,
|
||||
// all titles will be injected into this template
|
||||
titleTemplate: "%s | Mobilizon",
|
||||
};
|
||||
@@ -546,8 +600,15 @@ export default class EditEvent extends Vue {
|
||||
|
||||
@Watch("event")
|
||||
setInitialData(): void {
|
||||
if (this.isUpdate && this.unmodifiedEvent === undefined && this.event && this.event.uuid) {
|
||||
this.unmodifiedEvent = JSON.parse(JSON.stringify(this.event.toEditJSON()));
|
||||
if (
|
||||
this.isUpdate &&
|
||||
this.unmodifiedEvent === undefined &&
|
||||
this.event &&
|
||||
this.event.uuid
|
||||
) {
|
||||
this.unmodifiedEvent = JSON.parse(
|
||||
JSON.stringify(this.event.toEditJSON())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,8 +636,10 @@ export default class EditEvent extends Vue {
|
||||
const { data } = await this.$apollo.mutate({
|
||||
mutation: CREATE_EVENT,
|
||||
variables,
|
||||
update: (store, { data: { createEvent } }) => this.postCreateOrUpdate(store, createEvent),
|
||||
refetchQueries: ({ data: { createEvent } }) => this.postRefetchQueries(createEvent),
|
||||
update: (store, { data: { createEvent } }) =>
|
||||
this.postCreateOrUpdate(store, createEvent),
|
||||
refetchQueries: ({ data: { createEvent } }) =>
|
||||
this.postRefetchQueries(createEvent),
|
||||
});
|
||||
|
||||
this.$buefy.notification.open({
|
||||
@@ -606,8 +669,10 @@ export default class EditEvent extends Vue {
|
||||
await this.$apollo.mutate({
|
||||
mutation: EDIT_EVENT,
|
||||
variables,
|
||||
update: (store, { data: { updateEvent } }) => this.postCreateOrUpdate(store, updateEvent),
|
||||
refetchQueries: ({ data: { updateEvent } }) => this.postRefetchQueries(updateEvent),
|
||||
update: (store, { data: { updateEvent } }) =>
|
||||
this.postCreateOrUpdate(store, updateEvent),
|
||||
refetchQueries: ({ data: { updateEvent } }) =>
|
||||
this.postRefetchQueries(updateEvent),
|
||||
});
|
||||
|
||||
this.$buefy.notification.open({
|
||||
@@ -774,9 +839,13 @@ export default class EditEvent extends Vue {
|
||||
|
||||
try {
|
||||
if (this.event.picture && this.pictureFile) {
|
||||
const oldPictureFile = (await buildFileFromIMedia(this.event.picture)) as File;
|
||||
const oldPictureFile = (await buildFileFromIMedia(
|
||||
this.event.picture
|
||||
)) as File;
|
||||
const oldPictureFileContent = await readFileAsync(oldPictureFile);
|
||||
const newPictureFileContent = await readFileAsync(this.pictureFile as File);
|
||||
const newPictureFileContent = await readFileAsync(
|
||||
this.pictureFile as File
|
||||
);
|
||||
if (oldPictureFileContent === newPictureFileContent) {
|
||||
res.picture = { mediaId: this.event.picture.id };
|
||||
}
|
||||
@@ -814,7 +883,8 @@ export default class EditEvent extends Vue {
|
||||
this.event.options.showRemainingAttendeeCapacity = false;
|
||||
} else {
|
||||
this.event.options.maximumAttendeeCapacity =
|
||||
this.event.options.maximumAttendeeCapacity || DEFAULT_LIMIT_NUMBER_OF_PLACES;
|
||||
this.event.options.maximumAttendeeCapacity ||
|
||||
DEFAULT_LIMIT_NUMBER_OF_PLACES;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -878,7 +948,10 @@ export default class EditEvent extends Vue {
|
||||
}
|
||||
|
||||
get isEventModified(): boolean {
|
||||
return JSON.stringify(this.event.toEditJSON()) !== JSON.stringify(this.unmodifiedEvent);
|
||||
return (
|
||||
JSON.stringify(this.event.toEditJSON()) !==
|
||||
JSON.stringify(this.unmodifiedEvent)
|
||||
);
|
||||
}
|
||||
|
||||
get beginsOn(): Date {
|
||||
|
||||
@@ -18,16 +18,29 @@
|
||||
<h1 class="title" style="margin: 0">{{ event.title }}</h1>
|
||||
<div class="organizer">
|
||||
<span v-if="event.organizerActor && !event.attributedTo">
|
||||
<popover-actor-card :actor="event.organizerActor" :inline="true">
|
||||
<popover-actor-card
|
||||
:actor="event.organizerActor"
|
||||
:inline="true"
|
||||
>
|
||||
<span>
|
||||
{{
|
||||
$t("By @{username}", { username: usernameWithDomain(event.organizerActor) })
|
||||
$t("By @{username}", {
|
||||
username: usernameWithDomain(event.organizerActor),
|
||||
})
|
||||
}}
|
||||
</span>
|
||||
</popover-actor-card>
|
||||
</span>
|
||||
<span v-else-if="event.attributedTo && event.options.hideOrganizerWhenGroupEvent">
|
||||
<popover-actor-card :actor="event.attributedTo" :inline="true">
|
||||
<span
|
||||
v-else-if="
|
||||
event.attributedTo &&
|
||||
event.options.hideOrganizerWhenGroupEvent
|
||||
"
|
||||
>
|
||||
<popover-actor-card
|
||||
:actor="event.attributedTo"
|
||||
:inline="true"
|
||||
>
|
||||
{{
|
||||
$t("By @{group}", {
|
||||
group: usernameWithDomain(event.attributedTo),
|
||||
@@ -37,14 +50,26 @@
|
||||
</span>
|
||||
<span v-else-if="event.organizerActor && event.attributedTo">
|
||||
<i18n path="By {group}">
|
||||
<popover-actor-card :actor="event.attributedTo" slot="group" :inline="true">
|
||||
<popover-actor-card
|
||||
:actor="event.attributedTo"
|
||||
slot="group"
|
||||
:inline="true"
|
||||
>
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(event.attributedTo) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(
|
||||
event.attributedTo
|
||||
),
|
||||
},
|
||||
}"
|
||||
>
|
||||
{{ $t("@{group}", { group: usernameWithDomain(event.attributedTo) }) }}
|
||||
{{
|
||||
$t("@{group}", {
|
||||
group: usernameWithDomain(event.attributedTo),
|
||||
})
|
||||
}}
|
||||
</router-link>
|
||||
</popover-actor-card>
|
||||
</i18n>
|
||||
@@ -59,14 +84,23 @@
|
||||
<tag>{{ tag.title }}</tag>
|
||||
</router-link>
|
||||
</p>
|
||||
<b-tag type="is-warning" size="is-medium" v-if="event.draft">{{ $t("Draft") }}</b-tag>
|
||||
<span class="event-status" v-if="event.status !== EventStatus.CONFIRMED">
|
||||
<b-tag type="is-warning" v-if="event.status === EventStatus.TENTATIVE">{{
|
||||
$t("Event to be confirmed")
|
||||
}}</b-tag>
|
||||
<b-tag type="is-danger" v-if="event.status === EventStatus.CANCELLED">{{
|
||||
$t("Event cancelled")
|
||||
}}</b-tag>
|
||||
<b-tag type="is-warning" size="is-medium" v-if="event.draft">{{
|
||||
$t("Draft")
|
||||
}}</b-tag>
|
||||
<span
|
||||
class="event-status"
|
||||
v-if="event.status !== EventStatus.CONFIRMED"
|
||||
>
|
||||
<b-tag
|
||||
type="is-warning"
|
||||
v-if="event.status === EventStatus.TENTATIVE"
|
||||
>{{ $t("Event to be confirmed") }}</b-tag
|
||||
>
|
||||
<b-tag
|
||||
type="is-danger"
|
||||
v-if="event.status === EventStatus.CANCELLED"
|
||||
>{{ $t("Event cancelled") }}</b-tag
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
<div class="column is-3-tablet">
|
||||
@@ -95,7 +129,9 @@
|
||||
/>
|
||||
<b-button
|
||||
type="is-text"
|
||||
v-if="!actorIsParticipant && anonymousParticipation !== null"
|
||||
v-if="
|
||||
!actorIsParticipant && anonymousParticipation !== null
|
||||
"
|
||||
@click="cancelAnonymousParticipation"
|
||||
>{{ $t("Cancel anonymous participation") }}</b-button
|
||||
>
|
||||
@@ -103,7 +139,9 @@
|
||||
{{ $t("You are participating in this event anonymously") }}
|
||||
<b-tooltip
|
||||
:label="
|
||||
$t('This information is saved only on your computer. Click for details')
|
||||
$t(
|
||||
'This information is saved only on your computer. Click for details'
|
||||
)
|
||||
"
|
||||
>
|
||||
<router-link :to="{ name: RouteName.TERMS }">
|
||||
@@ -111,7 +149,11 @@
|
||||
</router-link>
|
||||
</b-tooltip>
|
||||
</small>
|
||||
<small v-else-if="!actorIsParticipant && anonymousParticipation === false">
|
||||
<small
|
||||
v-else-if="
|
||||
!actorIsParticipant && anonymousParticipation === false
|
||||
"
|
||||
>
|
||||
{{
|
||||
$t(
|
||||
"You are participating in this event anonymously but didn't confirm participation"
|
||||
@@ -119,7 +161,9 @@
|
||||
}}
|
||||
<b-tooltip
|
||||
:label="
|
||||
$t('This information is saved only on your computer. Click for details')
|
||||
$t(
|
||||
'This information is saved only on your computer. Click for details'
|
||||
)
|
||||
"
|
||||
>
|
||||
<router-link :to="{ name: RouteName.TERMS }">
|
||||
@@ -129,7 +173,12 @@
|
||||
</small>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button class="button is-primary" type="button" slot="trigger" disabled>
|
||||
<button
|
||||
class="button is-primary"
|
||||
type="button"
|
||||
slot="trigger"
|
||||
disabled
|
||||
>
|
||||
<template>
|
||||
<span>{{ $t("Event already passed") }}</span>
|
||||
</template>
|
||||
@@ -157,7 +206,10 @@
|
||||
<router-link
|
||||
class="participations-link"
|
||||
v-if="actorIsOrganizer && event.draft === false"
|
||||
:to="{ name: RouteName.PARTICIPATIONS, params: { eventId: event.uuid } }"
|
||||
:to="{
|
||||
name: RouteName.PARTICIPATIONS,
|
||||
params: { eventId: event.uuid },
|
||||
}"
|
||||
>
|
||||
<!-- We retire one because of the event creator who is a participant -->
|
||||
<span v-if="event.options.maximumAttendeeCapacity">
|
||||
@@ -177,9 +229,13 @@
|
||||
</span>
|
||||
<span v-else>
|
||||
{{
|
||||
$tc("No one is going to this event", event.participantStats.participant, {
|
||||
going: event.participantStats.participant,
|
||||
})
|
||||
$tc(
|
||||
"No one is going to this event",
|
||||
event.participantStats.participant,
|
||||
{
|
||||
going: event.participantStats.participant,
|
||||
}
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</router-link>
|
||||
@@ -201,9 +257,13 @@
|
||||
</span>
|
||||
<span v-else>
|
||||
{{
|
||||
$tc("No one is going to this event", event.participantStats.participant, {
|
||||
going: event.participantStats.participant,
|
||||
})
|
||||
$tc(
|
||||
"No one is going to this event",
|
||||
event.participantStats.participant,
|
||||
{
|
||||
going: event.participantStats.participant,
|
||||
}
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</span>
|
||||
@@ -221,7 +281,11 @@
|
||||
<b-icon icon="ticket-confirmation-outline" />
|
||||
</p>
|
||||
<b-dropdown position="is-bottom-left" aria-role="list">
|
||||
<b-button slot="trigger" role="button" icon-right="dots-horizontal">
|
||||
<b-button
|
||||
slot="trigger"
|
||||
role="button"
|
||||
icon-right="dots-horizontal"
|
||||
>
|
||||
{{ $t("Actions") }}
|
||||
<!-- <b-icon icon="dots-horizontal" /> -->
|
||||
</b-button>
|
||||
@@ -231,7 +295,10 @@
|
||||
v-if="actorIsOrganizer || event.draft"
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.EDIT_EVENT, params: { eventId: event.uuid } }"
|
||||
:to="{
|
||||
name: RouteName.EDIT_EVENT,
|
||||
params: { eventId: event.uuid },
|
||||
}"
|
||||
>
|
||||
{{ $t("Edit") }}
|
||||
<b-icon icon="pencil" />
|
||||
@@ -243,7 +310,10 @@
|
||||
v-if="actorIsOrganizer || event.draft"
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.DUPLICATE_EVENT, params: { eventId: event.uuid } }"
|
||||
:to="{
|
||||
name: RouteName.DUPLICATE_EVENT,
|
||||
params: { eventId: event.uuid },
|
||||
}"
|
||||
>
|
||||
{{ $t("Duplicate") }}
|
||||
<b-icon icon="content-duplicate" />
|
||||
@@ -263,7 +333,11 @@
|
||||
aria-role="menuitem"
|
||||
v-if="actorIsOrganizer || event.draft"
|
||||
/>
|
||||
<b-dropdown-item aria-role="listitem" v-if="!event.draft" @click="triggerShare()">
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-if="!event.draft"
|
||||
@click="triggerShare()"
|
||||
>
|
||||
<span>
|
||||
{{ $t("Share this event") }}
|
||||
<b-icon icon="share" />
|
||||
@@ -299,14 +373,23 @@
|
||||
<div class="sticky">
|
||||
<event-metadata-block
|
||||
:title="$t('Location')"
|
||||
:icon="physicalAddress ? physicalAddress.poiInfos.poiIcon.icon : 'earth'"
|
||||
:icon="
|
||||
physicalAddress
|
||||
? physicalAddress.poiInfos.poiIcon.icon
|
||||
: 'earth'
|
||||
"
|
||||
>
|
||||
<div class="address-wrapper">
|
||||
<span v-if="!physicalAddress">{{ $t("No address defined") }}</span>
|
||||
<span v-if="!physicalAddress">{{
|
||||
$t("No address defined")
|
||||
}}</span>
|
||||
<div class="address" v-if="physicalAddress">
|
||||
<div>
|
||||
<address>
|
||||
<p class="addressDescription" :title="physicalAddress.poiInfos.name">
|
||||
<p
|
||||
class="addressDescription"
|
||||
:title="physicalAddress.poiInfos.name"
|
||||
>
|
||||
{{ physicalAddress.poiInfos.name }}
|
||||
</p>
|
||||
<p>{{ physicalAddress.poiInfos.alternativeName }}</p>
|
||||
@@ -321,7 +404,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</event-metadata-block>
|
||||
<event-metadata-block :title="$t('Date and time')" icon="calendar">
|
||||
<event-metadata-block
|
||||
:title="$t('Date and time')"
|
||||
icon="calendar"
|
||||
>
|
||||
<event-full-date
|
||||
:beginsOn="event.beginsOn"
|
||||
:show-start-time="event.options.showStartTime"
|
||||
@@ -330,19 +416,27 @@
|
||||
/>
|
||||
</event-metadata-block>
|
||||
<event-metadata-block :title="$t('Organized by')">
|
||||
<popover-actor-card :actor="event.organizerActor" v-if="!event.attributedTo">
|
||||
<popover-actor-card
|
||||
:actor="event.organizerActor"
|
||||
v-if="!event.attributedTo"
|
||||
>
|
||||
<actor-card :actor="event.organizerActor" />
|
||||
</popover-actor-card>
|
||||
<router-link
|
||||
v-if="event.attributedTo"
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(event.attributedTo) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(event.attributedTo),
|
||||
},
|
||||
}"
|
||||
>
|
||||
<popover-actor-card
|
||||
:actor="event.attributedTo"
|
||||
v-if="!event.attributedTo || !event.options.hideOrganizerWhenGroupEvent"
|
||||
v-if="
|
||||
!event.attributedTo ||
|
||||
!event.options.hideOrganizerWhenGroupEvent
|
||||
"
|
||||
>
|
||||
<actor-card :actor="event.attributedTo" />
|
||||
</popover-actor-card>
|
||||
@@ -397,8 +491,13 @@
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<section class="more-events section" v-if="event.relatedEvents.length > 0">
|
||||
<h3 class="title has-text-centered">{{ $t("These events may interest you") }}</h3>
|
||||
<section
|
||||
class="more-events section"
|
||||
v-if="event.relatedEvents.length > 0"
|
||||
>
|
||||
<h3 class="title has-text-centered">
|
||||
{{ $t("These events may interest you") }}
|
||||
</h3>
|
||||
<div class="columns">
|
||||
<div
|
||||
class="column is-one-third-desktop"
|
||||
@@ -409,7 +508,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<b-modal :active.sync="isReportModalActive" has-modal-card ref="reportModal">
|
||||
<b-modal
|
||||
:active.sync="isReportModalActive"
|
||||
has-modal-card
|
||||
ref="reportModal"
|
||||
>
|
||||
<report-modal
|
||||
:on-confirm="reportEvent"
|
||||
:title="$t('Report this event')"
|
||||
@@ -417,14 +520,29 @@
|
||||
@close="$refs.reportModal.close()"
|
||||
/>
|
||||
</b-modal>
|
||||
<b-modal :active.sync="isShareModalActive" has-modal-card ref="shareModal">
|
||||
<share-event-modal :event="event" :eventCapacityOK="eventCapacityOK" />
|
||||
<b-modal
|
||||
:active.sync="isShareModalActive"
|
||||
has-modal-card
|
||||
ref="shareModal"
|
||||
>
|
||||
<share-event-modal
|
||||
:event="event"
|
||||
:eventCapacityOK="eventCapacityOK"
|
||||
/>
|
||||
</b-modal>
|
||||
<b-modal :active.sync="isJoinModalActive" has-modal-card ref="participationModal">
|
||||
<b-modal
|
||||
:active.sync="isJoinModalActive"
|
||||
has-modal-card
|
||||
ref="participationModal"
|
||||
>
|
||||
<identity-picker v-model="identity">
|
||||
<template v-slot:footer>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button" ref="cancelButton" @click="isJoinModalActive = false">
|
||||
<button
|
||||
class="button"
|
||||
ref="cancelButton"
|
||||
@click="isJoinModalActive = false"
|
||||
>
|
||||
{{ $t("Cancel") }}
|
||||
</button>
|
||||
<button
|
||||
@@ -449,7 +567,9 @@
|
||||
>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{{ $t("Participation confirmation") }}</p>
|
||||
<p class="modal-card-title">
|
||||
{{ $t("Participation confirmation") }}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section class="modal-card-body">
|
||||
@@ -460,7 +580,11 @@
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<form @submit.prevent="joinEvent(actorForConfirmation, messageForConfirmation)">
|
||||
<form
|
||||
@submit.prevent="
|
||||
joinEvent(actorForConfirmation, messageForConfirmation)
|
||||
"
|
||||
>
|
||||
<b-field :label="$t('Message')">
|
||||
<b-input
|
||||
type="textarea"
|
||||
@@ -485,7 +609,10 @@
|
||||
</section>
|
||||
</div>
|
||||
</b-modal>
|
||||
<b-modal v-if="physicalAddress && physicalAddress.geom" :active.sync="showMap">
|
||||
<b-modal
|
||||
v-if="physicalAddress && physicalAddress.geom"
|
||||
:active.sync="showMap"
|
||||
>
|
||||
<div class="map">
|
||||
<map-leaflet
|
||||
:coords="physicalAddress.geom"
|
||||
@@ -504,7 +631,12 @@
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Watch } from "vue-property-decorator";
|
||||
import BIcon from "buefy/src/components/icon/Icon.vue";
|
||||
import { EventJoinOptions, EventStatus, EventVisibility, ParticipantRole } from "@/types/enums";
|
||||
import {
|
||||
EventJoinOptions,
|
||||
EventStatus,
|
||||
EventVisibility,
|
||||
ParticipantRole,
|
||||
} from "@/types/enums";
|
||||
import {
|
||||
EVENT_PERSON_PARTICIPATION,
|
||||
EVENT_PERSON_PARTICIPATION_SUBSCRIPTION_CHANGED,
|
||||
@@ -558,7 +690,8 @@ import { IParticipant } from "../../types/participant.model";
|
||||
Tag,
|
||||
ActorCard,
|
||||
PopoverActorCard,
|
||||
"map-leaflet": () => import(/* webpackChunkName: "map" */ "../../components/Map.vue"),
|
||||
"map-leaflet": () =>
|
||||
import(/* webpackChunkName: "map" */ "../../components/Map.vue"),
|
||||
ShareEventModal: () =>
|
||||
import(
|
||||
/* webpackChunkName: "shareEventModal" */ "../../components/Event/ShareEventModal.vue"
|
||||
@@ -603,7 +736,12 @@ import { IParticipant } from "../../types/participant.model";
|
||||
return [];
|
||||
},
|
||||
skip() {
|
||||
return !this.currentActor || !this.event || !this.event.id || !this.currentActor.id;
|
||||
return (
|
||||
!this.currentActor ||
|
||||
!this.event ||
|
||||
!this.event.id ||
|
||||
!this.currentActor.id
|
||||
);
|
||||
},
|
||||
},
|
||||
config: CONFIG,
|
||||
@@ -714,7 +852,8 @@ export default class Event extends EventMixin {
|
||||
|
||||
this.$watch("eventDescription", (eventDescription) => {
|
||||
if (!eventDescription) return;
|
||||
const eventDescriptionElement = this.$refs.eventDescriptionElement as HTMLElement;
|
||||
const eventDescriptionElement = this.$refs
|
||||
.eventDescriptionElement as HTMLElement;
|
||||
|
||||
eventDescriptionElement.addEventListener("click", ($event) => {
|
||||
// TODO: Find the right type for target
|
||||
@@ -724,7 +863,14 @@ export default class Event extends EventMixin {
|
||||
if (target && target.matches(".hashtag") && target.href) {
|
||||
// some sanity checks taken from vue-router:
|
||||
// https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L106
|
||||
const { altKey, ctrlKey, metaKey, shiftKey, button, defaultPrevented } = $event;
|
||||
const {
|
||||
altKey,
|
||||
ctrlKey,
|
||||
metaKey,
|
||||
shiftKey,
|
||||
button,
|
||||
defaultPrevented,
|
||||
} = $event;
|
||||
// don't handle with control keys
|
||||
if (metaKey || altKey || ctrlKey || shiftKey) return;
|
||||
// don't handle when preventDefault called
|
||||
@@ -777,7 +923,9 @@ export default class Event extends EventMixin {
|
||||
forward,
|
||||
},
|
||||
});
|
||||
this.$notifier.success(this.$t("Event {eventTitle} reported", { eventTitle }) as string);
|
||||
this.$notifier.success(
|
||||
this.$t("Event {eventTitle} reported", { eventTitle }) as string
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
@@ -788,11 +936,16 @@ export default class Event extends EventMixin {
|
||||
this.actorForConfirmation = actor;
|
||||
}
|
||||
|
||||
async joinEvent(identity: IPerson, message: string | null = null): Promise<void> {
|
||||
async joinEvent(
|
||||
identity: IPerson,
|
||||
message: string | null = null
|
||||
): Promise<void> {
|
||||
this.isJoinConfirmationModalActive = false;
|
||||
this.isJoinModalActive = false;
|
||||
try {
|
||||
const { data: mutationData } = await this.$apollo.mutate<{ joinEvent: IParticipant }>({
|
||||
const { data: mutationData } = await this.$apollo.mutate<{
|
||||
joinEvent: IParticipant;
|
||||
}>({
|
||||
mutation: JOIN_EVENT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
@@ -808,7 +961,9 @@ export default class Event extends EventMixin {
|
||||
if (participationCachedData == null) return;
|
||||
const { person } = participationCachedData;
|
||||
if (person === null) {
|
||||
console.error("Cannot update participation cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update participation cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
person.participations.elements.push(data.joinEvent);
|
||||
@@ -826,7 +981,9 @@ export default class Event extends EventMixin {
|
||||
if (cachedData == null) return;
|
||||
const { event } = cachedData;
|
||||
if (event === null) {
|
||||
console.error("Cannot update event participant cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update event participant cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -861,9 +1018,12 @@ export default class Event extends EventMixin {
|
||||
title: this.$t('Leaving event "{title}"', {
|
||||
title: this.event.title,
|
||||
}) as string,
|
||||
message: this.$t('Are you sure you want to cancel your participation at event "{title}"?', {
|
||||
title: this.event.title,
|
||||
}) as string,
|
||||
message: this.$t(
|
||||
'Are you sure you want to cancel your participation at event "{title}"?',
|
||||
{
|
||||
title: this.event.title,
|
||||
}
|
||||
) as string,
|
||||
confirmText: this.$t("Leave event") as string,
|
||||
cancelText: this.$t("Cancel") as string,
|
||||
type: "is-danger",
|
||||
@@ -901,19 +1061,27 @@ export default class Event extends EventMixin {
|
||||
}
|
||||
|
||||
private participationConfirmedMessage() {
|
||||
this.$notifier.success(this.$t("Your participation has been confirmed") as string);
|
||||
this.$notifier.success(
|
||||
this.$t("Your participation has been confirmed") as string
|
||||
);
|
||||
}
|
||||
|
||||
private participationRequestedMessage() {
|
||||
this.$notifier.success(this.$t("Your participation has been requested") as string);
|
||||
this.$notifier.success(
|
||||
this.$t("Your participation has been requested") as string
|
||||
);
|
||||
}
|
||||
|
||||
private participationRejectedMessage() {
|
||||
this.$notifier.error(this.$t("Your participation has been rejected") as string);
|
||||
this.$notifier.error(
|
||||
this.$t("Your participation has been rejected") as string
|
||||
);
|
||||
}
|
||||
|
||||
private participationChangedMessage() {
|
||||
this.$notifier.info(this.$t("Your participation status has been changed") as string);
|
||||
this.$notifier.info(
|
||||
this.$t("Your participation status has been changed") as string
|
||||
);
|
||||
}
|
||||
|
||||
async downloadIcsEvent(): Promise<void> {
|
||||
@@ -963,13 +1131,15 @@ export default class Event extends EventMixin {
|
||||
if (this.actorIsOrganizer) return true;
|
||||
|
||||
return (
|
||||
this.participations.length > 0 && this.participations[0].role === ParticipantRole.PARTICIPANT
|
||||
this.participations.length > 0 &&
|
||||
this.participations[0].role === ParticipantRole.PARTICIPANT
|
||||
);
|
||||
}
|
||||
|
||||
get actorIsOrganizer(): boolean {
|
||||
return (
|
||||
this.participations.length > 0 && this.participations[0].role === ParticipantRole.CREATOR
|
||||
this.participations.length > 0 &&
|
||||
this.participations[0].role === ParticipantRole.CREATOR
|
||||
);
|
||||
}
|
||||
|
||||
@@ -982,12 +1152,18 @@ export default class Event extends EventMixin {
|
||||
get eventCapacityOK(): boolean {
|
||||
if (this.event.draft) return true;
|
||||
if (!this.event.options.maximumAttendeeCapacity) return true;
|
||||
return this.event.options.maximumAttendeeCapacity > this.event.participantStats.participant;
|
||||
return (
|
||||
this.event.options.maximumAttendeeCapacity >
|
||||
this.event.participantStats.participant
|
||||
);
|
||||
}
|
||||
|
||||
get numberOfPlacesStillAvailable(): number {
|
||||
if (this.event.draft) return this.event.options.maximumAttendeeCapacity;
|
||||
return this.event.options.maximumAttendeeCapacity - this.event.participantStats.participant;
|
||||
return (
|
||||
this.event.options.maximumAttendeeCapacity -
|
||||
this.event.participantStats.participant
|
||||
);
|
||||
}
|
||||
|
||||
get physicalAddress(): Address | null {
|
||||
@@ -1007,7 +1183,10 @@ export default class Event extends EventMixin {
|
||||
}
|
||||
|
||||
get ableToReport(): boolean {
|
||||
return this.config && (this.currentActor.id != null || this.config.anonymous.reports.allowed);
|
||||
return (
|
||||
this.config &&
|
||||
(this.currentActor.id != null || this.config.anonymous.reports.allowed)
|
||||
);
|
||||
}
|
||||
|
||||
get actorForReport(): IActor | null {
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
class="column is-one-quarter-desktop is-half-mobile"
|
||||
/>
|
||||
</div>
|
||||
<b-message v-if-else="events.length === 0 && $apollo.loading === false" type="is-danger">{{
|
||||
$t("No events found")
|
||||
}}</b-message>
|
||||
<b-message
|
||||
v-if-else="events.length === 0 && $apollo.loading === false"
|
||||
type="is-danger"
|
||||
>{{ $t("No events found") }}</b-message
|
||||
>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@
|
||||
</nav>
|
||||
<section>
|
||||
<h1 class="title" v-if="group">
|
||||
{{ $t("{group}'s events", { group: group.name || group.preferredUsername }) }}
|
||||
{{
|
||||
$t("{group}'s events", {
|
||||
group: group.name || group.preferredUsername,
|
||||
})
|
||||
}}
|
||||
</h1>
|
||||
<p v-if="isCurrentActorMember">
|
||||
{{
|
||||
@@ -56,7 +60,10 @@
|
||||
/>
|
||||
</transition-group>
|
||||
<b-message
|
||||
v-if="group.organizedEvents.elements.length === 0 && $apollo.loading === false"
|
||||
v-if="
|
||||
group.organizedEvents.elements.length === 0 &&
|
||||
$apollo.loading === false
|
||||
"
|
||||
type="is-danger"
|
||||
>
|
||||
{{ $t("No events found") }}
|
||||
@@ -124,7 +131,9 @@ export default class GroupEvents extends mixins(GroupMixin) {
|
||||
|
||||
get isCurrentActorMember(): boolean {
|
||||
if (!this.group || !this.memberships) return false;
|
||||
return this.memberships.map(({ parent: { id } }) => id).includes(this.group.id);
|
||||
return this.memberships
|
||||
.map(({ parent: { id } }) => id)
|
||||
.includes(this.group.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
<div class="columns is-centered">
|
||||
<b-button
|
||||
class="column is-narrow"
|
||||
v-if="hasMoreFutureParticipations && futureParticipations.length === limit"
|
||||
v-if="
|
||||
hasMoreFutureParticipations && futureParticipations.length === limit
|
||||
"
|
||||
@click="loadMoreFutureParticipations"
|
||||
size="is-large"
|
||||
type="is-primary"
|
||||
@@ -65,7 +67,9 @@
|
||||
<div class="columns is-centered">
|
||||
<b-button
|
||||
class="column is-narrow"
|
||||
v-if="hasMorePastParticipations && pastParticipations.length === limit"
|
||||
v-if="
|
||||
hasMorePastParticipations && pastParticipations.length === limit
|
||||
"
|
||||
@click="loadMorePastParticipations"
|
||||
size="is-large"
|
||||
type="is-primary"
|
||||
@@ -90,7 +94,10 @@
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import { ParticipantRole } from "@/types/enums";
|
||||
import { IParticipant, Participant } from "../../types/participant.model";
|
||||
import { LOGGED_USER_PARTICIPATIONS, LOGGED_USER_DRAFTS } from "../../graphql/actor";
|
||||
import {
|
||||
LOGGED_USER_PARTICIPATIONS,
|
||||
LOGGED_USER_DRAFTS,
|
||||
} from "../../graphql/actor";
|
||||
import { EventModel, IEvent } from "../../types/event.model";
|
||||
import EventListCard from "../../components/Event/EventListCard.vue";
|
||||
import EventCard from "../../components/Event/EventCard.vue";
|
||||
@@ -123,7 +130,8 @@ import Subtitle from "../../components/Utils/Subtitle.vue";
|
||||
page: 1,
|
||||
limit: 10,
|
||||
},
|
||||
update: (data) => data.loggedUser.drafts.map((event: IEvent) => new EventModel(event)),
|
||||
update: (data) =>
|
||||
data.loggedUser.drafts.map((event: IEvent) => new EventModel(event)),
|
||||
},
|
||||
pastParticipations: {
|
||||
query: LOGGED_USER_PARTICIPATIONS,
|
||||
@@ -170,7 +178,8 @@ export default class MyEvents extends Vue {
|
||||
revertSort = false
|
||||
): Map<string, Participant[]> {
|
||||
const res = participations.filter(
|
||||
({ event, role }) => event.beginsOn != null && role !== ParticipantRole.REJECTED
|
||||
({ event, role }) =>
|
||||
event.beginsOn != null && role !== ParticipantRole.REJECTED
|
||||
);
|
||||
if (revertSort) {
|
||||
res.sort(
|
||||
@@ -183,16 +192,22 @@ export default class MyEvents extends Vue {
|
||||
a.event.beginsOn.getTime() - b.event.beginsOn.getTime()
|
||||
);
|
||||
}
|
||||
return res.reduce((acc: Map<string, IParticipant[]>, participation: IParticipant) => {
|
||||
const month = new Date(participation.event.beginsOn).toLocaleDateString(undefined, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
});
|
||||
const filteredParticipations: IParticipant[] = acc.get(month) || [];
|
||||
filteredParticipations.push(participation);
|
||||
acc.set(month, filteredParticipations);
|
||||
return acc;
|
||||
}, new Map());
|
||||
return res.reduce(
|
||||
(acc: Map<string, IParticipant[]>, participation: IParticipant) => {
|
||||
const month = new Date(participation.event.beginsOn).toLocaleDateString(
|
||||
undefined,
|
||||
{
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
}
|
||||
);
|
||||
const filteredParticipations: IParticipant[] = acc.get(month) || [];
|
||||
filteredParticipations.push(participation);
|
||||
acc.set(month, filteredParticipations);
|
||||
return acc;
|
||||
},
|
||||
new Map()
|
||||
);
|
||||
}
|
||||
|
||||
get monthlyFutureParticipations(): Map<string, Participant[]> {
|
||||
@@ -216,7 +231,8 @@ export default class MyEvents extends Vue {
|
||||
const oldParticipations = previousResult.loggedUser.participations;
|
||||
const newParticipations = fetchMoreResult.loggedUser.participations;
|
||||
this.hasMoreFutureParticipations =
|
||||
newParticipations.total === oldParticipations.length + newParticipations.length;
|
||||
newParticipations.total ===
|
||||
oldParticipations.length + newParticipations.length;
|
||||
|
||||
return {
|
||||
loggedUser: {
|
||||
@@ -224,7 +240,10 @@ export default class MyEvents extends Vue {
|
||||
participations: {
|
||||
__typename: newParticipations.__typename,
|
||||
total: newParticipations.total,
|
||||
elements: [...oldParticipations.elements, ...newParticipations.elements],
|
||||
elements: [
|
||||
...oldParticipations.elements,
|
||||
...newParticipations.elements,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -245,7 +264,8 @@ export default class MyEvents extends Vue {
|
||||
const oldParticipations = previousResult.loggedUser.participations;
|
||||
const newParticipations = fetchMoreResult.loggedUser.participations;
|
||||
this.hasMorePastParticipations =
|
||||
newParticipations.total === oldParticipations.length + newParticipations.length;
|
||||
newParticipations.total ===
|
||||
oldParticipations.length + newParticipations.length;
|
||||
|
||||
return {
|
||||
loggedUser: {
|
||||
@@ -253,7 +273,10 @@ export default class MyEvents extends Vue {
|
||||
participations: {
|
||||
__typename: newParticipations.__typename,
|
||||
total: newParticipations.total,
|
||||
elements: [...oldParticipations.elements, ...newParticipations.elements],
|
||||
elements: [
|
||||
...oldParticipations.elements,
|
||||
...newParticipations.elements,
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MY_EVENTS }">{{ $t("My events") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MY_EVENTS }">{{
|
||||
$t("My events")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
@@ -72,10 +74,21 @@
|
||||
@page-change="(newPage) => (page = newPage)"
|
||||
@sort="(field, order) => $emit('sort', field, order)"
|
||||
>
|
||||
<b-table-column field="actor.preferredUsername" :label="$t('Participant')" v-slot="props">
|
||||
<b-table-column
|
||||
field="actor.preferredUsername"
|
||||
:label="$t('Participant')"
|
||||
v-slot="props"
|
||||
>
|
||||
<article class="media">
|
||||
<figure class="media-left image is-48x48" v-if="props.row.actor.avatar">
|
||||
<img class="is-rounded" :src="props.row.actor.avatar.url" alt="" />
|
||||
<figure
|
||||
class="media-left image is-48x48"
|
||||
v-if="props.row.actor.avatar"
|
||||
>
|
||||
<img
|
||||
class="is-rounded"
|
||||
:src="props.row.actor.avatar.url"
|
||||
alt=""
|
||||
/>
|
||||
</figure>
|
||||
<b-icon
|
||||
class="media-left"
|
||||
@@ -83,11 +96,18 @@
|
||||
size="is-large"
|
||||
icon="incognito"
|
||||
/>
|
||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||
<b-icon
|
||||
class="media-left"
|
||||
v-else
|
||||
size="is-large"
|
||||
icon="account-circle"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<span v-if="props.row.actor.preferredUsername !== 'anonymous'">
|
||||
<span v-if="props.row.actor.name">{{ props.row.actor.name }}</span
|
||||
<span v-if="props.row.actor.name">{{
|
||||
props.row.actor.name
|
||||
}}</span
|
||||
><br />
|
||||
<span class="is-size-7 has-text-grey"
|
||||
>@{{ usernameWithDomain(props.row.actor) }}</span
|
||||
@@ -101,7 +121,10 @@
|
||||
</article>
|
||||
</b-table-column>
|
||||
<b-table-column field="role" :label="$t('Role')" v-slot="props">
|
||||
<b-tag type="is-primary" v-if="props.row.role === ParticipantRole.CREATOR">
|
||||
<b-tag
|
||||
type="is-primary"
|
||||
v-if="props.row.role === ParticipantRole.CREATOR"
|
||||
>
|
||||
{{ $t("Organizer") }}
|
||||
</b-tag>
|
||||
<b-tag v-else-if="props.row.role === ParticipantRole.PARTICIPANT">
|
||||
@@ -110,18 +133,29 @@
|
||||
<b-tag v-else-if="props.row.role === ParticipantRole.NOT_CONFIRMED">
|
||||
{{ $t("Not confirmed") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-warning" v-else-if="props.row.role === ParticipantRole.NOT_APPROVED">
|
||||
<b-tag
|
||||
type="is-warning"
|
||||
v-else-if="props.row.role === ParticipantRole.NOT_APPROVED"
|
||||
>
|
||||
{{ $t("Not approved") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-danger" v-else-if="props.row.role === ParticipantRole.REJECTED">
|
||||
<b-tag
|
||||
type="is-danger"
|
||||
v-else-if="props.row.role === ParticipantRole.REJECTED"
|
||||
>
|
||||
{{ $t("Rejected") }}
|
||||
</b-tag>
|
||||
</b-table-column>
|
||||
<b-table-column field="metadata.message" :label="$t('Message')" v-slot="props">
|
||||
<b-table-column
|
||||
field="metadata.message"
|
||||
:label="$t('Message')"
|
||||
v-slot="props"
|
||||
>
|
||||
<span
|
||||
@click="toggleQueueDetails(props.row)"
|
||||
:class="{
|
||||
'ellipsed-message': props.row.metadata.message.length > MESSAGE_ELLIPSIS_LENGTH,
|
||||
'ellipsed-message':
|
||||
props.row.metadata.message.length > MESSAGE_ELLIPSIS_LENGTH,
|
||||
}"
|
||||
v-if="props.row.metadata && props.row.metadata.message"
|
||||
>
|
||||
@@ -223,7 +257,8 @@ const MESSAGE_ELLIPSIS_LENGTH = 130;
|
||||
},
|
||||
},
|
||||
filters: {
|
||||
ellipsize: (text?: string) => text && text.substr(0, MESSAGE_ELLIPSIS_LENGTH).concat("…"),
|
||||
ellipsize: (text?: string) =>
|
||||
text && text.substr(0, MESSAGE_ELLIPSIS_LENGTH).concat("…"),
|
||||
},
|
||||
})
|
||||
export default class Participants extends Vue {
|
||||
@@ -282,7 +317,10 @@ export default class Participants extends Vue {
|
||||
event: {
|
||||
...previousResult.event,
|
||||
participants: {
|
||||
elements: [...oldParticipants.elements, ...newParticipants.elements],
|
||||
elements: [
|
||||
...oldParticipants.elements,
|
||||
...newParticipants.elements,
|
||||
],
|
||||
total: newParticipants.total,
|
||||
__typename: oldParticipants.__typename,
|
||||
},
|
||||
@@ -339,7 +377,9 @@ export default class Participants extends Vue {
|
||||
*/
|
||||
get canAcceptParticipants(): boolean {
|
||||
return this.checkedRows.some((participant: IParticipant) =>
|
||||
[ParticipantRole.NOT_APPROVED, ParticipantRole.REJECTED].includes(participant.role)
|
||||
[ParticipantRole.NOT_APPROVED, ParticipantRole.REJECTED].includes(
|
||||
participant.role
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -348,7 +388,8 @@ export default class Participants extends Vue {
|
||||
*/
|
||||
get canRefuseParticipants(): boolean {
|
||||
return this.checkedRows.some(
|
||||
(participant: IParticipant) => participant.role !== ParticipantRole.REJECTED
|
||||
(participant: IParticipant) =>
|
||||
participant.role !== ParticipantRole.REJECTED
|
||||
);
|
||||
}
|
||||
|
||||
@@ -357,7 +398,11 @@ export default class Participants extends Vue {
|
||||
nl2br = nl2br;
|
||||
|
||||
toggleQueueDetails(row: IParticipant): void {
|
||||
if (row.metadata.message && row.metadata.message.length < MESSAGE_ELLIPSIS_LENGTH) return;
|
||||
if (
|
||||
row.metadata.message &&
|
||||
row.metadata.message.length < MESSAGE_ELLIPSIS_LENGTH
|
||||
)
|
||||
return;
|
||||
this.queueTable.toggleDetails(row);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
<label class="label">{{ $t("Federated Group Name") }}</label>
|
||||
<div class="field-body">
|
||||
<b-field
|
||||
:message="$t('Only alphanumeric lowercased characters and underscores are supported.')"
|
||||
:message="
|
||||
$t(
|
||||
'Only alphanumeric lowercased characters and underscores are supported.'
|
||||
)
|
||||
"
|
||||
>
|
||||
<b-input
|
||||
ref="preferredUsernameInput"
|
||||
@@ -27,7 +31,9 @@
|
||||
:useHtml5Validation="true"
|
||||
:validation-message="
|
||||
group.preferredUsername
|
||||
? $t('Only alphanumeric lowercased characters and underscores are supported.')
|
||||
? $t(
|
||||
'Only alphanumeric lowercased characters and underscores are supported.'
|
||||
)
|
||||
: null
|
||||
"
|
||||
/>
|
||||
@@ -60,7 +66,9 @@
|
||||
<picture-upload :textFallback="$t('Banner')" v-model="bannerFile" />
|
||||
</div>
|
||||
|
||||
<button class="button is-primary" native-type="submit">{{ $t("Create my group") }}</button>
|
||||
<button class="button is-primary" native-type="submit">
|
||||
{{ $t("Create my group") }}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</template>
|
||||
@@ -187,7 +195,9 @@ export default class CreateGroup extends mixins(IdentityEditionMixin) {
|
||||
}
|
||||
|
||||
private handleError(err: any) {
|
||||
this.errors.push(...err.graphQLErrors.map(({ message }: { message: string }) => message));
|
||||
this.errors.push(
|
||||
...err.graphQLErrors.map(({ message }: { message: string }) => message)
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{ $t("My groups") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MY_GROUPS }">{{
|
||||
$t("My groups")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link
|
||||
@@ -28,8 +30,15 @@
|
||||
<b-message v-if="isCurrentActorARejectedGroupMember" type="is-danger">
|
||||
{{ $t("You have been removed from this group's members.") }}
|
||||
</b-message>
|
||||
<b-message v-if="isCurrentActorAGroupMember && isCurrentActorARecentMember" type="is-info">
|
||||
{{ $t("Since you are a new member, private content can take a few minutes to appear.") }}
|
||||
<b-message
|
||||
v-if="isCurrentActorAGroupMember && isCurrentActorARecentMember"
|
||||
type="is-info"
|
||||
>
|
||||
{{
|
||||
$t(
|
||||
"Since you are a new member, private content can take a few minutes to appear."
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
<header class="block-container presentation">
|
||||
<div class="block-column media">
|
||||
@@ -96,7 +105,10 @@
|
||||
</div>
|
||||
<div class="block-column address" v-else>
|
||||
<address v-if="physicalAddress">
|
||||
<p class="addressDescription" :title="physicalAddress.poiInfos.name">
|
||||
<p
|
||||
class="addressDescription"
|
||||
:title="physicalAddress.poiInfos.name"
|
||||
>
|
||||
{{ physicalAddress.poiInfos.name }}
|
||||
</p>
|
||||
<p>{{ physicalAddress.poiInfos.alternativeName }}</p>
|
||||
@@ -113,11 +125,16 @@
|
||||
:label="$t('This group is invite-only')"
|
||||
position="is-bottom"
|
||||
>
|
||||
<b-button disabled type="is-primary">{{ $t("Join group") }}</b-button></b-tooltip
|
||||
<b-button disabled type="is-primary">{{
|
||||
$t("Join group")
|
||||
}}</b-button></b-tooltip
|
||||
>
|
||||
<b-button
|
||||
v-else-if="currentActor.id"
|
||||
@click="joinGroup"
|
||||
type="is-primary"
|
||||
>{{ $t("Join group") }}</b-button
|
||||
>
|
||||
<b-button v-else-if="currentActor.id" @click="joinGroup" type="is-primary">{{
|
||||
$t("Join group")
|
||||
}}</b-button>
|
||||
<b-button
|
||||
tag="router-link"
|
||||
:to="{
|
||||
@@ -129,7 +146,12 @@
|
||||
>{{ $t("Join group") }}</b-button
|
||||
>
|
||||
<b-dropdown aria-role="list" position="is-bottom-left">
|
||||
<b-button slot="trigger" role="button" icon-right="dots-horizontal"> </b-button>
|
||||
<b-button
|
||||
slot="trigger"
|
||||
role="button"
|
||||
icon-right="dots-horizontal"
|
||||
>
|
||||
</b-button>
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-if="ableToReport"
|
||||
@@ -143,7 +165,11 @@
|
||||
</b-dropdown>
|
||||
</p>
|
||||
</div>
|
||||
<img v-if="group.banner && group.banner.url" :src="group.banner.url" alt="" />
|
||||
<img
|
||||
v-if="group.banner && group.banner.url"
|
||||
:src="group.banner.url"
|
||||
alt=""
|
||||
/>
|
||||
</header>
|
||||
</div>
|
||||
<div v-if="isCurrentActorAGroupMember" class="block-container">
|
||||
@@ -192,16 +218,27 @@
|
||||
>
|
||||
<template v-slot:default>
|
||||
<div v-if="group.resources.elements.length > 0">
|
||||
<div v-for="resource in group.resources.elements" :key="resource.id">
|
||||
<div
|
||||
v-for="resource in group.resources.elements"
|
||||
:key="resource.id"
|
||||
>
|
||||
<resource-item
|
||||
:resource="resource"
|
||||
v-if="resource.type !== 'folder'"
|
||||
:inline="true"
|
||||
/>
|
||||
<folder-item :resource="resource" :group="group" v-else :inline="true" />
|
||||
<folder-item
|
||||
:resource="resource"
|
||||
:group="group"
|
||||
v-else
|
||||
:inline="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="group" class="content has-text-grey has-text-centered">
|
||||
<div
|
||||
v-else-if="group"
|
||||
class="content has-text-grey has-text-centered"
|
||||
>
|
||||
<p>{{ $t("No resources yet") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -230,7 +267,10 @@
|
||||
}"
|
||||
>
|
||||
<template v-slot:default>
|
||||
<div class="organized-events-wrapper" v-if="group && group.organizedEvents.total > 0">
|
||||
<div
|
||||
class="organized-events-wrapper"
|
||||
v-if="group && group.organizedEvents.total > 0"
|
||||
>
|
||||
<EventMinimalistCard
|
||||
v-for="event in group.organizedEvents.elements"
|
||||
:event="event"
|
||||
@@ -238,7 +278,10 @@
|
||||
class="organized-event"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="group" class="content has-text-grey has-text-centered">
|
||||
<div
|
||||
v-else-if="group"
|
||||
class="content has-text-grey has-text-centered"
|
||||
>
|
||||
<p>{{ $t("No public upcoming events") }}</p>
|
||||
</div>
|
||||
<b-skeleton animated v-else></b-skeleton>
|
||||
@@ -266,9 +309,16 @@
|
||||
>
|
||||
<template v-slot:default>
|
||||
<div v-if="group.posts.total > 0" class="posts-wrapper">
|
||||
<post-list-item v-for="post in group.posts.elements" :key="post.id" :post="post" />
|
||||
<post-list-item
|
||||
v-for="post in group.posts.elements"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="group" class="content has-text-grey has-text-centered">
|
||||
<div
|
||||
v-else-if="group"
|
||||
class="content has-text-grey has-text-centered"
|
||||
>
|
||||
<p>{{ $t("No posts yet") }}</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -292,14 +342,20 @@
|
||||
<div v-else class="public-container">
|
||||
<section>
|
||||
<subtitle>{{ $t("About") }}</subtitle>
|
||||
<div v-html="group.summary" v-if="group.summary && group.summary !== '<p></p>'" />
|
||||
<div
|
||||
v-html="group.summary"
|
||||
v-if="group.summary && group.summary !== '<p></p>'"
|
||||
/>
|
||||
<div v-else-if="group" class="content has-text-grey has-text-centered">
|
||||
<p>{{ $t("This group doesn't have a description yet.") }}</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<subtitle>{{ $t("Upcoming events") }}</subtitle>
|
||||
<div class="organized-events-wrapper" v-if="group && group.organizedEvents.total > 0">
|
||||
<div
|
||||
class="organized-events-wrapper"
|
||||
v-if="group && group.organizedEvents.total > 0"
|
||||
>
|
||||
<EventMinimalistCard
|
||||
v-for="event in group.organizedEvents.elements"
|
||||
:event="event"
|
||||
@@ -322,7 +378,11 @@
|
||||
<section>
|
||||
<subtitle>{{ $t("Latest posts") }}</subtitle>
|
||||
<div v-if="group.posts.total > 0" class="posts-wrapper">
|
||||
<post-list-item v-for="post in group.posts.elements" :key="post.id" :post="post" />
|
||||
<post-list-item
|
||||
v-for="post in group.posts.elements"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
/>
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.POSTS,
|
||||
@@ -336,7 +396,10 @@
|
||||
</div>
|
||||
<b-skeleton animated v-else></b-skeleton>
|
||||
</section>
|
||||
<b-modal v-if="physicalAddress && physicalAddress.geom" :active.sync="showMap">
|
||||
<b-modal
|
||||
v-if="physicalAddress && physicalAddress.geom"
|
||||
:active.sync="showMap"
|
||||
>
|
||||
<div class="map">
|
||||
<map-leaflet
|
||||
:coords="physicalAddress.geom"
|
||||
@@ -347,7 +410,11 @@
|
||||
/>
|
||||
</div>
|
||||
</b-modal>
|
||||
<b-modal :active.sync="isReportModalActive" has-modal-card ref="reportModal">
|
||||
<b-modal
|
||||
:active.sync="isReportModalActive"
|
||||
has-modal-card
|
||||
ref="reportModal"
|
||||
>
|
||||
<report-modal
|
||||
:on-confirm="reportGroup"
|
||||
:title="$t('Report this group')"
|
||||
@@ -402,7 +469,8 @@ import ReportModal from "../../components/Report/ReportModal.vue";
|
||||
GroupSection,
|
||||
Invitations,
|
||||
ReportModal,
|
||||
"map-leaflet": () => import(/* webpackChunkName: "map" */ "../../components/Map.vue"),
|
||||
"map-leaflet": () =>
|
||||
import(/* webpackChunkName: "map" */ "../../components/Map.vue"),
|
||||
},
|
||||
metaInfo() {
|
||||
return {
|
||||
@@ -469,7 +537,8 @@ export default class Group extends mixins(GroupMixin) {
|
||||
|
||||
rejectInvitation({ id: memberId }: { id: string }): void {
|
||||
const index = this.person.memberships.elements.findIndex(
|
||||
(membership) => membership.role === MemberRole.INVITED && membership.id === memberId
|
||||
(membership) =>
|
||||
membership.role === MemberRole.INVITED && membership.id === memberId
|
||||
);
|
||||
if (index > -1) {
|
||||
this.person.memberships.elements.splice(index, 1);
|
||||
@@ -492,11 +561,15 @@ export default class Group extends mixins(GroupMixin) {
|
||||
forward,
|
||||
},
|
||||
});
|
||||
this.$notifier.success(this.$t("Group {groupTitle} reported", { groupTitle }) as string);
|
||||
this.$notifier.success(
|
||||
this.$t("Group {groupTitle} reported", { groupTitle }) as string
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.$notifier.error(
|
||||
this.$t("Error while reporting group {groupTitle}", { groupTitle }) as string
|
||||
this.$t("Error while reporting group {groupTitle}", {
|
||||
groupTitle,
|
||||
}) as string
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -513,7 +586,9 @@ export default class Group extends mixins(GroupMixin) {
|
||||
|
||||
get groupMember(): IMember | undefined {
|
||||
if (!this.person || !this.person.id) return undefined;
|
||||
return this.person.memberships.elements.find(({ parent: { id } }) => id === this.group.id);
|
||||
return this.person.memberships.elements.find(
|
||||
({ parent: { id } }) => id === this.group.id
|
||||
);
|
||||
}
|
||||
|
||||
get groupMemberships(): (string | undefined)[] {
|
||||
@@ -521,9 +596,11 @@ export default class Group extends mixins(GroupMixin) {
|
||||
return this.person.memberships.elements
|
||||
.filter(
|
||||
(membership: IMember) =>
|
||||
![MemberRole.REJECTED, MemberRole.NOT_APPROVED, MemberRole.INVITED].includes(
|
||||
membership.role
|
||||
)
|
||||
![
|
||||
MemberRole.REJECTED,
|
||||
MemberRole.NOT_APPROVED,
|
||||
MemberRole.INVITED,
|
||||
].includes(membership.role)
|
||||
)
|
||||
.map(({ parent: { id } }) => id);
|
||||
}
|
||||
@@ -535,7 +612,10 @@ export default class Group extends mixins(GroupMixin) {
|
||||
}
|
||||
|
||||
get isCurrentActorAGroupMember(): boolean {
|
||||
return this.groupMemberships !== undefined && this.groupMemberships.includes(this.group.id);
|
||||
return (
|
||||
this.groupMemberships !== undefined &&
|
||||
this.groupMemberships.includes(this.group.id)
|
||||
);
|
||||
}
|
||||
|
||||
get isCurrentActorARejectedGroupMember(): boolean {
|
||||
@@ -573,7 +653,11 @@ export default class Group extends mixins(GroupMixin) {
|
||||
get members(): IMember[] {
|
||||
return this.group.members.elements.filter(
|
||||
(member) =>
|
||||
![MemberRole.INVITED, MemberRole.REJECTED, MemberRole.NOT_APPROVED].includes(member.role)
|
||||
![
|
||||
MemberRole.INVITED,
|
||||
MemberRole.REJECTED,
|
||||
MemberRole.NOT_APPROVED,
|
||||
].includes(member.role)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -584,7 +668,9 @@ export default class Group extends mixins(GroupMixin) {
|
||||
|
||||
get ableToReport(): boolean {
|
||||
return (
|
||||
this.config && (this.currentActor.id !== undefined || this.config.anonymous.reports.allowed)
|
||||
this.config &&
|
||||
(this.currentActor.id !== undefined ||
|
||||
this.config.anonymous.reports.allowed)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -725,7 +811,8 @@ div.container {
|
||||
.media-content {
|
||||
h2 {
|
||||
color: #3c376e;
|
||||
font-family: "Liberation Sans", "Helvetica Neue", Roboto, Helvetica, Arial, serif;
|
||||
font-family: "Liberation Sans", "Helvetica Neue", Roboto,
|
||||
Helvetica, Arial, serif;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,16 @@
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<section class="container section" v-if="group && isCurrentActorAGroupAdmin">
|
||||
<section
|
||||
class="container section"
|
||||
v-if="group && isCurrentActorAGroupAdmin"
|
||||
>
|
||||
<form @submit.prevent="inviteMember">
|
||||
<b-field :label="$t('Invite a new member')" custom-class="add-relay" horizontal>
|
||||
<b-field
|
||||
:label="$t('Invite a new member')"
|
||||
custom-class="add-relay"
|
||||
horizontal
|
||||
>
|
||||
<b-field
|
||||
grouped
|
||||
expanded
|
||||
@@ -42,10 +49,15 @@
|
||||
:message="inviteError"
|
||||
>
|
||||
<p class="control">
|
||||
<b-input v-model="newMemberUsername" :placeholder="$t('Ex: someone@mobilizon.org')" />
|
||||
<b-input
|
||||
v-model="newMemberUsername"
|
||||
:placeholder="$t('Ex: someone@mobilizon.org')"
|
||||
/>
|
||||
</p>
|
||||
<p class="control">
|
||||
<b-button type="is-primary" native-type="submit">{{ $t("Invite member") }}</b-button>
|
||||
<b-button type="is-primary" native-type="submit">{{
|
||||
$t("Invite member")
|
||||
}}</b-button>
|
||||
</p>
|
||||
</b-field>
|
||||
</b-field>
|
||||
@@ -97,15 +109,33 @@
|
||||
@page-change="triggerLoadMoreMemberPageChange"
|
||||
@sort="(field, order) => $emit('sort', field, order)"
|
||||
>
|
||||
<b-table-column field="actor.preferredUsername" :label="$t('Member')" v-slot="props">
|
||||
<b-table-column
|
||||
field="actor.preferredUsername"
|
||||
:label="$t('Member')"
|
||||
v-slot="props"
|
||||
>
|
||||
<article class="media">
|
||||
<figure class="media-left image is-48x48" v-if="props.row.actor.avatar">
|
||||
<img class="is-rounded" :src="props.row.actor.avatar.url" alt="" />
|
||||
<figure
|
||||
class="media-left image is-48x48"
|
||||
v-if="props.row.actor.avatar"
|
||||
>
|
||||
<img
|
||||
class="is-rounded"
|
||||
:src="props.row.actor.avatar.url"
|
||||
alt=""
|
||||
/>
|
||||
</figure>
|
||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||
<b-icon
|
||||
class="media-left"
|
||||
v-else
|
||||
size="is-large"
|
||||
icon="account-circle"
|
||||
/>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<span v-if="props.row.actor.name">{{ props.row.actor.name }}</span
|
||||
<span v-if="props.row.actor.name">{{
|
||||
props.row.actor.name
|
||||
}}</span
|
||||
><br />
|
||||
<span class="is-size-7 has-text-grey"
|
||||
>@{{ usernameWithDomain(props.row.actor) }}</span
|
||||
@@ -115,22 +145,37 @@
|
||||
</article>
|
||||
</b-table-column>
|
||||
<b-table-column field="role" :label="$t('Role')" v-slot="props">
|
||||
<b-tag type="is-primary" v-if="props.row.role === MemberRole.ADMINISTRATOR">
|
||||
<b-tag
|
||||
type="is-primary"
|
||||
v-if="props.row.role === MemberRole.ADMINISTRATOR"
|
||||
>
|
||||
{{ $t("Administrator") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-primary" v-else-if="props.row.role === MemberRole.MODERATOR">
|
||||
<b-tag
|
||||
type="is-primary"
|
||||
v-else-if="props.row.role === MemberRole.MODERATOR"
|
||||
>
|
||||
{{ $t("Moderator") }}
|
||||
</b-tag>
|
||||
<b-tag v-else-if="props.row.role === MemberRole.MEMBER">
|
||||
{{ $t("Member") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-warning" v-else-if="props.row.role === MemberRole.NOT_APPROVED">
|
||||
<b-tag
|
||||
type="is-warning"
|
||||
v-else-if="props.row.role === MemberRole.NOT_APPROVED"
|
||||
>
|
||||
{{ $t("Not approved") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-danger" v-else-if="props.row.role === MemberRole.REJECTED">
|
||||
<b-tag
|
||||
type="is-danger"
|
||||
v-else-if="props.row.role === MemberRole.REJECTED"
|
||||
>
|
||||
{{ $t("Rejected") }}
|
||||
</b-tag>
|
||||
<b-tag type="is-warning" v-else-if="props.row.role === MemberRole.INVITED">
|
||||
<b-tag
|
||||
type="is-warning"
|
||||
v-else-if="props.row.role === MemberRole.INVITED"
|
||||
>
|
||||
{{ $t("Invited") }}
|
||||
</b-tag>
|
||||
</b-table-column>
|
||||
@@ -144,13 +189,21 @@
|
||||
<b-table-column field="actions" :label="$t('Actions')" v-slot="props">
|
||||
<div class="buttons" v-if="props.row.actor.id !== currentActor.id">
|
||||
<b-button
|
||||
v-if="[MemberRole.MEMBER, MemberRole.MODERATOR].includes(props.row.role)"
|
||||
v-if="
|
||||
[MemberRole.MEMBER, MemberRole.MODERATOR].includes(
|
||||
props.row.role
|
||||
)
|
||||
"
|
||||
@click="promoteMember(props.row)"
|
||||
icon-left="chevron-double-up"
|
||||
>{{ $t("Promote") }}</b-button
|
||||
>
|
||||
<b-button
|
||||
v-if="[MemberRole.ADMINISTRATOR, MemberRole.MODERATOR].includes(props.row.role)"
|
||||
v-if="
|
||||
[MemberRole.ADMINISTRATOR, MemberRole.MODERATOR].includes(
|
||||
props.row.role
|
||||
)
|
||||
"
|
||||
@click="demoteMember(props.row)"
|
||||
icon-left="chevron-double-down"
|
||||
>{{ $t("Demote") }}</b-button
|
||||
@@ -187,7 +240,12 @@ import { FETCH_GROUP } from "@/graphql/group";
|
||||
import { MemberRole } from "@/types/enums";
|
||||
import { IMember } from "@/types/actor/member.model";
|
||||
import RouteName from "../../router/name";
|
||||
import { INVITE_MEMBER, GROUP_MEMBERS, REMOVE_MEMBER, UPDATE_MEMBER } from "../../graphql/member";
|
||||
import {
|
||||
INVITE_MEMBER,
|
||||
GROUP_MEMBERS,
|
||||
REMOVE_MEMBER,
|
||||
UPDATE_MEMBER,
|
||||
} from "../../graphql/member";
|
||||
import { usernameWithDomain } from "../../types/actor";
|
||||
|
||||
@Component({
|
||||
@@ -380,7 +438,10 @@ export default class GroupMembers extends mixins(GroupMixin) {
|
||||
role,
|
||||
},
|
||||
refetchQueries: [
|
||||
{ query: FETCH_GROUP, variables: { name: this.$route.params.preferredUsername } },
|
||||
{
|
||||
query: FETCH_GROUP,
|
||||
variables: { name: this.$route.params.preferredUsername },
|
||||
},
|
||||
],
|
||||
});
|
||||
let successMessage;
|
||||
|
||||
@@ -31,7 +31,10 @@
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<section class="container section" v-if="group && isCurrentActorAGroupAdmin">
|
||||
<section
|
||||
class="container section"
|
||||
v-if="group && isCurrentActorAGroupAdmin"
|
||||
>
|
||||
<form @submit.prevent="updateGroup">
|
||||
<b-field :label="$t('Group name')">
|
||||
<b-input v-model="group.name" />
|
||||
@@ -104,7 +107,11 @@
|
||||
|
||||
<p class="label">{{ $t("New members") }}</p>
|
||||
<div class="field">
|
||||
<b-radio v-model="group.openness" name="groupOpenness" :native-value="Openness.OPEN">
|
||||
<b-radio
|
||||
v-model="group.openness"
|
||||
name="groupOpenness"
|
||||
:native-value="Openness.OPEN"
|
||||
>
|
||||
{{ $t("Anyone can join freely") }}<br />
|
||||
<small>{{
|
||||
$t(
|
||||
@@ -134,8 +141,12 @@
|
||||
/>
|
||||
|
||||
<div class="buttons">
|
||||
<b-button native-type="submit" type="is-primary">{{ $t("Update group") }}</b-button>
|
||||
<b-button @click="confirmDeleteGroup" type="is-danger">{{ $t("Delete group") }}</b-button>
|
||||
<b-button native-type="submit" type="is-primary">{{
|
||||
$t("Update group")
|
||||
}}</b-button>
|
||||
<b-button @click="confirmDeleteGroup" type="is-danger">{{
|
||||
$t("Delete group")
|
||||
}}</b-button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
}}
|
||||
</p>
|
||||
<div class="buttons">
|
||||
<router-link class="button is-primary" :to="{ name: RouteName.CREATE_GROUP }">{{
|
||||
$t("Create group")
|
||||
}}</router-link>
|
||||
<router-link
|
||||
class="button is-primary"
|
||||
:to="{ name: RouteName.CREATE_GROUP }"
|
||||
>{{ $t("Create group") }}</router-link
|
||||
>
|
||||
</div>
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<invitations
|
||||
@@ -38,7 +40,10 @@
|
||||
>
|
||||
</b-pagination>
|
||||
</section>
|
||||
<b-message v-if="$apollo.loading === false && memberships.length === 0" type="is-danger">
|
||||
<b-message
|
||||
v-if="$apollo.loading === false && memberships.length === 0"
|
||||
type="is-danger"
|
||||
>
|
||||
{{ $t("No groups found") }}
|
||||
</b-message>
|
||||
</section>
|
||||
@@ -102,7 +107,8 @@ export default class MyGroups extends Vue {
|
||||
|
||||
rejectInvitation({ id: memberId }: { id: string }): void {
|
||||
const index = this.membershipsPages.elements.findIndex(
|
||||
(membership) => membership.role === MemberRole.INVITED && membership.id === memberId
|
||||
(membership) =>
|
||||
membership.role === MemberRole.INVITED && membership.id === memberId
|
||||
);
|
||||
if (index > -1) {
|
||||
this.membershipsPages.elements.splice(index, 1);
|
||||
@@ -139,7 +145,8 @@ export default class MyGroups extends Vue {
|
||||
get memberships(): IMember[] {
|
||||
if (!this.membershipsPages) return [];
|
||||
return this.membershipsPages.elements.filter(
|
||||
(member: IMember) => ![MemberRole.INVITED, MemberRole.REJECTED].includes(member.role)
|
||||
(member: IMember) =>
|
||||
![MemberRole.INVITED, MemberRole.REJECTED].includes(member.role)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
<div class="columns">
|
||||
<aside class="column is-one-quarter-desktop">
|
||||
<ul>
|
||||
<SettingMenuSection :title="$t('Settings')" :to="{ name: RouteName.GROUP_SETTINGS }">
|
||||
<SettingMenuSection
|
||||
:title="$t('Settings')"
|
||||
:to="{ name: RouteName.GROUP_SETTINGS }"
|
||||
>
|
||||
<SettingMenuItem
|
||||
:title="this.$t('Public')"
|
||||
:to="{ name: RouteName.GROUP_PUBLIC_SETTINGS }"
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
<template>
|
||||
<div id="homepage">
|
||||
<section class="hero" v-if="config && (!currentUser.id || !currentActor.id)">
|
||||
<section
|
||||
class="hero"
|
||||
v-if="config && (!currentUser.id || !currentActor.id)"
|
||||
>
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<h1 class="title">{{ config.slogan || $t("Gather ⋅ Organize ⋅ Mobilize") }}</h1>
|
||||
<h1 class="title">
|
||||
{{ config.slogan || $t("Gather ⋅ Organize ⋅ Mobilize") }}
|
||||
</h1>
|
||||
<p
|
||||
v-html="$t('Join <b>{instance}</b>, a Mobilizon instance', { instance: config.name })"
|
||||
v-html="
|
||||
$t('Join <b>{instance}</b>, a Mobilizon instance', {
|
||||
instance: config.name,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<p class="instance-description">{{ config.description }}</p>
|
||||
<!-- We don't invite to find other instances yet -->
|
||||
@@ -25,7 +34,11 @@
|
||||
>
|
||||
<!-- We don't invite to find other instances yet -->
|
||||
<!-- <b-button v-else type="is-link" tag="a" href="https://joinmastodon.org">{{ $t('Find an instance') }}</b-button> -->
|
||||
<b-button type="is-text" tag="router-link" :to="{ name: RouteName.ABOUT }">
|
||||
<b-button
|
||||
type="is-text"
|
||||
tag="router-link"
|
||||
:to="{ name: RouteName.ABOUT }"
|
||||
>
|
||||
{{ $t("Learn more about {instance}", { instance: config.name }) }}
|
||||
</b-button>
|
||||
</div>
|
||||
@@ -40,7 +53,10 @@
|
||||
<section class="events-featured">
|
||||
<h2 class="title">{{ $t("Featured events") }}</h2>
|
||||
<b-loading :active.sync="$apollo.loading" />
|
||||
<div v-if="filteredFeaturedEvents.length > 0" class="columns is-multiline">
|
||||
<div
|
||||
v-if="filteredFeaturedEvents.length > 0"
|
||||
class="columns is-multiline"
|
||||
>
|
||||
<div
|
||||
class="column is-one-third-desktop"
|
||||
v-for="event in filteredFeaturedEvents.slice(0, 6)"
|
||||
@@ -49,12 +65,17 @@
|
||||
<EventCard :event="event" />
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else type="is-danger">{{ $t("No events found") }}</b-message>
|
||||
<b-message v-else type="is-danger">{{
|
||||
$t("No events found")
|
||||
}}</b-message>
|
||||
</section>
|
||||
</div>
|
||||
<div id="picture" v-if="config && (!currentUser.id || !currentActor.id)">
|
||||
<div class="picture-container">
|
||||
<img src="/img/pics/2020-10-06-mobilizon-illustration-A_homepage.jpg" alt="" />
|
||||
<img
|
||||
src="/img/pics/2020-10-06-mobilizon-illustration-A_homepage.jpg"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="container section">
|
||||
<div class="columns">
|
||||
@@ -62,7 +83,9 @@
|
||||
<h3 class="title">{{ $t("A practical tool") }}</h3>
|
||||
<p
|
||||
v-html="
|
||||
$t('Mobilizon is a tool that helps you <b>find, create and organise events</b>.')
|
||||
$t(
|
||||
'Mobilizon is a tool that helps you <b>find, create and organise events</b>.'
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
@@ -88,32 +111,51 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<a class="button is-primary is-large" href="https://joinmobilizon.org">{{
|
||||
$t("Learn more about Mobilizon")
|
||||
}}</a>
|
||||
<a
|
||||
class="button is-primary is-large"
|
||||
href="https://joinmobilizon.org"
|
||||
>{{ $t("Learn more about Mobilizon") }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container section" v-if="config && loggedUser && loggedUser.settings">
|
||||
<div
|
||||
class="container section"
|
||||
v-if="config && loggedUser && loggedUser.settings"
|
||||
>
|
||||
<section v-if="currentActor.id">
|
||||
<b-message type="is-info" v-if="welcomeBack">{{
|
||||
$t("Welcome back {username}!", { username: currentActor.displayName() })
|
||||
$t("Welcome back {username}!", {
|
||||
username: currentActor.displayName(),
|
||||
})
|
||||
}}</b-message>
|
||||
<b-message type="is-info" v-if="newRegisteredUser">{{
|
||||
$t("Welcome to Mobilizon, {username}!", { username: currentActor.displayName() })
|
||||
$t("Welcome to Mobilizon, {username}!", {
|
||||
username: currentActor.displayName(),
|
||||
})
|
||||
}}</b-message>
|
||||
</section>
|
||||
<section v-if="currentActor.id && goingToEvents.size > 0" class="container">
|
||||
<section
|
||||
v-if="currentActor.id && goingToEvents.size > 0"
|
||||
class="container"
|
||||
>
|
||||
<h3 class="title">{{ $t("Upcoming") }}</h3>
|
||||
<b-loading :active.sync="$apollo.loading" />
|
||||
<div v-for="row of goingToEvents" class="upcoming-events" :key="row[0]">
|
||||
<span class="date-component-container" v-if="isInLessThanSevenDays(row[0])">
|
||||
<span
|
||||
class="date-component-container"
|
||||
v-if="isInLessThanSevenDays(row[0])"
|
||||
>
|
||||
<date-component :date="row[0]" />
|
||||
<subtitle v-if="isToday(row[0])">{{
|
||||
$tc("You have one event today.", row[1].length, { count: row[1].length })
|
||||
$tc("You have one event today.", row[1].length, {
|
||||
count: row[1].length,
|
||||
})
|
||||
}}</subtitle>
|
||||
<subtitle v-else-if="isTomorrow(row[0])">{{
|
||||
$tc("You have one event tomorrow.", row[1].length, { count: row[1].length })
|
||||
$tc("You have one event tomorrow.", row[1].length, {
|
||||
count: row[1].length,
|
||||
})
|
||||
}}</subtitle>
|
||||
<subtitle v-else-if="isInLessThanSevenDays(row[0])">
|
||||
{{
|
||||
@@ -155,7 +197,10 @@
|
||||
<section class="events-featured">
|
||||
<h2 class="title">{{ $t("Featured events") }}</h2>
|
||||
<b-loading :active.sync="$apollo.loading" />
|
||||
<div v-if="filteredFeaturedEvents.length > 0" class="columns is-multiline">
|
||||
<div
|
||||
v-if="filteredFeaturedEvents.length > 0"
|
||||
class="columns is-multiline"
|
||||
>
|
||||
<div
|
||||
class="column is-one-third-desktop"
|
||||
v-for="event in filteredFeaturedEvents.slice(0, 6)"
|
||||
@@ -164,7 +209,9 @@
|
||||
<EventCard :event="event" />
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else type="is-danger">{{ $t("No events found") }}</b-message>
|
||||
<b-message v-else type="is-danger">{{
|
||||
$t("No events found")
|
||||
}}</b-message>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -177,7 +224,10 @@ import { IParticipant, Participant } from "../types/participant.model";
|
||||
import { FETCH_EVENTS } from "../graphql/event";
|
||||
import EventListCard from "../components/Event/EventListCard.vue";
|
||||
import EventCard from "../components/Event/EventCard.vue";
|
||||
import { CURRENT_ACTOR_CLIENT, LOGGED_USER_PARTICIPATIONS } from "../graphql/actor";
|
||||
import {
|
||||
CURRENT_ACTOR_CLIENT,
|
||||
LOGGED_USER_PARTICIPATIONS,
|
||||
} from "../graphql/actor";
|
||||
import { IPerson, Person } from "../types/actor";
|
||||
import { ICurrentUser, IUser } from "../types/current-user.model";
|
||||
import { CURRENT_USER_CLIENT, USER_SETTINGS } from "../graphql/user";
|
||||
@@ -286,7 +336,9 @@ export default class Home extends Vue {
|
||||
return window.localStorage.getItem("new-registered-user") === "yes";
|
||||
}
|
||||
|
||||
thisWeek(row: [string, Map<string, IParticipant>]): Map<string, IParticipant> {
|
||||
thisWeek(
|
||||
row: [string, Map<string, IParticipant>]
|
||||
): Map<string, IParticipant> {
|
||||
if (this.isInLessThanSevenDays(row[0])) {
|
||||
return row[1];
|
||||
}
|
||||
@@ -330,7 +382,9 @@ export default class Home extends Vue {
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
calculateDiffDays(date: string): number {
|
||||
return Math.ceil((new Date(date).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24);
|
||||
return Math.ceil(
|
||||
(new Date(date).getTime() - new Date().getTime()) / 1000 / 60 / 60 / 24
|
||||
);
|
||||
}
|
||||
|
||||
get goingToEvents(): Map<string, Map<string, IParticipant>> {
|
||||
@@ -342,14 +396,22 @@ export default class Home extends Vue {
|
||||
role !== ParticipantRole.REJECTED
|
||||
);
|
||||
res.sort(
|
||||
(a: IParticipant, b: IParticipant) => a.event.beginsOn.getTime() - b.event.beginsOn.getTime()
|
||||
(a: IParticipant, b: IParticipant) =>
|
||||
a.event.beginsOn.getTime() - b.event.beginsOn.getTime()
|
||||
);
|
||||
|
||||
return res.reduce(
|
||||
(acc: Map<string, Map<string, IParticipant>>, participation: IParticipant) => {
|
||||
(
|
||||
acc: Map<string, Map<string, IParticipant>>,
|
||||
participation: IParticipant
|
||||
) => {
|
||||
const day = new Date(participation.event.beginsOn).toDateString();
|
||||
const participations: Map<string, IParticipant> = acc.get(day) || new Map();
|
||||
participations.set(`${participation.event.uuid}${participation.actor.id}`, participation);
|
||||
const participations: Map<string, IParticipant> =
|
||||
acc.get(day) || new Map();
|
||||
participations.set(
|
||||
`${participation.event.uuid}${participation.actor.id}`,
|
||||
participation
|
||||
);
|
||||
acc.set(day, participations);
|
||||
return acc;
|
||||
},
|
||||
@@ -365,7 +427,8 @@ export default class Home extends Vue {
|
||||
role !== ParticipantRole.REJECTED
|
||||
);
|
||||
res.sort(
|
||||
(a: IParticipant, b: IParticipant) => a.event.beginsOn.getTime() - b.event.beginsOn.getTime()
|
||||
(a: IParticipant, b: IParticipant) =>
|
||||
a.event.beginsOn.getTime() - b.event.beginsOn.getTime()
|
||||
);
|
||||
return res;
|
||||
}
|
||||
@@ -377,7 +440,9 @@ export default class Home extends Vue {
|
||||
return this.events.filter(
|
||||
({ id }) =>
|
||||
!this.currentUserParticipations
|
||||
.filter((participation) => participation.role === ParticipantRole.CREATOR)
|
||||
.filter(
|
||||
(participation) => participation.role === ParticipantRole.CREATOR
|
||||
)
|
||||
.map(({ event: { id: eventId } }) => eventId)
|
||||
.includes(id)
|
||||
);
|
||||
@@ -396,7 +461,10 @@ export default class Home extends Vue {
|
||||
@Watch("loggedUser")
|
||||
detectEmptyUserSettings(loggedUser: IUser): void {
|
||||
if (loggedUser && loggedUser.id && loggedUser.settings === null) {
|
||||
this.$router.push({ name: RouteName.WELCOME_SCREEN, params: { step: "1" } });
|
||||
this.$router.push({
|
||||
name: RouteName.WELCOME_SCREEN,
|
||||
params: { step: "1" },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.REPORT_LOGS }">{{
|
||||
@@ -16,7 +18,11 @@
|
||||
<ul v-if="actionLogs.length > 0">
|
||||
<li v-for="log in actionLogs" :key="log.id">
|
||||
<div class="box">
|
||||
<img class="image" :src="log.actor.avatar.url" v-if="log.actor.avatar" />
|
||||
<img
|
||||
class="image"
|
||||
:src="log.actor.avatar.url"
|
||||
v-if="log.actor.avatar"
|
||||
/>
|
||||
<i18n
|
||||
v-if="log.action === ActionLogAction.REPORT_UPDATE_CLOSED"
|
||||
tag="span"
|
||||
@@ -24,13 +30,23 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.REPORT,
|
||||
params: { reportId: log.object.id },
|
||||
}"
|
||||
slot="report"
|
||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||
>{{
|
||||
$t("report #{report_number}", {
|
||||
report_number: log.object.id,
|
||||
})
|
||||
}}
|
||||
</router-link>
|
||||
</i18n>
|
||||
<i18n
|
||||
@@ -40,13 +56,23 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.REPORT,
|
||||
params: { reportId: log.object.id },
|
||||
}"
|
||||
slot="report"
|
||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||
>{{
|
||||
$t("report #{report_number}", {
|
||||
report_number: log.object.id,
|
||||
})
|
||||
}}
|
||||
</router-link>
|
||||
</i18n>
|
||||
<i18n
|
||||
@@ -56,13 +82,23 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.REPORT,
|
||||
params: { reportId: log.object.id },
|
||||
}"
|
||||
slot="report"
|
||||
>{{ $t("report #{report_number}", { report_number: log.object.id }) }}
|
||||
>{{
|
||||
$t("report #{report_number}", {
|
||||
report_number: log.object.id,
|
||||
})
|
||||
}}
|
||||
</router-link>
|
||||
</i18n>
|
||||
<i18n
|
||||
@@ -72,16 +108,28 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
v-if="log.object.report"
|
||||
:to="{ name: RouteName.REPORT, params: { reportId: log.object.report.id } }"
|
||||
:to="{
|
||||
name: RouteName.REPORT,
|
||||
params: { reportId: log.object.report.id },
|
||||
}"
|
||||
slot="report"
|
||||
>{{ $t("report #{report_number}", { report_number: log.object.report.id }) }}
|
||||
>{{
|
||||
$t("report #{report_number}", {
|
||||
report_number: log.object.report.id,
|
||||
})
|
||||
}}
|
||||
</router-link>
|
||||
<span v-else slot="report">{{ $t("a non-existent report") }}</span>
|
||||
<span v-else slot="report">{{
|
||||
$t("a non-existent report")
|
||||
}}</span>
|
||||
</i18n>
|
||||
<i18n
|
||||
v-else-if="log.action === ActionLogAction.EVENT_DELETION"
|
||||
@@ -90,7 +138,10 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<b slot="title">{{ log.object.title }}</b>
|
||||
@@ -102,12 +153,18 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
slot="profile"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.object.id },
|
||||
}"
|
||||
>{{ displayNameAndUsername(log.object) }}
|
||||
</router-link>
|
||||
</i18n>
|
||||
@@ -118,12 +175,18 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
slot="profile"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.object.id },
|
||||
}"
|
||||
>{{ displayNameAndUsername(log.object) }}
|
||||
</router-link>
|
||||
</i18n>
|
||||
@@ -134,13 +197,19 @@
|
||||
>
|
||||
<router-link
|
||||
slot="moderator"
|
||||
:to="{ name: RouteName.ADMIN_PROFILE, params: { id: log.actor.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: log.actor.id },
|
||||
}"
|
||||
>@{{ log.actor.preferredUsername }}</router-link
|
||||
>
|
||||
<router-link
|
||||
v-if="log.object.confirmedAt"
|
||||
slot="user"
|
||||
:to="{ name: RouteName.ADMIN_USER_PROFILE, params: { id: log.object.id } }"
|
||||
:to="{
|
||||
name: RouteName.ADMIN_USER_PROFILE,
|
||||
params: { id: log.object.id },
|
||||
}"
|
||||
>{{ log.object.email }}
|
||||
</router-link>
|
||||
<b v-else slot="user">{{ log.object.email }}</b>
|
||||
|
||||
@@ -3,20 +3,32 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs" v-if="report">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.REPORTS }">{{
|
||||
$t("Reports")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.REPORT, params: { id: report.id } }">{{
|
||||
$t("Report #{reportNumber}", { reportNumber: report.id })
|
||||
}}</router-link>
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORT, params: { id: report.id } }"
|
||||
>{{
|
||||
$t("Report #{reportNumber}", { reportNumber: report.id })
|
||||
}}</router-link
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<section>
|
||||
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error">
|
||||
<b-message
|
||||
title="Error"
|
||||
type="is-danger"
|
||||
v-for="error in errors"
|
||||
:key="error"
|
||||
>
|
||||
{{ error }}
|
||||
</b-message>
|
||||
<div class="container" v-if="report">
|
||||
@@ -116,7 +128,9 @@
|
||||
<tr>
|
||||
<td>{{ $t("Status") }}</td>
|
||||
<td>
|
||||
<span v-if="report.status === ReportStatusEnum.OPEN">{{ $t("Open") }}</span>
|
||||
<span v-if="report.status === ReportStatusEnum.OPEN">{{
|
||||
$t("Open")
|
||||
}}</span>
|
||||
<span v-else-if="report.status === ReportStatusEnum.CLOSED">
|
||||
{{ $t("Closed") }}
|
||||
</span>
|
||||
@@ -129,7 +143,12 @@
|
||||
<tr v-if="report.event && report.comments.length > 0">
|
||||
<td>{{ $t("Event") }}</td>
|
||||
<td>
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.EVENT,
|
||||
params: { uuid: report.event.uuid },
|
||||
}"
|
||||
>
|
||||
{{ report.event.title }}
|
||||
</router-link>
|
||||
<span class="is-pulled-right">
|
||||
@@ -159,7 +178,9 @@
|
||||
</div>
|
||||
|
||||
<div class="box" v-if="report.event && report.comments.length === 0">
|
||||
<router-link :to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.EVENT, params: { uuid: report.event.uuid } }"
|
||||
>
|
||||
<h3 class="title">{{ report.event.title }}</h3>
|
||||
<p v-html="report.event.description" />
|
||||
</router-link>
|
||||
@@ -184,10 +205,18 @@
|
||||
<div class="box" v-if="comment">
|
||||
<article class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48" v-if="comment.actor && comment.actor.avatar">
|
||||
<figure
|
||||
class="image is-48x48"
|
||||
v-if="comment.actor && comment.actor.avatar"
|
||||
>
|
||||
<img :src="comment.actor.avatar.url" alt="Image" />
|
||||
</figure>
|
||||
<b-icon class="media-left" v-else size="is-large" icon="account-circle" />
|
||||
<b-icon
|
||||
class="media-left"
|
||||
v-else
|
||||
size="is-large"
|
||||
icon="account-circle"
|
||||
/>
|
||||
</div>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
@@ -214,10 +243,25 @@
|
||||
</div>
|
||||
|
||||
<h2 class="title" v-if="report.notes.length > 0">{{ $t("Notes") }}</h2>
|
||||
<div class="box note" v-for="note in report.notes" :id="`note-${note.id}`" :key="note.id">
|
||||
<div
|
||||
class="box note"
|
||||
v-for="note in report.notes"
|
||||
:id="`note-${note.id}`"
|
||||
:key="note.id"
|
||||
>
|
||||
<p>{{ note.content }}</p>
|
||||
<router-link :to="{ name: RouteName.ADMIN_PROFILE, params: { id: note.moderator.id } }">
|
||||
<img alt class="image" :src="note.moderator.avatar.url" v-if="note.moderator.avatar" />
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.ADMIN_PROFILE,
|
||||
params: { id: note.moderator.id },
|
||||
}"
|
||||
>
|
||||
<img
|
||||
alt
|
||||
class="image"
|
||||
:src="note.moderator.avatar.url"
|
||||
v-if="note.moderator.avatar"
|
||||
/>
|
||||
@{{ note.moderator.preferredUsername }}
|
||||
</router-link>
|
||||
<br />
|
||||
@@ -230,9 +274,15 @@
|
||||
|
||||
<form @submit="addNote()">
|
||||
<b-field :label="$t('New note')" label-for="newNoteInput">
|
||||
<b-input type="textarea" v-model="noteContent" id="newNoteInput"></b-input>
|
||||
<b-input
|
||||
type="textarea"
|
||||
v-model="noteContent"
|
||||
id="newNoteInput"
|
||||
></b-input>
|
||||
</b-field>
|
||||
<b-button type="submit" @click="addNote">{{ $t("Add a note") }}</b-button>
|
||||
<b-button type="submit" @click="addNote">{{
|
||||
$t("Add a note")
|
||||
}}</b-button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
@@ -315,7 +365,9 @@ export default class Report extends Vue {
|
||||
if (cachedData == null) return;
|
||||
const { report } = cachedData;
|
||||
if (report === null) {
|
||||
console.error("Cannot update event notes cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update event notes cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const note = data.createReportNote;
|
||||
@@ -419,7 +471,9 @@ export default class Report extends Vue {
|
||||
if (reportCachedData == null) return;
|
||||
const { report } = reportCachedData;
|
||||
if (report === null) {
|
||||
console.error("Cannot update event notes cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update event notes cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const updatedReport = data.updateReportStatus;
|
||||
|
||||
@@ -3,40 +3,61 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{ $t("Moderation") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.MODERATION }">{{
|
||||
$t("Moderation")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.REPORTS }">{{ $t("Reports") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.REPORTS }">{{
|
||||
$t("Reports")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<section>
|
||||
<b-field>
|
||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.OPEN">{{
|
||||
$t("Open")
|
||||
}}</b-radio-button>
|
||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.RESOLVED">{{
|
||||
$t("Resolved")
|
||||
}}</b-radio-button>
|
||||
<b-radio-button v-model="filterReports" :native-value="ReportStatusEnum.CLOSED">{{
|
||||
$t("Closed")
|
||||
}}</b-radio-button>
|
||||
<b-radio-button
|
||||
v-model="filterReports"
|
||||
:native-value="ReportStatusEnum.OPEN"
|
||||
>{{ $t("Open") }}</b-radio-button
|
||||
>
|
||||
<b-radio-button
|
||||
v-model="filterReports"
|
||||
:native-value="ReportStatusEnum.RESOLVED"
|
||||
>{{ $t("Resolved") }}</b-radio-button
|
||||
>
|
||||
<b-radio-button
|
||||
v-model="filterReports"
|
||||
:native-value="ReportStatusEnum.CLOSED"
|
||||
>{{ $t("Closed") }}</b-radio-button
|
||||
>
|
||||
</b-field>
|
||||
<ul v-if="reports.length > 0">
|
||||
<li v-for="report in reports" :key="report.id">
|
||||
<router-link :to="{ name: RouteName.REPORT, params: { reportId: report.id } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.REPORT, params: { reportId: report.id } }"
|
||||
>
|
||||
<report-card :report="report" />
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-else>
|
||||
<b-message v-if="filterReports === ReportStatusEnum.OPEN" type="is-info">
|
||||
<b-message
|
||||
v-if="filterReports === ReportStatusEnum.OPEN"
|
||||
type="is-info"
|
||||
>
|
||||
{{ $t("No open reports yet") }}
|
||||
</b-message>
|
||||
<b-message v-if="filterReports === ReportStatusEnum.RESOLVED" type="is-info">
|
||||
<b-message
|
||||
v-if="filterReports === ReportStatusEnum.RESOLVED"
|
||||
type="is-info"
|
||||
>
|
||||
{{ $t("No resolved reports yet") }}
|
||||
</b-message>
|
||||
<b-message v-if="filterReports === ReportStatusEnum.CLOSED" type="is-info">
|
||||
<b-message
|
||||
v-if="filterReports === ReportStatusEnum.CLOSED"
|
||||
type="is-info"
|
||||
>
|
||||
{{ $t("No closed reports yet") }}
|
||||
</b-message>
|
||||
</div>
|
||||
|
||||
@@ -2,13 +2,26 @@
|
||||
<section class="section container has-text-centered not-found">
|
||||
<div class="columns is-vertical is-centered">
|
||||
<div class="column is-half">
|
||||
<img src="/img/pics/2020-10-06-mobilizon-illustration-E_realisation.jpg" alt="" />
|
||||
<h1 class="title">{{ $t("The page you're looking for doesn't exist.") }}</h1>
|
||||
<img
|
||||
src="/img/pics/2020-10-06-mobilizon-illustration-E_realisation.jpg"
|
||||
alt=""
|
||||
/>
|
||||
<h1 class="title">
|
||||
{{ $t("The page you're looking for doesn't exist.") }}
|
||||
</h1>
|
||||
<p>
|
||||
{{ $t("Please make sure the address is correct and that the page hasn't been moved.") }}
|
||||
{{
|
||||
$t(
|
||||
"Please make sure the address is correct and that the page hasn't been moved."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t("Please contact this instance's Mobilizon admin if you think this is a mistake.") }}
|
||||
{{
|
||||
$t(
|
||||
"Please contact this instance's Mobilizon admin if you think this is a mistake."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<!-- The following should just be replaced with the SearchField component but it fails for some reason -->
|
||||
<form @submit="enter">
|
||||
@@ -21,7 +34,9 @@
|
||||
v-model="searchText"
|
||||
/>
|
||||
<p class="control">
|
||||
<button type="submit" class="button is-primary">{{ $t("Search") }}</button>
|
||||
<button type="submit" class="button is-primary">
|
||||
{{ $t("Search") }}
|
||||
</button>
|
||||
</p>
|
||||
</b-field>
|
||||
</form>
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
:type="errors.title ? 'is-danger' : null"
|
||||
:message="errors.title"
|
||||
>
|
||||
<b-input size="is-large" aria-required="true" required v-model="post.title" />
|
||||
<b-input
|
||||
size="is-large"
|
||||
aria-required="true"
|
||||
required
|
||||
v-model="post.title"
|
||||
/>
|
||||
</b-field>
|
||||
|
||||
<tag-input v-model="post.tags" :data="tags" path="title" />
|
||||
@@ -61,7 +66,9 @@
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<span class="navbar-item">
|
||||
<b-button type="is-text" @click="$router.go(-1)">{{ $t("Cancel") }}</b-button>
|
||||
<b-button type="is-text" @click="$router.go(-1)">{{
|
||||
$t("Cancel")
|
||||
}}</b-button>
|
||||
</span>
|
||||
<span class="navbar-item" v-if="this.isUpdate">
|
||||
<b-button type="is-danger is-outlined" @click="deletePost">{{
|
||||
@@ -76,7 +83,9 @@
|
||||
</span>
|
||||
<span class="navbar-item">
|
||||
<b-button type="is-primary" native-type="submit">
|
||||
<span v-if="isUpdate === false || post.draft === true">{{ $t("Publish") }}</span>
|
||||
<span v-if="isUpdate === false || post.draft === true">{{
|
||||
$t("Publish")
|
||||
}}</span>
|
||||
|
||||
<span v-else>{{ $t("Update post") }}</span>
|
||||
</b-button>
|
||||
@@ -108,7 +117,12 @@ import GroupMixin from "@/mixins/group";
|
||||
import { PostVisibility } from "@/types/enums";
|
||||
import { TAGS } from "../../graphql/tags";
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
import { FETCH_POST, CREATE_POST, UPDATE_POST, DELETE_POST } from "../../graphql/post";
|
||||
import {
|
||||
FETCH_POST,
|
||||
CREATE_POST,
|
||||
UPDATE_POST,
|
||||
DELETE_POST,
|
||||
} from "../../graphql/post";
|
||||
|
||||
import { IPost } from "../../types/post.model";
|
||||
import Editor from "../../components/Editor.vue";
|
||||
@@ -210,7 +224,10 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
},
|
||||
});
|
||||
if (data && data.updatePost) {
|
||||
this.$router.push({ name: RouteName.POST, params: { slug: data.updatePost.slug } });
|
||||
this.$router.push({
|
||||
name: RouteName.POST,
|
||||
params: { slug: data.updatePost.slug },
|
||||
});
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
@@ -225,14 +242,22 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
},
|
||||
});
|
||||
if (data && data.createPost) {
|
||||
this.$router.push({ name: RouteName.POST, params: { slug: data.createPost.slug } });
|
||||
this.$router.push({
|
||||
name: RouteName.POST,
|
||||
params: { slug: data.createPost.slug },
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.errors = error.graphQLErrors.reduce((acc: { [key: string]: any }, localError: any) => {
|
||||
acc[localError.field] = EditPost.transformMessage(localError.message);
|
||||
return acc;
|
||||
}, {});
|
||||
this.errors = error.graphQLErrors.reduce(
|
||||
(acc: { [key: string]: any }, localError: any) => {
|
||||
acc[localError.field] = EditPost.transformMessage(
|
||||
localError.message
|
||||
);
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,7 +272,9 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
if (data && this.post.attributedTo) {
|
||||
this.$router.push({
|
||||
name: RouteName.POSTS,
|
||||
params: { preferredUsername: usernameWithDomain(this.post.attributedTo) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(this.post.attributedTo),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -278,9 +305,13 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
}
|
||||
try {
|
||||
if (this.post.picture) {
|
||||
const oldPictureFile = (await buildFileFromIMedia(this.post.picture)) as File;
|
||||
const oldPictureFile = (await buildFileFromIMedia(
|
||||
this.post.picture
|
||||
)) as File;
|
||||
const oldPictureFileContent = await readFileAsync(oldPictureFile);
|
||||
const newPictureFileContent = await readFileAsync(this.pictureFile as File);
|
||||
const newPictureFileContent = await readFileAsync(
|
||||
this.pictureFile as File
|
||||
);
|
||||
if (oldPictureFileContent === newPictureFileContent) {
|
||||
obj.picture = { mediaId: this.post.picture.id };
|
||||
}
|
||||
@@ -303,7 +334,8 @@ export default class EditPost extends mixins(GroupMixin) {
|
||||
return (
|
||||
this.person &&
|
||||
this.person.memberships.elements.some(
|
||||
({ parent: { id }, role }) => id === this.actualGroup.id && roles.includes(role)
|
||||
({ parent: { id }, role }) =>
|
||||
id === this.actualGroup.id && roles.includes(role)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -158,7 +158,9 @@ export default class PostList extends mixins(GroupMixin) {
|
||||
|
||||
get isCurrentActorMember(): boolean {
|
||||
if (!this.group || !this.memberships) return false;
|
||||
return this.memberships.map(({ parent: { id } }) => id).includes(this.group.id);
|
||||
return this.memberships
|
||||
.map(({ parent: { id } }) => id)
|
||||
.includes(this.group.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,20 +8,34 @@
|
||||
slot="author"
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: usernameWithDomain(post.attributedTo) },
|
||||
params: {
|
||||
preferredUsername: usernameWithDomain(post.attributedTo),
|
||||
},
|
||||
}"
|
||||
>{{ post.attributedTo.name }}</router-link
|
||||
>
|
||||
</i18n>
|
||||
<p class="published" v-if="!post.draft">{{ post.publishAt | formatDateTimeString }}</p>
|
||||
<small v-if="post.visibility === PostVisibility.PRIVATE" class="has-text-grey">
|
||||
<p class="published" v-if="!post.draft">
|
||||
{{ post.publishAt | formatDateTimeString }}
|
||||
</p>
|
||||
<small
|
||||
v-if="post.visibility === PostVisibility.PRIVATE"
|
||||
class="has-text-grey"
|
||||
>
|
||||
<b-icon icon="lock" size="is-small" />
|
||||
{{ $t("Accessible only to members", { group: post.attributedTo.name }) }}
|
||||
{{
|
||||
$t("Accessible only to members", { group: post.attributedTo.name })
|
||||
}}
|
||||
</small>
|
||||
<p class="buttons" v-if="isCurrentActorMember">
|
||||
<b-tag type="is-warning" size="is-medium" v-if="post.draft">{{ $t("Draft") }}</b-tag>
|
||||
<b-tag type="is-warning" size="is-medium" v-if="post.draft">{{
|
||||
$t("Draft")
|
||||
}}</b-tag>
|
||||
<router-link
|
||||
v-if="currentActor.id === post.author.id || isCurrentActorAGroupModerator"
|
||||
v-if="
|
||||
currentActor.id === post.author.id ||
|
||||
isCurrentActorAGroupModerator
|
||||
"
|
||||
:to="{ name: RouteName.POST_EDIT, params: { slug: post.slug } }"
|
||||
tag="button"
|
||||
class="button is-text"
|
||||
@@ -128,7 +142,9 @@ export default class Post extends mixins(GroupMixin) {
|
||||
|
||||
get isCurrentActorMember(): boolean {
|
||||
if (!this.post.attributedTo || !this.memberships) return false;
|
||||
return this.memberships.map(({ parent: { id } }) => id).includes(this.post.attributedTo.id);
|
||||
return this.memberships
|
||||
.map(({ parent: { id } }) => id)
|
||||
.includes(this.post.attributedTo.id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,10 @@
|
||||
>
|
||||
</li>
|
||||
<li
|
||||
:class="{ 'is-active': index + 1 === ResourceMixin.resourcePathArray(resource).length }"
|
||||
:class="{
|
||||
'is-active':
|
||||
index + 1 === ResourceMixin.resourcePathArray(resource).length,
|
||||
}"
|
||||
v-for="(pathFragment, index) in filteredPath"
|
||||
:key="pathFragment"
|
||||
>
|
||||
@@ -29,7 +32,10 @@
|
||||
:to="{
|
||||
name: RouteName.RESOURCE_FOLDER,
|
||||
params: {
|
||||
path: ResourceMixin.resourcePathArray(resource).slice(0, index + 1),
|
||||
path: ResourceMixin.resourcePathArray(resource).slice(
|
||||
0,
|
||||
index + 1
|
||||
),
|
||||
preferredUsername: usernameWithDomain(resource.actor),
|
||||
},
|
||||
}"
|
||||
@@ -44,11 +50,17 @@
|
||||
<b-icon icon="folder" />
|
||||
{{ $t("New folder") }}
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item aria-role="listitem" @click="createLinkResourceModal = true">
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
@click="createLinkResourceModal = true"
|
||||
>
|
||||
<b-icon icon="link" />
|
||||
{{ $t("New link") }}
|
||||
</b-dropdown-item>
|
||||
<hr class="dropdown-divider" v-if="config.resourceProviders.length" />
|
||||
<hr
|
||||
class="dropdown-divider"
|
||||
v-if="config.resourceProviders.length"
|
||||
/>
|
||||
<b-dropdown-item
|
||||
aria-role="listitem"
|
||||
v-for="resourceProvider in config.resourceProviders"
|
||||
@@ -64,7 +76,9 @@
|
||||
</nav>
|
||||
<section>
|
||||
<p v-if="resource.path === '/'" class="module-description">
|
||||
{{ $t("A place to store links to documents or resources of any type.") }}
|
||||
{{
|
||||
$t("A place to store links to documents or resources of any type.")
|
||||
}}
|
||||
</p>
|
||||
<div class="list-header">
|
||||
<div class="list-header-right">
|
||||
@@ -94,7 +108,10 @@
|
||||
v-if="resource.children.total > 0"
|
||||
>
|
||||
<transition-group>
|
||||
<div v-for="localResource in resource.children.elements" :key="localResource.id">
|
||||
<div
|
||||
v-for="localResource in resource.children.elements"
|
||||
:key="localResource.id"
|
||||
>
|
||||
<div class="resource-item">
|
||||
<div
|
||||
class="resource-checkbox"
|
||||
@@ -121,7 +138,10 @@
|
||||
</div>
|
||||
</transition-group>
|
||||
</draggable>
|
||||
<div class="content has-text-centered has-text-grey" v-if="resource.children.total === 0">
|
||||
<div
|
||||
class="content has-text-centered has-text-grey"
|
||||
v-if="resource.children.total === 0"
|
||||
>
|
||||
<p>{{ $t("No resources in this folder") }}</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -133,7 +153,9 @@
|
||||
<b-input aria-required="true" v-model="updatedResource.title" />
|
||||
</b-field>
|
||||
|
||||
<b-button native-type="submit">{{ $t("Rename resource") }}</b-button>
|
||||
<b-button native-type="submit">{{
|
||||
$t("Rename resource")
|
||||
}}</b-button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
@@ -158,7 +180,9 @@
|
||||
<b-input aria-required="true" v-model="newResource.title" />
|
||||
</b-field>
|
||||
|
||||
<b-button native-type="submit">{{ createResourceButtonLabel }}</b-button>
|
||||
<b-button native-type="submit">{{
|
||||
createResourceButtonLabel
|
||||
}}</b-button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
@@ -188,7 +212,9 @@
|
||||
<b-input type="textarea" v-model="newResource.summary" />
|
||||
</b-field>
|
||||
|
||||
<b-button native-type="submit">{{ $t("Create resource") }}</b-button>
|
||||
<b-button native-type="submit">{{
|
||||
$t("Create resource")
|
||||
}}</b-button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
@@ -204,7 +230,11 @@ import { RefetchQueryDescription } from "apollo-client/core/watchQueryOptions";
|
||||
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
|
||||
import { IActor, usernameWithDomain } from "../../types/actor";
|
||||
import RouteName from "../../router/name";
|
||||
import { IResource, mapServiceTypeToIcon, IProvider } from "../../types/resource";
|
||||
import {
|
||||
IResource,
|
||||
mapServiceTypeToIcon,
|
||||
IProvider,
|
||||
} from "../../types/resource";
|
||||
import {
|
||||
CREATE_RESOURCE,
|
||||
DELETE_RESOURCE,
|
||||
@@ -320,7 +350,9 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
actorId: this.resource.actor.id,
|
||||
resourceUrl: this.newResource.resourceUrl,
|
||||
parentId:
|
||||
this.resource.id && this.resource.id.startsWith("root_") ? null : this.resource.id,
|
||||
this.resource.id && this.resource.id.startsWith("root_")
|
||||
? null
|
||||
: this.resource.id,
|
||||
type: this.newResource.type,
|
||||
},
|
||||
refetchQueries: () => this.postRefreshQueries(),
|
||||
@@ -379,7 +411,9 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
const randomString = [...Array(10)]
|
||||
.map(() => Math.random().toString(36)[3])
|
||||
.join("")
|
||||
.replace(/(.|$)/g, (c) => c[!Math.round(Math.random()) ? "toString" : "toLowerCase"]());
|
||||
.replace(/(.|$)/g, (c) =>
|
||||
c[!Math.round(Math.random()) ? "toString" : "toLowerCase"]()
|
||||
);
|
||||
switch (provider.type) {
|
||||
case "ethercalc":
|
||||
case "etherpad":
|
||||
@@ -442,7 +476,9 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
},
|
||||
refetchQueries: () => this.postRefreshQueries(),
|
||||
});
|
||||
this.validCheckedResources = this.validCheckedResources.filter((id) => id !== resourceID);
|
||||
this.validCheckedResources = this.validCheckedResources.filter(
|
||||
(id) => id !== resourceID
|
||||
);
|
||||
delete this.checkedResources[resourceID];
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -460,8 +496,12 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
this.updatedResource = { ...resource };
|
||||
}
|
||||
|
||||
async moveResource(resource: IResource, oldParent: IResource | undefined): Promise<void> {
|
||||
const parentPath = oldParent && oldParent.path ? oldParent.path || "/" : "/";
|
||||
async moveResource(
|
||||
resource: IResource,
|
||||
oldParent: IResource | undefined
|
||||
): Promise<void> {
|
||||
const parentPath =
|
||||
oldParent && oldParent.path ? oldParent.path || "/" : "/";
|
||||
await this.updateResource(resource, parentPath);
|
||||
this.moveModal = false;
|
||||
}
|
||||
@@ -471,7 +511,10 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
this.renameModal = false;
|
||||
}
|
||||
|
||||
async updateResource(resource: IResource, parentPath: string | null = null): Promise<void> {
|
||||
async updateResource(
|
||||
resource: IResource,
|
||||
parentPath: string | null = null
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.$apollo.mutate<{ updateResource: IResource }>({
|
||||
mutation: UPDATE_RESOURCE,
|
||||
@@ -483,7 +526,8 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
},
|
||||
refetchQueries: () => this.postRefreshQueries(),
|
||||
update: (store, { data }) => {
|
||||
if (!data || data.updateResource == null || parentPath == null) return;
|
||||
if (!data || data.updateResource == null || parentPath == null)
|
||||
return;
|
||||
if (!this.resource.actor) return;
|
||||
|
||||
console.log("Removing ressource from old parent");
|
||||
@@ -497,7 +541,9 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
if (oldParentCachedData == null) return;
|
||||
const { resource: oldParentCachedResource } = oldParentCachedData;
|
||||
if (oldParentCachedResource == null) {
|
||||
console.error("Cannot update resource cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update resource cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const updatedResource: IResource = data.updateResource;
|
||||
@@ -532,7 +578,9 @@ export default class Resources extends Mixins(ResourceMixin) {
|
||||
if (newParentCachedData == null) return;
|
||||
const { resource: newParentCachedResource } = newParentCachedData;
|
||||
if (newParentCachedResource == null) {
|
||||
console.error("Cannot update resource cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update resource cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
size="is-large"
|
||||
expanded
|
||||
v-model="search"
|
||||
:placeholder="$t('For instance: London, Taekwondo, Architecture…')"
|
||||
:placeholder="
|
||||
$t('For instance: London, Taekwondo, Architecture…')
|
||||
"
|
||||
/>
|
||||
</b-field>
|
||||
<b-field grouped group-multiline position="is-right" expanded>
|
||||
@@ -41,7 +43,11 @@
|
||||
</b-field>
|
||||
<b-field :label="$t('Date')" label-for="date">
|
||||
<b-select v-model="when" id="date" :disabled="activeTab !== 0">
|
||||
<option v-for="(option, index) in options" :key="index" :value="option">
|
||||
<option
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
:value="option"
|
||||
>
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</b-select>
|
||||
@@ -54,13 +60,19 @@
|
||||
<b-loading :active.sync="$apollo.loading"></b-loading>
|
||||
<h2 class="title">{{ $t("Featured events") }}</h2>
|
||||
<div v-if="events.length > 0" class="columns is-multiline">
|
||||
<div class="column is-one-third-desktop" v-for="event in events" :key="event.uuid">
|
||||
<div
|
||||
class="column is-one-third-desktop"
|
||||
v-for="event in events"
|
||||
:key="event.uuid"
|
||||
>
|
||||
<EventCard :event="event" />
|
||||
</div>
|
||||
</div>
|
||||
<b-message v-else-if="events.length === 0 && $apollo.loading === false" type="is-danger">{{
|
||||
$t("No events found")
|
||||
}}</b-message>
|
||||
<b-message
|
||||
v-else-if="events.length === 0 && $apollo.loading === false"
|
||||
type="is-danger"
|
||||
>{{ $t("No events found") }}</b-message
|
||||
>
|
||||
</section>
|
||||
<b-tabs v-else v-model="activeTab" type="is-boxed" class="searchTabs">
|
||||
<b-tab-item>
|
||||
@@ -240,7 +252,11 @@ export default class Search extends Vue {
|
||||
|
||||
events: IEvent[] = [];
|
||||
|
||||
searchEvents: Paginate<IEvent> & { initial: boolean } = { total: 0, elements: [], initial: true };
|
||||
searchEvents: Paginate<IEvent> & { initial: boolean } = {
|
||||
total: 0,
|
||||
elements: [],
|
||||
initial: true,
|
||||
};
|
||||
|
||||
searchGroups: Paginate<IGroup> = { total: 0, elements: [] };
|
||||
|
||||
@@ -250,7 +266,8 @@ export default class Search extends Vue {
|
||||
|
||||
search: string = (this.$route.query.term as string) || "";
|
||||
|
||||
activeTab: SearchTabs = tabsName[this.$route.query.searchType as "events" | "groups"] || 0;
|
||||
activeTab: SearchTabs =
|
||||
tabsName[this.$route.query.searchType as "events" | "groups"] || 0;
|
||||
|
||||
location: IAddress = new Address();
|
||||
|
||||
@@ -277,7 +294,9 @@ export default class Search extends Vue {
|
||||
},
|
||||
{
|
||||
label: this.$t("Next week") as string,
|
||||
start: startOfWeek(addWeeks(new Date(), 1), { locale: this.$dateFnsLocale }),
|
||||
start: startOfWeek(addWeeks(new Date(), 1), {
|
||||
locale: this.$dateFnsLocale,
|
||||
}),
|
||||
end: endOfWeek(addWeeks(new Date(), 1), { locale: this.$dateFnsLocale }),
|
||||
},
|
||||
{
|
||||
@@ -343,7 +362,10 @@ export default class Search extends Vue {
|
||||
const now = new Date();
|
||||
const endOfWeekDate = endOfWeek(now, { locale: this.$dateFnsLocale });
|
||||
const startOfWeekDate = startOfWeek(now, { locale: this.$dateFnsLocale });
|
||||
const [start, end] = eachWeekendOfInterval({ start: startOfWeekDate, end: endOfWeekDate });
|
||||
const [start, end] = eachWeekendOfInterval({
|
||||
start: startOfWeekDate,
|
||||
end: endOfWeekDate,
|
||||
});
|
||||
return { start: startOfDay(start), end: endOfDay(end) };
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ import { ICurrentUser } from "../types/current-user.model";
|
||||
apollo: {
|
||||
identities: {
|
||||
query: IDENTITIES,
|
||||
update: (data) => data.identities.map((identity: IPerson) => new Person(identity)),
|
||||
update: (data) =>
|
||||
data.identities.map((identity: IPerson) => new Person(identity)),
|
||||
},
|
||||
currentUser: CURRENT_USER_CLIENT,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{
|
||||
$t("Account")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS_GENERAL }">{{
|
||||
@@ -26,9 +28,12 @@
|
||||
</i18n>
|
||||
<b-message v-if="!canChangeEmail" type="is-warning" :closable="false">
|
||||
{{
|
||||
$t("Your email address was automatically set based on your {provider} account.", {
|
||||
provider: providerName(loggedUser.provider),
|
||||
})
|
||||
$t(
|
||||
"Your email address was automatically set based on your {provider} account.",
|
||||
{
|
||||
provider: providerName(loggedUser.provider),
|
||||
}
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
<b-notification
|
||||
@@ -40,9 +45,19 @@
|
||||
v-for="error in changeEmailErrors"
|
||||
>{{ error }}</b-notification
|
||||
>
|
||||
<form @submit.prevent="resetEmailAction" ref="emailForm" class="form" v-if="canChangeEmail">
|
||||
<form
|
||||
@submit.prevent="resetEmailAction"
|
||||
ref="emailForm"
|
||||
class="form"
|
||||
v-if="canChangeEmail"
|
||||
>
|
||||
<b-field :label="$t('New email')">
|
||||
<b-input aria-required="true" required type="email" v-model="newEmail" />
|
||||
<b-input
|
||||
aria-required="true"
|
||||
required
|
||||
type="email"
|
||||
v-model="newEmail"
|
||||
/>
|
||||
</b-field>
|
||||
<p class="help">{{ $t("You'll receive a confirmation email.") }}</p>
|
||||
<b-field :label="$t('Password')">
|
||||
@@ -67,9 +82,12 @@
|
||||
</div>
|
||||
<b-message v-if="!canChangePassword" type="is-warning" :closable="false">
|
||||
{{
|
||||
$t("You can't change your password because you are registered through {provider}.", {
|
||||
provider: providerName(loggedUser.provider),
|
||||
})
|
||||
$t(
|
||||
"You can't change your password because you are registered through {provider}.",
|
||||
{
|
||||
provider: providerName(loggedUser.provider),
|
||||
}
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
<b-notification
|
||||
@@ -109,7 +127,9 @@
|
||||
</b-field>
|
||||
<button
|
||||
class="button is-primary"
|
||||
:disabled="!($refs.passwordForm && $refs.passwordForm.checkValidity())"
|
||||
:disabled="
|
||||
!($refs.passwordForm && $refs.passwordForm.checkValidity())
|
||||
"
|
||||
>
|
||||
{{ $t("Change my password") }}
|
||||
</button>
|
||||
@@ -117,7 +137,9 @@
|
||||
<div class="setting-title">
|
||||
<h2>{{ $t("Delete account") }}</h2>
|
||||
</div>
|
||||
<p class="content">{{ $t("Deleting my account will delete all of my identities.") }}</p>
|
||||
<p class="content">
|
||||
{{ $t("Deleting my account will delete all of my identities.") }}
|
||||
</p>
|
||||
<b-button @click="openDeleteAccountModal" type="is-danger">
|
||||
{{ $t("Delete my account") }}
|
||||
</b-button>
|
||||
@@ -132,8 +154,12 @@
|
||||
<div class="hero-body has-text-centered">
|
||||
<div class="container">
|
||||
<div class="columns">
|
||||
<div class="column is-one-third-desktop is-offset-one-third-desktop">
|
||||
<h1 class="title">{{ $t("Deleting your Mobilizon account") }}</h1>
|
||||
<div
|
||||
class="column is-one-third-desktop is-offset-one-third-desktop"
|
||||
>
|
||||
<h1 class="title">
|
||||
{{ $t("Deleting your Mobilizon account") }}
|
||||
</h1>
|
||||
<p class="content">
|
||||
{{
|
||||
$t(
|
||||
@@ -141,10 +167,14 @@
|
||||
)
|
||||
}}
|
||||
<br />
|
||||
<b>{{ $t("There will be no way to recover your data.") }}</b>
|
||||
<b>{{
|
||||
$t("There will be no way to recover your data.")
|
||||
}}</b>
|
||||
</p>
|
||||
<p class="content" v-if="hasUserGotAPassword">
|
||||
{{ $t("Please enter your password to confirm this action.") }}
|
||||
{{
|
||||
$t("Please enter your password to confirm this action.")
|
||||
}}
|
||||
</p>
|
||||
<form @submit.prevent="deleteAccount">
|
||||
<b-field v-if="hasUserGotAPassword">
|
||||
@@ -156,12 +186,19 @@
|
||||
:placeholder="$t('Password')"
|
||||
/>
|
||||
</b-field>
|
||||
<b-button native-type="submit" type="is-danger" size="is-large">
|
||||
<b-button
|
||||
native-type="submit"
|
||||
type="is-danger"
|
||||
size="is-large"
|
||||
>
|
||||
{{ $t("Delete everything") }}
|
||||
</b-button>
|
||||
</form>
|
||||
<div class="cancel-button">
|
||||
<b-button type="is-light" @click="isDeleteAccountModalActive = false">
|
||||
<b-button
|
||||
type="is-light"
|
||||
@click="isDeleteAccountModalActive = false"
|
||||
>
|
||||
{{ $t("Cancel") }}
|
||||
</b-button>
|
||||
</div>
|
||||
@@ -179,7 +216,12 @@
|
||||
import { IAuthProvider } from "@/types/enums";
|
||||
import { Component, Vue, Ref } from "vue-property-decorator";
|
||||
import { Route } from "vue-router";
|
||||
import { CHANGE_EMAIL, CHANGE_PASSWORD, DELETE_ACCOUNT, LOGGED_USER } from "../../graphql/user";
|
||||
import {
|
||||
CHANGE_EMAIL,
|
||||
CHANGE_PASSWORD,
|
||||
DELETE_ACCOUNT,
|
||||
LOGGED_USER,
|
||||
} from "../../graphql/user";
|
||||
import RouteName from "../../router/name";
|
||||
import { IUser } from "../../types/current-user.model";
|
||||
import { logout, SELECTED_PROVIDERS } from "../../utils/auth";
|
||||
@@ -248,7 +290,9 @@ export default class AccountSettings extends Vue {
|
||||
},
|
||||
});
|
||||
|
||||
this.$notifier.success(this.$t("The password was successfully changed") as string);
|
||||
this.$notifier.success(
|
||||
this.$t("The password was successfully changed") as string
|
||||
);
|
||||
} catch (err) {
|
||||
this.handleErrors("password", err);
|
||||
}
|
||||
@@ -264,12 +308,16 @@ export default class AccountSettings extends Vue {
|
||||
await this.$apollo.mutate({
|
||||
mutation: DELETE_ACCOUNT,
|
||||
variables: {
|
||||
password: this.hasUserGotAPassword ? this.passwordForAccountDeletion : null,
|
||||
password: this.hasUserGotAPassword
|
||||
? this.passwordForAccountDeletion
|
||||
: null,
|
||||
},
|
||||
});
|
||||
await logout(this.$apollo.provider.defaultClient);
|
||||
this.$buefy.notification.open({
|
||||
message: this.$t("Your account has been successfully deleted") as string,
|
||||
message: this.$t(
|
||||
"Your account has been successfully deleted"
|
||||
) as string,
|
||||
type: "is-success",
|
||||
position: "is-bottom-right",
|
||||
duration: 5000,
|
||||
@@ -300,7 +348,8 @@ export default class AccountSettings extends Vue {
|
||||
get hasUserGotAPassword(): boolean {
|
||||
return (
|
||||
this.loggedUser &&
|
||||
(this.loggedUser.provider == null || this.loggedUser.provider === IAuthProvider.LDAP)
|
||||
(this.loggedUser.provider == null ||
|
||||
this.loggedUser.provider === IAuthProvider.LDAP)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{
|
||||
$t("Account")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.NOTIFICATIONS }">{{
|
||||
@@ -27,11 +29,16 @@
|
||||
{{ $t("Other notification options:") }}
|
||||
</p>
|
||||
<div class="field">
|
||||
<b-checkbox v-model="notificationOnDay" @input="updateSetting({ notificationOnDay })">
|
||||
<b-checkbox
|
||||
v-model="notificationOnDay"
|
||||
@input="updateSetting({ notificationOnDay })"
|
||||
>
|
||||
<strong>{{ $t("Notification on the day of the event") }}</strong>
|
||||
<p>
|
||||
{{
|
||||
$t("We'll use your timezone settings to send a recap of the morning of the event.")
|
||||
$t(
|
||||
"We'll use your timezone settings to send a recap of the morning of the event."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div v-if="loggedUser.settings && loggedUser.settings.timezone">
|
||||
@@ -40,18 +47,29 @@
|
||||
timezone: loggedUser.settings.timezone,
|
||||
})
|
||||
}}</em>
|
||||
<router-link class="change-timezone" :to="{ name: RouteName.PREFERENCES }">{{
|
||||
$t("Change timezone")
|
||||
}}</router-link>
|
||||
<router-link
|
||||
class="change-timezone"
|
||||
:to="{ name: RouteName.PREFERENCES }"
|
||||
>{{ $t("Change timezone") }}</router-link
|
||||
>
|
||||
</div>
|
||||
<span v-else>{{ $t("You can pick your timezone into your preferences.") }}</span>
|
||||
<span v-else>{{
|
||||
$t("You can pick your timezone into your preferences.")
|
||||
}}</span>
|
||||
</b-checkbox>
|
||||
</div>
|
||||
<div class="field">
|
||||
<b-checkbox v-model="notificationEachWeek" @input="updateSetting({ notificationEachWeek })">
|
||||
<b-checkbox
|
||||
v-model="notificationEachWeek"
|
||||
@input="updateSetting({ notificationEachWeek })"
|
||||
>
|
||||
<strong>{{ $t("Recap every week") }}</strong>
|
||||
<p>
|
||||
{{ $t("You'll get a weekly recap every Monday for upcoming events, if you have any.") }}
|
||||
{{
|
||||
$t(
|
||||
"You'll get a weekly recap every Monday for upcoming events, if you have any."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</b-checkbox>
|
||||
</div>
|
||||
@@ -76,7 +94,9 @@
|
||||
<h2>{{ $t("Organizer notifications") }}</h2>
|
||||
</div>
|
||||
<div class="field is-primary">
|
||||
<strong>{{ $t("Notifications for manually approved participations to an event") }}</strong>
|
||||
<strong>{{
|
||||
$t("Notifications for manually approved participations to an event")
|
||||
}}</strong>
|
||||
<p>
|
||||
{{
|
||||
$t(
|
||||
@@ -130,7 +150,9 @@ export default class Notifications extends Vue {
|
||||
mounted(): void {
|
||||
this.notificationPendingParticipationValues = {
|
||||
[INotificationPendingEnum.NONE]: this.$t("Do not receive any mail"),
|
||||
[INotificationPendingEnum.DIRECT]: this.$t("Receive one email per request"),
|
||||
[INotificationPendingEnum.DIRECT]: this.$t(
|
||||
"Receive one email per request"
|
||||
),
|
||||
[INotificationPendingEnum.ONE_HOUR]: this.$t("Hourly email summary"),
|
||||
[INotificationPendingEnum.ONE_DAY]: this.$t("Daily email summary"),
|
||||
};
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
<nav class="breadcrumb" aria-label="breadcrumbs">
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{ $t("Account") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.ACCOUNT_SETTINGS }">{{
|
||||
$t("Account")
|
||||
}}</router-link>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.PREFERENCES }">{{ $t("Preferences") }}</router-link>
|
||||
<router-link :to="{ name: RouteName.PREFERENCES }">{{
|
||||
$t("Preferences")
|
||||
}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -28,7 +32,11 @@
|
||||
:loading="!config || !loggedUser"
|
||||
v-model="selectedTimezone"
|
||||
>
|
||||
<optgroup :label="group" v-for="(groupTimezones, group) in timezones" :key="group">
|
||||
<optgroup
|
||||
:label="group"
|
||||
v-for="(groupTimezones, group) in timezones"
|
||||
:key="group"
|
||||
>
|
||||
<option
|
||||
v-for="timezone in groupTimezones"
|
||||
:value="`${group}/${timezone}`"
|
||||
@@ -44,7 +52,9 @@
|
||||
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
})
|
||||
}}</em>
|
||||
<b-message v-else type="is-danger">{{ $t("Unable to detect timezone.") }}</b-message>
|
||||
<b-message v-else type="is-danger">{{
|
||||
$t("Unable to detect timezone.")
|
||||
}}</b-message>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -52,7 +62,11 @@
|
||||
import { Component, Vue, Watch } from "vue-property-decorator";
|
||||
import { saveLocaleData } from "@/utils/auth";
|
||||
import { TIMEZONES } from "../../graphql/config";
|
||||
import { USER_SETTINGS, SET_USER_SETTINGS, UPDATE_USER_LOCALE } from "../../graphql/user";
|
||||
import {
|
||||
USER_SETTINGS,
|
||||
SET_USER_SETTINGS,
|
||||
UPDATE_USER_LOCALE,
|
||||
} from "../../graphql/user";
|
||||
import { IConfig } from "../../types/config.model";
|
||||
import { IUser } from "../../types/current-user.model";
|
||||
import langs from "../../i18n/langs.json";
|
||||
@@ -93,28 +107,39 @@ export default class Preferences extends Vue {
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
sanitize(timezone: string): string {
|
||||
return timezone.split("_").join(" ").replace("St ", "St. ").split("/").join(" - ");
|
||||
return timezone
|
||||
.split("_")
|
||||
.join(" ")
|
||||
.replace("St ", "St. ")
|
||||
.split("/")
|
||||
.join(" - ");
|
||||
}
|
||||
|
||||
get timezones(): Record<string, string[]> {
|
||||
if (!this.config || !this.config.timezones) return {};
|
||||
return this.config.timezones.reduce((acc: { [key: string]: Array<string> }, val: string) => {
|
||||
const components = val.split("/");
|
||||
const [prefix, suffix] = [components.shift() as string, components.join("/")];
|
||||
const pushOrCreate = (
|
||||
acc2: { [key: string]: Array<string> },
|
||||
prefix2: string,
|
||||
suffix2: string
|
||||
) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
(acc2[prefix2] = acc2[prefix2] || []).push(suffix2);
|
||||
return acc2;
|
||||
};
|
||||
if (suffix) {
|
||||
return pushOrCreate(acc, prefix, suffix);
|
||||
}
|
||||
return pushOrCreate(acc, this.$t("Other") as string, prefix);
|
||||
}, {});
|
||||
return this.config.timezones.reduce(
|
||||
(acc: { [key: string]: Array<string> }, val: string) => {
|
||||
const components = val.split("/");
|
||||
const [prefix, suffix] = [
|
||||
components.shift() as string,
|
||||
components.join("/"),
|
||||
];
|
||||
const pushOrCreate = (
|
||||
acc2: { [key: string]: Array<string> },
|
||||
prefix2: string,
|
||||
suffix2: string
|
||||
) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
(acc2[prefix2] = acc2[prefix2] || []).push(suffix2);
|
||||
return acc2;
|
||||
};
|
||||
if (suffix) {
|
||||
return pushOrCreate(acc, prefix, suffix);
|
||||
}
|
||||
return pushOrCreate(acc, this.$t("Other") as string, prefix);
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
@Watch("selectedTimezone")
|
||||
|
||||
@@ -6,13 +6,20 @@
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.GROUP,
|
||||
params: { preferredUsername: todo.todoList.actor.preferredUsername },
|
||||
params: {
|
||||
preferredUsername: todo.todoList.actor.preferredUsername,
|
||||
},
|
||||
}"
|
||||
>{{ todo.todoList.actor.name }}</router-link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{ name: RouteName.TODO_LIST, params: { id: todo.todoList.id } }">
|
||||
<router-link
|
||||
:to="{
|
||||
name: RouteName.TODO_LIST,
|
||||
params: { id: todo.todoList.id },
|
||||
}"
|
||||
>
|
||||
{{ todo.todoList.title }}
|
||||
</router-link>
|
||||
</li>
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
>
|
||||
</li>
|
||||
<li class="is-active">
|
||||
<router-link :to="{ name: RouteName.TODO_LIST, params: { id: todoList.id } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.TODO_LIST, params: { id: todoList.id } }"
|
||||
>
|
||||
{{ todoList.title }}
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -95,7 +97,9 @@ export default class TodoList extends Vue {
|
||||
if (cachedData == null) return;
|
||||
const { todoList } = cachedData;
|
||||
if (todoList === null) {
|
||||
console.error("Cannot update event notes cache, because of null value.");
|
||||
console.error(
|
||||
"Cannot update event notes cache, because of null value."
|
||||
);
|
||||
return;
|
||||
}
|
||||
const newTodo: ITodo = data.createTodo;
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
<section>
|
||||
<p>
|
||||
{{
|
||||
$t("Create to-do lists for all the tasks you need to do, assign them and set due dates.")
|
||||
$t(
|
||||
"Create to-do lists for all the tasks you need to do, assign them and set due dates."
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<form class="form" @submit.prevent="createNewTodoList">
|
||||
@@ -35,7 +37,9 @@
|
||||
<b-button native-type="submit">{{ $t("Create a new list") }}</b-button>
|
||||
</form>
|
||||
<div v-for="todoList in todoLists" :key="todoList.id">
|
||||
<router-link :to="{ name: RouteName.TODO_LIST, params: { id: todoList.id } }">
|
||||
<router-link
|
||||
:to="{ name: RouteName.TODO_LIST, params: { id: todoList.id } }"
|
||||
>
|
||||
<h3 class="is-size-3">
|
||||
{{
|
||||
$tc("{title} ({count} todos)", todoList.todos.total, {
|
||||
@@ -45,7 +49,11 @@
|
||||
}}
|
||||
</h3>
|
||||
</router-link>
|
||||
<compact-todo :todo="todo" v-for="todo in todoList.todos.elements" :key="todo.id" />
|
||||
<compact-todo
|
||||
:todo="todo"
|
||||
v-for="todo in todoList.todos.elements"
|
||||
:key="todo.id"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<section class="section container">
|
||||
<h1 class="title" v-if="loading">{{ $t("Your email is being changed") }}</h1>
|
||||
<h1 class="title" v-if="loading">
|
||||
{{ $t("Your email is being changed") }}
|
||||
</h1>
|
||||
<div v-else>
|
||||
<div v-if="failed">
|
||||
<b-message :title="$t('Error while changing email')" type="is-danger">
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
type="is-danger"
|
||||
:aria-close-label="$t('Close')"
|
||||
>{{
|
||||
$t("Error while login with {provider}. Retry or login another way.", {
|
||||
provider: $route.query.provider,
|
||||
})
|
||||
$t(
|
||||
"Error while login with {provider}. Retry or login another way.",
|
||||
{
|
||||
provider: $route.query.provider,
|
||||
}
|
||||
)
|
||||
}}</b-message
|
||||
>
|
||||
<b-message
|
||||
@@ -25,12 +28,20 @@
|
||||
type="is-danger"
|
||||
:aria-close-label="$t('Close')"
|
||||
>{{
|
||||
$t("Error while login with {provider}. This login provider doesn't exist.", {
|
||||
provider: $route.query.provider,
|
||||
})
|
||||
$t(
|
||||
"Error while login with {provider}. This login provider doesn't exist.",
|
||||
{
|
||||
provider: $route.query.provider,
|
||||
}
|
||||
)
|
||||
}}</b-message
|
||||
>
|
||||
<b-message :title="$t('Error')" type="is-danger" v-for="error in errors" :key="error">
|
||||
<b-message
|
||||
:title="$t('Error')"
|
||||
type="is-danger"
|
||||
v-for="error in errors"
|
||||
:key="error"
|
||||
>
|
||||
{{ error }}
|
||||
</b-message>
|
||||
<form @submit="loginAction">
|
||||
@@ -56,24 +67,35 @@
|
||||
</b-field>
|
||||
|
||||
<p class="control has-text-centered" v-if="!submitted">
|
||||
<button class="button is-primary is-large">{{ $t("Login") }}</button>
|
||||
<button class="button is-primary is-large">
|
||||
{{ $t("Login") }}
|
||||
</button>
|
||||
</p>
|
||||
<b-loading :is-full-page="false" v-model="submitted" />
|
||||
|
||||
<div class="control" v-if="config && config.auth.oauthProviders.length > 0">
|
||||
<div
|
||||
class="control"
|
||||
v-if="config && config.auth.oauthProviders.length > 0"
|
||||
>
|
||||
<auth-providers :oauthProviders="config.auth.oauthProviders" />
|
||||
</div>
|
||||
|
||||
<p class="control">
|
||||
<router-link
|
||||
class="button is-text"
|
||||
:to="{ name: RouteName.SEND_PASSWORD_RESET, params: { email: credentials.email } }"
|
||||
:to="{
|
||||
name: RouteName.SEND_PASSWORD_RESET,
|
||||
params: { email: credentials.email },
|
||||
}"
|
||||
>{{ $t("Forgot your password ?") }}</router-link
|
||||
>
|
||||
</p>
|
||||
<router-link
|
||||
class="button is-text"
|
||||
:to="{ name: RouteName.RESEND_CONFIRMATION, params: { email: credentials.email } }"
|
||||
:to="{
|
||||
name: RouteName.RESEND_CONFIRMATION,
|
||||
params: { email: credentials.email },
|
||||
}"
|
||||
>{{ $t("Didn't receive the instructions?") }}</router-link
|
||||
>
|
||||
<p class="control" v-if="config && config.registrationsOpen">
|
||||
@@ -101,10 +123,20 @@ import { Route } from "vue-router";
|
||||
import { ICurrentUser } from "@/types/current-user.model";
|
||||
import { LoginError, LoginErrorCode } from "@/types/enums";
|
||||
import { LOGIN } from "../../graphql/auth";
|
||||
import { validateEmailField, validateRequiredField } from "../../utils/validators";
|
||||
import { initializeCurrentActor, NoIdentitiesException, saveUserData } from "../../utils/auth";
|
||||
import {
|
||||
validateEmailField,
|
||||
validateRequiredField,
|
||||
} from "../../utils/validators";
|
||||
import {
|
||||
initializeCurrentActor,
|
||||
NoIdentitiesException,
|
||||
saveUserData,
|
||||
} from "../../utils/auth";
|
||||
import { ILogin } from "../../types/login.model";
|
||||
import { CURRENT_USER_CLIENT, UPDATE_CURRENT_USER_CLIENT } from "../../graphql/user";
|
||||
import {
|
||||
CURRENT_USER_CLIENT,
|
||||
UPDATE_CURRENT_USER_CLIENT,
|
||||
} from "../../graphql/user";
|
||||
import RouteName from "../../router/name";
|
||||
import { CONFIG } from "../../graphql/config";
|
||||
import { IConfig } from "../../types/config.model";
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
<section class="section container columns is-mobile is-centered">
|
||||
<div class="card column is-half-desktop">
|
||||
<h1>{{ $t("Password reset") }}</h1>
|
||||
<b-message title="Error" type="is-danger" v-for="error in errors" :key="error">{{
|
||||
error
|
||||
}}</b-message>
|
||||
<b-message
|
||||
title="Error"
|
||||
type="is-danger"
|
||||
v-for="error in errors"
|
||||
:key="error"
|
||||
>{{ error }}</b-message
|
||||
>
|
||||
<form @submit="resetAction">
|
||||
<b-field :label="$t('Password')">
|
||||
<b-input
|
||||
|
||||
@@ -13,13 +13,20 @@ export default class ProviderValidate extends Vue {
|
||||
const refreshToken = this.getValueFromMeta("auth-refresh-token");
|
||||
const userId = this.getValueFromMeta("auth-user-id");
|
||||
const userEmail = this.getValueFromMeta("auth-user-email");
|
||||
const userRole = this.getValueFromMeta("auth-user-role") as ICurrentUserRole;
|
||||
const userRole = this.getValueFromMeta(
|
||||
"auth-user-role"
|
||||
) as ICurrentUserRole;
|
||||
|
||||
if (!(userId && userEmail && userRole && accessToken && refreshToken)) {
|
||||
await this.$router.push("/");
|
||||
} else {
|
||||
const login = {
|
||||
user: { id: userId, email: userEmail, role: userRole, isLoggedIn: true },
|
||||
user: {
|
||||
id: userId,
|
||||
email: userEmail,
|
||||
role: userRole,
|
||||
isLoggedIn: true,
|
||||
},
|
||||
accessToken,
|
||||
refreshToken,
|
||||
};
|
||||
@@ -39,7 +46,10 @@ export default class ProviderValidate extends Vue {
|
||||
const { loggedUser } = data;
|
||||
|
||||
if (loggedUser.defaultActor) {
|
||||
await changeIdentity(this.$apollo.provider.defaultClient, loggedUser.defaultActor);
|
||||
await changeIdentity(
|
||||
this.$apollo.provider.defaultClient,
|
||||
loggedUser.defaultActor
|
||||
);
|
||||
await this.$router.push({ name: RouteName.HOME });
|
||||
} else {
|
||||
// If the user didn't register any profile yet, let's create one for them
|
||||
|
||||
@@ -3,13 +3,24 @@
|
||||
<section class="hero">
|
||||
<div class="hero-body">
|
||||
<h1 class="title">
|
||||
{{ $t("Register an account on {instanceName}!", { instanceName: config.name }) }}
|
||||
{{
|
||||
$t("Register an account on {instanceName}!", {
|
||||
instanceName: config.name,
|
||||
})
|
||||
}}
|
||||
</h1>
|
||||
<i18n tag="p" path="{instanceName} is an instance of the {mobilizon} software.">
|
||||
<i18n
|
||||
tag="p"
|
||||
path="{instanceName} is an instance of the {mobilizon} software."
|
||||
>
|
||||
<b slot="instanceName">{{ config.name }}</b>
|
||||
<a href="https://joinmobilizon.org" target="_blank" class="out" slot="mobilizon">{{
|
||||
$t("Mobilizon")
|
||||
}}</a>
|
||||
<a
|
||||
href="https://joinmobilizon.org"
|
||||
target="_blank"
|
||||
class="out"
|
||||
slot="mobilizon"
|
||||
>{{ $t("Mobilizon") }}</a
|
||||
>
|
||||
</i18n>
|
||||
</div>
|
||||
</section>
|
||||
@@ -21,10 +32,26 @@
|
||||
<div class="content">
|
||||
<ul>
|
||||
<li>{{ $t("To create and manage your events") }}</li>
|
||||
<li>{{ $t("To create and manage multiples identities from a same account") }}</li>
|
||||
<li>{{ $t("To register for an event by choosing one of your identities") }}</li>
|
||||
<li>
|
||||
{{
|
||||
$t(
|
||||
"To create and manage multiples identities from a same account"
|
||||
)
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
{{
|
||||
$t(
|
||||
"To register for an event by choosing one of your identities"
|
||||
)
|
||||
}}
|
||||
</li>
|
||||
<li v-if="config.features.groups">
|
||||
{{ $t("To create or join an group and start organizing with other people") }}
|
||||
{{
|
||||
$t(
|
||||
"To create or join an group and start organizing with other people"
|
||||
)
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -34,7 +61,9 @@
|
||||
}}</router-link>
|
||||
<hr />
|
||||
<div class="content">
|
||||
<subtitle>{{ $t("About {instance}", { instance: config.name }) }}</subtitle>
|
||||
<subtitle>{{
|
||||
$t("About {instance}", { instance: config.name })
|
||||
}}</subtitle>
|
||||
<div class="content" v-html="config.description"></div>
|
||||
<i18n
|
||||
path="Please read the {fullRules} published by {instance}'s administrators."
|
||||
@@ -87,13 +116,22 @@
|
||||
</b-field>
|
||||
|
||||
<b-checkbox required>
|
||||
<i18n tag="span" path="I agree to the {instanceRules} and {termsOfService}">
|
||||
<router-link class="out" slot="instanceRules" :to="{ name: RouteName.RULES }">{{
|
||||
$t("instance rules")
|
||||
}}</router-link>
|
||||
<router-link class="out" slot="termsOfService" :to="{ name: RouteName.TERMS }">{{
|
||||
$t("terms of service")
|
||||
}}</router-link>
|
||||
<i18n
|
||||
tag="span"
|
||||
path="I agree to the {instanceRules} and {termsOfService}"
|
||||
>
|
||||
<router-link
|
||||
class="out"
|
||||
slot="instanceRules"
|
||||
:to="{ name: RouteName.RULES }"
|
||||
>{{ $t("instance rules") }}</router-link
|
||||
>
|
||||
<router-link
|
||||
class="out"
|
||||
slot="termsOfService"
|
||||
:to="{ name: RouteName.TERMS }"
|
||||
>{{ $t("terms of service") }}</router-link
|
||||
>
|
||||
</i18n>
|
||||
</b-checkbox>
|
||||
|
||||
@@ -111,7 +149,10 @@
|
||||
<p class="control has-text-centered">
|
||||
<router-link
|
||||
class="button is-text"
|
||||
:to="{ name: RouteName.RESEND_CONFIRMATION, params: { email: credentials.email } }"
|
||||
:to="{
|
||||
name: RouteName.RESEND_CONFIRMATION,
|
||||
params: { email: credentials.email },
|
||||
}"
|
||||
>{{ $t("Didn't receive the instructions?") }}</router-link
|
||||
>
|
||||
</p>
|
||||
@@ -120,20 +161,28 @@
|
||||
class="button is-text"
|
||||
:to="{
|
||||
name: RouteName.LOGIN,
|
||||
params: { email: credentials.email, password: credentials.password },
|
||||
params: {
|
||||
email: credentials.email,
|
||||
password: credentials.password,
|
||||
},
|
||||
}"
|
||||
>{{ $t("Login") }}</router-link
|
||||
>
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
<div class="control" v-if="config && config.auth.oauthProviders.length > 0">
|
||||
<div
|
||||
class="control"
|
||||
v-if="config && config.auth.oauthProviders.length > 0"
|
||||
>
|
||||
<auth-providers :oauthProviders="config.auth.oauthProviders" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div v-if="errors.length > 0">
|
||||
<b-message type="is-danger" v-for="error in errors" :key="error">{{ error }}</b-message>
|
||||
<b-message type="is-danger" v-for="error in errors" :key="error">{{
|
||||
error
|
||||
}}</b-message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -211,10 +260,13 @@ export default class Register extends Vue {
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.errors = error.graphQLErrors.reduce((acc: { [key: string]: any }, localError: any) => {
|
||||
acc[localError.field] = localError.message;
|
||||
return acc;
|
||||
}, {});
|
||||
this.errors = error.graphQLErrors.reduce(
|
||||
(acc: { [key: string]: any }, localError: any) => {
|
||||
acc[localError.field] = localError.message;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
this.sendingForm = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,22 @@
|
||||
</h1>
|
||||
<form v-if="!validationSent" @submit="resendConfirmationAction">
|
||||
<b-field :label="$t('Email address')">
|
||||
<b-input aria-required="true" required type="email" v-model="credentials.email" />
|
||||
<b-input
|
||||
aria-required="true"
|
||||
required
|
||||
type="email"
|
||||
v-model="credentials.email"
|
||||
/>
|
||||
</b-field>
|
||||
<p class="control">
|
||||
<b-button type="is-primary" native-type="submit">
|
||||
{{ $t("Send the confirmation email again") }}
|
||||
</b-button>
|
||||
<router-link :to="{ name: RouteName.LOGIN }" class="button is-text">{{
|
||||
$t("Cancel")
|
||||
}}</router-link>
|
||||
<router-link
|
||||
:to="{ name: RouteName.LOGIN }"
|
||||
class="button is-text"
|
||||
>{{ $t("Cancel") }}</router-link
|
||||
>
|
||||
</p>
|
||||
</form>
|
||||
<div v-else>
|
||||
@@ -28,7 +35,11 @@
|
||||
}}
|
||||
</b-message>
|
||||
<b-message type="is-info">
|
||||
{{ $t("Please check your spam folder if you didn't receive the email.") }}
|
||||
{{
|
||||
$t(
|
||||
"Please check your spam folder if you didn't receive the email."
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,7 +49,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||
import { validateEmailField, validateRequiredField } from "../../utils/validators";
|
||||
import {
|
||||
validateEmailField,
|
||||
validateRequiredField,
|
||||
} from "../../utils/validators";
|
||||
import { RESEND_CONFIRMATION_EMAIL } from "../../graphql/auth";
|
||||
import RouteName from "../../router/name";
|
||||
|
||||
|
||||
@@ -23,23 +23,38 @@
|
||||
</b-message>
|
||||
<form @submit="sendResetPasswordTokenAction" v-if="!validationSent">
|
||||
<b-field :label="$t('Email address')">
|
||||
<b-input aria-required="true" required type="email" v-model="credentials.email" />
|
||||
<b-input
|
||||
aria-required="true"
|
||||
required
|
||||
type="email"
|
||||
v-model="credentials.email"
|
||||
/>
|
||||
</b-field>
|
||||
<p class="control">
|
||||
<b-button type="is-primary" native-type="submit">
|
||||
{{ $t("Submit") }}
|
||||
</b-button>
|
||||
<router-link :to="{ name: RouteName.LOGIN }" class="button is-text">{{
|
||||
$t("Cancel")
|
||||
}}</router-link>
|
||||
<router-link
|
||||
:to="{ name: RouteName.LOGIN }"
|
||||
class="button is-text"
|
||||
>{{ $t("Cancel") }}</router-link
|
||||
>
|
||||
</p>
|
||||
</form>
|
||||
<div v-else>
|
||||
<b-message type="is-success" :closable="false" title="Success">
|
||||
{{ $t("We just sent an email to {email}", { email: credentials.email }) }}
|
||||
{{
|
||||
$t("We just sent an email to {email}", {
|
||||
email: credentials.email,
|
||||
})
|
||||
}}
|
||||
</b-message>
|
||||
<b-message type="is-info">
|
||||
{{ $t("Please check your spam folder if you didn't receive the email.") }}
|
||||
{{
|
||||
$t(
|
||||
"Please check your spam folder if you didn't receive the email."
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,7 +64,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||
import { validateEmailField, validateRequiredField } from "../../utils/validators";
|
||||
import {
|
||||
validateEmailField,
|
||||
validateRequiredField,
|
||||
} from "../../utils/validators";
|
||||
import { SEND_RESET_PASSWORD } from "../../graphql/auth";
|
||||
import RouteName from "../../router/name";
|
||||
|
||||
|
||||
@@ -56,9 +56,12 @@ import { IConfig } from "../../types/config.model";
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
NotificationsOnboarding: () => import("../../components/Settings/NotificationsOnboarding.vue"),
|
||||
SettingsOnboarding: () => import("../../components/Settings/SettingsOnboarding.vue"),
|
||||
ProfileOnboarding: () => import("../../components/Account/ProfileOnboarding.vue"),
|
||||
NotificationsOnboarding: () =>
|
||||
import("../../components/Settings/NotificationsOnboarding.vue"),
|
||||
SettingsOnboarding: () =>
|
||||
import("../../components/Settings/SettingsOnboarding.vue"),
|
||||
ProfileOnboarding: () =>
|
||||
import("../../components/Account/ProfileOnboarding.vue"),
|
||||
},
|
||||
apollo: {
|
||||
config: TIMEZONES,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<template>
|
||||
<section class="section container">
|
||||
<h1 class="title" v-if="loading">{{ $t("Your account is being validated") }}</h1>
|
||||
<h1 class="title" v-if="loading">
|
||||
{{ $t("Your account is being validated") }}
|
||||
</h1>
|
||||
<div v-else>
|
||||
<div v-if="failed">
|
||||
<b-message :title="$t('Error while validating account')" type="is-danger">
|
||||
<b-message
|
||||
:title="$t('Error while validating account')"
|
||||
type="is-danger"
|
||||
>
|
||||
{{
|
||||
$t("Either the account is already validated, either the validation token is incorrect.")
|
||||
$t(
|
||||
"Either the account is already validated, either the validation token is incorrect."
|
||||
)
|
||||
}}
|
||||
</b-message>
|
||||
</div>
|
||||
@@ -60,7 +67,10 @@ export default class Validate extends Vue {
|
||||
});
|
||||
|
||||
if (user.defaultActor) {
|
||||
await changeIdentity(this.$apollo.provider.defaultClient, user.defaultActor);
|
||||
await changeIdentity(
|
||||
this.$apollo.provider.defaultClient,
|
||||
user.defaultActor
|
||||
);
|
||||
await this.$router.push({ name: RouteName.HOME });
|
||||
} else {
|
||||
// If the user didn't register any profile yet, let's create one for them
|
||||
|
||||
Reference in New Issue
Block a user