Implement join/leave button on group profile

This commit is contained in:
Wolfgang Huß 2022-09-12 20:00:48 +02:00
parent aac951e7a7
commit 330bdd4df5
14 changed files with 179 additions and 10 deletions

View File

@ -19,7 +19,6 @@ import { followUserMutation, unfollowUserMutation } from '~/graphql/User'
export default {
name: 'HcFollowButton',
props: {
followId: { type: String, default: null },
isFollowed: { type: Boolean, default: false },

View File

@ -0,0 +1,91 @@
<template>
<base-button
class="track-button"
:disabled="disabled"
:loading="loading"
:icon="icon"
:filled="isMember && !hovered"
:danger="isMember && hovered"
@mouseenter.native="onHover"
@mouseleave.native="hovered = false"
@click.prevent="toggle"
>
{{ label }}
</base-button>
</template>
<script>
import { joinGroupMutation } from '~/graphql/groups'
export default {
name: 'JoinLeaveButton',
props: {
groupId: { type: String, required: true },
userId: { type: String, required: true },
isMember: { type: Boolean, required: true },
},
data() {
return {
disabled: false,
loading: false,
hovered: false,
}
},
computed: {
icon() {
if (this.isMember && this.hovered) {
return 'close'
} else {
return this.isMember ? 'check' : 'plus'
}
},
label() {
if (this.isMember) {
return this.$t('group.joinLeaveButton.iAmMember')
} else {
return this.$t('group.joinLeaveButton.join')
}
},
},
watch: {
isMember() {
this.loading = false
this.hovered = false
},
},
methods: {
onHover() {
if (!this.disabled && !this.loading) {
this.hovered = true
}
},
async toggle() {
const join = !this.isMember
const mutation = join ? joinGroupMutation : null // Wolle: implement "leaveGroupMutation"
this.hovered = false
const optimisticResult = { joinedByCurrentUser: join }
this.$emit('optimistic', optimisticResult)
try {
const { data } = await this.$apollo.mutate({
mutation,
variables: { groupId: this.groupId, userId: this.userId },
})
const joinedGroup = join ? data.JoinGroup : { myRoleInGroup: null } // Wolle: implement "leaveGroupMutation"
this.$emit('update', joinedGroup)
} catch (error) {
optimisticResult.joinedByCurrentUser = !join
this.$emit('optimistic', optimisticResult)
}
},
},
}
</script>
<style lang="scss">
.track-button {
display: block;
width: 100%;
}
</style>

View File

@ -373,6 +373,10 @@
"group": {
"foundation": "Gründung",
"goal": "Ziel:",
"joinLeaveButton": {
"iAmMember": "Bin Mitglied",
"join": "Beitreten"
},
"membersCount": "Mitglieder",
"membersListTitle": "Gruppenmitglieder"
},

View File

@ -373,6 +373,10 @@
"group": {
"foundation": "Foundation",
"goal": "Goal:",
"joinLeaveButton": {
"iAmMember": "I'm a member",
"join": "Join"
},
"membersCount": "Members",
"membersListTitle": "Group Members"
},

View File

@ -300,6 +300,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -289,6 +289,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -297,6 +297,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -85,6 +85,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -169,6 +169,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -335,6 +335,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -314,6 +314,10 @@
"group": {
"foundation": null,
"goal": null,
"joinLeaveButton": {
"iAmMember": null,
"join": null
},
"membersCount": null,
"membersListTitle": null
},

View File

@ -49,7 +49,11 @@
<ds-flex-item>
<client-only>
<ds-number :label="$t('group.membersCount')">
<count-to slot="count" :start-val="0" :end-val="groupMembers.length" />
<count-to
slot="count"
:start-val="membersCountStartValue"
:end-val="groupMembers.length"
/>
</ds-number>
</client-only>
</ds-flex-item>
@ -72,7 +76,7 @@
</client-only>
</ds-flex-item> -->
</ds-flex>
<div v-if="!isGroupMember" class="action-buttons">
<div class="action-buttons">
<!-- <base-button v-if="user.isBlocked" @click="unblockUser(user)">
{{ $t('settings.blocked-users.unblock') }}
</base-button>
@ -86,6 +90,15 @@
@optimistic="optimisticFollow"
@update="updateFollow"
/> -->
<join-leave-button
:groupId="group ? group.id : ''"
:userId="currentUser.id"
:isMember="isGroupMember"
@optimistic="optimisticJoinLeave"
@update="updateJoinLeave"
/>
<!-- implement:
v-if="!user.isMuted && !user.isBlocked" -->
</div>
<template v-if="group.about">
<hr />
@ -200,8 +213,9 @@ import AvatarUploader from '~/components/Uploader/AvatarUploader'
// import ContentMenu from '~/components/ContentMenu/ContentMenu'
import CountTo from '~/components/CountTo.vue'
import Empty from '~/components/Empty/Empty'
// import FollowButton from '~/components/FollowButton.vue'
// import FollowButton from '~/components/Button/FollowButton'
// import FollowList from '~/components/features/ProfileList/FollowList'
import JoinLeaveButton from '~/components/Button/JoinLeaveButton'
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import PostTeaser from '~/components/PostTeaser/PostTeaser.vue'
@ -226,6 +240,7 @@ export default {
Empty,
// FollowButton,
// FollowList,
JoinLeaveButton,
PostTeaser,
ProfileAvatar,
ProfileList,
@ -253,22 +268,26 @@ export default {
// followedByCountStartValue: 0,
// followedByCount: 7,
// followingCount: 7,
membersCount: Infinity,
membersCountStartValue: 0,
membersCountToLoad: Infinity,
updateGroupMutation,
}
},
computed: {
currentUser() {
return this.$store.getters['auth/user']
},
group() {
return this.Group ? this.Group[0] : {}
return this.Group[0] ? this.Group[0] : {}
},
groupMembers() {
return this.GroupMembers ? this.GroupMembers : []
},
isGroupOwner() {
return this.group.myRole === 'owner'
return this.group ? this.group.myRole === 'owner' : false
},
isGroupMember() {
return this.group.myRole
return this.group ? !!this.group.myRole : false
},
groupName() {
const { name } = this.group || {}
@ -405,8 +424,32 @@ export default {
// this.user.followedByCurrentUser = followedByCurrentUser
// this.user.followedBy = followedBy
// },
optimisticJoinLeave({ joinedByCurrentUser }) {
/*
* Note: "membersCountStartValue" is updated to avoid counting from 0 when join/leave
*/
this.membersCountStartValue = this.GroupMembers.length
if (joinedByCurrentUser) {
// this.membersCountToLoad++
this.GroupMembers = [this.currentUser, ...this.GroupMembers]
} else {
// this.membersCountToLoad--
this.GroupMembers = this.GroupMembers.filter((user) => user.id !== this.currentUser.id)
}
},
updateJoinLeave({ myRoleInGroup }) {
this.Group = [{ ...this.Group[0], myRole: myRoleInGroup }] // if we assign it directly "this.group" will not be updated
const currentUserInGroupMembers = this.GroupMembers.find(
(user) => user.id === this.currentUser.id,
)
if (currentUserInGroupMembers) {
currentUserInGroupMembers.myRoleInGroup = myRoleInGroup
} else {
this.$apollo.queries.GroupMembers.refetch()
}
},
fetchAllMembers() {
this.membersCount = Infinity
this.membersCountToLoad = Infinity
},
},
apollo: {

View File

@ -172,7 +172,7 @@
import uniqBy from 'lodash/uniqBy'
import postListActions from '~/mixins/postListActions'
import PostTeaser from '~/components/PostTeaser/PostTeaser.vue'
import HcFollowButton from '~/components/FollowButton.vue'
import HcFollowButton from '~/components/Button/FollowButton'
import HcCountTo from '~/components/CountTo.vue'
import HcBadges from '~/components/Badges.vue'
import FollowList, { followListVisibleCount } from '~/components/features/ProfileList/FollowList'