2019-01-21 14:08:22 +00:00
< template >
2019-10-01 18:10:53 +00:00
< div class = "container" >
2021-08-04 09:23:37 +00:00
< b -loading :active.sync ="$apollo.queries.event.loading" / >
< div class = "wrapper" >
< event -banner :picture ="event.picture" / >
< div class = "intro-wrapper" >
< div class = "date-calendar-icon-wrapper" >
< date -calendar -icon :date ="event.beginsOn" / >
< / div >
< section class = "intro" >
< div class = "columns" >
< div class = "column" >
< 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"
>
< span >
{ {
$t ( "By @{username}" , {
username : usernameWithDomain ( event . organizerActor ) ,
} )
} }
< / span >
< / p o p o v e r - a c t o r - c a r d >
< / span >
< span
v - else - if = "
event . attributedTo &&
event . options . hideOrganizerWhenGroupEvent
"
>
< popover -actor -card
: actor = "event.attributedTo"
: inline = "true"
>
{ {
$t ( "By @{group}" , {
group : usernameWithDomain ( event . attributedTo ) ,
} )
} }
< / p o p o v e r - a c t o r - c a r d >
< / span >
< span v -else -if = " event.organizerActor & & event.attributedTo " >
< i18n path = "By {group}" >
2020-11-30 09:24:11 +00:00
< popover -actor -card
2021-08-04 09:23:37 +00:00
: actor = "event.attributedTo"
slot = "group"
2020-11-30 09:24:11 +00:00
: inline = "true"
>
2021-08-04 09:23:37 +00:00
< router -link
: to = " {
name : RouteName . GROUP ,
params : {
preferredUsername : usernameWithDomain (
event . attributedTo
) ,
} ,
} "
>
2020-11-30 09:24:11 +00:00
{ {
2021-08-04 09:23:37 +00:00
$t ( "@{group}" , {
group : usernameWithDomain ( event . attributedTo ) ,
2020-11-30 09:24:11 +00:00
} )
} }
2021-08-04 09:23:37 +00:00
< / r o u t e r - l i n k >
2020-02-18 07:57:00 +00:00
< / p o p o v e r - a c t o r - c a r d >
2021-08-04 09:23:37 +00:00
< / i18n >
< / span >
< / div >
< p class = "tags" v-if ="event.tags && event.tags.length > 0" >
< router -link
v - for = "tag in event.tags"
: key = "tag.title"
: to = "{ name: RouteName.TAG, params: { tag: tag.title } }"
>
< tag > { { tag . title } } < / tag >
< / r o u t e r - l i n k >
< / p >
< b -tag type = "is-warning" size = "is-medium" v -if = " event.draft "
> { { $t ( "Draft" ) } }
< / b - t a g >
< 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 - t a g
>
< b -tag
type = "is-danger"
v - if = "event.status === EventStatus.CANCELLED"
> { { $t ( "Event cancelled" ) } } < / b - t a g
>
< / span >
< / div >
< div class = "column is-3-tablet" >
< participation -section
: participation = "participations[0]"
: event = "event"
: anonymousParticipation = "anonymousParticipation"
@ join - event = "joinEvent"
@ join - modal = "isJoinModalActive = true"
@ join - event - with - confirmation = "joinEventWithConfirmation"
@ confirm - leave = "confirmLeave"
@ cancel - anonymous - participation = "cancelAnonymousParticipation"
/ >
< div class = "has-text-right" >
< template class = "visibility" v-if ="!event.draft" >
< p v-if ="event.visibility === EventVisibility.PUBLIC" >
{ { $t ( "Public event" ) } }
< b -icon icon = "earth" / >
< / p >
< p v-if ="event.visibility === EventVisibility.UNLISTED" >
{ { $t ( "Private event" ) } }
< b -icon icon = "link" / >
< / p >
< / template >
< template v-if ="!event.local && organizer.domain" >
< a :href ="event.url" >
< tag > { { organizer . domain } } < / tag >
< / a >
< / template >
< p >
< router -link
class = "participations-link"
v - if = "canManageEvent && event.draft === false"
: to = " {
name : RouteName . PARTICIPATIONS ,
params : { eventId : event . uuid } ,
} "
2019-10-23 10:36:11 +00:00
>
2021-08-04 09:23:37 +00:00
<!-- We retire one because of the event creator who is a participant -- >
2021-10-20 09:49:55 +00:00
< span v-if ="maximumAttendeeCapacity" >
2020-02-18 07:57:00 +00:00
{ {
2021-08-04 09:23:37 +00:00
$tc (
"{available}/{capacity} available places" ,
2021-10-20 09:49:55 +00:00
maximumAttendeeCapacity -
2021-08-04 09:23:37 +00:00
event . participantStats . participant ,
{
available :
2021-10-20 09:49:55 +00:00
maximumAttendeeCapacity -
2021-08-04 09:23:37 +00:00
event . participantStats . participant ,
2021-10-20 09:49:55 +00:00
capacity : maximumAttendeeCapacity ,
2021-08-04 09:23:37 +00:00
}
)
2020-02-18 07:57:00 +00:00
} }
2021-08-04 09:23:37 +00:00
< / span >
< span v-else >
{ {
$tc (
"No one is participating|One person participating|{going} people participating" ,
event . participantStats . participant ,
{
going : event . participantStats . participant ,
}
)
} }
< / span >
< / r o u t e r - l i n k >
< span v-else >
2021-10-20 09:49:55 +00:00
< span v-if ="maximumAttendeeCapacity" >
2021-08-04 09:23:37 +00:00
{ {
$tc (
"{available}/{capacity} available places" ,
2021-10-20 09:49:55 +00:00
maximumAttendeeCapacity -
2021-08-04 09:23:37 +00:00
event . participantStats . participant ,
{
available :
2021-10-20 09:49:55 +00:00
maximumAttendeeCapacity -
2021-08-04 09:23:37 +00:00
event . participantStats . participant ,
2021-10-20 09:49:55 +00:00
capacity : maximumAttendeeCapacity ,
2021-08-04 09:23:37 +00:00
}
)
} }
< / span >
< span v-else >
{ {
$tc (
"No one is participating|One person participating|{going} people participating" ,
event . participantStats . participant ,
{
going : event . participantStats . participant ,
}
)
} }
< / span >
2021-06-10 08:33:16 +00:00
< / span >
2021-08-04 09:23:37 +00:00
< b -tooltip
type = "is-dark"
v - if = "!event.local"
: label = "
$t (
'The actual number of participants may differ, as this event is hosted on another instance.'
)
"
2020-02-18 07:57:00 +00:00
>
2021-08-04 09:23:37 +00:00
< b -icon size = "is-small" icon = "help-circle-outline" / >
< / b - t o o l t i p >
< b -icon icon = "ticket-confirmation-outline" / >
2019-04-03 15:29:03 +00:00
< / p >
2021-08-04 09:23:37 +00:00
< b -dropdown position = "is-bottom-left" aria -role = " list " >
< b -button
slot = "trigger"
role = "button"
icon - right = "dots-horizontal"
2020-02-18 07:57:00 +00:00
>
2021-08-04 09:23:37 +00:00
{ { $t ( "Actions" ) } }
< / b - b u t t o n >
< b -dropdown -item
aria - role = "listitem"
has - link
v - if = "canManageEvent || event.draft"
2020-04-22 22:27:09 +00:00
>
< router -link
2020-11-30 09:24:11 +00:00
: to = " {
2021-08-04 09:23:37 +00:00
name : RouteName . EDIT _EVENT ,
2020-11-30 09:24:11 +00:00
params : { eventId : event . uuid } ,
} "
2020-04-22 22:27:09 +00:00
>
2021-08-04 09:23:37 +00:00
{ { $t ( "Edit" ) } }
< b -icon icon = "pencil" / >
2020-04-22 22:27:09 +00:00
< / r o u t e r - l i n k >
2021-08-04 09:23:37 +00:00
< / b - d r o p d o w n - i t e m >
< b -dropdown -item
aria - role = "listitem"
has - link
v - if = "canManageEvent || event.draft"
>
< router -link
: to = " {
name : RouteName . DUPLICATE _EVENT ,
params : { eventId : event . uuid } ,
} "
2021-06-10 08:33:16 +00:00
>
2021-08-04 09:23:37 +00:00
{ { $t ( "Duplicate" ) } }
< b -icon icon = "content-duplicate" / >
< / r o u t e r - l i n k >
< / b - d r o p d o w n - i t e m >
< b -dropdown -item
aria - role = "listitem"
v - if = "canManageEvent || event.draft"
@ click = "openDeleteEventModalWrapper"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "openDeleteEventModalWrapper"
2021-08-04 09:23:37 +00:00
>
{ { $t ( "Delete" ) } }
< b -icon icon = "delete" / >
< / b - d r o p d o w n - i t e m >
2021-06-10 08:33:16 +00:00
2021-08-04 09:23:37 +00:00
< hr
2021-10-10 14:24:12 +00:00
role = "presentation"
2021-08-04 09:23:37 +00:00
class = "dropdown-divider"
aria - role = "menuitem"
v - if = "canManageEvent || event.draft"
/ >
< b -dropdown -item
aria - role = "listitem"
v - if = "!event.draft"
@ click = "triggerShare()"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "triggerShare()"
2021-08-04 09:23:37 +00:00
>
< span >
{ { $t ( "Share this event" ) } }
< b -icon icon = "share" / >
< / span >
< / b - d r o p d o w n - i t e m >
< b -dropdown -item
aria - role = "listitem"
@ click = "downloadIcsEvent()"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "downloadIcsEvent()"
2021-08-04 09:23:37 +00:00
v - if = "!event.draft"
>
< span >
{ { $t ( "Add to my calendar" ) } }
< b -icon icon = "calendar-plus" / >
< / span >
< / b - d r o p d o w n - i t e m >
< b -dropdown -item
aria - role = "listitem"
v - if = "ableToReport"
@ click = "isReportModalActive = true"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "isReportModalActive = true"
2021-08-04 09:23:37 +00:00
>
< span >
{ { $t ( "Report" ) } }
< b -icon icon = "flag" / >
< / span >
< / b - d r o p d o w n - i t e m >
< / b - d r o p d o w n >
2020-02-18 07:57:00 +00:00
< / div >
< / div >
2021-08-04 09:23:37 +00:00
< / div >
< / section >
< / div >
< div class = "event-description-wrapper" >
< aside class = "event-metadata" >
< div class = "sticky" >
2021-08-09 12:26:11 +00:00
< event -metadata -sidebar
v - if = "event && config"
: event = "event"
: config = "config"
2021-10-10 14:25:50 +00:00
: user = "loggedUser"
@ showMapModal = "showMap = true"
2021-08-09 12:26:11 +00:00
/ >
2021-08-04 09:23:37 +00:00
< / div >
< / aside >
< div class = "event-description-comments" >
< section class = "event-description" >
< subtitle > { { $t ( "About this event" ) } } < / subtitle >
< p v-if ="!event.description" >
{ { $t ( "The event organizer didn't add any description." ) } }
< / p >
< div v-else >
< div
class = "description-content"
ref = "eventDescriptionElement"
v - html = "event.description"
/ >
2019-04-03 15:29:03 +00:00
< / div >
2021-08-04 09:23:37 +00:00
< / section >
2021-08-09 12:26:11 +00:00
< section class = "integration-wrappers" >
< component
v - for = "(metadata, integration) in integrations"
: is = "integration"
: key = "integration"
: metadata = "metadata"
/ >
< / section >
2021-08-04 09:23:37 +00:00
< section class = "comments" ref = "commentsObserver" >
< a href = "#comments" >
< subtitle id = "comments" > { { $t ( "Comments" ) } } < / subtitle >
< / a >
< comment -tree v -if = " loadComments " :event ="event" / >
< / 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 >
< div class = "columns" >
< div
class = "column is-one-third-desktop"
v - for = "relatedEvent in event.relatedEvents"
: key = "relatedEvent.uuid"
>
< EventCard :event ="relatedEvent" / >
2019-04-03 15:29:03 +00:00
< / div >
2020-02-18 07:57:00 +00:00
< / div >
2021-08-04 09:23:37 +00:00
< / section >
< b -modal
: active . sync = "isReportModalActive"
has - modal - card
ref = "reportModal"
>
< report -modal
: on - confirm = "reportEvent"
: title = "$t('Report this event')"
: outside - domain = "organizerDomain"
@ close = "$refs.reportModal.close()"
/ >
< / b - m o d a l >
< b -modal
: active . sync = "isShareModalActive"
has - modal - card
ref = "shareModal"
>
< share -event -modal :event ="event" :eventCapacityOK ="eventCapacityOK" / >
< / b - m o d a l >
< 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"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "isJoinModalActive = false"
2021-08-04 09:23:37 +00:00
>
{ { $t ( "Cancel" ) } }
< / button >
< button
class = "button is-primary"
ref = "confirmButton"
@ click = "
event . joinOptions === EventJoinOptions . RESTRICTED
? joinEventWithConfirmation ( identity )
: joinEvent ( identity )
"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "
event . joinOptions === EventJoinOptions . RESTRICTED
? joinEventWithConfirmation ( identity )
: joinEvent ( identity )
"
2021-08-04 09:23:37 +00:00
>
{ { $t ( "Confirm my particpation" ) } }
< / button >
< / footer >
< / template >
< / i d e n t i t y - p i c k e r >
< / b - m o d a l >
< b -modal
: active . sync = "isJoinConfirmationModalActive"
has - modal - card
ref = "joinConfirmationModal"
>
< div class = "modal-card" >
< header class = "modal-card-head" >
< p class = "modal-card-title" >
{ { $t ( "Participation confirmation" ) } }
< / p >
< / header >
< section class = "modal-card-body" >
< p >
{ {
$t (
"The event organiser has chosen to validate manually participations. Do you want to add a little note to explain why you want to participate to this event?"
)
} }
< / p >
< form
@ submit . prevent = "
joinEvent ( actorForConfirmation , messageForConfirmation )
"
2020-02-18 07:57:00 +00:00
>
2021-08-04 09:23:37 +00:00
< b -field :label ="$t('Message')" >
< b -input
type = "textarea"
size = "is-medium"
v - model = "messageForConfirmation"
minlength = "10"
> < / b - i n p u t >
< / b - f i e l d >
< div class = "buttons" >
< b -button
native - type = "button"
2020-11-30 09:24:11 +00:00
class = "button"
ref = "cancelButton"
2021-08-04 09:23:37 +00:00
@ click = "isJoinConfirmationModalActive = false"
2021-10-10 14:24:12 +00:00
@ keyup . enter = "isJoinConfirmationModalActive = false"
2021-08-04 09:23:37 +00:00
> { { $t ( "Cancel" ) } }
< / b - b u t t o n >
< b -button type = "is-primary" native -type = " submit " >
{ { $t ( "Confirm my participation" ) } }
< / b - b u t t o n >
< / div >
< / form >
< / section >
< / div >
< / b - m o d a l >
2021-10-10 14:25:50 +00:00
< b -modal
class = "map-modal"
v - if = "event.physicalAddress && event.physicalAddress.geom"
: active . sync = "showMap"
has - modal - card
full - screen
: can - cancel = "['escape', 'outside']"
>
< template # default = "props" >
< event -map
: routingType = "routingType"
: address = "event.physicalAddress"
@ close = "props.close"
/ >
< / template >
< / b - m o d a l >
2021-08-04 09:23:37 +00:00
< / div >
2019-10-08 20:27:14 +00:00
< / div >
2019-01-21 14:08:22 +00:00
< / template >
< script lang = "ts" >
2020-02-18 07:57:00 +00:00
import { Component , Prop , Watch } from "vue-property-decorator" ;
import BIcon from "buefy/src/components/icon/Icon.vue" ;
2020-11-30 09:24:11 +00:00
import {
EventJoinOptions ,
EventStatus ,
EventVisibility ,
2021-07-26 15:15:40 +00:00
MemberRole ,
2020-11-30 09:24:11 +00:00
ParticipantRole ,
} from "@/types/enums" ;
2020-02-18 07:57:00 +00:00
import {
EVENT _PERSON _PARTICIPATION ,
EVENT _PERSON _PARTICIPATION _SUBSCRIPTION _CHANGED ,
FETCH _EVENT ,
JOIN _EVENT ,
} from "../../graphql/event" ;
2021-07-26 15:15:40 +00:00
import {
CURRENT _ACTOR _CLIENT ,
PERSON _MEMBERSHIP _GROUP ,
} from "../../graphql/actor" ;
2020-11-27 18:27:44 +00:00
import { EventModel , IEvent } from "../../types/event.model" ;
2020-09-30 13:25:30 +00:00
import { IActor , IPerson , Person , usernameWithDomain } from "../../types/actor" ;
2020-02-18 07:57:00 +00:00
import { GRAPHQL _API _ENDPOINT } from "../../api/_entrypoint" ;
import DateCalendarIcon from "../../components/Event/DateCalendarIcon.vue" ;
import EventCard from "../../components/Event/EventCard.vue" ;
import ReportModal from "../../components/Report/ReportModal.vue" ;
import { IReport } from "../../types/report.model" ;
import { CREATE _REPORT } from "../../graphql/report" ;
import EventMixin from "../../mixins/event" ;
import IdentityPicker from "../Account/IdentityPicker.vue" ;
2020-12-04 14:12:00 +00:00
import ParticipationSection from "../../components/Participation/ParticipationSection.vue" ;
2020-02-18 07:57:00 +00:00
import RouteName from "../../router/name" ;
import CommentTree from "../../components/Comment/CommentTree.vue" ;
import "intersection-observer" ;
import { CONFIG } from "../../graphql/config" ;
2019-12-20 12:04:34 +00:00
import {
AnonymousParticipationNotFoundError ,
getLeaveTokenForParticipation ,
isParticipatingInThisEvent ,
2020-02-18 07:57:00 +00:00
removeAnonymousParticipation ,
} from "../../services/AnonymousParticipationStorage" ;
import { IConfig } from "../../types/config.model" ;
import Subtitle from "../../components/Utils/Subtitle.vue" ;
import Tag from "../../components/Tag.vue" ;
2021-08-09 12:26:11 +00:00
import EventMetadataSidebar from "../../components/Event/EventMetadataSidebar.vue" ;
2021-06-10 08:33:16 +00:00
import EventBanner from "../../components/Event/EventBanner.vue" ;
2021-10-10 14:25:50 +00:00
import EventMap from "../../components/Event/EventMap.vue" ;
2020-02-18 07:57:00 +00:00
import PopoverActorCard from "../../components/Account/PopoverActorCard.vue" ;
2020-11-27 18:27:44 +00:00
import { IParticipant } from "../../types/participant.model" ;
2021-06-11 12:21:27 +00:00
import { ApolloCache , FetchResult } from "@apollo/client/core" ;
2021-08-09 12:26:11 +00:00
import { IEventMetadataDescription } from "@/types/event-metadata" ;
import { eventMetaDataList } from "../../services/EventMetadata" ;
2021-10-10 14:25:50 +00:00
import { USER _SETTINGS } from "@/graphql/user" ;
import { IUser } from "@/types/current-user.model" ;
2019-01-21 14:08:22 +00:00
2020-12-17 10:26:25 +00:00
// noinspection TypeScriptValidateTypes
2019-01-21 14:08:22 +00:00
@ Component ( {
2019-03-22 16:35:07 +00:00
components : {
2020-02-18 07:47:41 +00:00
Subtitle ,
2019-04-03 15:29:03 +00:00
EventCard ,
BIcon ,
DateCalendarIcon ,
2019-09-09 07:31:08 +00:00
ReportModal ,
2019-09-26 14:38:58 +00:00
IdentityPicker ,
2020-12-04 14:12:00 +00:00
ParticipationSection ,
2019-11-15 17:36:47 +00:00
CommentTree ,
2020-02-18 07:57:00 +00:00
Tag ,
PopoverActorCard ,
2021-06-10 08:33:16 +00:00
EventBanner ,
2021-08-09 12:26:11 +00:00
EventMetadataSidebar ,
2021-10-10 14:25:50 +00:00
EventMap ,
2020-06-05 13:20:53 +00:00
ShareEventModal : ( ) =>
import (
/* webpackChunkName: "shareEventModal" */ "../../components/Event/ShareEventModal.vue"
) ,
2021-08-09 12:26:11 +00:00
"integration-twitch" : ( ) =>
import (
/* webpackChunkName: "twitchIntegration" */ "../../components/Event/Integrations/Twitch.vue"
) ,
"integration-peertube" : ( ) =>
import (
/* webpackChunkName: "PeerTubeIntegration" */ "../../components/Event/Integrations/PeerTube.vue"
) ,
"integration-youtube" : ( ) =>
import (
/* webpackChunkName: "YouTubeIntegration" */ "../../components/Event/Integrations/YouTube.vue"
) ,
2021-08-17 08:28:03 +00:00
"integration-jitsi-meet" : ( ) =>
import (
/* webpackChunkName: "JitsiMeetIntegration" */ "../../components/Event/Integrations/JitsiMeet.vue"
) ,
2021-08-17 08:41:39 +00:00
"integration-etherpad" : ( ) =>
import (
/* webpackChunkName: "EtherpadIntegration" */ "../../components/Event/Integrations/Etherpad.vue"
) ,
2019-03-22 16:35:07 +00:00
} ,
2019-01-21 14:08:22 +00:00
apollo : {
event : {
query : FETCH _EVENT ,
2020-08-27 09:53:24 +00:00
fetchPolicy : "cache-and-network" ,
2019-01-21 14:08:22 +00:00
variables ( ) {
return {
2019-03-22 09:57:14 +00:00
uuid : this . uuid ,
2019-01-21 14:08:22 +00:00
} ;
2019-03-22 09:57:14 +00:00
} ,
2019-10-02 17:14:39 +00:00
error ( { graphQLErrors } ) {
this . handleErrors ( graphQLErrors ) ;
} ,
2019-01-21 14:08:22 +00:00
} ,
2021-10-10 14:25:50 +00:00
currentActor : CURRENT _ACTOR _CLIENT ,
loggedUser : USER _SETTINGS ,
2019-09-26 14:38:58 +00:00
participations : {
query : EVENT _PERSON _PARTICIPATION ,
2020-08-27 09:53:24 +00:00
fetchPolicy : "cache-and-network" ,
2019-09-26 14:38:58 +00:00
variables ( ) {
return {
eventId : this . event . id ,
2019-10-04 16:28:25 +00:00
actorId : this . currentActor . id ,
2019-09-26 14:38:58 +00:00
} ;
} ,
2019-12-03 10:29:51 +00:00
subscribeToMore : {
document : EVENT _PERSON _PARTICIPATION _SUBSCRIPTION _CHANGED ,
variables ( ) {
return {
eventId : this . event . id ,
actorId : this . currentActor . id ,
} ;
} ,
} ,
2019-09-26 14:38:58 +00:00
update : ( data ) => {
2020-06-15 16:12:49 +00:00
if ( data && data . person ) return data . person . participations . elements ;
2019-09-26 14:38:58 +00:00
return [ ] ;
} ,
2019-10-04 16:28:25 +00:00
skip ( ) {
2020-11-30 09:24:11 +00:00
return (
! this . currentActor ||
! this . event ||
! this . event . id ||
! this . currentActor . id
) ;
2019-10-04 16:28:25 +00:00
} ,
2019-09-26 14:38:58 +00:00
} ,
2021-07-26 15:15:40 +00:00
person : {
query : PERSON _MEMBERSHIP _GROUP ,
fetchPolicy : "cache-and-network" ,
variables ( ) {
return {
id : this . currentActor . id ,
group : usernameWithDomain ( this . event ? . attributedTo ) ,
} ;
} ,
skip ( ) {
return (
2021-08-13 14:56:51 +00:00
! this . currentActor . id ||
2021-07-26 15:15:40 +00:00
! this . event ? . attributedTo ||
! this . event ? . attributedTo ? . preferredUsername
) ;
} ,
} ,
2019-12-20 12:04:34 +00:00
config : CONFIG ,
2019-03-22 09:57:14 +00:00
} ,
2019-10-10 14:47:38 +00:00
metaInfo ( ) {
return {
2020-09-22 13:04:29 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2019-10-10 14:47:38 +00:00
// @ts-ignore
title : this . eventTitle ,
2019-10-14 10:56:37 +00:00
meta : [
2020-09-22 13:04:29 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2019-10-14 10:56:37 +00:00
// @ts-ignore
2020-02-18 07:57:00 +00:00
{ name : "description" , content : this . eventDescription } ,
2019-10-14 10:56:37 +00:00
] ,
2019-10-10 14:47:38 +00:00
} ;
} ,
2019-01-21 14:08:22 +00:00
} )
2019-09-18 15:32:37 +00:00
export default class Event extends EventMixin {
2019-01-21 14:08:22 +00:00
@ Prop ( { type : String , required : true } ) uuid ! : string ;
2019-11-15 17:36:47 +00:00
event : IEvent = new EventModel ( ) ;
2020-02-18 07:57:00 +00:00
2019-09-11 07:59:01 +00:00
currentActor ! : IPerson ;
2020-02-18 07:57:00 +00:00
2019-09-26 14:38:58 +00:00
identity : IPerson = new Person ( ) ;
2020-02-18 07:57:00 +00:00
2019-12-20 12:04:34 +00:00
config ! : IConfig ;
2020-02-18 07:57:00 +00:00
2021-07-26 15:15:40 +00:00
person ! : IPerson ;
2021-10-10 14:25:50 +00:00
loggedUser ! : IUser ;
2019-09-26 14:38:58 +00:00
participations : IParticipant [ ] = [ ] ;
2020-02-18 07:57:00 +00:00
oldParticipationRole ! : string ;
isReportModalActive = false ;
2020-06-05 13:20:53 +00:00
isShareModalActive = false ;
2020-02-18 07:57:00 +00:00
isJoinModalActive = false ;
isJoinConfirmationModalActive = false ;
2019-04-03 15:29:03 +00:00
EventVisibility = EventVisibility ;
2020-02-18 07:57:00 +00:00
2019-10-11 13:58:46 +00:00
EventStatus = EventStatus ;
2020-02-18 07:57:00 +00:00
2020-03-05 18:32:34 +00:00
EventJoinOptions = EventJoinOptions ;
2020-02-18 07:57:00 +00:00
usernameWithDomain = usernameWithDomain ;
2019-10-03 10:32:20 +00:00
RouteName = RouteName ;
2020-02-18 07:57:00 +00:00
2019-11-15 17:36:47 +00:00
observer ! : IntersectionObserver ;
2020-02-18 07:57:00 +00:00
loadComments = false ;
anonymousParticipation : boolean | null = null ;
2020-03-05 18:32:34 +00:00
actorForConfirmation ! : IPerson ;
2020-02-18 07:57:00 +00:00
messageForConfirmation = "" ;
2019-09-26 14:38:58 +00:00
2020-09-22 13:04:29 +00:00
get eventTitle ( ) : undefined | string {
2019-10-10 14:47:38 +00:00
if ( ! this . event ) return undefined ;
return this . event . title ;
}
2020-09-22 13:04:29 +00:00
get eventDescription ( ) : undefined | string {
2019-10-14 10:56:37 +00:00
if ( ! this . event ) return undefined ;
return this . event . description ;
}
2020-09-22 13:04:29 +00:00
async mounted ( ) : Promise < void > {
2019-09-26 14:38:58 +00:00
this . identity = this . currentActor ;
2020-02-18 07:57:00 +00:00
if ( this . $route . hash . includes ( "#comment-" ) ) {
2019-11-15 17:36:47 +00:00
this . loadComments = true ;
}
2019-12-20 12:04:34 +00:00
try {
2020-02-18 07:57:00 +00:00
if ( window . isSecureContext ) {
2021-05-17 17:01:08 +00:00
this . anonymousParticipation =
await this . anonymousParticipationConfirmed ( ) ;
2020-02-18 07:57:00 +00:00
}
2019-12-20 12:04:34 +00:00
} catch ( e ) {
if ( e instanceof AnonymousParticipationNotFoundError ) {
this . anonymousParticipation = null ;
} else {
console . error ( e ) ;
}
}
2020-02-18 07:57:00 +00:00
this . observer = new IntersectionObserver (
( entries ) => {
// eslint-disable-next-line no-restricted-syntax
for ( const entry of entries ) {
if ( entry ) {
this . loadComments = entry . isIntersecting || this . loadComments ;
}
2019-11-15 17:36:47 +00:00
}
2020-02-18 07:57:00 +00:00
} ,
{
rootMargin : "-50px 0px -50px" ,
2019-11-15 17:36:47 +00:00
}
2020-02-18 07:57:00 +00:00
) ;
2019-11-15 17:36:47 +00:00
this . observer . observe ( this . $refs . commentsObserver as Element ) ;
2019-10-23 10:36:11 +00:00
2020-02-18 07:57:00 +00:00
this . $watch ( "eventDescription" , ( eventDescription ) => {
2019-10-23 10:36:11 +00:00
if ( ! eventDescription ) return ;
2020-11-30 09:24:11 +00:00
const eventDescriptionElement = this . $refs
. eventDescriptionElement as HTMLElement ;
2019-10-23 10:36:11 +00:00
2020-02-18 07:57:00 +00:00
eventDescriptionElement . addEventListener ( "click" , ( $event ) => {
2019-10-23 10:36:11 +00:00
// TODO: Find the right type for target
2020-02-18 07:57:00 +00:00
let { target } : { target : any } = $event ;
while ( target && target . tagName !== "A" ) target = target . parentNode ;
2019-10-23 10:36:11 +00:00
// handle only links that occur inside the component and do not reference external resources
2020-02-18 07:57:00 +00:00
if ( target && target . matches ( ".hashtag" ) && target . href ) {
2019-10-23 10:36:11 +00:00
// some sanity checks taken from vue-router:
// https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L106
2020-11-30 09:24:11 +00:00
const {
altKey ,
ctrlKey ,
metaKey ,
shiftKey ,
button ,
defaultPrevented ,
} = $event ;
2019-10-23 10:36:11 +00:00
// don't handle with control keys
if ( metaKey || altKey || ctrlKey || shiftKey ) return ;
// don't handle when preventDefault called
if ( defaultPrevented ) return ;
// don't handle right clicks
if ( button !== undefined && button !== 0 ) return ;
// don't handle if `target="_blank"`
if ( target && target . getAttribute ) {
2020-02-18 07:57:00 +00:00
const linkTarget = target . getAttribute ( "target" ) ;
2019-10-23 10:36:11 +00:00
if ( /\b_blank\b/i . test ( linkTarget ) ) return ;
}
// don't handle same page links/anchors
const url = new URL ( target . href ) ;
const to = url . pathname ;
if ( window . location . pathname !== to && $event . preventDefault ) {
$event . preventDefault ( ) ;
this . $router . push ( to ) ;
}
}
} ) ;
} ) ;
2020-06-23 15:20:21 +00:00
2021-02-04 16:18:08 +00:00
this . $on ( "event-deleted" , ( ) => {
2020-06-23 15:20:21 +00:00
return this . $router . push ( { name : RouteName . HOME } ) ;
2020-06-25 09:38:12 +00:00
} ) ;
2019-09-26 14:38:58 +00:00
}
2019-01-21 14:08:22 +00:00
2019-09-18 15:32:37 +00:00
/ * *
* Delete the event , then redirect to home .
* /
2020-09-22 13:04:29 +00:00
async openDeleteEventModalWrapper ( ) : Promise < void > {
2020-11-19 16:06:28 +00:00
await this . openDeleteEventModal ( this . event ) ;
2019-01-21 14:08:22 +00:00
}
2020-09-22 13:04:29 +00:00
async reportEvent ( content : string , forward : boolean ) : Promise < void > {
2019-09-09 07:31:08 +00:00
this . isReportModalActive = false ;
2020-09-22 13:04:29 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2020-06-09 12:07:49 +00:00
// @ts-ignore
this . $refs . reportModal . close ( ) ;
2021-02-03 08:34:22 +00:00
if ( ! this . organizer ) return ;
2019-09-09 07:31:08 +00:00
const eventTitle = this . event . title ;
2020-11-19 16:06:28 +00:00
2019-09-09 07:31:08 +00:00
try {
await this . $apollo . mutate < IReport > ( {
mutation : CREATE _REPORT ,
variables : {
eventId : this . event . id ,
2021-02-03 08:34:22 +00:00
reportedId : this . organizer ? this . organizer . id : null ,
2019-09-09 07:31:08 +00:00
content ,
2019-12-03 10:29:51 +00:00
forward ,
2019-09-09 07:31:08 +00:00
} ,
} ) ;
2020-11-30 09:24:11 +00:00
this . $notifier . success (
this . $t ( "Event {eventTitle} reported" , { eventTitle } ) as string
) ;
2019-09-09 07:31:08 +00:00
} catch ( error ) {
console . error ( error ) ;
}
}
2020-09-22 13:04:29 +00:00
joinEventWithConfirmation ( actor : IPerson ) : void {
2020-03-05 18:32:34 +00:00
this . isJoinConfirmationModalActive = true ;
this . actorForConfirmation = actor ;
}
2020-11-30 09:24:11 +00:00
async joinEvent (
identity : IPerson ,
message : string | null = null
) : Promise < void > {
2020-03-05 18:32:34 +00:00
this . isJoinConfirmationModalActive = false ;
2019-09-11 07:59:01 +00:00
this . isJoinModalActive = false ;
2019-01-21 14:08:22 +00:00
try {
2020-11-30 09:24:11 +00:00
const { data : mutationData } = await this . $apollo . mutate < {
joinEvent : IParticipant ;
} > ( {
2019-02-22 10:24:41 +00:00
mutation : JOIN _EVENT ,
variables : {
2019-02-25 16:20:06 +00:00
eventId : this . event . id ,
2020-12-01 11:33:44 +00:00
actorId : identity . id ,
2020-03-05 18:32:34 +00:00
message ,
2019-02-22 10:24:41 +00:00
} ,
2021-06-11 12:21:27 +00:00
update : (
store : ApolloCache < {
joinEvent : IParticipant ;
} > ,
{ data } : FetchResult
) => {
2019-09-02 16:52:23 +00:00
if ( data == null ) return ;
2019-09-26 14:38:58 +00:00
const participationCachedData = store . readQuery < { person : IPerson } > ( {
query : EVENT _PERSON _PARTICIPATION ,
2019-10-11 13:06:58 +00:00
variables : { eventId : this . event . id , actorId : identity . id } ,
2019-09-26 14:38:58 +00:00
} ) ;
2021-05-17 16:20:25 +00:00
if ( participationCachedData ? . person == undefined ) {
2020-11-30 09:24:11 +00:00
console . error (
"Cannot update participation cache, because of null value."
) ;
2019-09-26 14:38:58 +00:00
return ;
}
store . writeQuery ( {
query : EVENT _PERSON _PARTICIPATION ,
2019-10-11 13:06:58 +00:00
variables : { eventId : this . event . id , actorId : identity . id } ,
2021-05-17 16:20:25 +00:00
data : {
person : {
... participationCachedData ? . person ,
participations : {
elements : [ data . joinEvent ] ,
total : 1 ,
} ,
} ,
} ,
2019-09-26 14:38:58 +00:00
} ) ;
2020-02-18 07:57:00 +00:00
const cachedData = store . readQuery < { event : IEvent } > ( {
query : FETCH _EVENT ,
variables : { uuid : this . event . uuid } ,
} ) ;
2019-09-02 16:52:23 +00:00
if ( cachedData == null ) return ;
const { event } = cachedData ;
2019-02-22 10:24:41 +00:00
if ( event === null ) {
2020-11-30 09:24:11 +00:00
console . error (
"Cannot update event participant cache, because of null value."
) ;
2019-03-22 09:57:14 +00:00
return ;
2019-02-22 10:24:41 +00:00
}
2021-05-17 16:20:25 +00:00
const participantStats = { ... event . participantStats } ;
2019-02-22 10:24:41 +00:00
2019-09-26 14:38:58 +00:00
if ( data . joinEvent . role === ParticipantRole . NOT _APPROVED ) {
2021-05-17 16:20:25 +00:00
participantStats . notApproved += 1 ;
2019-09-26 14:38:58 +00:00
} else {
2021-05-17 16:20:25 +00:00
participantStats . going += 1 ;
participantStats . participant += 1 ;
2019-09-26 14:38:58 +00:00
}
2019-02-22 10:24:41 +00:00
2020-02-18 07:57:00 +00:00
store . writeQuery ( {
query : FETCH _EVENT ,
variables : { uuid : this . uuid } ,
2021-05-17 16:20:25 +00:00
data : {
event : {
... event ,
participantStats ,
} ,
} ,
2020-02-18 07:57:00 +00:00
} ) ;
2019-03-22 09:57:14 +00:00
} ,
2019-01-21 14:08:22 +00:00
} ) ;
2020-02-18 07:57:00 +00:00
if ( mutationData ) {
if ( mutationData . joinEvent . role === ParticipantRole . NOT _APPROVED ) {
2019-12-03 10:29:51 +00:00
this . participationRequestedMessage ( ) ;
} else {
this . participationConfirmedMessage ( ) ;
}
2019-10-13 08:51:22 +00:00
}
2019-01-21 14:08:22 +00:00
} catch ( error ) {
console . error ( error ) ;
}
}
2020-09-22 13:04:29 +00:00
confirmLeave ( ) : void {
2019-09-11 07:59:01 +00:00
this . $buefy . dialog . confirm ( {
2020-02-18 07:57:00 +00:00
title : this . $t ( 'Leaving event "{title}"' , {
title : this . event . title ,
} ) as string ,
2020-11-30 09:24:11 +00:00
message : this . $t (
'Are you sure you want to cancel your participation at event "{title}"?' ,
{
title : this . event . title ,
}
) as string ,
2020-02-18 07:57:00 +00:00
confirmText : this . $t ( "Leave event" ) as string ,
cancelText : this . $t ( "Cancel" ) as string ,
type : "is-danger" ,
2019-09-11 07:59:01 +00:00
hasIcon : true ,
2019-12-20 12:04:34 +00:00
onConfirm : ( ) => {
if ( this . currentActor . id ) {
this . leaveEvent ( this . event , this . currentActor . id ) ;
}
} ,
2019-09-11 07:59:01 +00:00
} ) ;
}
2020-02-18 07:57:00 +00:00
@ Watch ( "participations" )
2020-09-22 13:04:29 +00:00
watchParticipations ( ) : void {
2019-12-03 10:29:51 +00:00
if ( this . participations . length > 0 ) {
2020-02-18 07:57:00 +00:00
if (
this . oldParticipationRole &&
this . participations [ 0 ] . role !== ParticipantRole . NOT _APPROVED &&
this . oldParticipationRole !== this . participations [ 0 ] . role
) {
2019-12-03 10:29:51 +00:00
switch ( this . participations [ 0 ] . role ) {
case ParticipantRole . PARTICIPANT :
this . participationConfirmedMessage ( ) ;
break ;
case ParticipantRole . REJECTED :
this . participationRejectedMessage ( ) ;
break ;
default :
this . participationChangedMessage ( ) ;
break ;
}
}
this . oldParticipationRole = this . participations [ 0 ] . role ;
}
}
private participationConfirmedMessage ( ) {
2020-11-30 09:24:11 +00:00
this . $notifier . success (
this . $t ( "Your participation has been confirmed" ) as string
) ;
2019-12-03 10:29:51 +00:00
}
private participationRequestedMessage ( ) {
2020-11-30 09:24:11 +00:00
this . $notifier . success (
this . $t ( "Your participation has been requested" ) as string
) ;
2019-12-03 10:29:51 +00:00
}
private participationRejectedMessage ( ) {
2020-11-30 09:24:11 +00:00
this . $notifier . error (
this . $t ( "Your participation has been rejected" ) as string
) ;
2019-12-03 10:29:51 +00:00
}
private participationChangedMessage ( ) {
2020-11-30 09:24:11 +00:00
this . $notifier . info (
this . $t ( "Your participation status has been changed" ) as string
) ;
2019-12-03 10:29:51 +00:00
}
2020-09-22 13:04:29 +00:00
async downloadIcsEvent ( ) : Promise < void > {
2020-02-18 07:57:00 +00:00
const data = await (
await fetch ( ` ${ GRAPHQL _API _ENDPOINT } /events/ ${ this . uuid } /export/ics ` )
) . text ( ) ;
const blob = new Blob ( [ data ] , { type : "text/calendar" } ) ;
const link = document . createElement ( "a" ) ;
2019-03-21 19:23:42 +00:00
link . href = window . URL . createObjectURL ( blob ) ;
link . download = ` ${ this . event . title } .ics ` ;
document . body . appendChild ( link ) ;
link . click ( ) ;
document . body . removeChild ( link ) ;
2019-01-21 14:08:22 +00:00
}
2020-09-22 13:04:29 +00:00
triggerShare ( ) : void {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2020-06-05 13:20:53 +00:00
// @ts-ignore-start
if ( navigator . share ) {
navigator
2020-09-22 13:04:29 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2020-06-05 13:20:53 +00:00
// @ts-ignore
. share ( {
title : this . event . title ,
url : this . event . url ,
} )
. then ( ( ) => console . log ( "Successful share" ) )
. catch ( ( error : any ) => console . log ( "Error sharing" , error ) ) ;
} else {
this . isShareModalActive = true ;
// send popup
}
2020-09-22 13:04:29 +00:00
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
2020-06-05 13:20:53 +00:00
// @ts-ignore-end
}
2020-10-01 13:07:15 +00:00
handleErrors ( errors : any [ ] ) : void {
2020-02-18 07:57:00 +00:00
if (
2020-10-01 13:07:15 +00:00
errors . some ( ( error ) => error . status _code === 404 ) ||
errors . some ( ( { message } ) => message . includes ( "has invalid value $uuid" ) )
2020-02-18 07:57:00 +00:00
) {
2020-10-01 13:07:15 +00:00
this . $router . replace ( { name : RouteName . PAGE _NOT _FOUND } ) ;
2019-10-02 17:14:39 +00:00
}
}
2020-09-22 13:04:29 +00:00
get actorIsParticipant ( ) : boolean {
2019-09-26 14:38:58 +00:00
if ( this . actorIsOrganizer ) return true ;
2019-02-22 10:24:41 +00:00
2020-02-18 07:57:00 +00:00
return (
2020-11-30 09:24:11 +00:00
this . participations . length > 0 &&
this . participations [ 0 ] . role === ParticipantRole . PARTICIPANT
2020-02-18 07:57:00 +00:00
) ;
2019-01-21 14:08:22 +00:00
}
2019-02-22 10:24:41 +00:00
2020-09-22 13:04:29 +00:00
get actorIsOrganizer ( ) : boolean {
2020-02-18 07:57:00 +00:00
return (
2020-11-30 09:24:11 +00:00
this . participations . length > 0 &&
this . participations [ 0 ] . role === ParticipantRole . CREATOR
2020-02-18 07:57:00 +00:00
) ;
2019-01-21 14:08:22 +00:00
}
2019-04-03 15:29:03 +00:00
2021-07-26 15:15:40 +00:00
get hasGroupPrivileges ( ) : boolean {
return (
this . person ? . memberships ? . total > 0 &&
[ MemberRole . MODERATOR , MemberRole . ADMINISTRATOR ] . includes (
this . person ? . memberships ? . elements [ 0 ] . role
)
) ;
}
get canManageEvent ( ) : boolean {
return this . actorIsOrganizer || this . hasGroupPrivileges ;
}
2020-09-22 13:04:29 +00:00
get endDate ( ) : Date {
2020-02-18 07:57:00 +00:00
return this . event . endsOn !== null && this . event . endsOn > this . event . beginsOn
? this . event . endsOn
: this . event . beginsOn ;
2019-10-02 15:59:07 +00:00
}
2021-10-20 09:49:55 +00:00
get maximumAttendeeCapacity ( ) : number {
return this . event ? . options ? . maximumAttendeeCapacity ;
}
2019-10-11 13:06:58 +00:00
get eventCapacityOK ( ) : boolean {
2019-11-08 18:37:14 +00:00
if ( this . event . draft ) return true ;
2021-10-20 09:49:55 +00:00
if ( ! this . maximumAttendeeCapacity ) return true ;
2020-11-30 09:24:11 +00:00
return (
2021-10-20 09:49:55 +00:00
this . event ? . options ? . maximumAttendeeCapacity >
2020-11-30 09:24:11 +00:00
this . event . participantStats . participant
) ;
2019-10-11 13:06:58 +00:00
}
get numberOfPlacesStillAvailable ( ) : number {
2021-10-20 09:49:55 +00:00
if ( this . event . draft ) return this . maximumAttendeeCapacity ;
2020-11-30 09:24:11 +00:00
return (
2021-10-20 09:49:55 +00:00
this . maximumAttendeeCapacity - this . event . participantStats . participant
2020-11-30 09:24:11 +00:00
) ;
2019-10-11 13:06:58 +00:00
}
2019-12-20 12:04:34 +00:00
async anonymousParticipationConfirmed ( ) : Promise < boolean > {
2020-02-18 07:57:00 +00:00
return isParticipatingInThisEvent ( this . uuid ) ;
2019-12-20 12:04:34 +00:00
}
2020-09-22 13:04:29 +00:00
async cancelAnonymousParticipation ( ) : Promise < void > {
2020-02-18 07:57:00 +00:00
const token = ( await getLeaveTokenForParticipation ( this . uuid ) ) as string ;
2019-12-20 12:04:34 +00:00
await this . leaveEvent ( this . event , this . config . anonymous . actorId , token ) ;
await removeAnonymousParticipation ( this . uuid ) ;
this . anonymousParticipation = null ;
}
2020-06-09 12:07:49 +00:00
get ableToReport ( ) : boolean {
2020-11-30 09:24:11 +00:00
return (
this . config &&
( this . currentActor . id != null || this . config . anonymous . reports . allowed )
) ;
2020-06-09 12:07:49 +00:00
}
2020-09-30 13:25:30 +00:00
2021-02-03 08:34:22 +00:00
get organizer ( ) : IActor | null {
2020-09-30 13:25:30 +00:00
if ( this . event . attributedTo && this . event . attributedTo . id ) {
return this . event . attributedTo ;
}
if ( this . event . organizerActor ) {
return this . event . organizerActor ;
}
return null ;
}
2021-02-03 08:34:22 +00:00
get organizerDomain ( ) : string | null {
if ( this . organizer ) {
return this . organizer . domain ;
2020-09-30 13:25:30 +00:00
}
return null ;
}
2021-08-09 12:26:11 +00:00
metadataToComponent : Record < string , string > = {
"mz:live:twitch:url" : "integration-twitch" ,
"mz:live:peertube:url" : "integration-peertube" ,
"mz:live:youtube:url" : "integration-youtube" ,
2021-08-17 08:28:03 +00:00
"mz:visio:jitsi_meet" : "integration-jitsi-meet" ,
2021-08-17 08:41:39 +00:00
"mz:notes:etherpad:url" : "integration-etherpad" ,
2021-08-09 12:26:11 +00:00
} ;
get integrations ( ) : Record < string , IEventMetadataDescription > {
return this . event . metadata
. map ( ( val ) => {
const def = eventMetaDataList . find ( ( dat ) => dat . key === val . key ) ;
return {
... def ,
... val ,
} ;
} )
. reduce ( ( acc : Record < string , IEventMetadataDescription > , metadata ) => {
const component = this . metadataToComponent [ metadata . key ] ;
if ( component !== undefined ) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
acc [ component ] = metadata ;
}
return acc ;
} , { } ) ;
}
2021-10-10 14:25:50 +00:00
showMap = false ;
get routingType ( ) : string | undefined {
return this . config ? . maps ? . routing ? . type ;
}
2019-01-21 14:08:22 +00:00
}
< / script >
2019-04-03 15:29:03 +00:00
< style lang = "scss" scoped >
2020-02-18 07:57:00 +00:00
. section {
2020-06-17 13:54:24 +00:00
padding : 1 rem 2 rem 4 rem ;
2020-02-18 07:57:00 +00:00
}
. fade - enter - active ,
. fade - leave - active {
transition : opacity 0.5 s ;
}
. fade - enter ,
. fade - leave - to {
opacity : 0 ;
}
div . sidebar {
display : flex ;
flex - wrap : wrap ;
flex - direction : column ;
position : relative ;
& : : before {
content : "" ;
background : # b3b3b2 ;
position : absolute ;
bottom : 30 px ;
top : 30 px ;
left : 0 ;
height : calc ( 100 % - 60 px ) ;
width : 1 px ;
2019-12-20 12:04:34 +00:00
}
2020-02-18 07:57:00 +00:00
div . organizer {
display : inline - flex ;
padding - top : 10 px ;
a {
color : # 4 a4a4a ;
span {
line - height : 2.7 rem ;
padding - right : 6 px ;
}
}
2019-10-08 20:27:14 +00:00
}
2020-02-18 07:57:00 +00:00
}
2021-06-10 08:33:16 +00:00
. intro {
2020-02-18 07:57:00 +00:00
background : white ;
2021-04-13 14:58:41 +00:00
. is - 3 - tablet {
width : initial ;
}
2020-02-18 07:57:00 +00:00
p . tags {
2020-06-17 13:54:24 +00:00
a {
text - decoration : none ;
}
2020-12-17 10:26:25 +00:00
2020-02-18 07:57:00 +00:00
span {
& . tag {
margin : 0 2 px ;
}
}
2019-10-08 20:27:14 +00:00
}
2020-02-18 07:57:00 +00:00
}
. event - description - wrapper {
display : flex ;
flex - wrap : wrap ;
flex - direction : column ;
padding : 0 ;
2019-10-08 20:27:14 +00:00
2020-02-18 07:57:00 +00:00
@ media all and ( min - width : 672 px ) {
flex - direction : row - reverse ;
2019-10-10 12:50:44 +00:00
}
2020-02-18 07:57:00 +00:00
& > aside ,
& > div {
@ media all and ( min - width : 672 px ) {
margin : 2 rem auto ;
}
2019-10-14 09:41:57 +00:00
}
2020-02-18 07:57:00 +00:00
aside . event - metadata {
min - width : 20 rem ;
flex : 1 ;
@ media all and ( min - width : 672 px ) {
padding - left : 1 rem ;
}
. sticky {
position : sticky ;
background : white ;
top : 50 px ;
2021-06-14 14:14:17 +00:00
padding : 1 rem ;
2019-04-03 15:29:03 +00:00
}
2021-04-26 15:27:27 +00:00
}
2020-02-18 07:57:00 +00:00
div . event - description - comments {
min - width : 20 rem ;
padding : 1 rem ;
flex : 2 ;
background : white ;
}
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
. description - content {
2020-11-16 09:04:47 +00:00
: : v - deep h1 {
2020-02-18 07:57:00 +00:00
font - size : 2 rem ;
2019-10-11 13:06:58 +00:00
}
2019-04-03 15:29:03 +00:00
2020-11-16 09:04:47 +00:00
: : v - deep h2 {
2020-02-18 07:57:00 +00:00
font - size : 1.5 rem ;
2019-04-03 15:29:03 +00:00
}
2020-11-16 09:04:47 +00:00
: : v - deep h3 {
2020-02-18 07:57:00 +00:00
font - size : 1.25 rem ;
}
2019-04-03 15:29:03 +00:00
2020-11-16 09:04:47 +00:00
: : v - deep ul {
2020-02-18 07:57:00 +00:00
list - style - type : disc ;
2019-04-03 15:29:03 +00:00
}
2020-11-16 09:04:47 +00:00
: : v - deep li {
2020-02-18 07:57:00 +00:00
margin : 10 px auto 10 px 2 rem ;
}
2019-04-03 15:29:03 +00:00
2020-11-16 09:04:47 +00:00
: : v - deep blockquote {
2020-02-18 07:57:00 +00:00
border - left : 0.2 em solid # 333 ;
display : block ;
padding - left : 1 em ;
}
2019-04-03 15:29:03 +00:00
2020-11-16 09:04:47 +00:00
: : v - deep p {
2020-02-18 07:57:00 +00:00
margin : 10 px auto ;
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
a {
display : inline - block ;
2019-04-03 15:29:03 +00:00
padding : 0.3 rem ;
background : $secondary ;
2020-02-18 07:57:00 +00:00
color : # 111 ;
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
& : empty {
display : none ;
2019-04-03 15:29:03 +00:00
}
}
}
}
2020-02-18 07:57:00 +00:00
}
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
. comments {
padding - top : 3 rem ;
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
a h3 # comments {
margin - bottom : 10 px ;
2019-04-03 15:29:03 +00:00
}
2020-02-18 07:57:00 +00:00
}
2019-04-03 15:29:03 +00:00
2020-02-18 07:57:00 +00:00
. more - events {
background : white ;
2021-06-14 14:14:17 +00:00
padding : 1 rem 1 rem 4 rem ;
& > . title {
font - size : 1.5 rem ;
}
2020-02-18 07:57:00 +00:00
}
. dropdown . dropdown - trigger span {
cursor : pointer ;
}
a . dropdown - item ,
. dropdown . dropdown - menu . has - link a ,
button . dropdown - item {
white - space : nowrap ;
width : 100 % ;
padding - right : 1 rem ;
text - align : right ;
}
2020-06-17 13:54:24 +00:00
a . participations - link {
text - decoration : none ;
}
. event - status . tag {
font - size : 1 rem ;
}
2020-07-07 08:54:10 +00:00
2020-12-17 10:26:25 +00:00
. no - border {
border : 0 ;
cursor : auto ;
2020-07-07 08:54:10 +00:00
}
2021-06-10 08:05:47 +00:00
. wrapper ,
. intro - wrapper {
display : flex ;
flex - direction : column ;
}
. intro - wrapper {
position : relative ;
padding : 0 16 px 16 px ;
background : # fff ;
. date - calendar - icon - wrapper {
margin - top : 16 px ;
height : 0 ;
display : flex ;
align - items : flex - end ;
align - self : flex - start ;
margin - bottom : 7 px ;
margin - left : 0 rem ;
}
}
. title {
margin : 0 ;
font - size : 2 rem ;
}
2019-03-22 16:35:07 +00:00
< / style >