Merge branch 'show-identity-on-group-card' into 'master'

Show identity on group card

Closes #473 et #415

See merge request framasoft/mobilizon!737
This commit is contained in:
Thomas Citharel 2020-12-01 17:57:10 +01:00
commit 88cba1629d
7 changed files with 201 additions and 148 deletions

View File

@ -22,7 +22,7 @@
</b-tag> </b-tag>
<router-link <router-link
:to="{ name: RouteName.TAG, params: { tag: tag.title } }" :to="{ name: RouteName.TAG, params: { tag: tag.title } }"
v-for="tag in event.tags.slice(0, 3)" v-for="tag in (event.tags || []).slice(0, 3)"
:key="tag.slug" :key="tag.slug"
> >
<b-tag type="is-light">{{ tag.title }}</b-tag> <b-tag type="is-light">{{ tag.title }}</b-tag>

View File

@ -1,7 +1,13 @@
<template> <template>
<article class="box"> <article class="box">
<div class="columns"> <div class="identity-header">
<div class="content column"> <figure class="image is-24x24" v-if="participation.actor.avatar">
<img class="is-rounded" :src="participation.actor.avatar.url" alt="" />
</figure>
{{ displayNameAndUsername(participation.actor) }}
</div>
<div class="list-card">
<div class="content">
<div class="title-wrapper"> <div class="title-wrapper">
<div class="date-component"> <div class="date-component">
<date-calendar-icon :date="participation.event.beginsOn" /> <date-calendar-icon :date="participation.event.beginsOn" />
@ -45,23 +51,10 @@
<i18n tag="span" path="Organized by {name}"> <i18n tag="span" path="Organized by {name}">
<popover-actor-card <popover-actor-card
slot="name" slot="name"
:actor="participation.event.organizerActor" :actor="organizerActor"
:inline="true" :inline="true"
> >
{{ participation.event.organizerActor.displayName() }} {{ organizerActor.displayName() }}
</popover-actor-card>
</i18n>
<i18n
v-if="participation.role === ParticipantRole.PARTICIPANT"
path="Going as {name}"
tag="span"
>
<popover-actor-card
slot="name"
:actor="participation.actor"
:inline="true"
>
{{ participation.actor.displayName() }}
</popover-actor-card> </popover-actor-card>
</i18n> </i18n>
</span> </span>
@ -128,88 +121,90 @@
</span> </span>
</div> </div>
</div> </div>
<div class="actions column is-narrow"> <div class="actions">
<ul> <b-dropdown aria-role="list" position="is-bottom-left">
<li <b-button slot="trigger" role="button" icon-right="dots-horizontal">
{{ $t("Actions") }}
</b-button>
<b-dropdown-item
v-if=" v-if="
![ ![
ParticipantRole.PARTICIPANT, ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED, ParticipantRole.NOT_APPROVED,
].includes(participation.role) ].includes(participation.role)
" "
aria-role="listitem"
@click="
gotToWithCheck(participation, {
name: RouteName.EDIT_EVENT,
params: { eventId: participation.event.uuid },
})
"
> >
<b-button <b-icon icon="pencil" />
type="is-text" {{ $t("Edit") }}
@click=" </b-dropdown-item>
gotToWithCheck(participation, {
name: RouteName.EDIT_EVENT, <b-dropdown-item
params: { eventId: participation.event.uuid }, v-if="participation.role === ParticipantRole.CREATOR"
}) aria-role="listitem"
" @click="
icon-left="pencil" gotToWithCheck(participation, {
>{{ $t("Edit") }}</b-button name: RouteName.DUPLICATE_EVENT,
> params: { eventId: participation.event.uuid },
</li> })
<li v-if="participation.role === ParticipantRole.CREATOR"> "
<b-button >
type="is-text" <b-icon icon="content-duplicate" />
@click=" {{ $t("Duplicate") }}
gotToWithCheck(participation, { </b-dropdown-item>
name: RouteName.DUPLICATE_EVENT,
params: { eventId: participation.event.uuid }, <b-dropdown-item
})
"
icon-left="content-duplicate"
>
{{ $t("Duplicate") }}
</b-button>
</li>
<li
v-if=" v-if="
![ ![
ParticipantRole.PARTICIPANT, ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED, ParticipantRole.NOT_APPROVED,
].includes(participation.role) ].includes(participation.role)
" "
aria-role="listitem"
@click="openDeleteEventModalWrapper" @click="openDeleteEventModalWrapper"
> >
<b-button type="is-text" icon-left="delete">{{ <b-icon icon="delete" />
$t("Delete") {{ $t("Delete") }}
}}</b-button> </b-dropdown-item>
</li>
<li <b-dropdown-item
v-if=" v-if="
![ ![
ParticipantRole.PARTICIPANT, ParticipantRole.PARTICIPANT,
ParticipantRole.NOT_APPROVED, ParticipantRole.NOT_APPROVED,
].includes(participation.role) ].includes(participation.role)
" "
aria-role="listitem"
@click="
gotToWithCheck(participation, {
name: RouteName.PARTICIPATIONS,
params: { eventId: participation.event.uuid },
})
"
> >
<b-button <b-icon icon="account-multiple-plus" />
type="is-text" {{ $t("Manage participations") }}
@click=" </b-dropdown-item>
gotToWithCheck(participation, {
name: RouteName.PARTICIPATIONS, <b-dropdown-item aria-role="listitem" has-link>
params: { eventId: participation.event.uuid }, <router-link
})
"
icon-left="account-multiple-plus"
>{{ $t("Manage participations") }}</b-button
>
</li>
<li>
<b-button
tag="router-link"
icon-left="view-compact"
type="is-text"
:to="{ :to="{
name: RouteName.EVENT, name: RouteName.EVENT,
params: { uuid: participation.event.uuid }, params: { uuid: participation.event.uuid },
}" }"
>{{ $t("View event page") }}</b-button
> >
</li> <b-icon icon="view-compact" />
</ul> {{ $t("View event page") }}
</router-link>
</b-dropdown-item>
</b-dropdown>
</div> </div>
</div> </div>
</article> </article>
@ -223,7 +218,7 @@ import { RawLocation, Route } from "vue-router";
import { EventVisibility, ParticipantRole } from "@/types/enums"; import { EventVisibility, ParticipantRole } from "@/types/enums";
import { IParticipant } from "../../types/participant.model"; import { IParticipant } from "../../types/participant.model";
import { IEventCardOptions } from "../../types/event.model"; import { IEventCardOptions } from "../../types/event.model";
import { IPerson } from "../../types/actor"; import { displayNameAndUsername, IActor, IPerson } from "../../types/actor";
import ActorMixin from "../../mixins/actor"; import ActorMixin from "../../mixins/actor";
import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor"; import { CURRENT_ACTOR_CLIENT } from "../../graphql/actor";
import EventMixin from "../../mixins/event"; import EventMixin from "../../mixins/event";
@ -268,6 +263,8 @@ export default class EventListCard extends mixins(ActorMixin, EventMixin) {
EventVisibility = EventVisibility; EventVisibility = EventVisibility;
displayNameAndUsername = displayNameAndUsername;
RouteName = RouteName; RouteName = RouteName;
get mergedOptions(): IEventCardOptions { get mergedOptions(): IEventCardOptions {
@ -307,6 +304,16 @@ export default class EventListCard extends mixins(ActorMixin, EventMixin) {
} }
return this.$router.push(route); 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;
}
} }
</script> </script>
@ -333,74 +340,71 @@ article.box {
line-height: 1.75em; line-height: 1.75em;
} }
} }
div.content {
.list-card {
display: flex;
align-items: center;
.actions {
padding-right: 7.5px;
cursor: pointer;
}
div.content {
flex: 1;
padding: 5px;
.participation-actor span,
.participant-stats span {
padding: 0 5px;
button {
height: auto;
padding-top: 0;
}
}
div.title-wrapper {
display: flex;
align-items: center;
div.date-component {
flex: 0;
margin-right: 16px;
}
a {
text-decoration: none;
}
.title {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
font-weight: 400;
line-height: 1em;
font-size: 1.6em;
padding-bottom: 5px;
margin: auto 0;
}
}
}
}
.identity-header {
background: $yellow-2;
display: flex;
padding: 5px; padding: 5px;
.participation-actor span, figure {
.participant-stats span { padding-right: 3px;
padding: 0 5px;
button {
height: auto;
padding-top: 0;
}
}
div.title-wrapper {
display: flex;
align-items: center;
div.date-component {
flex: 0;
margin-right: 16px;
}
a {
text-decoration: none;
}
.title {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
font-weight: 400;
line-height: 1em;
font-size: 1.6em;
padding-bottom: 5px;
margin: auto 0;
}
} }
} }
.actions { & > .columns {
ul li { padding: 1.25rem;
margin: 0 auto;
.is-link {
cursor: pointer;
}
.button.is-text {
text-decoration: none;
::v-deep span:first-child i.mdi::before {
font-size: 24px !important;
}
::v-deep span:last-child {
padding-left: 4px;
}
&:hover {
background: #f5f5f5;
}
}
* {
font-size: 0.8rem;
color: $background-color;
}
}
} }
padding: 0;
} }
</style> </style>

View File

@ -1,5 +1,11 @@
<template> <template>
<div class="card"> <div class="card">
<div class="identity-header">
<figure class="image is-24x24" v-if="member.actor.avatar">
<img class="is-rounded" :src="member.actor.avatar.url" alt="" />
</figure>
{{ displayNameAndUsername(member.actor) }}
</div>
<div class="card-content"> <div class="card-content">
<div> <div>
<div class="media"> <div class="media">
@ -60,7 +66,7 @@
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { usernameWithDomain } from "@/types/actor"; import { displayNameAndUsername, usernameWithDomain } from "@/types/actor";
import { IMember } from "@/types/actor/member.model"; import { IMember } from "@/types/actor/member.model";
import { MemberRole } from "@/types/enums"; import { MemberRole } from "@/types/enums";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
@ -73,20 +79,34 @@ export default class GroupMemberCard extends Vue {
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
displayNameAndUsername = displayNameAndUsername;
MemberRole = MemberRole; MemberRole = MemberRole;
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.card-content { .card {
display: flex; .card-content {
align-items: center; display: flex;
align-items: center;
& > div:first-child { & > div:first-child {
flex: 1; flex: 1;
}
& > div:last-child {
cursor: pointer;
}
} }
& > div:last-child { .identity-header {
cursor: pointer; background: $yellow-2;
display: flex;
padding: 5px;
figure {
padding-right: 3px;
}
} }
} }
</style> </style>

View File

@ -211,6 +211,18 @@ export const LOGGED_USER_PARTICIPATIONS = gql`
url url
} }
} }
attributedTo {
avatar {
id
url
}
preferredUsername
name
summary
domain
url
id
}
participantStats { participantStats {
going going
notApproved notApproved
@ -220,6 +232,11 @@ export const LOGGED_USER_PARTICIPATIONS = gql`
maximumAttendeeCapacity maximumAttendeeCapacity
remainingAttendeeCapacity remainingAttendeeCapacity
} }
tags {
id
slug
title
}
} }
id id
role role
@ -287,6 +304,16 @@ export const LOGGED_USER_MEMBERSHIPS = gql`
elements { elements {
id id
role role
actor {
id
avatar {
id
url
}
preferredUsername
name
domain
}
parent { parent {
id id
preferredUsername preferredUsername

View File

@ -279,7 +279,6 @@
icon-right="dots-horizontal" icon-right="dots-horizontal"
> >
{{ $t("Actions") }} {{ $t("Actions") }}
<!-- <b-icon icon="dots-horizontal" /> -->
</b-button> </b-button>
<b-dropdown-item <b-dropdown-item
aria-role="listitem" aria-role="listitem"

View File

@ -21,8 +21,8 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
alias Mobilizon.Federation.ActivityPub.Types.Ownable alias Mobilizon.Federation.ActivityPub.Types.Ownable
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible} alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Tombstone alias Mobilizon.Tombstone
alias Mobilizon.Web.Email.Participation
alias Mobilizon.Web.Endpoint alias Mobilizon.Web.Endpoint
alias Mobilizon.Web.Email.{Group, Participation}
require Logger require Logger

View File

@ -56,7 +56,10 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
description: "The event's organizer (as a person)" description: "The event's organizer (as a person)"
) )
field(:attributed_to, :actor, description: "Who the event is attributed to (often a group)") field(:attributed_to, :actor,
resolve: dataloader(Actors),
description: "Who the event is attributed to (often a group)"
)
field(:tags, list_of(:tag), field(:tags, list_of(:tag),
resolve: &Tag.list_tags_for_event/3, resolve: &Tag.list_tags_for_event/3,