Added paging (#10150)

* Added paging

* Escaped regex

* Fixed challenge side effect tests
This commit is contained in:
Keith Holliday 2018-03-23 14:13:08 -05:00 committed by GitHub
parent 0f339d8d3e
commit 298a6a743c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 18 deletions

View file

@ -227,4 +227,53 @@ describe('GET challenges/user', () => {
expect(foundChallengeIndex).to.eql(1);
});
});
context('filters and paging', () => {
let user, guild, member;
const categories = [{
slug: 'newCat',
name: 'New Category',
}];
before(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: {
name: 'TestGuild',
type: 'guild',
privacy: 'public',
},
members: 1,
});
user = groupLeader;
guild = group;
member = members[0];
for (let i = 0; i < 11; i += 1) {
await generateChallenge(user, group); // eslint-disable-line
}
});
it('returns public guilds filtered by category', async () => {
const categoryChallenge = await generateChallenge(user, guild, {categories});
const challenges = await user.get(`/challenges/user?categories=${categories[0].slug}`);
expect(challenges[0]._id).to.eql(categoryChallenge._id);
expect(challenges.length).to.eql(1);
});
it('paginates challenges', async () => {
const challenges = await user.get('/challenges/user');
const challengesPaged = await user.get('/challenges/user?page=1&owned=owned');
expect(challenges.length).to.eql(10);
expect(challengesPaged.length).to.eql(2);
});
it('filters by owned', async () => {
const challenges = await member.get('/challenges/user?owned=owned');
expect(challenges.length).to.eql(0);
});
});
});

View file

@ -17,6 +17,9 @@
.row
.col-12.col-md-6(v-for='challenge in filteredChallenges', v-if='!memberOf(challenge)')
challenge-item(:challenge='challenge')
.row
.col-12.text-center
button.btn.btn-secondary(@click='loadMore()') {{ $t('loadMore') }}
</template>
<style lang='scss' scoped>
@ -89,23 +92,16 @@ export default {
],
search: '',
filters: {},
page: 0,
};
},
mounted () {
this.loadchallanges();
// @TODO: do we need to load groups for filters still?
},
computed: {
...mapState({user: 'user.data'}),
filteredChallenges () {
let search = this.search;
let filters = this.filters;
let user = this.$store.state.user.data;
// @TODO: Move this to the server
return this.challenges.filter((challenge) => {
return this.filterChallenge(challenge, filters, search, user);
});
return this.challenges;
},
},
methods: {
@ -114,21 +110,53 @@ export default {
},
updateSearch (eventData) {
this.search = eventData.searchTerm;
this.page = 0;
this.loadchallanges();
},
updateFilters (eventData) {
this.filters = eventData;
this.page = 0;
this.loadchallanges();
},
createChallenge () {
this.$root.$emit('bv::show::modal', 'challenge-modal');
},
async loadchallanges () {
this.loading = true;
this.challenges = await this.$store.dispatch('challenges:getUserChallenges');
let categories = '';
if (this.filters.categories) {
categories = this.filters.categories.join(',');
}
let owned = '';
// @TODO: we skip ownership === 2 because it is the same as === 0 right now
if (this.filters.ownership && this.filters.ownership.length === 1) {
owned = this.filters.ownership[0];
}
const challenges = await this.$store.dispatch('challenges:getUserChallenges', {
page: this.page,
search: this.search,
categories,
owned,
});
if (this.page === 0) {
this.challenges = challenges;
} else {
this.challenges = this.challenges.concat(challenges);
}
this.loading = false;
},
challengeCreated (challenge) {
this.challenges.push(challenge);
},
async loadMore () {
this.page += 1;
this.loadchallanges();
},
},
};
</script>

View file

@ -145,7 +145,7 @@ export default {
this.$emit('search', {
searchTerm: newSearch,
});
}, 250),
}, 500),
},
methods: {
emitFilters () {

View file

@ -1,5 +1,6 @@
import axios from 'axios';
import omit from 'lodash/omit';
import encodeParams from 'client/libs/encodeParams';
export async function createChallenge (store, payload) {
let response = await axios.post('/api/v3/challenges', payload.challenge);
@ -34,8 +35,24 @@ export async function leaveChallenge (store, payload) {
export async function getUserChallenges (store, payload) {
let url = '/api/v3/challenges/user';
if (payload && payload.member) url += '?member=true';
let response = await axios.get(url);
let {
member,
page,
search,
categories,
owned,
} = payload;
let query = {};
if (member) query.member = member;
if (page) query.page = page;
if (search) query.search = search;
if (categories) query.categories = categories;
if (owned) query.owned = owned;
const parms = encodeParams(query);
const response = await axios.get(`${url}?${parms}`);
return response.data.data;
}

View file

@ -340,22 +340,55 @@ api.getUserChallenges = {
url: '/challenges/user',
middlewares: [authWithHeaders()],
async handler (req, res) {
let user = res.locals.user;
const CHALLENGES_PER_PAGE = 10;
const page = req.query.page || 0;
const user = res.locals.user;
let orOptions = [
{_id: {$in: user.challenges}}, // Challenges where the user is participating
{leader: user._id}, // Challenges where I'm the leader
];
const owned = req.query.owned;
if (!owned) {
orOptions.push({leader: user._id});
}
if (!req.query.member) {
orOptions.push({
group: {$in: user.getGroups()},
}); // Challenges in groups where I'm a member
}
let challenges = await Challenge.find({
$or: orOptions,
})
let query = {
$and: [{$or: orOptions}],
};
if (owned && owned === 'not_owned') {
query.$and.push({leader: {$ne: user._id}});
}
if (owned && owned === 'owned') {
query.$and.push({leader: user._id});
}
if (req.query.search) {
const searchOr = {$or: []};
const searchWords = _.escapeRegExp(req.query.search).split(' ').join('|');
const searchQuery = { $regex: new RegExp(`${searchWords}`, 'i') };
searchOr.$or.push({name: searchQuery});
searchOr.$or.push({description: searchQuery});
query.$and.push(searchOr);
}
if (req.query.categories) {
let categorySlugs = req.query.categories.split(',');
query.categories = { $elemMatch: { slug: {$in: categorySlugs} } };
}
const challenges = await Challenge.find(query)
.sort('-createdAt')
.limit(CHALLENGES_PER_PAGE)
.skip(CHALLENGES_PER_PAGE * page)
// see below why we're not using populate
// .populate('group', basicGroupFields)
// .populate('leader', nameFields)