mobilizon.chapril.org-mobil.../js/src/components/Event/EventParticipationCard.vue
Tykayn fa372387aa Please read the [UPGRADE.md](https://framagit.org/framasoft/mobilizon/-/blob/main/UPGRADE.md#upgrading-from-13-to-20) file as well.
- Added possibility to follow groups and be notified from new upcoming events
 - Export list of participants to CSV, `PDF` and `ODS`
 - Allow to set timezone for an event. The timezone is automatically defined from the address if one is defined. If the event timezone is different than the user's current one, a toggle is shown to switch between the two.
 - Added initial support for Right To Left languages (such as arabic) and [BiDi](https://en.wikipedia.org/wiki/Bidirectional_text)
 - Group followers and members get an notification email by default when a group publishes a new event (subject to activity notification settings)
 - Group admins can now approve or deny new memberships
 - Build releases in `arm` and `arm64` format in addition to `amd64`
 - Build Docker images in `arm` and `arm64` format in addition to `amd64`
 - Added possibility to indicate the event is fully online
 - Added possibility to search only for online events
 - Added possibility to search only in past events
 - Detect event, comments and posts languages automatically. Allows setting language
 - Allow to change an user's password through the users.modify mix task
 - Add instance setting so that only the admin can create groups
 - Add instance setting so that only groups can create events
 - Added JSON-LD metadata about the event in emails
 - Added a quick link to email notification settings at the bottom of emails
 - Allow to access Mobilizon with a specific language directly by using `https://instance.tld/lang` where `lang` is a language supported by Mobilizon
 - Added organizer actor name (profile or group) in the icalendar export
 - Add initial support for federation with Gancio
 
 - Multiple UI improvements, including post, event and participation cards, discussions and emails. The « My Events » page was also redesigned to allow showing events from your groups.
 - Various accessibility improvements
 - Event update notification is send to participants ~30 minutes after the event update, so that successive edits are throttled.
 - Event, post and comments titles and content now have expose their detected language in HTML, for improved screen reader experience
 - Delete current actor ID as well from local storage when unlogging
 - Show a default text for instance contact in default terms text when no instance contact is set
 - Only show locatecontrol button in leaflet map when we can do geolocation
 - Disable push column in notification settings when push is not available
 - Show actual language instead of language code in Users admin view
 - Empty old & new passwords fields when successful password change
 - Don't link to the group page from admin when actor is suspended
 - Warn participants when the event organizer is suspended (and therefore the event cancelled)
 - Improve metadata on public page
 - Make sure some event action pages (participate remotely or without an account) don't get indexed by search engines
 - Only send `Tombstone` element in `Delete` activities, not the whole previous deleted element.
 - Make sure `Delete` activity are send correctly to everyone
 - Only add address and tags to event icalendar export if they exist
 - `master` branch has been renamed to `main`
 - Mention following groups on the registration page
 - Add missing group name to activity notifications
 - Warn while registering and logging when the email contains uppercase characters
 - Improve json-ld metadata on event live streams
 - Add "eventAttendanceMode" to JSON-ld schema.org event representation
 - Improve sending pending participation notifications
 - Add "formerType" and "delete" attributes on Tombstones ActivityPub objects representation
 - Improve MyEvents page description text
 
 - Support for Elixir < 1.12 and OTP < 22
 
 - Fix tags autocomplete
 - Fix config onboarding after LDAP initial connexion
 - Fix events pagination on tags page
 - Fixed deduplicated files from orphan media being deleted as well
 - Fix deleting own account
 - Fix search returning user profiles instead of only groups
 - Fix federating geo coordinates
 - Fix an issue with group activity items when moving resources
 - Fix an issue with Identity Picker
 - Fix an issue with TagInput
 - Fix an issue when leaving a group
 - Fix admin settings edition
 - Fix an issue when showing public page of suspended group
 - Removed non existing page (`/about/mobilizon`) from sitemap
 - Fix action logs containing group suspension events
 - Fixed group physical address not exposed to ActivityPub
 - Release front-end files are no longer in duplicate
 - Only show datetime timezone toggle on event if the timezone offset is different from our own
 - Fix error when determining audience for Discussion when deleting a comment
 - Fix a couple of accessibility issues
 - Limit to acceptable tags when pasting raw HTML into comment fields on front-end
 - Fixed group map display
 - Fixed updating group physical address
 - Allow group members to access group drafts
 - Improve group refreshment workflow
 - Fixed date signature generation for federation
 - Fixed an issue when duplicating a group event from another profile
 - Fixed event metadata not saved on eventcreation
 - Use a different pagination parameter for searched events and featured events on search page
 - Fixed creating group activities when creating events with some fields
 - Move release package at correct path for CI upload
 - Fixed event contacts that were not exposed and fetched over federation
 - Don't sign fetch when fetching actor for a given signature
 - Some various HTTP signatures issues
 - Fixed actor AP representation of avatar
 - Handle errors when fetching actor pictures
 - Fixed sending group events to followers on Mastodon
 - Fixed actors avatars and banners being deleted if the same file was also an orphan media
 - Fix spacing in organizer picker
 - Increase number of close events and follow group events
 - Fix accessing user profile in admin section
 - Set initial values for some EventMetadata elements, fixing submitting them right away with no value
 - Avoid giving an error page if the apollo futureParticipations query is undefined
 - Fixed path to exports in production
 - Fixed padding below truncated title of event cards
 - Fixed exports that weren't enabled in Docker
 - Fixed error page when event end date is null
 - Fixed event language not being allowed to be null
 
 - Fixed private messages sent as event replies from Mastodon that were shown publically as public comments. They are now discarded.
 
 - Czech
 - Gaelic
 - German
 - Hungarian
 - Indonesian
 - Norwegian Nynorsk
 - Occitan
 - Persian
 - Portuguese (Brazil)
 - Russian
 - Slovenian
 - Spanish
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEExMITpfxOHHCvHn8FoGG53eDKB3MFAmGcqEMACgkQoGG53eDK
 B3NlaA/8DoAi7WtC+//xJn8tRSP04PuTRDi2+9/D9a6gjTsiPXDZdb7LtEHuCg1H
 PXsHNQOTafUM/T7b7XTGmKOST3Sz2kw4eKJArCyF+NuvCwiO/Iw/v1wev2Mo8vvC
 eDBbMkfiCnMHcytqheeM9gvGuRyIOfgQ9uPk54snfa9a+SGELR5XDRKhwBlGAs6i
 nkUPbOB72oCou79HZ6CjyLTG6CoWUVsheuvAEhYw52e5JlWSJb9yOUdnOYUV1sr6
 OzLct996Z3IOQX4ToaQ+Re99tOaEyqO98aHsv+Wbz128sku0WrfseKn9zi3PL6cF
 LYjtZ9+0dwdNi3MfgKoEoWJaMlN3+6WUw/blcVP+6b6Ibn5YV/HkVacke/rGoAry
 oiEjP4HFKnvT83dTBn+LRcU6MY3MrZsarjUACjcKIwpTiylw9gaqA0i7dPBdW35p
 Q4c1gIh1Q/aE5OKCxXGLrg6s1SNZ754cAyEVo85UnF8Iu4wiaY++ImvyG8xIoOWf
 vQuya3LcDT8Gj9KY/LWMrVT8LJ2ij5t8oRMFLIHLlfiWbq05m6QaBIWr5sLjLO/N
 w2N1//ZHNM3sJNl0bZgc9g4lhcdj52VhOcHLot4fdcg5RTektCK0ky5uTD4WJ3GP
 keihAR8ZtFLaiAQ+XN6ng3IhrgQcpIdJBLlHzgFBx2rTBy8a0Tw=
 =jyrf
 -----END PGP SIGNATURE-----

Merge tag '2.0.0' into chapril

Please read the [UPGRADE.md](https://framagit.org/framasoft/mobilizon/-/blob/main/UPGRADE.md#upgrading-from-13-to-20) file as well.

- Added possibility to follow groups and be notified from new upcoming events
- Export list of participants to CSV, `PDF` and `ODS`
- Allow to set timezone for an event. The timezone is automatically defined from the address if one is defined. If the event timezone is different than the user's current one, a toggle is shown to switch between the two.
- Added initial support for Right To Left languages (such as arabic) and [BiDi](https://en.wikipedia.org/wiki/Bidirectional_text)
- Group followers and members get an notification email by default when a group publishes a new event (subject to activity notification settings)
- Group admins can now approve or deny new memberships
- Build releases in `arm` and `arm64` format in addition to `amd64`
- Build Docker images in `arm` and `arm64` format in addition to `amd64`
- Added possibility to indicate the event is fully online
- Added possibility to search only for online events
- Added possibility to search only in past events
- Detect event, comments and posts languages automatically. Allows setting language
- Allow to change an user's password through the users.modify mix task
- Add instance setting so that only the admin can create groups
- Add instance setting so that only groups can create events
- Added JSON-LD metadata about the event in emails
- Added a quick link to email notification settings at the bottom of emails
- Allow to access Mobilizon with a specific language directly by using `https://instance.tld/lang` where `lang` is a language supported by Mobilizon
- Added organizer actor name (profile or group) in the icalendar export
- Add initial support for federation with Gancio

- Multiple UI improvements, including post, event and participation cards, discussions and emails. The « My Events » page was also redesigned to allow showing events from your groups.
- Various accessibility improvements
- Event update notification is send to participants ~30 minutes after the event update, so that successive edits are throttled.
- Event, post and comments titles and content now have expose their detected language in HTML, for improved screen reader experience
- Delete current actor ID as well from local storage when unlogging
- Show a default text for instance contact in default terms text when no instance contact is set
- Only show locatecontrol button in leaflet map when we can do geolocation
- Disable push column in notification settings when push is not available
- Show actual language instead of language code in Users admin view
- Empty old & new passwords fields when successful password change
- Don't link to the group page from admin when actor is suspended
- Warn participants when the event organizer is suspended (and therefore the event cancelled)
- Improve metadata on public page
- Make sure some event action pages (participate remotely or without an account) don't get indexed by search engines
- Only send `Tombstone` element in `Delete` activities, not the whole previous deleted element.
- Make sure `Delete` activity are send correctly to everyone
- Only add address and tags to event icalendar export if they exist
- `master` branch has been renamed to `main`
- Mention following groups on the registration page
- Add missing group name to activity notifications
- Warn while registering and logging when the email contains uppercase characters
- Improve json-ld metadata on event live streams
- Add "eventAttendanceMode" to JSON-ld schema.org event representation
- Improve sending pending participation notifications
- Add "formerType" and "delete" attributes on Tombstones ActivityPub objects representation
- Improve MyEvents page description text

- Support for Elixir < 1.12 and OTP < 22

- Fix tags autocomplete
- Fix config onboarding after LDAP initial connexion
- Fix events pagination on tags page
- Fixed deduplicated files from orphan media being deleted as well
- Fix deleting own account
- Fix search returning user profiles instead of only groups
- Fix federating geo coordinates
- Fix an issue with group activity items when moving resources
- Fix an issue with Identity Picker
- Fix an issue with TagInput
- Fix an issue when leaving a group
- Fix admin settings edition
- Fix an issue when showing public page of suspended group
- Removed non existing page (`/about/mobilizon`) from sitemap
- Fix action logs containing group suspension events
- Fixed group physical address not exposed to ActivityPub
- Release front-end files are no longer in duplicate
- Only show datetime timezone toggle on event if the timezone offset is different from our own
- Fix error when determining audience for Discussion when deleting a comment
- Fix a couple of accessibility issues
- Limit to acceptable tags when pasting raw HTML into comment fields on front-end
- Fixed group map display
- Fixed updating group physical address
- Allow group members to access group drafts
- Improve group refreshment workflow
- Fixed date signature generation for federation
- Fixed an issue when duplicating a group event from another profile
- Fixed event metadata not saved on eventcreation
- Use a different pagination parameter for searched events and featured events on search page
- Fixed creating group activities when creating events with some fields
- Move release package at correct path for CI upload
- Fixed event contacts that were not exposed and fetched over federation
- Don't sign fetch when fetching actor for a given signature
- Some various HTTP signatures issues
- Fixed actor AP representation of avatar
- Handle errors when fetching actor pictures
- Fixed sending group events to followers on Mastodon
- Fixed actors avatars and banners being deleted if the same file was also an orphan media
- Fix spacing in organizer picker
- Increase number of close events and follow group events
- Fix accessing user profile in admin section
- Set initial values for some EventMetadata elements, fixing submitting them right away with no value
- Avoid giving an error page if the apollo futureParticipations query is undefined
- Fixed path to exports in production
- Fixed padding below truncated title of event cards
- Fixed exports that weren't enabled in Docker
- Fixed error page when event end date is null
- Fixed event language not being allowed to be null

- Fixed private messages sent as event replies from Mastodon that were shown publically as public comments. They are now discarded.

- Czech
- Gaelic
- German
- Hungarian
- Indonesian
- Norwegian Nynorsk
- Occitan
- Persian
- Portuguese (Brazil)
- Russian
- Slovenian
- Spanish
2021-12-09 15:51:26 +01:00

539 lines
15 KiB
Vue

<template>
<article class="box mb-5 mt-4">
<div class="identity-header" dir="auto">
<figure class="image is-24x24" v-if="participation.actor.avatar">
<img
class="is-rounded"
:src="participation.actor.avatar.url"
alt=""
height="24"
width="24"
/>
</figure>
<b-icon v-else icon="account-circle" />
{{ displayNameAndUsername(participation.actor) }}
</div>
<div class="list-card">
<div class="content-and-actions">
<div class="event-preview mr-0 ml-0">
<div>
<div class="date-component">
<date-calendar-icon
:date="participation.event.beginsOn"
:small="true"
/>
</div>
<router-link
:to="{
name: RouteName.EVENT,
params: { uuid: participation.event.uuid },
}"
>
<lazy-image-wrapper
:rounded="true"
:picture="participation.event.picture"
style="
height: 100%;
position: absolute;
top: 0;
left: 0;
width: 100%;
"
/>
</router-link>
</div>
</div>
<div class="list-card-content">
<div class="title-wrapper" dir="auto">
<router-link
:to="{
name: RouteName.EVENT,
params: { uuid: participation.event.uuid },
}"
>
<h3 class="title" :lang="participation.event.language">
{{ participation.event.title }}
</h3>
</router-link>
</div>
<inline-address
v-if="participation.event.physicalAddress"
class="event-subtitle"
:physical-address="participation.event.physicalAddress"
/>
<div
class="event-subtitle"
v-else-if="
participation.event.options &&
participation.event.options.isOnline
"
>
<b-icon icon="video" />
<span>{{ $t("Online") }}</span>
</div>
<div class="event-subtitle event-organizer">
<figure
class="image is-24x24"
v-if="
organizer(participation.event) &&
organizer(participation.event).avatar
"
>
<img
class="is-rounded"
:src="organizer(participation.event).avatar.url"
alt=""
/>
</figure>
<b-icon v-else icon="account-circle" />
<span class="organizer-name">
{{ organizerDisplayName(participation.event) }}
</span>
</div>
<div class="event-subtitle event-participants">
<b-icon
:class="{ 'has-text-danger': lastSeatsLeft }"
icon="account-group"
/>
<span
class="participant-stats"
v-if="participation.role !== ParticipantRole.NOT_APPROVED"
>
<!-- Less than 10 seats left -->
<span class="has-text-danger" v-if="lastSeatsLeft">
{{
$t("{number} seats left", {
number: seatsLeft,
})
}}
</span>
<span
v-else-if="
participation.event.options.maximumAttendeeCapacity !== 0
"
>
{{
$tc(
"{available}/{capacity} available places",
participation.event.options.maximumAttendeeCapacity -
participation.event.participantStats.participant,
{
available:
participation.event.options.maximumAttendeeCapacity -
participation.event.participantStats.participant,
capacity:
participation.event.options.maximumAttendeeCapacity,
}
)
}}
</span>
<span v-else>
{{
$tc(
"{count} participants",
participation.event.participantStats.participant,
{
count: participation.event.participantStats.participant,
}
)
}}
</span>
<b-button
v-if="participation.event.participantStats.notApproved > 0"
type="is-text"
@click="
gotToWithCheck(participation, {
name: RouteName.PARTICIPATIONS,
query: { role: ParticipantRole.NOT_APPROVED },
params: { eventId: participation.event.uuid },
})
"
>
{{
$tc(
"{count} requests waiting",
participation.event.participantStats.notApproved,
{
count: participation.event.participantStats.notApproved,
}
)
}}
</b-button>
</span>
</div>
</div>
<div class="actions">
<b-dropdown aria-role="list" position="is-bottom-left">
<b-button slot="trigger" role="button" icon-right="dots-horizontal">
{{ $t("Actions") }}
</b-button>
<b-dropdown-item
v-if="
![
ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED,
].includes(participation.role)
"
aria-role="listitem"
@click="
gotToWithCheck(participation, {
name: RouteName.EDIT_EVENT,
params: { eventId: participation.event.uuid },
})
"
>
<b-icon icon="pencil" />
{{ $t("Edit") }}
</b-dropdown-item>
<b-dropdown-item
v-if="participation.role === ParticipantRole.CREATOR"
aria-role="listitem"
@click="
gotToWithCheck(participation, {
name: RouteName.DUPLICATE_EVENT,
params: { eventId: participation.event.uuid },
})
"
>
<b-icon icon="content-duplicate" />
{{ $t("Duplicate") }}
</b-dropdown-item>
<b-dropdown-item
v-if="
![
ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED,
].includes(participation.role)
"
aria-role="listitem"
@click="openDeleteEventModalWrapper"
>
<b-icon icon="delete" />
{{ $t("Delete") }}
</b-dropdown-item>
<b-dropdown-item
v-if="
![
ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED,
].includes(participation.role)
"
aria-role="listitem"
@click="
gotToWithCheck(participation, {
name: RouteName.PARTICIPATIONS,
params: { eventId: participation.event.uuid },
})
"
>
<b-icon icon="account-multiple-plus" />
{{ $t("Manage participations") }}
</b-dropdown-item>
<b-dropdown-item aria-role="listitem" has-link>
<router-link
:to="{
name: RouteName.EVENT,
params: { uuid: participation.event.uuid },
}"
>
<b-icon icon="view-compact" />
{{ $t("View event page") }}
</router-link>
</b-dropdown-item>
</b-dropdown>
</div>
</div>
</div>
</article>
</template>
<script lang="ts">
import { Component, Prop } from "vue-property-decorator";
import DateCalendarIcon from "@/components/Event/DateCalendarIcon.vue";
import { mixins } from "vue-class-component";
import { RawLocation, Route } from "vue-router";
import { EventVisibility, ParticipantRole } from "@/types/enums";
import { IParticipant } from "../../types/participant.model";
import {
IEventCardOptions,
organizer,
organizerDisplayName,
} from "../../types/event.model";
import { displayNameAndUsername, IActor, IPerson } from "../../types/actor";
import ActorMixin from "../../mixins/actor";
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
import EventMixin from "../../mixins/event";
import RouteName from "../../router/name";
import { changeIdentity } from "../../utils/auth";
import PopoverActorCard from "../Account/PopoverActorCard.vue";
import LazyImageWrapper from "@/components/Image/LazyImageWrapper.vue";
import InlineAddress from "@/components/Address/InlineAddress.vue";
import { PropType } from "vue";
const defaultOptions: IEventCardOptions = {
hideDate: true,
loggedPerson: false,
hideDetails: false,
organizerActor: null,
memberofGroup: false,
};
@Component({
components: {
DateCalendarIcon,
PopoverActorCard,
LazyImageWrapper,
InlineAddress,
},
apollo: {
currentActor: {
query: CURRENT_ACTOR_CLIENT,
},
},
})
export default class EventParticipationCard extends mixins(
ActorMixin,
EventMixin
) {
/**
* The participation associated
*/
@Prop({ required: true, type: Object as PropType<IParticipant> })
participation!: IParticipant;
/**
* Options are merged with default options
*/
@Prop({ required: false, default: () => defaultOptions })
options!: IEventCardOptions;
currentActor!: IPerson;
ParticipantRole = ParticipantRole;
EventVisibility = EventVisibility;
displayNameAndUsername = displayNameAndUsername;
organizerDisplayName = organizerDisplayName;
organizer = organizer;
RouteName = RouteName;
get mergedOptions(): IEventCardOptions {
return { ...defaultOptions, ...this.options };
}
/**
* Delete the event
*/
async openDeleteEventModalWrapper(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
await this.openDeleteEventModal(this.participation.event);
}
async gotToWithCheck(
participation: IParticipant,
route: RawLocation
): Promise<Route> {
if (
participation.actor.id !== this.currentActor.id &&
participation.event.organizerActor
) {
const organizerActor = participation.event.organizerActor as IPerson;
await changeIdentity(this.$apollo.provider.defaultClient, organizerActor);
this.$buefy.notification.open({
message: this.$t(
"Current identity has been changed to {identityName} in order to manage this event.",
{
identityName: organizerActor.preferredUsername,
}
) as string,
type: "is-info",
position: "is-bottom-right",
duration: 5000,
});
}
return this.$router.push(route);
}
get organizerActor(): IActor | undefined {
if (
this.participation.event.attributedTo &&
this.participation.event.attributedTo.id
) {
return this.participation.event.attributedTo;
}
return this.participation.event.organizerActor;
}
get seatsLeft(): number | null {
if (this.participation.event.options.maximumAttendeeCapacity > 0) {
return (
this.participation.event.options.maximumAttendeeCapacity -
this.participation.event.participantStats.participant
);
}
return null;
}
get lastSeatsLeft(): boolean {
if (this.seatsLeft) {
return this.seatsLeft < 10;
}
return false;
}
}
</script>
<style lang="scss" scoped>
@use "@/styles/_mixins" as *;
@use "@/styles/_event-card";
@import "~bulma/sass/utilities/mixins.sass";
article.box {
div.tag-container {
position: absolute;
top: 10px;
right: 0;
@include margin-left(-5px);
z-index: 10;
max-width: 40%;
span.tag {
margin: 5px auto;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 1);
/*word-break: break-all;*/
text-overflow: ellipsis;
overflow: hidden;
display: block;
/*text-align: right;*/
font-size: 1em;
/*padding: 0 1px;*/
line-height: 1.75em;
}
}
.list-card {
display: flex;
padding: 0 6px 0 0;
position: relative;
flex-direction: column;
.content-and-actions {
display: grid;
grid-gap: 5px 10px;
grid-template-areas: "preview" "body" "actions";
@include tablet {
grid-template-columns: 1fr 3fr;
grid-template-areas: "preview body" "actions actions";
}
@include desktop {
grid-template-columns: 1fr 3fr 1fr;
grid-template-areas: "preview body actions";
}
.event-preview {
grid-area: preview;
& > div {
height: 128px;
width: 100%;
position: relative;
div.date-component {
display: flex;
position: absolute;
bottom: 5px;
left: 5px;
z-index: 1;
}
img {
width: 100%;
object-position: center;
object-fit: cover;
height: 100%;
}
}
}
.actions {
padding: 7px;
cursor: pointer;
align-self: center;
justify-self: center;
grid-area: actions;
}
div.list-card-content {
flex: 1;
padding: 5px;
grid-area: body;
.participant-stats {
display: flex;
align-items: center;
padding: 0 5px;
}
div.title-wrapper {
display: flex;
align-items: center;
padding-top: 5px;
a {
text-decoration: none;
padding-bottom: 5px;
}
.title {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
font-size: 18px;
line-height: 24px;
margin: auto 0;
font-weight: bold;
color: $title-color;
}
}
}
}
}
.identity-header {
background: $yellow-2;
display: flex;
padding: 5px;
figure,
span.icon {
@include padding-right(3px);
}
}
& > .columns {
padding: 1.25rem;
}
padding: 0;
}
.content h3.event-title-card {
line-height: 1em;
margin-top: 0.5em;
}
.participation-actor {
margin-top: 1em;
}
</style>