Various group and posts improvements

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2022-04-19 17:58:11 +02:00
parent 74e8dd1eb2
commit 5bbb9713d4
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
6 changed files with 245 additions and 131 deletions

View File

@ -9,7 +9,7 @@
:rounded="true" :rounded="true"
style="height: 120px" style="height: 120px"
/> />
<div class="title-info-wrapper has-text-grey-dark"> <div class="title-info-wrapper has-text-grey-dark px-1">
<h3 class="post-minimalist-title" :lang="post.language"> <h3 class="post-minimalist-title" :lang="post.language">
{{ post.title }} {{ post.title }}
</h3> </h3>

View File

@ -9,7 +9,7 @@
<!-- @slot Mandatory title --> <!-- @slot Mandatory title -->
<slot /> <slot />
</h2> </h2>
<p v-show="$slots.desc"> <p v-show="$slots.desc" :class="descriptionClasses">
<!-- @slot Optional description --> <!-- @slot Optional description -->
<slot name="desc" /> <slot name="desc" />
</p> </p>
@ -21,6 +21,8 @@ import { Component, Prop, Vue } from "vue-property-decorator";
@Component @Component
export default class EmptyContent extends Vue { export default class EmptyContent extends Vue {
@Prop({ type: String, required: true }) icon!: string; @Prop({ type: String, required: true }) icon!: string;
@Prop({ type: String, required: false, default: "" })
descriptionClasses!: string;
@Prop({ type: Boolean, required: false, default: false }) inline!: boolean; @Prop({ type: Boolean, required: false, default: false }) inline!: boolean;
@Prop({ type: Boolean, required: false, default: false }) center!: boolean; @Prop({ type: Boolean, required: false, default: false }) center!: boolean;
} }

View File

@ -1321,5 +1321,14 @@
"You may now close this page or {return_to_the_homepage}.": "You may now close this page or {return_to_the_homepage}.", "You may now close this page or {return_to_the_homepage}.": "You may now close this page or {return_to_the_homepage}.",
"This group is a remote group, it's possible the original instance has more informations.": "This group is a remote group, it's possible the original instance has more informations.", "This group is a remote group, it's possible the original instance has more informations.": "This group is a remote group, it's possible the original instance has more informations.",
"View the group profile on the original instance": "View the group profile on the original instance", "View the group profile on the original instance": "View the group profile on the original instance",
"View past events": "View past events" "View past events": "View past events",
"Get informed of the upcoming public events": "Get informed of the upcoming public events",
"Join": "Join",
"Become part of the community and start organizing events": "Become part of the community and start organizing events",
"Follow requests will be approved by a group moderator": "Follow requests will be approved by a group moderator",
"Follow request pending approval": "Follow request pending approval",
"Your membership is pending approval": "Your membership is pending approval",
"Activate notifications": "Activate notifications",
"Deactivate notifications": "Deactivate notifications",
"Membership requests will be approved by a group moderator": "Membership requests will be approved by a group moderator"
} }

View File

@ -1312,5 +1312,14 @@
"No instance found.": "Aucune instance trouvée.", "No instance found.": "Aucune instance trouvée.",
"This group is a remote group, it's possible the original instance has more informations.": "Ce groupe est un groupe distant, il est possible que l'instance d'origine ait plus d'informations.", "This group is a remote group, it's possible the original instance has more informations.": "Ce groupe est un groupe distant, il est possible que l'instance d'origine ait plus d'informations.",
"View the group profile on the original instance": "Afficher le profil du groupe sur l'instance d'origine", "View the group profile on the original instance": "Afficher le profil du groupe sur l'instance d'origine",
"View past events": "Voir les événements passés" "View past events": "Voir les événements passés",
"Get informed of the upcoming public events": "Soyez informé⋅e des événements publics à venir",
"Join": "Rejoindre",
"Become part of the community and start organizing events": "Faites partie de la communauté et commencez à organiser des événements",
"Follow requests will be approved by a group moderator": "Les demandes de suivi seront approuvées par un⋅e modérateur⋅ice du groupe",
"Follow request pending approval": "Demande de suivi en attente d'approbation",
"Your membership is pending approval": "Votre adhésion est en attente d'approbation",
"Activate notifications": "Activer les notifications",
"Deactivate notifications": "Désactiver les notifications",
"Membership requests will be approved by a group moderator": "Les demandes d'adhésion seront approuvées par un⋅e modérateur⋅ice du groupe"
} }

View File

@ -27,11 +27,11 @@
<div class="title-container"> <div class="title-container">
<h1 v-if="group.name">{{ group.name }}</h1> <h1 v-if="group.name">{{ group.name }}</h1>
<b-skeleton v-else :animated="true" /> <b-skeleton v-else :animated="true" />
<small <span
dir="ltr" dir="ltr"
class="has-text-grey-dark" class="has-text-grey-dark"
v-if="group.preferredUsername" v-if="group.preferredUsername"
>@{{ usernameWithDomain(group) }}</small >@{{ usernameWithDomain(group) }}</span
> >
<b-skeleton v-else :animated="true" /> <b-skeleton v-else :animated="true" />
<br /> <br />
@ -78,7 +78,7 @@
> >
</p> </p>
</div> </div>
<div class="buttons"> <div class="flex gap-2">
<b-button <b-button
outlined outlined
icon-left="timeline-text" icon-left="timeline-text"
@ -101,78 +101,123 @@
}" }"
>{{ $t("Group settings") }}</b-button >{{ $t("Group settings") }}</b-button
> >
<b-tooltip <b-dropdown
v-if=" aria-role="list"
(!isCurrentActorAGroupMember || previewPublic) && trap-focus
group.openness === Openness.INVITE_ONLY v-show="showJoinButton && showFollowButton"
"
:label="$t('This group is invite-only')"
position="is-bottom"
>
<b-button disabled type="is-primary">{{
$t("Join group")
}}</b-button></b-tooltip
>
<b-button
v-else-if="
((!isCurrentActorAGroupMember &&
!isCurrentActorAPendingGroupMember) ||
previewPublic) &&
currentActor.id
"
@click="joinGroup"
@keyup.enter="joinGroup"
type="is-primary"
:disabled="previewPublic"
>{{ $t("Join group") }}</b-button
> >
<template #trigger>
<b-button
:label="$t('Follow')"
type="is-primary"
icon-left="rss"
icon-right="menu-down"
/>
</template>
<b-dropdown-item
aria-role="listitem"
class="p-0"
custom
:focusable="false"
:disabled="
isCurrentActorPendingFollow && currentActor.id !== undefined
"
>
<button class="media py-4 px-2 w-full" @click="followGroup">
<b-icon class="media-left" icon="rss" />
<div class="media-content">
<h3 class="font-medium text-lg">{{ $t("Follow") }}</h3>
<p class="whitespace-normal md:whitespace-nowrap text-sm">
{{ $t("Get informed of the upcoming public events") }}
</p>
<p
v-if="
doesGroupManuallyApprovesFollowers &&
!isCurrentActorPendingFollow
"
class="whitespace-normal md:whitespace-nowrap text-sm italic"
>
{{
$t(
"Follow requests will be approved by a group moderator"
)
}}
</p>
<p
v-if="isCurrentActorPendingFollow && currentActor.id"
class="whitespace-normal md:whitespace-nowrap text-sm italic"
>
{{ $t("Follow request pending approval") }}
</p>
</div>
</button>
</b-dropdown-item>
<b-dropdown-item
aria-role="listitem"
class="p-0 border-t border-solid"
custom
:focusable="false"
:disabled="
isGroupInviteOnly || isCurrentActorAPendingGroupMember
"
>
<button class="media py-4 px-2 w-full" @click="joinGroup">
<b-icon
class="media-left"
icon="account-multiple-plus"
></b-icon>
<div class="media-content">
<h3 class="font-medium text-lg">{{ $t("Join") }}</h3>
<div v-if="showJoinButton">
<p
class="whitespace-normal md:whitespace-nowrap text-sm"
>
{{
$t(
"Become part of the community and start organizing events"
)
}}
</p>
<p
v-if="isGroupInviteOnly"
class="whitespace-normal md:whitespace-nowrap text-sm italic"
>
{{ $t("This group is invite-only") }}
</p>
<p
v-if="
areGroupMembershipsModerated &&
!isCurrentActorAPendingGroupMember
"
class="whitespace-normal md:whitespace-nowrap text-sm italic"
>
{{
$t(
"Membership requests will be approved by a group moderator"
)
}}
</p>
<p
v-if="isCurrentActorAPendingGroupMember"
class="whitespace-normal md:whitespace-nowrap text-sm italic"
>
{{ $t("Your membership is pending approval") }}
</p>
</div>
</div>
</button>
</b-dropdown-item>
</b-dropdown>
<b-button <b-button
outlined outlined
v-else-if="isCurrentActorAPendingGroupMember" v-if="isCurrentActorAPendingGroupMember"
@click="leaveGroup" @click="leaveGroup"
@keyup.enter="leaveGroup" @keyup.enter="leaveGroup"
type="is-primary" type="is-primary"
>{{ $t("Cancel membership request") }}</b-button >{{ $t("Cancel membership request") }}</b-button
> >
<b-button
tag="router-link"
:to="{
name: RouteName.GROUP_JOIN,
params: { preferredUsername: usernameWithDomain(group) },
}"
v-else-if="!isCurrentActorAGroupMember || previewPublic"
:disabled="previewPublic"
type="is-primary"
>{{ $t("Join group") }}</b-button
>
<b-button
v-if="
((!isCurrentActorFollowing && !isCurrentActorAGroupMember) ||
previewPublic) &&
!isCurrentActorPendingFollow &&
currentActor.id
"
@click="followGroup"
@keyup.enter="followGroup"
type="is-primary"
:disabled="isCurrentActorPendingFollow"
>{{ $t("Follow") }}</b-button
>
<b-button
tag="router-link"
:to="{
name: RouteName.GROUP_FOLLOW,
params: { preferredUsername: usernameWithDomain(group) },
}"
v-else-if="
!isCurrentActorPendingFollow &&
!isCurrentActorFollowing &&
previewPublic
"
:disabled="previewPublic"
type="is-primary"
>{{ $t("Follow") }}</b-button
>
<b-button <b-button
outlined outlined
v-if="isCurrentActorPendingFollow && currentActor.id" v-if="isCurrentActorPendingFollow && currentActor.id"
@ -192,12 +237,20 @@
v-if="isCurrentActorFollowing" v-if="isCurrentActorFollowing"
@click="toggleFollowNotify" @click="toggleFollowNotify"
@keyup.enter="toggleFollowNotify" @keyup.enter="toggleFollowNotify"
class="notification-button p-1.5"
outlined
:icon-left=" :icon-left="
isCurrentActorFollowingNotify isCurrentActorFollowingNotify
? 'bell-outline' ? 'bell-outline'
: 'bell-off-outline' : 'bell-off-outline'
" "
></b-button> >
<span class="sr-only">{{
isCurrentActorFollowingNotify
? $t("Activate notifications")
: $t("Deactivate notifications")
}}</span>
</b-button>
<b-button <b-button
outlined outlined
icon-left="share" icon-left="share"
@ -308,28 +361,6 @@
) )
}} }}
</b-message> </b-message>
<b-message
v-if="
!isCurrentActorAGroupMember &&
!isCurrentActorAPendingGroupMember &&
!isCurrentActorPendingFollow &&
!isCurrentActorFollowing
"
type="is-info"
has-icon
class="m-3"
>
<i18n
path="Following the group will allow you to be informed of the {group_upcoming_public_events}, whereas joining the group means you will {access_to_group_private_content_as_well}, including group discussions, group resources and members-only posts."
>
<b slot="group_upcoming_public_events">{{
$t("group's upcoming public events")
}}</b>
<b slot="access_to_group_private_content_as_well">{{
$t("access to the group's private content as well")
}}</b>
</i18n>
</b-message>
</div> </div>
</header> </header>
</div> </div>
@ -506,6 +537,13 @@
$t("View full profile") $t("View full profile")
}}</a> }}</a>
</b-message> </b-message>
<event-metadata-block :title="$t('About')">
<div
dir="auto"
v-html="group.summary"
v-if="group.summary && group.summary !== '<p></p>'"
/>
</event-metadata-block>
<event-metadata-block :title="$t('Members')" icon="account-group"> <event-metadata-block :title="$t('Members')" icon="account-group">
{{ {{
$tc("{count} members", group.members.total, { $tc("{count} members", group.members.total, {
@ -553,17 +591,6 @@
</div> </div>
</aside> </aside>
<div class="main-content"> <div class="main-content">
<section>
<subtitle>{{ $t("About") }}</subtitle>
<div
dir="auto"
v-html="group.summary"
v-if="group.summary && group.summary !== '<p></p>'"
/>
<empty-content v-else-if="group" icon="image-text" :inline="true">
{{ $t("This group doesn't have a description yet.") }}
</empty-content>
</section>
<section> <section>
<subtitle>{{ $t("Upcoming events") }}</subtitle> <subtitle>{{ $t("Upcoming events") }}</subtitle>
<div <div
@ -577,7 +604,12 @@
class="organized-event" class="organized-event"
/> />
</div> </div>
<empty-content v-else-if="group" icon="calendar" :inline="true"> <empty-content
v-else-if="group"
icon="calendar"
:inline="true"
description-classes="flex flex-col items-stretch"
>
{{ $t("No public upcoming events") }} {{ $t("No public upcoming events") }}
<template #desc> <template #desc>
<template v-if="isCurrentActorFollowing"> <template v-if="isCurrentActorFollowing">
@ -594,7 +626,7 @@
</template> </template>
<b-button <b-button
tag="router-link" tag="router-link"
class="my-2" class="my-2 self-center"
type="is-text" type="is-text"
:to="{ :to="{
name: RouteName.GROUP_EVENTS, name: RouteName.GROUP_EVENTS,
@ -621,8 +653,8 @@
> >
</div> </div>
</section> </section>
<section> <section class="flex flex-col items-stretch">
<subtitle>{{ $t("Latest posts") }}</subtitle> <subtitle class="ml-0">{{ $t("Latest posts") }}</subtitle>
<multi-post-list-item <multi-post-list-item
v-if=" v-if="
@ -642,13 +674,16 @@
{{ $t("No posts yet") }} {{ $t("No posts yet") }}
</empty-content> </empty-content>
<b-skeleton animated v-else-if="$apollo.loading"></b-skeleton> <b-skeleton animated v-else-if="$apollo.loading"></b-skeleton>
<router-link <b-button
class="self-center my-2"
v-if="posts.total > 0" v-if="posts.total > 0"
tag="router-link"
type="is-text"
:to="{ :to="{
name: RouteName.POSTS, name: RouteName.POSTS,
params: { preferredUsername: usernameWithDomain(group) }, params: { preferredUsername: usernameWithDomain(group) },
}" }"
>{{ $t("View all posts") }}</router-link >{{ $t("View all posts") }}</b-button
> >
</section> </section>
</div> </div>
@ -806,25 +841,38 @@ export default class Group extends mixins(GroupMixin) {
} }
async joinGroup(): Promise<void> { async joinGroup(): Promise<void> {
const [group, currentActorId] = [ if (!this.currentActor?.id) {
usernameWithDomain(this.group), this.$router.push({
this.currentActor.id, name: RouteName.GROUP_JOIN,
]; params: { preferredUsername: usernameWithDomain(this.group) },
this.$apollo.mutate({ });
mutation: JOIN_GROUP, return;
variables: { }
groupId: this.group.id, try {
}, const [group, currentActorId] = [
refetchQueries: [ usernameWithDomain(this.group),
{ this.currentActor.id,
query: PERSON_STATUS_GROUP, ];
variables: { await this.$apollo.mutate({
id: currentActorId, mutation: JOIN_GROUP,
group, variables: {
}, groupId: this.group.id,
}, },
], refetchQueries: [
}); {
query: PERSON_STATUS_GROUP,
variables: {
id: currentActorId,
group,
},
},
],
});
} catch (error: any) {
if (error.graphQLErrors && error.graphQLErrors.length > 0) {
this.$notifier.error(error.graphQLErrors[0].message);
}
}
} }
protected async openLeaveGroupModal(): Promise<void> { protected async openLeaveGroupModal(): Promise<void> {
@ -870,6 +918,13 @@ export default class Group extends mixins(GroupMixin) {
} }
async followGroup(): Promise<void> { async followGroup(): Promise<void> {
if (!this.currentActor?.id) {
this.$router.push({
name: RouteName.GROUP_FOLLOW,
params: { preferredUsername: usernameWithDomain(this.group) },
});
return;
}
try { try {
const [group, currentActorId] = [ const [group, currentActorId] = [
usernameWithDomain(this.group), usernameWithDomain(this.group),
@ -1088,6 +1143,41 @@ export default class Group extends mixins(GroupMixin) {
}), }),
}; };
} }
get showFollowButton(): boolean {
return (
(!this.isCurrentActorFollowing || this.previewPublic) &&
this.currentActor?.id !== undefined
);
}
get showJoinButton(): boolean {
return (
(!this.isCurrentActorAGroupMember || this.previewPublic) &&
this.currentActor?.id !== undefined
);
}
get isGroupInviteOnly(): boolean {
return (
(!this.isCurrentActorAGroupMember || this.previewPublic) &&
this.group?.openness === Openness.INVITE_ONLY
);
}
get areGroupMembershipsModerated(): boolean {
return (
(!this.isCurrentActorAGroupMember || this.previewPublic) &&
this.group?.openness === Openness.MODERATED
);
}
get doesGroupManuallyApprovesFollowers(): boolean {
return (
(!this.isCurrentActorAGroupMember || this.previewPublic) &&
this.group?.manuallyApprovesFollowers
);
}
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -1380,4 +1470,7 @@ div.container {
height: 60vh; height: 60vh;
width: 100%; width: 100%;
} }
button.button.notification-button ::v-deep span.icon.is-small {
margin: 0 !important;
}
</style> </style>

View File

@ -15,7 +15,9 @@
v-if="post.draft" v-if="post.draft"
>{{ $t("Draft") }}</b-tag >{{ $t("Draft") }}</b-tag
> >
<h1 class="title" :lang="post.language">{{ post.title }}</h1> <h1 class="title text-3xl" :lang="post.language">
{{ post.title }}
</h1>
</div> </div>
<p class="metadata"> <p class="metadata">
<router-link <router-link
@ -441,7 +443,6 @@ article.post {
h1.title { h1.title {
margin: 0; margin: 0;
font-weight: 500; font-weight: 500;
font-size: 38px;
font-family: "Roboto", "Helvetica", "Arial", serif; font-family: "Roboto", "Helvetica", "Arial", serif;
} }