From d2a0d4194a62e09167a76ff9d09e6df1808cf09a Mon Sep 17 00:00:00 2001 From: KoRnoliX <63583686+dabrowskif@users.noreply.github.com> Date: Thu, 7 Oct 2021 22:41:22 +0200 Subject: [PATCH] Fix casting items on parties that exceed it limit by showing Load More Button (#13509) * Fixed party size and notification when inviting Fixed party limit to 30 members (previously 31) and pop-up when trying to invite someone, when party has already reached it's members limit, to properly show members number. * Fixed View Party button in header Fixed View Party button in header to properly show Load More button when party size exceeds party limit. * Fixed View Party button to properly open party Fixed View Party button to properly open party members list on refreshing the main page, this bug was caused by previous commit. * Fixed SelectMembersModal to properly show Load More button Fixed SelectMembersModal (the modal that apperas when casting cards/specials on party member) to properly show Load More button when party size exceeds party limit * fix(test): limit now technically 29 plus leader * fix(test): adjust for tweakage Co-authored-by: Sabe Jones --- .../groups/POST-groups_invite.test.js | 4 +- .../client/src/components/header/index.vue | 27 +++++++-- .../src/components/selectMembersModal.vue | 57 ++++++++++++++++++- website/common/script/constants.js | 2 +- website/server/models/group.js | 2 +- 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/test/api/v3/integration/groups/POST-groups_invite.test.js b/test/api/v3/integration/groups/POST-groups_invite.test.js index f677f038fe..157cf01294 100644 --- a/test/api/v3/integration/groups/POST-groups_invite.test.js +++ b/test/api/v3/integration/groups/POST-groups_invite.test.js @@ -7,7 +7,7 @@ import { } from '../../../../helpers/api-integration/v3'; const INVITES_LIMIT = 100; -const PARTY_LIMIT_MEMBERS = 30; +const PARTY_LIMIT_MEMBERS = 29; const MAX_EMAIL_INVITES_BY_USER = 200; describe('Post /groups/:groupId/invite', () => { @@ -650,7 +650,7 @@ describe('Post /groups/:groupId/invite', () => { .to.eventually.be.rejected.and.eql({ code: 400, error: 'BadRequest', - message: t('partyExceedsMembersLimit', { maxMembersParty: PARTY_LIMIT_MEMBERS }), + message: t('partyExceedsMembersLimit', { maxMembersParty: PARTY_LIMIT_MEMBERS + 1 }), }); }).timeout(10000); }); diff --git a/website/client/src/components/header/index.vue b/website/client/src/components/header/index.vue index bbe5842a7d..202c5f0e45 100644 --- a/website/client/src/components/header/index.vue +++ b/website/client/src/components/header/index.vue @@ -145,6 +145,9 @@ export default { currentWidth: 0, inviteModalGroup: undefined, inviteModalGroupType: undefined, + group: {}, + members: [], + membersLoaded: false, }; }, computed: { @@ -236,14 +239,26 @@ export default { this.$root.$emit('bv::show::modal', 'create-party-modal'); } }, - async showPartyMembers () { - const party = await this.$store.dispatch('party:getParty'); + loadMembers (payload = null) { + // Remove unnecessary data + if (payload && payload.challengeId) { + delete payload.challengeId; + } + return this.$store.dispatch('members:getGroupMembers', payload); + }, + async showPartyMembers () { + this.group = await this.$store.dispatch('party:getParty'); + this.group = this.$store.state.party.data; + this.membersLoaded = true; + this.members = this.partyMembers; + this.$store.state.memberModalOptions.loading = false; this.$root.$emit('habitica:show-member-modal', { - groupId: party.data._id, - viewingMembers: this.partyMembers, - group: party.data, - fetchMoreMembers: p => this.$store.dispatch('members:getGroupMembers', p), + groupId: this.group._id, + group: this.group, + memberCount: this.group.memberCount, + viewingMembers: this.members, + fetchMoreMembers: this.loadMembers, }); }, setPartyMembersWidth ($event) { diff --git a/website/client/src/components/selectMembersModal.vue b/website/client/src/components/selectMembersModal.vue index 9326f09c7d..ef9d9eeb1d 100644 --- a/website/client/src/components/selectMembersModal.vue +++ b/website/client/src/components/selectMembersModal.vue @@ -74,6 +74,19 @@ v-if="members.length > 3" class="row gradient" > +
+
+ +
+
@@ -157,11 +170,13 @@ export default { components: { MemberDetails, }, - props: ['group', 'hideBadge', 'item'], + props: ['hideBadge', 'item'], data () { return { sortOption: '', members: [], + group: {}, + invites: [], memberToRemove: '', sortOptions: [ { @@ -210,6 +225,14 @@ export default { return this.members; }, + isLoadMoreAvailable () { + // Only available if the current length of `members` is less than the + // total size of the Group/Challenge + return this.members.length < this.$store.state.memberModalOptions.memberCount; + }, + challengeId () { + return this.$store.state.memberModalOptions.challengeId; + }, groupId () { return this.$store.state.groupId || this.group._id; }, @@ -222,7 +245,20 @@ export default { }, }, methods: { + loadMembers (payload = null) { + // Remove unnecessary data + if (payload && payload.challengeId) { + delete payload.challengeId; + } + + return this.$store.dispatch('members:getGroupMembers', payload); + }, async getMembers () { + this.group = await this.$store.dispatch('party:getParty'); + this.group = this.$store.state.party.data; + this.$store.state.memberModalOptions.memberCount = this.group.memberCount; + this.$store.state.memberModalOptions.fetchMoreMembers = this.loadMembers; + const { groupId } = this; if (groupId && groupId !== 'challenge') { const members = await this.$store.dispatch('members:getGroupMembers', { @@ -230,6 +266,11 @@ export default { includeAllPublicFields: true, }); this.members = members; + const invites = await this.$store.dispatch('members:getGroupInvites', { + groupId, + includeAllPublicFields: true, + }); + this.invites = invites; } if ((!this.members || this.members.length === 0) @@ -241,6 +282,20 @@ export default { if (!this.members || (this.members.length === 0 && !this.groupId)) { this.members = [this.user]; } + this.$store.state.memberModalOptions.viewingMembers = this.members; + }, + async loadMoreMembers () { + const lastMember = this.members[this.members.length - 1]; + if (!lastMember) return; + + const newMembers = await this.$store.state.memberModalOptions.fetchMoreMembers({ + challengeId: this.challengeId, + groupId: this.groupId, + lastMemberId: lastMember._id, + includeAllPublicFields: true, + }); + + this.members = this.members.concat(newMembers); }, close () { this.$root.$emit('bv::hide::modal', 'select-member-modal'); diff --git a/website/common/script/constants.js b/website/common/script/constants.js index 27ec659adc..f2eb56a4c0 100644 --- a/website/common/script/constants.js +++ b/website/common/script/constants.js @@ -28,7 +28,7 @@ export const SUPPORTED_SOCIAL_NETWORKS = [ export const GUILDS_PER_PAGE = 30; // number of guilds to return per page when using pagination -export const PARTY_LIMIT_MEMBERS = 30; +export const PARTY_LIMIT_MEMBERS = 29; export const MINIMUM_PASSWORD_LENGTH = 8; diff --git a/website/server/models/group.js b/website/server/models/group.js index 38b913c139..e8d98627fe 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -491,7 +491,7 @@ schema.statics.validateInvitations = async function getInvitationErr (invites, r memberCount += totalInvites; if (memberCount > shared.constants.PARTY_LIMIT_MEMBERS) { - throw new BadRequest(res.t('partyExceedsMembersLimit', { maxMembersParty: shared.constants.PARTY_LIMIT_MEMBERS })); + throw new BadRequest(res.t('partyExceedsMembersLimit', { maxMembersParty: shared.constants.PARTY_LIMIT_MEMBERS + 1 })); } } };