2019-01-21 15:08:22 +01:00
< template >
2019-10-01 20:10:53 +02:00
< div class = "container" >
2019-12-03 11:29:51 +01:00
< b -loading :active.sync ="$apollo.loading" / >
2019-10-08 22:27:14 +02:00
< transition appear name = "fade" mode = "out-in" >
2019-11-15 18:36:47 +01:00
< div >
2020-02-18 08:57:00 +01:00
< div
class = "header-picture"
v - if = "event.picture"
: style = "`background-image: url('${event.picture.url}')`"
/ >
2019-10-14 11:41:57 +02:00
< div class = "header-picture-default" v -else / >
2020-02-18 08:57:00 +01:00
< section class = "section intro" >
< div class = "columns" >
< div class = "column is-1-tablet" >
< date -calendar -icon :date ="event.beginsOn" / >
< / div >
< 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 {username} and {group}" >
< popover -actor -card
: actor = "event.organizerActor"
slot = "username"
: inline = "true"
>
{ {
$t ( "@{username}" , { username : usernameWithDomain ( event . organizerActor ) } )
} }
< / p o p o v e r - a c t o r - c a r d >
< popover -actor -card :actor ="event.attributedTo" slot = "group" :inline ="true" >
< router -link
: to = " {
name : RouteName . GROUP ,
params : { preferredUsername : usernameWithDomain ( event . attributedTo ) } ,
} "
>
{ { $t ( "@{group}" , { group : usernameWithDomain ( event . attributedTo ) } ) } }
< / r o u t e r - l i n k >
< / p o p o v e r - a c t o r - c a r d >
< / 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 >
< / div >
< div class = "column is-3-tablet" >
< div >
< div
class = "event-participation has-text-right"
v - if = "new Date(endDate) > new Date()"
>
< participation -button
v - if = "
anonymousParticipation === null &&
( config . anonymous . participation . allowed ||
( currentActor . id &&
! actorIsOrganizer &&
! event . draft &&
( eventCapacityOK || actorIsParticipant ) &&
event . status !== EventStatus . CANCELLED ) )
"
: participation = "participations[0]"
: event = "event"
: current - actor = "currentActor"
@ joinEvent = "joinEvent"
@ joinModal = "isJoinModalActive = true"
@ joinEventWithConfirmation = "joinEventWithConfirmation"
@ confirmLeave = "confirmLeave"
/ >
< b -button
type = "is-text"
v - if = "anonymousParticipation !== null"
@ click = "cancelAnonymousParticipation"
> { { $t ( "Cancel anonymous participation" ) } } < / b - b u t t o n
>
< small v-if ="anonymousParticipation" >
{ { $t ( "You are participating in this event anonymously" ) } }
< b -tooltip
: label = "
$t ( 'This information is saved only on your computer. Click for details' )
"
>
< router -link : to = "{ name: RouteName.TERMS }" >
< b -icon size = "is-small" icon = "help-circle-outline" / >
< / r o u t e r - l i n k >
2019-12-20 13:04:34 +01:00
< / b - t o o l t i p >
2020-02-18 08:57:00 +01:00
< / small >
< small v -else -if = " anonymousParticipation = = = false " >
{ {
$t (
"You are participating in this event anonymously but didn't confirm participation"
)
} }
< b -tooltip
: label = "
$t ( 'This information is saved only on your computer. Click for details' )
"
>
< router -link : to = "{ name: RouteName.TERMS }" >
< b -icon size = "is-small" icon = "help-circle-outline" / >
< / r o u t e r - l i n k >
< / b - t o o l t i p >
< / small >
< / div >
< div v-else >
< button class = "button is-primary" type = "button" slot = "trigger" disabled >
< template >
< span > { { $t ( "Event already passed" ) } } < / span >
< / template >
< b -icon icon = "menu-down" / >
< / button >
2019-10-11 15:06:58 +02:00
< / div >
2019-10-08 22:27:14 +02:00
< / div >
2020-02-18 08:57:00 +01:00
< div class = "has-text-right" >
< tag type = "is-warning" size = "is-medium" v-if ="event.draft" > {{ $ t ( " Draft " ) }} < / tag >
< span class = "event-status" v-if ="event.status !== EventStatus.CONFIRMED" >
< tag type = "is-warning" v-if ="event.status === EventStatus.TENTATIVE" > {{
$t ( "Event to be confirmed" )
} } < / tag >
< tag type = "is-danger" v-if ="event.status === EventStatus.CANCELLED" > {{
$t ( "Event cancelled" )
} } < / tag >
< / span >
< 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" >
< a :href ="event.url" >
< tag > { { event . organizerActor . domain } } < / tag >
< / a >
< / template >
< p >
2019-10-23 12:36:11 +02:00
< router -link
2020-02-18 08:57:00 +01:00
v - if = "actorIsOrganizer && event.draft === false"
: to = "{ name: RouteName.PARTICIPATIONS, params: { eventId: event.uuid } }"
2019-10-23 12:36:11 +02:00
>
2020-02-18 08:57:00 +01:00
< span v-if ="event.options.maximumAttendeeCapacity" >
{ {
$tc ( "{going}/{capacity} available places" , event . participantStats . going , {
approved : event . participantStats . going ,
capacity : event . options . maximumAttendeeCapacity ,
} )
} }
< / span >
< span v-else >
{ {
$tc ( "No one is going to this event" , event . participantStats . going , {
going : event . participantStats . going ,
} )
} }
< / span >
2019-10-23 12:36:11 +02:00
< / r o u t e r - l i n k >
2020-02-18 08:57:00 +01:00
< span v-else >
< span v-if ="event.options.maximumAttendeeCapacity" >
{ {
$tc ( "{going}/{capacity} available places" , event . participantStats . going , {
approved : event . participantStats . going ,
capacity : event . options . maximumAttendeeCapacity ,
} )
} }
< / span >
< span v-else >
{ {
$tc ( "No one is going to this event" , event . participantStats . going , {
going : event . participantStats . going ,
} )
} }
< / span >
< / span >
< 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.'
)
"
>
< 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 17:29:03 +02:00
< / p >
2020-02-18 08:57:00 +01:00
< b -dropdown position = "is-bottom-left" aria -role = " list " >
< span slot = "trigger" role = "button" >
Actions
< b -icon icon = "dots-horizontal" / >
< / span >
< b -dropdown -item
aria - role = "listitem"
has - link
v - if = "actorIsOrganizer || event.draft"
>
2019-10-08 22:27:14 +02:00
< router -link
2020-02-18 08:57:00 +01:00
: to = "{ name: RouteName.EDIT_EVENT, params: { eventId: event.uuid } }"
2019-10-08 22:27:14 +02:00
>
2020-02-18 08:57:00 +01:00
{ { $t ( "Edit" ) } }
< b -icon icon = "pencil" / >
2019-10-08 22:27:14 +02:00
< / r o u t e r - l i n k >
2020-02-18 08:57:00 +01:00
< / b - d r o p d o w n - i t e m >
2020-04-23 00:27:09 +02:00
< b -dropdown -item
aria - role = "listitem"
has - link
v - if = "actorIsOrganizer || event.draft"
>
< router -link
: to = "{ name: RouteName.DUPLICATE_EVENT, params: { eventId: event.uuid } }"
>
{ { $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 >
2020-02-18 08:57:00 +01:00
< b -dropdown -item
aria - role = "listitem"
v - if = "actorIsOrganizer || event.draft"
@ click = "openDeleteEventModalWrapper"
>
{ { $t ( "Delete" ) } }
< b -icon icon = "delete" / >
< / b - d r o p d o w n - i t e m >
< hr
class = "dropdown-divider"
aria - role = "menuitem"
v - if = "actorIsOrganizer || event.draft"
/ >
2020-06-05 15:20:53 +02:00
< b -dropdown -item aria -role = " listitem " v-if ="!event.draft" @click="triggerShare()" >
< span >
2020-02-18 08:57:00 +01:00
{ { $t ( "Share this event" ) } }
< b -icon icon = "share" / >
2020-06-05 15:20:53 +02:00
< / span >
2020-02-18 08:57:00 +01:00
< / b - d r o p d o w n - i t e m >
< b -dropdown -item
aria - role = "listitem"
@ click = "downloadIcsEvent()"
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 " >
< span @ click = "isReportModalActive = true" >
{ { $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 >
< / div >
< / div >
< / div >
< / section >
< div class = "event-description-wrapper" >
< aside class = "event-metadata" >
< div class = "sticky" >
< event -metadata -block
: title = "$t('Location')"
: icon = "physicalAddress ? physicalAddress.poiInfos.poiIcon.icon : 'earth'"
>
2019-10-08 22:27:14 +02:00
< div class = "address-wrapper" >
2020-02-18 08:57:00 +01:00
< span v-if ="!physicalAddress" > {{ $ t ( " No address defined " ) }} < / span >
2019-11-08 19:37:14 +01:00
< div class = "address" v-if ="physicalAddress" >
2020-02-18 08:57:00 +01:00
< div >
2019-11-08 19:37:14 +01:00
< address >
2020-02-18 08:57:00 +01:00
< p class = "addressDescription" :title ="physicalAddress.poiInfos.name" >
{ { physicalAddress . poiInfos . name } }
< / p >
< p > { { physicalAddress . poiInfos . alternativeName } } < / p >
2019-11-08 19:37:14 +01:00
< / address >
2020-02-18 08:57:00 +01:00
< / div >
< span
class = "map-show-button"
@ click = "showMap = !showMap"
v - if = "physicalAddress && physicalAddress.geom"
> { { $t ( "Show map" ) } } < / s p a n
>
2019-04-03 17:29:03 +02:00
< / div >
2020-02-18 08:57:00 +01:00
< b -modal
v - if = "physicalAddress && physicalAddress.geom"
: active . sync = "showMap"
scroll = "keep"
>
2019-10-08 22:27:14 +02:00
< div class = "map" >
< map -leaflet
2020-02-18 08:57:00 +01:00
: coords = "physicalAddress.geom"
: marker = " {
text : physicalAddress . fullName ,
icon : physicalAddress . poiInfos . poiIcon . icon ,
} "
2019-10-08 22:27:14 +02:00
/ >
< / div >
< / b - m o d a l >
< / div >
2020-02-18 08:57:00 +01:00
< / e v e n t - m e t a d a t a - b l o c k >
< event -metadata -block : title = "$t('Date and time')" icon = "calendar" >
< event -full -date
: beginsOn = "event.beginsOn"
: show - start - time = "event.options.showStartTime"
: show - end - time = "event.options.showEndTime"
: endsOn = "event.endsOn"
/ >
< / e v e n t - m e t a d a t a - b l o c k >
< event -metadata -block :title ="$t('Contact')" >
< popover -actor -card
: actor = "event.organizerActor"
v - if = "!event.attributedTo || !event.options.hideOrganizerWhenGroupEvent"
>
< actor -card :actor ="event.organizerActor" / >
< / p o p o v e r - a c t o r - c a r d >
< router -link
v - if = "event.attributedTo"
: to = " {
name : RouteName . GROUP ,
params : { preferredUsername : usernameWithDomain ( event . attributedTo ) } ,
} "
>
< popover -actor -card
: actor = "event.attributedTo"
v - if = "!event.attributedTo || !event.options.hideOrganizerWhenGroupEvent"
>
< actor -card :actor ="event.attributedTo" / >
< / p o p o v e r - a c t o r - c a r d >
< / r o u t e r - l i n k >
< / e v e n t - m e t a d a t a - b l o c k >
< event -metadata -block
v - if = "event.onlineAddress && urlToHostname(event.onlineAddress)"
icon = "link"
: title = "$t('Website')"
>
< a
target = "_blank"
rel = "noopener noreferrer"
: href = "event.onlineAddress"
: title = "
$t ( 'View page on {hostname} (in a new window)' , {
hostname : urlToHostname ( event . onlineAddress ) ,
} )
"
> { { urlToHostname ( event . onlineAddress ) } } < / a
>
< / e v e n t - m e t a d a t a - b l o c k >
2019-04-03 17:29:03 +02:00
< / div >
2020-02-18 08:57:00 +01:00
< / aside >
< div class = "event-description-comments" >
< section class = "event-description" >
< subtitle > { { $t ( "About this event" ) } } < / subtitle >
2019-10-08 22:27:14 +02:00
< p v-if ="!event.description" >
{ { $t ( "The event organizer didn't add any description." ) } }
< / p >
2020-02-18 08:57:00 +01:00
< div v-else >
< div
class = "description-content"
ref = "eventDescriptionElement"
v - html = "event.description"
/ >
2019-04-12 17:00:55 +02:00
< / div >
2020-02-18 08:57:00 +01:00
< / section >
< section class = "comments" ref = "commentsObserver" >
< a href = "#comments" >
< subtitle id = "comments" > { { $t ( "Comments" ) } } < / subtitle >
< / a >
< comment -tree v -if = " loadComments " :event ="event" / >
< / section >
2019-04-03 17:29:03 +02:00
< / div >
2020-02-18 08:57:00 +01:00
< / 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 >
2019-10-08 22:27:14 +02:00
< div class = "columns" >
2020-02-18 08:57:00 +01:00
< div
class = "column is-one-third-desktop"
v - for = "relatedEvent in event.relatedEvents"
: key = "relatedEvent.uuid"
>
2019-10-08 22:27:14 +02:00
< EventCard :event ="relatedEvent" / >
< / div >
2019-01-21 15:08:22 +01:00
< / div >
2019-10-08 22:27:14 +02:00
< / section >
< b -modal :active.sync ="isReportModalActive" has -modal -card ref = "reportModal" >
2020-02-18 08:57:00 +01:00
< report -modal
: on - confirm = "reportEvent"
: title = "$t('Report this event')"
: outside - domain = "event.organizerActor.domain"
@ close = "$refs.reportModal.close()"
/ >
2019-10-08 22:27:14 +02:00
< / b - m o d a l >
2020-06-05 15:20:53 +02:00
< b -modal :active.sync ="isShareModalActive" has -modal -card ref = "shareModal" >
< share -event -modal :event ="event" :eventCapacityOK ="eventCapacityOK" / >
< / b - m o d a l >
2019-10-08 22:27:14 +02:00
< b -modal :active.sync ="isJoinModalActive" has -modal -card ref = "participationModal" >
2020-02-18 08:57:00 +01:00
< identity -picker v-model ="identity" >
< template v -slot : footer >
< footer class = "modal-card-foot" >
< button class = "button" ref = "cancelButton" @ click = "isJoinModalActive = false" >
{ { $t ( "Cancel" ) } }
< / button >
< button
class = "button is-primary"
ref = "confirmButton"
@ click = "
event . joinOptions === EventJoinOptions . RESTRICTED
? joinEventWithConfirmation ( identity )
: joinEvent ( identity )
"
>
{ { $t ( "Confirm my particpation" ) } }
< / button >
< / footer >
< / template >
< / i d e n t i t y - p i c k e r >
2019-10-08 22:27:14 +02:00
< / b - m o d a l >
2020-02-18 08:57:00 +01:00
< b -modal
: active . sync = "isJoinConfirmationModalActive"
has - modal - card
ref = "joinConfirmationModal"
>
2020-03-05 19:32:34 +01:00
< div class = "modal-card" >
< header class = "modal-card-head" >
2020-02-18 08:57:00 +01:00
< p class = "modal-card-title" > { { $t ( "Participation confirmation" ) } } < / p >
2020-03-05 19:32:34 +01:00
< / header >
< section class = "modal-card-body" >
2020-02-18 08:57:00 +01:00
< 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 >
2020-03-05 19:32:34 +01:00
< form @ submit.prevent = " joinEvent ( actorForConfirmation , messageForConfirmation ) " >
< b -field :label ="$t('Message')" >
< b -input
2020-02-18 08:57:00 +01:00
type = "textarea"
size = "is-medium"
v - model = "messageForConfirmation"
minlength = "10"
> < / b - i n p u t >
2020-03-05 19:32:34 +01:00
< / b - f i e l d >
< div class = "buttons" >
< b -button
2020-02-18 08:57:00 +01:00
native - type = "button"
class = "button"
ref = "cancelButton"
@ click = "isJoinConfirmationModalActive = false"
> { { $t ( "Cancel" ) } } < / b - b u t t o n
>
< b -button type = "is-primary" native -type = " submit " >
{ { $t ( "Confirm my participation" ) } }
2020-03-05 19:32:34 +01:00
< / b - b u t t o n >
< / div >
< / form >
< / section >
< / div >
< / b - m o d a l >
2020-02-18 08:57:00 +01:00
< / div >
2019-10-08 22:27:14 +02:00
< / transition >
< / div >
2019-01-21 15:08:22 +01:00
< / template >
< script lang = "ts" >
2020-02-18 08:57:00 +01:00
import { Component , Prop , Watch } from "vue-property-decorator" ;
import BIcon from "buefy/src/components/icon/Icon.vue" ;
import { GraphQLError } from "graphql" ;
import {
EVENT _PERSON _PARTICIPATION ,
EVENT _PERSON _PARTICIPATION _SUBSCRIPTION _CHANGED ,
FETCH _EVENT ,
JOIN _EVENT ,
} from "../../graphql/event" ;
import { CURRENT _ACTOR _CLIENT } from "../../graphql/actor" ;
2019-12-03 11:29:51 +01:00
import {
2020-02-18 08:57:00 +01:00
EventModel ,
EventStatus ,
EventVisibility ,
IEvent ,
IParticipant ,
ParticipantRole ,
EventJoinOptions ,
} from "../../types/event.model" ;
import { IPerson , Person , usernameWithDomain } from "../../types/actor" ;
import { GRAPHQL _API _ENDPOINT } from "../../api/_entrypoint" ;
import DateCalendarIcon from "../../components/Event/DateCalendarIcon.vue" ;
import EventCard from "../../components/Event/EventCard.vue" ;
import EventFullDate from "../../components/Event/EventFullDate.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" ;
import ParticipationButton from "../../components/Event/ParticipationButton.vue" ;
import RouteName from "../../router/name" ;
import { Address } from "../../types/address.model" ;
import CommentTree from "../../components/Comment/CommentTree.vue" ;
import "intersection-observer" ;
import { CONFIG } from "../../graphql/config" ;
2019-12-20 13:04:34 +01:00
import {
AnonymousParticipationNotFoundError ,
getLeaveTokenForParticipation ,
isParticipatingInThisEvent ,
2020-02-18 08:57:00 +01:00
removeAnonymousParticipation ,
} from "../../services/AnonymousParticipationStorage" ;
import { IConfig } from "../../types/config.model" ;
import Subtitle from "../../components/Utils/Subtitle.vue" ;
import Tag from "../../components/Tag.vue" ;
import EventMetadataBlock from "../../components/Event/EventMetadataBlock.vue" ;
import ActorCard from "../../components/Account/ActorCard.vue" ;
import PopoverActorCard from "../../components/Account/PopoverActorCard.vue" ;
2019-01-21 15:08:22 +01:00
@ Component ( {
2019-03-22 17:35:07 +01:00
components : {
2020-02-18 08:57:00 +01:00
EventMetadataBlock ,
2020-02-18 08:47:41 +01:00
Subtitle ,
2019-04-03 17:29:03 +02:00
EventFullDate ,
EventCard ,
BIcon ,
DateCalendarIcon ,
2019-09-09 09:31:08 +02:00
ReportModal ,
2019-09-26 16:38:58 +02:00
IdentityPicker ,
ParticipationButton ,
2019-11-15 18:36:47 +01:00
CommentTree ,
2020-02-18 08:57:00 +01:00
Tag ,
ActorCard ,
PopoverActorCard ,
"map-leaflet" : ( ) => import ( /* webpackChunkName: "map" */ "../../components/Map.vue" ) ,
2020-06-05 15:20:53 +02:00
ShareEventModal : ( ) =>
import (
/* webpackChunkName: "shareEventModal" */ "../../components/Event/ShareEventModal.vue"
) ,
2019-03-22 17:35:07 +01:00
} ,
2019-01-21 15:08:22 +01:00
apollo : {
event : {
query : FETCH _EVENT ,
variables ( ) {
return {
2019-03-22 10:57:14 +01:00
uuid : this . uuid ,
2019-01-21 15:08:22 +01:00
} ;
2019-03-22 10:57:14 +01:00
} ,
2019-10-02 19:14:39 +02:00
error ( { graphQLErrors } ) {
this . handleErrors ( graphQLErrors ) ;
} ,
2019-01-21 15:08:22 +01:00
} ,
2019-09-11 09:59:01 +02:00
currentActor : {
query : CURRENT _ACTOR _CLIENT ,
2019-03-22 10:57:14 +01:00
} ,
2019-09-26 16:38:58 +02:00
participations : {
query : EVENT _PERSON _PARTICIPATION ,
variables ( ) {
return {
eventId : this . event . id ,
2019-10-04 18:28:25 +02:00
actorId : this . currentActor . id ,
2019-09-26 16:38:58 +02:00
} ;
} ,
2019-12-03 11:29:51 +01:00
subscribeToMore : {
document : EVENT _PERSON _PARTICIPATION _SUBSCRIPTION _CHANGED ,
variables ( ) {
return {
eventId : this . event . id ,
actorId : this . currentActor . id ,
} ;
} ,
} ,
2019-09-26 16:38:58 +02:00
update : ( data ) => {
if ( data && data . person ) return data . person . participations ;
return [ ] ;
} ,
2019-10-04 18:28:25 +02:00
skip ( ) {
2019-10-07 16:48:13 +02:00
return ! this . currentActor || ! this . event || ! this . event . id || ! this . currentActor . id ;
2019-10-04 18:28:25 +02:00
} ,
2019-09-26 16:38:58 +02:00
} ,
2019-12-20 13:04:34 +01:00
config : CONFIG ,
2019-03-22 10:57:14 +01:00
} ,
2019-10-10 16:47:38 +02:00
metaInfo ( ) {
return {
// if no subcomponents specify a metaInfo.title, this title will be used
2020-02-18 08:57:00 +01:00
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
2019-10-10 16:47:38 +02:00
// @ts-ignore
title : this . eventTitle ,
// all titles will be injected into this template
2020-02-18 08:57:00 +01:00
titleTemplate : "%s | Mobilizon" ,
2019-10-14 12:56:37 +02:00
meta : [
2020-02-18 08:57:00 +01:00
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
2019-10-14 12:56:37 +02:00
// @ts-ignore
2020-02-18 08:57:00 +01:00
{ name : "description" , content : this . eventDescription } ,
2019-10-14 12:56:37 +02:00
] ,
2019-10-10 16:47:38 +02:00
} ;
} ,
2019-01-21 15:08:22 +01:00
} )
2019-09-18 17:32:37 +02:00
export default class Event extends EventMixin {
2019-01-21 15:08:22 +01:00
@ Prop ( { type : String , required : true } ) uuid ! : string ;
2019-11-15 18:36:47 +01:00
event : IEvent = new EventModel ( ) ;
2020-02-18 08:57:00 +01:00
2019-09-11 09:59:01 +02:00
currentActor ! : IPerson ;
2020-02-18 08:57:00 +01:00
2019-09-26 16:38:58 +02:00
identity : IPerson = new Person ( ) ;
2020-02-18 08:57:00 +01:00
2019-12-20 13:04:34 +01:00
config ! : IConfig ;
2020-02-18 08:57:00 +01:00
2019-09-26 16:38:58 +02:00
participations : IParticipant [ ] = [ ] ;
2020-02-18 08:57:00 +01:00
oldParticipationRole ! : string ;
showMap = false ;
isReportModalActive = false ;
2020-06-05 15:20:53 +02:00
isShareModalActive = false ;
2020-02-18 08:57:00 +01:00
isJoinModalActive = false ;
isJoinConfirmationModalActive = false ;
2019-04-03 17:29:03 +02:00
EventVisibility = EventVisibility ;
2020-02-18 08:57:00 +01:00
2019-10-11 15:58:46 +02:00
EventStatus = EventStatus ;
2020-02-18 08:57:00 +01:00
2020-03-05 19:32:34 +01:00
EventJoinOptions = EventJoinOptions ;
2020-02-18 08:57:00 +01:00
usernameWithDomain = usernameWithDomain ;
2019-10-03 12:32:20 +02:00
RouteName = RouteName ;
2020-02-18 08:57:00 +01:00
2019-11-15 18:36:47 +01:00
observer ! : IntersectionObserver ;
2020-02-18 08:57:00 +01:00
loadComments = false ;
anonymousParticipation : boolean | null = null ;
2020-03-05 19:32:34 +01:00
actorForConfirmation ! : IPerson ;
2020-02-18 08:57:00 +01:00
messageForConfirmation = "" ;
2019-09-26 16:38:58 +02:00
2019-10-10 16:47:38 +02:00
get eventTitle ( ) {
if ( ! this . event ) return undefined ;
return this . event . title ;
}
2019-10-14 12:56:37 +02:00
get eventDescription ( ) {
if ( ! this . event ) return undefined ;
return this . event . description ;
}
2019-12-20 13:04:34 +01:00
async mounted ( ) {
2019-09-26 16:38:58 +02:00
this . identity = this . currentActor ;
2020-02-18 08:57:00 +01:00
if ( this . $route . hash . includes ( "#comment-" ) ) {
2019-11-15 18:36:47 +01:00
this . loadComments = true ;
}
2019-12-20 13:04:34 +01:00
try {
2020-02-18 08:57:00 +01:00
if ( window . isSecureContext ) {
this . anonymousParticipation = await this . anonymousParticipationConfirmed ( ) ;
}
2019-12-20 13:04:34 +01:00
} catch ( e ) {
if ( e instanceof AnonymousParticipationNotFoundError ) {
this . anonymousParticipation = null ;
} else {
console . error ( e ) ;
}
}
2020-02-18 08:57:00 +01: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 18:36:47 +01:00
}
2020-02-18 08:57:00 +01:00
} ,
{
rootMargin : "-50px 0px -50px" ,
2019-11-15 18:36:47 +01:00
}
2020-02-18 08:57:00 +01:00
) ;
2019-11-15 18:36:47 +01:00
this . observer . observe ( this . $refs . commentsObserver as Element ) ;
2019-10-23 12:36:11 +02:00
2020-02-18 08:57:00 +01:00
this . $watch ( "eventDescription" , ( eventDescription ) => {
2019-10-23 12:36:11 +02:00
if ( ! eventDescription ) return ;
2019-11-15 18:36:47 +01:00
const eventDescriptionElement = this . $refs . eventDescriptionElement as HTMLElement ;
2019-10-23 12:36:11 +02:00
2020-02-18 08:57:00 +01:00
eventDescriptionElement . addEventListener ( "click" , ( $event ) => {
2019-10-23 12:36:11 +02:00
// TODO: Find the right type for target
2020-02-18 08:57:00 +01:00
let { target } : { target : any } = $event ;
while ( target && target . tagName !== "A" ) target = target . parentNode ;
2019-10-23 12:36:11 +02:00
// handle only links that occur inside the component and do not reference external resources
2020-02-18 08:57:00 +01:00
if ( target && target . matches ( ".hashtag" ) && target . href ) {
2019-10-23 12:36:11 +02:00
// 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 ;
// 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 08:57:00 +01:00
const linkTarget = target . getAttribute ( "target" ) ;
2019-10-23 12:36:11 +02: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 ) ;
}
}
} ) ;
} ) ;
2019-09-26 16:38:58 +02:00
}
2019-01-21 15:08:22 +01:00
2019-09-18 17:32:37 +02:00
/ * *
* Delete the event , then redirect to home .
* /
async openDeleteEventModalWrapper ( ) {
await this . openDeleteEventModal ( this . event , this . currentActor ) ;
2019-01-21 15:08:22 +01:00
}
2019-09-09 09:31:08 +02:00
async reportEvent ( content : string , forward : boolean ) {
this . isReportModalActive = false ;
2019-09-11 09:59:01 +02:00
if ( ! this . event . organizerActor ) return ;
2019-09-09 09:31:08 +02:00
const eventTitle = this . event . title ;
try {
await this . $apollo . mutate < IReport > ( {
mutation : CREATE _REPORT ,
variables : {
eventId : this . event . id ,
2019-11-15 18:36:47 +01:00
reporterId : this . currentActor . id ,
reportedId : this . event . organizerActor . id ,
2019-09-09 09:31:08 +02:00
content ,
2019-12-03 11:29:51 +01:00
forward ,
2019-09-09 09:31:08 +02:00
} ,
} ) ;
2020-02-18 08:57:00 +01:00
this . $notifier . success ( this . $t ( "Event {eventTitle} reported" , { eventTitle } ) as string ) ;
2019-09-09 09:31:08 +02:00
} catch ( error ) {
console . error ( error ) ;
}
}
2020-03-05 19:32:34 +01:00
joinEventWithConfirmation ( actor : IPerson ) {
this . isJoinConfirmationModalActive = true ;
this . actorForConfirmation = actor ;
}
2020-02-18 08:57:00 +01:00
async joinEvent ( identity : IPerson , message : string | null = null ) {
2020-03-05 19:32:34 +01:00
this . isJoinConfirmationModalActive = false ;
2019-09-11 09:59:01 +02:00
this . isJoinModalActive = false ;
2019-01-21 15:08:22 +01:00
try {
2020-02-18 08:57:00 +01:00
const { data : mutationData } = await this . $apollo . mutate < { joinEvent : IParticipant } > ( {
2019-02-22 11:24:41 +01:00
mutation : JOIN _EVENT ,
variables : {
2019-02-25 17:20:06 +01:00
eventId : this . event . id ,
2019-09-11 09:59:01 +02:00
actorId : identity . id ,
2020-03-05 19:32:34 +01:00
message ,
2019-02-22 11:24:41 +01:00
} ,
2019-09-02 18:52:23 +02:00
update : ( store , { data } ) => {
if ( data == null ) return ;
2019-09-26 16:38:58 +02:00
const participationCachedData = store . readQuery < { person : IPerson } > ( {
query : EVENT _PERSON _PARTICIPATION ,
2019-10-11 15:06:58 +02:00
variables : { eventId : this . event . id , actorId : identity . id } ,
2019-09-26 16:38:58 +02:00
} ) ;
if ( participationCachedData == null ) return ;
const { person } = participationCachedData ;
if ( person === null ) {
2020-02-18 08:57:00 +01:00
console . error ( "Cannot update participation cache, because of null value." ) ;
2019-09-26 16:38:58 +02:00
return ;
}
person . participations . push ( data . joinEvent ) ;
store . writeQuery ( {
query : EVENT _PERSON _PARTICIPATION ,
2019-10-11 15:06:58 +02:00
variables : { eventId : this . event . id , actorId : identity . id } ,
2019-09-26 16:38:58 +02:00
data : { person } ,
} ) ;
2020-02-18 08:57:00 +01:00
const cachedData = store . readQuery < { event : IEvent } > ( {
query : FETCH _EVENT ,
variables : { uuid : this . event . uuid } ,
} ) ;
2019-09-02 18:52:23 +02:00
if ( cachedData == null ) return ;
const { event } = cachedData ;
2019-02-22 11:24:41 +01:00
if ( event === null ) {
2020-02-18 08:57:00 +01:00
console . error ( "Cannot update event participant cache, because of null value." ) ;
2019-03-22 10:57:14 +01:00
return ;
2019-02-22 11:24:41 +01:00
}
2019-09-26 16:38:58 +02:00
if ( data . joinEvent . role === ParticipantRole . NOT _APPROVED ) {
2020-02-18 08:57:00 +01:00
event . participantStats . notApproved += 1 ;
2019-09-26 16:38:58 +02:00
} else {
2020-02-18 08:57:00 +01:00
event . participantStats . going += 1 ;
event . participantStats . participant += 1 ;
2019-09-26 16:38:58 +02:00
}
2019-02-22 11:24:41 +01:00
2020-02-18 08:57:00 +01:00
store . writeQuery ( {
query : FETCH _EVENT ,
variables : { uuid : this . uuid } ,
data : { event } ,
} ) ;
2019-03-22 10:57:14 +01:00
} ,
2019-01-21 15:08:22 +01:00
} ) ;
2020-02-18 08:57:00 +01:00
if ( mutationData ) {
if ( mutationData . joinEvent . role === ParticipantRole . NOT _APPROVED ) {
2019-12-03 11:29:51 +01:00
this . participationRequestedMessage ( ) ;
} else {
this . participationConfirmedMessage ( ) ;
}
2019-10-13 10:51:22 +02:00
}
2019-01-21 15:08:22 +01:00
} catch ( error ) {
console . error ( error ) ;
}
}
2019-09-11 09:59:01 +02:00
confirmLeave ( ) {
this . $buefy . dialog . confirm ( {
2020-02-18 08:57:00 +01:00
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 ,
confirmText : this . $t ( "Leave event" ) as string ,
cancelText : this . $t ( "Cancel" ) as string ,
type : "is-danger" ,
2019-09-11 09:59:01 +02:00
hasIcon : true ,
2019-12-20 13:04:34 +01:00
onConfirm : ( ) => {
if ( this . currentActor . id ) {
this . leaveEvent ( this . event , this . currentActor . id ) ;
}
} ,
2019-09-11 09:59:01 +02:00
} ) ;
}
2020-02-18 08:57:00 +01:00
@ Watch ( "participations" )
2019-12-03 11:29:51 +01:00
watchParticipations ( ) {
if ( this . participations . length > 0 ) {
2020-02-18 08:57:00 +01:00
if (
this . oldParticipationRole &&
this . participations [ 0 ] . role !== ParticipantRole . NOT _APPROVED &&
this . oldParticipationRole !== this . participations [ 0 ] . role
) {
2019-12-03 11:29:51 +01: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-02-18 08:57:00 +01:00
this . $notifier . success ( this . $t ( "Your participation has been confirmed" ) as string ) ;
2019-12-03 11:29:51 +01:00
}
private participationRequestedMessage ( ) {
2020-02-18 08:57:00 +01:00
this . $notifier . success ( this . $t ( "Your participation has been requested" ) as string ) ;
2019-12-03 11:29:51 +01:00
}
private participationRejectedMessage ( ) {
2020-02-18 08:57:00 +01:00
this . $notifier . error ( this . $t ( "Your participation has been rejected" ) as string ) ;
2019-12-03 11:29:51 +01:00
}
private participationChangedMessage ( ) {
2020-02-18 08:57:00 +01:00
this . $notifier . info ( this . $t ( "Your participation status has been changed" ) as string ) ;
2019-12-03 11:29:51 +01:00
}
2019-03-21 20:23:42 +01:00
async downloadIcsEvent ( ) {
2020-02-18 08:57:00 +01: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 20:23:42 +01: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 15:08:22 +01:00
}
2020-06-05 15:20:53 +02:00
triggerShare ( ) {
// @ts-ignore-start
if ( navigator . share ) {
navigator
// @ts-ignore
. share ( {
title : this . event . title ,
text : Event . textDescription ,
url : this . event . url ,
} )
. then ( ( ) => console . log ( "Successful share" ) )
. catch ( ( error : any ) => console . log ( "Error sharing" , error ) ) ;
} else {
this . isShareModalActive = true ;
// send popup
}
// @ts-ignore-end
}
2020-02-18 08:57:00 +01:00
async handleErrors ( errors : GraphQLError [ ] ) {
if (
errors [ 0 ] . message . includes ( "not found" ) ||
errors [ 0 ] . message . includes ( "has invalid value $uuid" )
) {
2019-10-02 19:14:39 +02:00
await this . $router . push ( { name : RouteName . PAGE _NOT _FOUND } ) ;
}
}
2019-09-26 16:38:58 +02:00
get actorIsParticipant ( ) {
if ( this . actorIsOrganizer ) return true ;
2019-02-22 11:24:41 +01:00
2020-02-18 08:57:00 +01:00
return (
this . participations . length > 0 && this . participations [ 0 ] . role === ParticipantRole . PARTICIPANT
) ;
2019-01-21 15:08:22 +01:00
}
2019-02-22 11:24:41 +01:00
2019-09-26 16:38:58 +02:00
get actorIsOrganizer ( ) {
2020-02-18 08:57:00 +01:00
return (
this . participations . length > 0 && this . participations [ 0 ] . role === ParticipantRole . CREATOR
) ;
2019-01-21 15:08:22 +01:00
}
2019-04-03 17:29:03 +02:00
2019-10-02 17:59:07 +02:00
get endDate ( ) {
2020-02-18 08:57:00 +01:00
return this . event . endsOn !== null && this . event . endsOn > this . event . beginsOn
? this . event . endsOn
: this . event . beginsOn ;
2019-10-02 17:59:07 +02:00
}
2020-02-18 08:57:00 +01:00
static get textDescription ( ) : string {
2019-10-10 12:25:32 +02:00
const meta = document . querySelector ( "meta[property='og:description']" ) ;
2020-02-18 08:57:00 +01:00
if ( ! meta ) return "" ;
const desc = meta . getAttribute ( "content" ) || "" ;
2019-10-14 12:56:37 +02:00
return desc . substring ( 0 , 1000 ) ;
2019-04-03 17:29:03 +02:00
}
2019-09-09 15:49:31 +02:00
2019-10-11 15:06:58 +02:00
get eventCapacityOK ( ) : boolean {
2019-11-08 19:37:14 +01:00
if ( this . event . draft ) return true ;
2019-10-11 15:06:58 +02:00
if ( ! this . event . options . maximumAttendeeCapacity ) return true ;
2019-10-25 17:43:37 +02:00
return this . event . options . maximumAttendeeCapacity > this . event . participantStats . participant ;
2019-10-11 15:06:58 +02:00
}
get numberOfPlacesStillAvailable ( ) : number {
2019-11-08 19:37:14 +01:00
if ( this . event . draft ) return this . event . options . maximumAttendeeCapacity ;
2019-10-25 17:43:37 +02:00
return this . event . options . maximumAttendeeCapacity - this . event . participantStats . participant ;
2019-10-11 15:06:58 +02:00
}
2020-02-18 08:57:00 +01:00
get physicalAddress ( ) : Address | null {
2019-11-08 19:37:14 +01:00
if ( ! this . event . physicalAddress ) return null ;
return new Address ( this . event . physicalAddress ) ;
}
2019-12-20 13:04:34 +01:00
async anonymousParticipationConfirmed ( ) : Promise < boolean > {
2020-02-18 08:57:00 +01:00
return isParticipatingInThisEvent ( this . uuid ) ;
2019-12-20 13:04:34 +01:00
}
async cancelAnonymousParticipation ( ) {
2020-02-18 08:57:00 +01:00
const token = ( await getLeaveTokenForParticipation ( this . uuid ) ) as string ;
2019-12-20 13:04:34 +01:00
await this . leaveEvent ( this . event , this . config . anonymous . actorId , token ) ;
await removeAnonymousParticipation ( this . uuid ) ;
this . anonymousParticipation = null ;
}
2019-01-21 15:08:22 +01:00
}
< / script >
2019-04-03 17:29:03 +02:00
< style lang = "scss" scoped >
2020-02-18 08:57:00 +01:00
@ import "../../variables" ;
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. section {
padding : 1 rem 1.5 rem ;
}
. fade - enter - active ,
. fade - leave - active {
transition : opacity 0.5 s ;
}
. fade - enter ,
. fade - leave - to {
opacity : 0 ;
}
. header - picture ,
. header - picture - default {
height : 400 px ;
background - size : cover ;
background - position : center ;
background - repeat : no - repeat ;
}
. header - picture - default {
background - image : url ( "/img/mobilizon_default_card.png" ) ;
}
2019-12-20 13:04:34 +01:00
2020-02-18 08:57:00 +01:00
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 13:04:34 +01:00
}
2020-02-18 08:57:00 +01: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 22:27:14 +02:00
}
2020-02-18 08:57:00 +01:00
}
. intro . section {
background : white ;
p . tags {
span {
& . tag {
margin : 0 2 px ;
}
}
2019-10-08 22:27:14 +02:00
}
2020-02-18 08:57:00 +01:00
}
. event - description - wrapper {
display : flex ;
flex - wrap : wrap ;
flex - direction : column ;
padding : 0 ;
2019-10-08 22:27:14 +02:00
2020-02-18 08:57:00 +01:00
@ media all and ( min - width : 672 px ) {
flex - direction : row - reverse ;
2019-10-10 14:50:44 +02:00
}
2020-02-18 08:57:00 +01:00
& > aside ,
& > div {
@ media all and ( min - width : 672 px ) {
margin : 2 rem auto ;
}
2019-10-14 11:41:57 +02:00
}
2020-02-18 08:57:00 +01: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 ;
padding : 2 rem ;
2019-04-03 17:29:03 +02:00
}
div . address - wrapper {
display : flex ;
flex : 1 ;
flex - wrap : wrap ;
div . address {
flex : 1 ;
. map - show - button {
cursor : pointer ;
}
2020-02-18 08:57:00 +01:00
address {
font - style : normal ;
flex - wrap : wrap ;
2019-04-03 17:29:03 +02:00
display : flex ;
2020-02-18 08:57:00 +01:00
justify - content : flex - start ;
span . addressDescription {
text - overflow : ellipsis ;
white - space : nowrap ;
flex : 1 0 auto ;
min - width : 100 % ;
max - width : 4 rem ;
overflow : hidden ;
2019-04-03 17:29:03 +02:00
}
2020-02-18 08:57:00 +01:00
: not ( . addressDescription ) {
color : rgba ( 46 , 62 , 72 , 0.6 ) ;
flex : 1 ;
min - width : 100 % ;
2019-04-03 17:29:03 +02:00
}
}
}
div . map {
height : 900 px ;
width : 100 % ;
padding : 25 px 5 px 0 ;
}
}
2019-10-14 14:38:21 +02:00
span . online - address {
display : flex ;
}
2019-04-03 17:29:03 +02:00
}
2020-02-18 08:57:00 +01:00
div . event - description - comments {
min - width : 20 rem ;
padding : 1 rem ;
flex : 2 ;
background : white ;
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. description - content {
/deep/ h1 {
font - size : 2 rem ;
2019-10-11 15:06:58 +02:00
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
/deep/ h2 {
font - size : 1.5 rem ;
2019-04-03 17:29:03 +02:00
}
2020-02-18 08:57:00 +01:00
/deep/ h3 {
font - size : 1.25 rem ;
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
/deep/ ul {
list - style - type : disc ;
2019-04-03 17:29:03 +02:00
}
2020-02-18 08:57:00 +01:00
/deep/ li {
margin : 10 px auto 10 px 2 rem ;
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
/deep/ blockquote {
border - left : 0.2 em solid # 333 ;
display : block ;
padding - left : 1 em ;
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
/deep/ p {
margin : 10 px auto ;
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
a {
display : inline - block ;
2019-04-03 17:29:03 +02:00
padding : 0.3 rem ;
background : $secondary ;
2020-02-18 08:57:00 +01:00
color : # 111 ;
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
& : empty {
display : none ;
2019-04-03 17:29:03 +02:00
}
}
}
}
2020-02-18 08:57:00 +01:00
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. comments {
padding - top : 3 rem ;
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
a h3 # comments {
margin - bottom : 10 px ;
2019-04-03 17:29:03 +02:00
}
2020-02-18 08:57:00 +01:00
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. share {
border - bottom : solid 1 px $primary ;
border - top : solid 1 px $primary ;
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. diaspora span svg {
height : 2 rem ;
width : 2 rem ;
}
2019-11-15 18:36:47 +01:00
2020-02-18 08:57:00 +01:00
. columns {
& > * {
padding : 2 rem 0 ;
2019-11-15 18:36:47 +01:00
}
2019-10-14 12:56:37 +02:00
2020-02-18 08:57:00 +01:00
h3 {
display : block ;
color : $primary ;
font - size : 3 rem ;
text - decoration : underline ;
text - decoration - color : $secondary ;
max - width : 20 rem ;
2019-10-14 12:56:37 +02:00
}
2019-10-10 11:05:53 +02:00
2020-02-18 08:57:00 +01:00
. column : first - child {
h3 {
margin : 0 auto 1 rem ;
font - weight : normal ;
2019-10-15 11:40:25 +02:00
}
2020-02-18 08:57:00 +01:00
small . maximumNumberOfPlacesWarning {
margin : 0 auto 1 rem ;
2019-10-10 11:05:53 +02:00
display : block ;
2019-04-03 17:29:03 +02:00
}
}
2020-02-18 08:57:00 +01:00
. column : last - child {
2019-10-11 15:06:58 +02:00
h3 {
2020-02-18 08:57:00 +01:00
margin - right : 0 ;
2019-10-11 15:06:58 +02:00
}
2020-02-18 08:57:00 +01:00
}
2019-10-11 15:06:58 +02:00
2020-02-18 08:57:00 +01:00
. add - to - calendar {
display : flex ;
2019-10-11 15:06:58 +02:00
2020-02-18 08:57:00 +01:00
h3 {
margin - left : 0 ;
cursor : pointer ;
2019-10-11 15:06:58 +02:00
}
2020-02-18 08:57:00 +01:00
img {
max - width : 250 px ;
2019-10-11 15:06:58 +02:00
}
2020-02-18 08:57:00 +01:00
& : : before {
content : "" ;
background : # b3b3b2 ;
position : absolute ;
bottom : 25 % ;
height : 40 % ;
width : 1 px ;
2019-04-03 17:29:03 +02:00
}
}
}
2020-02-18 08:57:00 +01:00
}
2019-04-03 17:29:03 +02:00
2020-02-18 08:57:00 +01:00
. more - events {
background : white ;
}
. 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 ;
}
2019-03-22 17:35:07 +01:00
< / style >