Merge branch 'discussion-list-if-not-member' into 'master'

Discussion list if not member

See merge request framasoft/mobilizon!792
This commit is contained in:
Thomas Citharel 2021-01-15 08:45:52 +01:00
commit d2d6842504
5 changed files with 149 additions and 17 deletions

View File

@ -163,14 +163,14 @@ pages:
- mkdir public - mkdir public
- mix docs - mix docs
- mv doc public/backend - mv doc public/backend
# #- yarn run --cwd "js" styleguide:build # #- yarn run --cwd "js" styleguide:build
# #- mv js/styleguide public/frontend # #- mv js/styleguide public/frontend
# rules: rules:
# - if: '$CI_COMMIT_BRANCH == "master"' - if: '$CI_COMMIT_BRANCH == "master"'
# artifacts: artifacts:
# expire_in: 1 hour expire_in: 1 hour
# paths: paths:
# - public - public
.docker: &docker .docker: &docker
stage: docker stage: docker

View File

@ -0,0 +1,37 @@
<template>
<div class="empty-content" :class="{ inline }" role="note">
<b-icon :icon="icon" size="is-large" />
<h2 class="empty-content__title">
<!-- @slot Mandatory title -->
<slot />
</h2>
<p v-show="$slots.desc">
<!-- @slot Optional description -->
<slot name="desc" />
</p>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class EmptyContent extends Vue {
@Prop({ type: String, required: true }) icon!: string;
@Prop({ type: Boolean, required: false, default: false }) inline!: boolean;
}
</script>
<style lang="scss">
.empty-content {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20vh;
&__title {
margin-bottom: 10px;
}
&.inline {
margin-top: 5vh;
}
}
</style>

View File

@ -27,7 +27,7 @@
</li> </li>
</ul> </ul>
</nav> </nav>
<section> <section v-if="isCurrentActorAGroupMember">
<p> <p>
{{ {{
$t( $t(
@ -39,7 +39,7 @@
tag="router-link" tag="router-link"
:to="{ :to="{
name: RouteName.CREATE_DISCUSSION, name: RouteName.CREATE_DISCUSSION,
params: { preferredUsername: this.preferredUsername }, params: { preferredUsername },
}" }"
>{{ $t("New discussion") }}</b-button >{{ $t("New discussion") }}</b-button
> >
@ -50,18 +50,38 @@
:key="discussion.id" :key="discussion.id"
/> />
</div> </div>
<empty-content v-else icon="chat">
{{ $t("There's no discussions yet") }}
</empty-content>
</section>
<section class="section" v-else>
<empty-content icon="chat">
{{ $t("Only group members can access discussions") }}
<template #desc>
<router-link
:to="{ name: RouteName.GROUP, params: { preferredUsername } }"
>
{{ $t("Return to the group page") }}
</router-link>
</template>
</empty-content>
</section> </section>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator"; import { Component, Prop, Vue } from "vue-property-decorator";
import { FETCH_GROUP } from "@/graphql/group"; import { FETCH_GROUP } from "@/graphql/group";
import { IGroup, usernameWithDomain } from "@/types/actor"; import { IActor, IGroup, IPerson, usernameWithDomain } from "@/types/actor";
import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue"; import DiscussionListItem from "@/components/Discussion/DiscussionListItem.vue";
import RouteName from "../../router/name"; import RouteName from "../../router/name";
import { MemberRole } from "@/types/enums";
import { CURRENT_ACTOR_CLIENT, PERSON_MEMBERSHIPS } from "@/graphql/actor";
import { GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED } from "@/graphql/event";
import { IMember } from "@/types/actor/member.model";
import EmptyContent from "@/components/Utils/EmptyContent.vue";
@Component({ @Component({
components: { DiscussionListItem }, components: { DiscussionListItem, EmptyContent },
apollo: { apollo: {
group: { group: {
query: FETCH_GROUP, query: FETCH_GROUP,
@ -75,6 +95,30 @@ import RouteName from "../../router/name";
return !this.preferredUsername; return !this.preferredUsername;
}, },
}, },
person: {
query: PERSON_MEMBERSHIPS,
fetchPolicy: "cache-and-network",
variables() {
return {
id: this.currentActor.id,
};
},
subscribeToMore: {
document: GROUP_MEMBERSHIP_SUBSCRIPTION_CHANGED,
variables() {
return {
actorId: this.currentActor.id,
};
},
skip() {
return !this.currentActor || !this.currentActor.id;
},
},
skip() {
return !this.currentActor || !this.currentActor.id;
},
},
currentActor: CURRENT_ACTOR_CLIENT,
}, },
metaInfo() { metaInfo() {
return { return {
@ -89,11 +133,36 @@ import RouteName from "../../router/name";
export default class DiscussionsList extends Vue { export default class DiscussionsList extends Vue {
@Prop({ type: String, required: true }) preferredUsername!: string; @Prop({ type: String, required: true }) preferredUsername!: string;
person!: IPerson;
group!: IGroup; group!: IGroup;
currentActor!: IActor;
RouteName = RouteName; RouteName = RouteName;
usernameWithDomain = usernameWithDomain; usernameWithDomain = usernameWithDomain;
get groupMemberships(): (string | undefined)[] {
if (!this.person || !this.person.id) return [];
return this.person.memberships.elements
.filter(
(membership: IMember) =>
![
MemberRole.REJECTED,
MemberRole.NOT_APPROVED,
MemberRole.INVITED,
].includes(membership.role)
)
.map(({ parent: { id } }) => id);
}
get isCurrentActorAGroupMember(): boolean {
return (
this.groupMemberships !== undefined &&
this.groupMemberships.includes(this.group.id)
);
}
} }
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -218,11 +218,9 @@
</div> </div>
</b-table-column> </b-table-column>
<template slot="empty"> <template slot="empty">
<section class="section"> <empty-content icon="account" inline>
<div class="content has-text-grey has-text-centered"> {{ $t("No member matches the filters") }}
<p>{{ $t("No member matches the filters") }}</p> </empty-content>
</div>
</section>
</template> </template>
</b-table> </b-table>
</section> </section>
@ -247,6 +245,7 @@ import {
UPDATE_MEMBER, UPDATE_MEMBER,
} from "../../graphql/member"; } from "../../graphql/member";
import { usernameWithDomain } from "../../types/actor"; import { usernameWithDomain } from "../../types/actor";
import EmptyContent from "@/components/Utils/EmptyContent.vue";
@Component({ @Component({
apollo: { apollo: {
@ -263,6 +262,9 @@ import { usernameWithDomain } from "../../types/actor";
update: (data) => data.group.members, update: (data) => data.group.members,
}, },
}, },
components: {
EmptyContent,
},
}) })
export default class GroupMembers extends mixins(GroupMixin) { export default class GroupMembers extends mixins(GroupMixin) {
loading = true; loading = true;

View File

@ -136,10 +136,13 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
""" """
test "find_group/3 returns a group by its username", %{conn: conn, actor: actor, user: user} do test "find_group/3 returns a group by its username", %{conn: conn, actor: actor, user: user} do
user2 = insert(:user)
insert(:actor, user: user2)
group = insert(:group) group = insert(:group)
insert(:member, parent: group, actor: actor, role: :administrator) insert(:member, parent: group, actor: actor, role: :administrator)
insert(:member, parent: group, role: :member) insert(:member, parent: group, role: :member)
# Unlogged
res = res =
conn conn
|> AbsintheHelpers.graphql_query( |> AbsintheHelpers.graphql_query(
@ -157,6 +160,26 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
assert res["data"]["group"]["members"]["total"] == 2 assert res["data"]["group"]["members"]["total"] == 2
assert res["data"]["group"]["members"]["elements"] == [] assert res["data"]["group"]["members"]["elements"] == []
# Login with non-member
res =
conn
|> auth_conn(user2)
|> AbsintheHelpers.graphql_query(
query: @group_query,
variables: %{
preferredUsername: group.preferred_username
}
)
assert res["errors"] == nil
assert res["data"]["group"]["preferredUsername"] ==
group.preferred_username
assert res["data"]["group"]["members"]["total"] == 2
assert res["data"]["group"]["members"]["elements"] == []
# Login with member
res = res =
conn conn
|> auth_conn(user) |> auth_conn(user)
@ -179,6 +202,7 @@ defmodule Mobilizon.Web.Resolvers.GroupTest do
assert admin["actor"]["preferredUsername"] == assert admin["actor"]["preferredUsername"] ==
actor.preferred_username actor.preferred_username
# Non existent username
res = res =
conn conn
|> AbsintheHelpers.graphql_query( |> AbsintheHelpers.graphql_query(