Adding support for loading more members for Groups (#9740)

* Adding support for loading more members for Groups

Addresses issue #9720

Member modal component was only loading the maximum limit for queries
made to _getMembersForItem in /members, except with Challenges, which
was able to display a "Load More" button to retrieve another set of
users from the Challenge.

Made a few changes to how the GET request was being made when querying
for more members, added an easier way to know whether or not to display
the Load More button, and extracted some of the actions that were
too tightly coupled with the membersModal.vue.

* Fixes for failing lint tests

* Removing unnecessary async/await usage

* Fixing party view in header section

* Resolving missed conflict

* Adding necessary data for View Party in index header for web client to load party members
This commit is contained in:
Jon Lim 2018-02-05 13:42:54 -05:00 committed by Matteo Pagliazzi
parent a61d911c48
commit 9a00779698
6 changed files with 110 additions and 37 deletions

View file

@ -281,7 +281,7 @@ export default {
},
async loadChallenge () {
this.challenge = await this.$store.dispatch('challenges:getChallenge', {challengeId: this.searchId});
this.members = await this.$store.dispatch('members:getChallengeMembers', {challengeId: this.searchId});
this.members = await this.loadMembers({ challengeId: this.searchId, includeAllPublicFields: true });
let tasks = await this.$store.dispatch('tasks:getChallengeTasks', {challengeId: this.searchId});
this.tasksByType = {
habit: [],
@ -293,6 +293,21 @@ export default {
this.tasksByType[task.type].push(task);
});
},
/**
* Method for loading members of a group, with optional parameters for
* modifying requests.
*
* @param {Object} payload Used for modifying requests for members
*/
loadMembers (payload = null) {
// Remove unnecessary data
if (payload && payload.groupId) {
delete payload.groupId;
}
return this.$store.dispatch('members:getChallengeMembers', payload);
},
editTask (task) {
this.taskFormPurpose = 'edit';
this.editingTask = cloneDeep(task);
@ -334,7 +349,9 @@ export default {
this.$store.state.memberModalOptions.challengeId = this.challenge._id;
this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
this.$store.state.memberModalOptions.group = this.group;
this.$store.state.memberModalOptions.memberCount = this.challenge.memberCount;
this.$store.state.memberModalOptions.viewingMembers = this.members;
this.$store.state.memberModalOptions.fetchMoreMembers = this.loadMembers;
this.$root.$emit('bv::show::modal', 'members-modal');
},
async joinChallenge () {

View file

@ -375,6 +375,7 @@ export default {
silverGuildBadgeIcon,
bronzeGuildBadgeIcon,
}),
members: [],
selectedQuest: {},
sections: {
quest: true,
@ -456,14 +457,43 @@ export default {
},
},
methods: {
load () {
this.fetchGuild();
acceptCommunityGuidelines () {
this.$store.dispatch('user:set', {'flags.communityGuidelinesAccepted': true});
},
async load () {
if (this.isParty) {
this.searchId = 'party';
// @TODO: Set up from old client. Decide what we need and what we don't
// Check Desktop notifs
// Load invites
}
await this.fetchGuild();
// Fetch group members on load
this.members = await this.loadMembers({
groupId: this.group._id,
includeAllPublicFields: true,
});
this.$root.$on('updatedGroup', group => {
let updatedGroup = extend(this.group, group);
this.$set(this.group, updatedGroup);
});
},
/**
* Method for loading members of a group, with optional parameters for
* modifying requests.
*
* @param {Object} payload Used for modifying requests for members
*/
loadMembers (payload = null) {
// Remove unnecessary data
if (payload && payload.challengeId) {
delete payload.challengeId;
}
return this.$store.dispatch('members:getGroupMembers', payload);
},
// @TODO: abstract autocomplete
// https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
getCoord (e, text) {
@ -500,6 +530,9 @@ export default {
showMemberModal () {
this.$store.state.memberModalOptions.groupId = this.group._id;
this.$store.state.memberModalOptions.group = this.group;
this.$store.state.memberModalOptions.memberCount = this.group.memberCount;
this.$store.state.memberModalOptions.viewingMembers = this.members;
this.$store.state.memberModalOptions.fetchMoreMembers = this.loadMembers;
this.$root.$emit('bv::show::modal', 'members-modal');
},
async sendMessage () {

View file

@ -48,7 +48,7 @@ div
span.dropdown-icon-item
.svg-icon.inline(v-html="icons.removeIcon")
span.text {{$t('removeManager2')}}
.row(v-if='groupId === "challenge"')
.row(v-if='isLoadMoreAvailable')
.col-12.text-center
button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }}
.row.gradient(v-if='members.length > 3')
@ -296,6 +296,11 @@ export default {
isAdmin () {
return Boolean(this.user.contributor.admin);
},
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;
},
groupIsSubscribed () {
return this.group.purchased.active;
},
@ -311,16 +316,6 @@ export default {
sortedMembers () {
let sortedMembers = this.members;
if (this.searchTerm) {
sortedMembers = sortedMembers.filter(member => {
return (
member.profile.name
.toLowerCase()
.indexOf(this.searchTerm.toLowerCase()) !== -1
);
});
}
if (!isEmpty(this.sortOption)) {
// Use the memberlist filtered by searchTerm
sortedMembers = orderBy(sortedMembers, [this.sortOption.param], [this.sortOption.order]);
@ -337,6 +332,15 @@ export default {
group () {
this.getMembers();
},
// Watches `searchTerm` and if present, performs a `searchMembers` action
// and usual `getMembers` otherwise
searchTerm () {
if (this.searchTerm) {
this.searchMembers(this.searchTerm);
} else {
this.getMembers();
}
},
},
methods: {
sendMessage (member) {
@ -345,22 +349,24 @@ export default {
userName: member.profile.name,
});
},
async searchMembers (searchTerm = '') {
this.members = await this.$store.state.memberModalOptions.fetchMoreMembers({
challengeId: this.challengeId,
groupId: this.groupId,
searchTerm,
includeAllPublicFields: true,
});
},
async getMembers () {
let groupId = this.groupId;
if (groupId && groupId !== 'challenge') {
let members = await this.$store.dispatch('members:getGroupMembers', {
groupId,
includeAllPublicFields: true,
});
this.members = members;
if (groupId && groupId !== 'challenge') {
let invites = await this.$store.dispatch('members:getGroupInvites', {
groupId,
includeAllPublicFields: true,
});
this.invites = invites;
}
if (this.$store.state.memberModalOptions.viewingMembers.length > 0) {
this.members = this.$store.state.memberModalOptions.viewingMembers;
}
@ -425,9 +431,11 @@ export default {
const lastMember = this.members[this.members.length - 1];
if (!lastMember) return;
let newMembers = await this.$store.dispatch('members:getChallengeMembers', {
challengeId: this.challengeId,
lastMemberId: lastMember._id,
let newMembers = await this.$store.state.memberModalOptions.fetchMoreMembers({
challengeId: this.challengeId,
groupId: this.groupId,
lastMemberId: lastMember._id,
includeAllPublicFields: true,
});
this.members = this.members.concat(newMembers);

View file

@ -160,9 +160,10 @@ export default {
},
openPartyModal () {
if (this.user.party._id) {
// Set the party details for the members-modal component
this.$store.state.memberModalOptions.groupId = this.user.party._id;
// @TODO: do we need to fetch party?
// this.$store.state.memberModalOptions.group = this.$store.state.party;
this.$store.state.memberModalOptions.viewingMembers = this.partyMembers;
this.$store.state.memberModalOptions.group = this.user.party;
this.$root.$emit('bv::show::modal', 'members-modal');
return;
}
@ -174,9 +175,10 @@ export default {
}
},
},
async created () {
created () {
if (this.user.party && this.user.party._id) {
await this.getPartyMembers(true);
this.$store.state.memberModalOptions.groupId = this.user.party._id;
this.getPartyMembers();
}
},
};

View file

@ -7,15 +7,21 @@ let apiV3Prefix = '/api/v3';
export async function getGroupMembers (store, payload) {
let url = `${apiV3Prefix}/groups/${payload.groupId}/members`;
const params = {};
if (payload.includeAllPublicFields) {
url += '?includeAllPublicFields=true';
params.includeAllPublicFields = true;
}
if (payload.lastMemberId) {
params.lastId = payload.lastMemberId;
}
if (payload.searchTerm) {
url += `?search=${payload.searchTerm}`;
params.search = payload.searchTerm;
}
let response = await axios.get(url);
let response = await axios.get(url, { params });
return response.data.data;
}
@ -35,17 +41,23 @@ export async function getGroupInvites (store, payload) {
}
export async function getChallengeMembers (store, payload) {
let url = `${apiV3Prefix}/challenges/${payload.challengeId}/members?includeAllPublicFields=true`;
let url = `${apiV3Prefix}/challenges/${payload.challengeId}/members`;
const params = {};
if (payload.includeAllPublicFields) {
params.includeAllPublicFields = true;
}
if (payload.lastMemberId) {
url += `&lastId=${payload.lastMemberId}`;
params.lastId = payload.lastMemberId;
}
if (payload.searchTerm) {
url += `&search=${payload.searchTerm}`;
params.search = payload.searchTerm;
}
let response = await axios.get(url);
let response = await axios.get(url, { params });
return response.data.data;
}

View file

@ -248,7 +248,8 @@ function _getMembersForItem (type) {
}
if (req.query.search) {
query['profile.name'] = {$regex: req.query.search};
// Creates a RegExp expression when querying for profile.name
query['profile.name'] = { $regex: new RegExp(req.query.search, 'i') };
}
} else if (type === 'group-invites') {
if (group.type === 'guild') { // eslint-disable-line no-lonely-if