Challenges plus misc fixes (#8961)

* Forced full refresh after deletE

* Fixed styles on firefox

* Removed instagram link

* Added information to modal

* Fixed deleteing and task keeping

* Added redirect to challenge detail after created

* Updated challenge item styles

* Added new limit option to challenges
This commit is contained in:
Keith Holliday
2017-08-17 11:03:32 -06:00
committed by GitHub
parent f5cf27a79e
commit de63622cdd
13 changed files with 60 additions and 33 deletions

View File

@@ -119,6 +119,16 @@ describe('GET challenges/user', () => {
}); });
}); });
it('should return not return challenges in user groups if we send member true param', async () => {
let challenges = await member.get(`/challenges/user?member=${true}`);
let foundChallenge1 = _.find(challenges, { _id: challenge._id });
expect(foundChallenge1).to.not.exist;
let foundChallenge2 = _.find(challenges, { _id: challenge2._id });
expect(foundChallenge2).to.not.exist;
});
it('should return newest challenges first', async () => { it('should return newest challenges first', async () => {
let challenges = await user.get('/challenges/user'); let challenges = await user.get('/challenges/user');

View File

@@ -65,7 +65,7 @@
.social-circle .social-circle
a(href='https://twitter.com/habitica', target='_blank') a(href='https://twitter.com/habitica', target='_blank')
.social-icon.svg-icon(v-html='icons.twitter') .social-icon.svg-icon(v-html='icons.twitter')
.social-circle // @TODO: Not ready yet .social-circle
a(href='https://www.instagram.com/habitica/', target='_blank') a(href='https://www.instagram.com/habitica/', target='_blank')
.social-icon.svg-icon.instagram(v-html='icons.instagram') .social-icon.svg-icon.instagram(v-html='icons.instagram')
.social-circle .social-circle

View File

@@ -257,11 +257,18 @@ export default {
await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId}); await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId});
}, },
async leaveChallenge () { async leaveChallenge () {
let keepChallenge = confirm('Do you want to keep challenge tasks?');
let keep = 'keep-all';
if (!keepChallenge) keep = 'remove-all';
let index = findIndex(this.user.challenges, (challengeId) => { let index = findIndex(this.user.challenges, (challengeId) => {
return challengeId === this.challengeId; return challengeId === this.challengeId;
}); });
this.user.challenges.splice(index, 1); this.user.challenges.splice(index, 1);
await this.$store.dispatch('challenges:leaveChallenge', {challengeId: this.challengeId}); await this.$store.dispatch('challenges:leaveChallenge', {
challengeId: this.challengeId,
keep,
});
}, },
closeChallenge () { closeChallenge () {
this.$root.$emit('show::modal', 'close-challenge-modal'); this.$root.$emit('show::modal', 'close-challenge-modal');

View File

@@ -1,6 +1,7 @@
<template lang="pug"> <template lang="pug">
.card .card
router-link(:to="{ name: 'challenge', params: { challengeId: challenge._id } }") .row
router-link.col-12(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
h3 {{challenge.name}} h3 {{challenge.name}}
.row .row
.col-6 .col-6
@@ -24,7 +25,6 @@
.row.description .row.description
.col-12 .col-12
| {{challenge.description}} | {{challenge.description}}
.container.well-wrapper
.well.row .well.row
.col-3 .col-3
.count-details .count-details
@@ -48,7 +48,7 @@
div {{$t('reward')}} div {{$t('reward')}}
</template> </template>
<style lang='scss' scoped> <style lang="scss" scoped>
@import '~client/assets/scss/colors.scss'; @import '~client/assets/scss/colors.scss';
.card { .card {
@@ -104,7 +104,6 @@
.description { .description {
color: $gray-200; color: $gray-200;
margin-top: 2em;
margin-bottom: 2em; margin-bottom: 2em;
overflow: hidden; overflow: hidden;
} }
@@ -142,8 +141,8 @@
width: 26px; width: 26px;
} }
.count-details { .count-details span {
padding-left: 1em; margin-right: .5em;
} }
.count { .count {

View File

@@ -14,11 +14,12 @@
strong(v-once) {{$t('description')}}* strong(v-once) {{$t('description')}}*
div.description-count.float-right {{charactersRemaining}} {{ $t('charactersRemaining') }} div.description-count.float-right {{charactersRemaining}} {{ $t('charactersRemaining') }}
b-form-input.description-textarea(type="text", textarea, :placeholder="$t('challengeDescriptionPlaceHolder')", v-model="workingChallenge.description") b-form-input.description-textarea(type="text", textarea, :placeholder="$t('challengeDescriptionPlaceHolder')", v-model="workingChallenge.description")
// @TODO: Implemenet in V2 .form-group .form-group
label label
strong(v-once) {{$t('guildInformation')}}* strong(v-once) Challenge Information*
a.float-right {{ $t('markdownFormattingHelp') }} a.float-right {{ $t('markdownFormattingHelp') }}
b-form-input.information-textarea(type="text", textarea, :placeholder="$t('challengeInformationPlaceHolder')", v-model="workingChallenge.information") b-form-input.information-textarea(type="text", textarea,
:placeholder="$t('challengeInformationPlaceHolder')", v-model="workingChallenge.description")
.form-group(v-if='creating') .form-group(v-if='creating')
label label
strong(v-once) {{$t('where')}} strong(v-once) {{$t('where')}}
@@ -296,6 +297,7 @@ export default {
this.$emit('createChallenge', challenge); this.$emit('createChallenge', challenge);
this.ressetWorkingChallenge(); this.ressetWorkingChallenge();
this.$root.$emit('hide::modal', 'challenge-modal'); this.$root.$emit('hide::modal', 'challenge-modal');
this.$router.push(`/challenges/${challenge._id}`);
}, },
updateChallenge () { updateChallenge () {
this.$emit('updatedChallenge', { this.$emit('updatedChallenge', {

View File

@@ -20,7 +20,7 @@ div
.col-12 .col-12
strong(v-once) {{$t('doYouWantedToDeleteChallenge')}} strong(v-once) {{$t('doYouWantedToDeleteChallenge')}}
.col-12 .col-12
button.btn.btn-danger(v-once) {{$t('deleteChallenge')}} button.btn.btn-danger(v-once, @click='deleteChallenge()') {{$t('deleteChallenge')}}
.footer-wrap(slot="modal-footer") .footer-wrap(slot="modal-footer")
</template> </template>

View File

@@ -11,7 +11,7 @@
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }} // @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true) b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}} b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
button.btn.btn-secondary.create-challenge-button(@click='createChallenge()') button.btn.btn-secondary.create-challenge-button.float-right(@click='createChallenge()')
.svg-icon.positive-icon(v-html="icons.positiveIcon") .svg-icon.positive-icon(v-html="icons.positiveIcon")
span(v-once) {{$t('createChallenge')}} span(v-once) {{$t('createChallenge')}}
.row .row

View File

@@ -11,7 +11,7 @@
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }} // @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
b-dropdown(:text="$t('sort')", right=true) b-dropdown(:text="$t('sort')", right=true)
b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}} b-dropdown-item(v-for='sortOption in sortOptions', :key="sortOption.value", @click='sort(sortOption.value)') {{sortOption.text}}
button.btn.btn-secondary.create-challenge-button(@click='createChallenge()') button.btn.btn-secondary.create-challenge-button.float-right(@click='createChallenge()')
.svg-icon.positive-icon(v-html="icons.positiveIcon") .svg-icon.positive-icon(v-html="icons.positiveIcon")
span(v-once) {{$t('createChallenge')}} span(v-once) {{$t('createChallenge')}}
@@ -153,7 +153,9 @@ export default {
this.$root.$emit('show::modal', 'challenge-modal'); this.$root.$emit('show::modal', 'challenge-modal');
}, },
async loadchallanges () { async loadchallanges () {
this.challenges = await this.$store.dispatch('challenges:getUserChallenges'); this.challenges = await this.$store.dispatch('challenges:getUserChallenges', {
member: true,
});
}, },
challengeCreated (challenge) { challengeCreated (challenge) {
this.challenges.push(challenge); this.challenges.push(challenge);

View File

@@ -59,7 +59,7 @@ export default {
}, },
}); });
localStorage.clear(); localStorage.clear();
this.$router.push('/home'); window.location.href = '/home';
this.$root.$emit('hide::modal', 'reset'); this.$root.$emit('hide::modal', 'reset');
}, },
}, },

View File

@@ -146,27 +146,27 @@
.form(v-if='user.auth.local', name='changeUsername', novalidate) .form(v-if='user.auth.local', name='changeUsername', novalidate)
//-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }} //-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }}
.form-group .form-group
input.form-control(type='text', :placeholder="$t('newUsername')", v-model='usernameUpdates.username', required) input.form-control(type='text', :placeholder="$t('newUsername')", v-model='usernameUpdates.username')
.form-group .form-group
input.form-control(type='password', :placeholder="$t('password')", v-model='usernameUpdates.password', required) input.form-control(type='password', :placeholder="$t('password')", v-model='usernameUpdates.password')
button.btn.btn-primary(type='submit', @click='changeUser("username", usernameUpdates)') {{ $t('submit') }} button.btn.btn-primary(type='submit', @click='changeUser("username", usernameUpdates)') {{ $t('submit') }}
h5 {{ $t('changeEmail') }} h5 {{ $t('changeEmail') }}
.form(v-if='user.auth.local', name='changeEmail', novalidate) .form(v-if='user.auth.local', name='changeEmail', novalidate)
.form-group .form-group
input.form-control(type='text', :placeholder="$t('newEmail')", v-model='emailUpdates.newEmail', required) input.form-control(type='text', :placeholder="$t('newEmail')", v-model='emailUpdates.newEmail')
.form-group .form-group
input.form-control(type='password', :placeholder="$t('password')", v-model='emailUpdates.password', required) input.form-control(type='password', :placeholder="$t('password')", v-model='emailUpdates.password')
button.btn.btn-primary(type='submit', @click='changeUser("email", emailUpdates)') {{ $t('submit') }} button.btn.btn-primary(type='submit', @click='changeUser("email", emailUpdates)') {{ $t('submit') }}
h5 {{ $t('changePass') }} h5 {{ $t('changePass') }}
.form(v-if='user.auth.local', name='changePassword', novalidate) .form(v-if='user.auth.local', name='changePassword', novalidate)
.form-group .form-group
input.form-control(type='password', :placeholder="$t('oldPass')", v-model='passwordUpdates.password', required) input.form-control(type='password', :placeholder="$t('oldPass')", v-model='passwordUpdates.password')
.form-group .form-group
input.form-control(type='password', :placeholder="$t('newPass')", v-model='passwordUpdates.newPassword', required) input.form-control(type='password', :placeholder="$t('newPass')", v-model='passwordUpdates.newPassword')
.form-group .form-group
input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='passwordUpdates.confirmPassword', required) input.form-control(type='password', :placeholder="$t('confirmPass')", v-model='passwordUpdates.confirmPassword')
button.btn.btn-primary(type='submit', @click='changeUser("password", passwordUpdates)') {{ $t('submit') }} button.btn.btn-primary(type='submit', @click='changeUser("password", passwordUpdates)') {{ $t('submit') }}
div div

View File

@@ -186,7 +186,7 @@ const router = new VueRouter({
}, },
{ {
name: 'challenge', name: 'challenge',
path: 'challenges/:challengeId', path: ':challengeId',
component: ChallengeDetail, component: ChallengeDetail,
props: true, props: true,
}, },

View File

@@ -26,9 +26,10 @@ export async function leaveChallenge (store, payload) {
return response.data.data; return response.data.data;
} }
export async function getUserChallenges () { export async function getUserChallenges (store, payload) {
let response = await axios.get('/api/v3/challenges/user'); let url = '/api/v3/challenges/user';
if (payload && payload.member) url += '?member=true';
let response = await axios.get(url);
return response.data.data; return response.data.data;
} }

View File

@@ -371,13 +371,19 @@ api.getUserChallenges = {
middlewares: [authWithHeaders()], middlewares: [authWithHeaders()],
async handler (req, res) { async handler (req, res) {
let user = res.locals.user; let 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
];
if (!req.query.member) {
orOptions.push({
group: {$in: user.getGroups()},
}); // Challenges in groups where I'm a member
}
let challenges = await Challenge.find({ let challenges = await Challenge.find({
$or: [ $or: orOptions,
{_id: {$in: user.challenges}}, // Challenges where the user is participating
{group: {$in: user.getGroups()}}, // Challenges in groups where I'm a member
{leader: user._id}, // Challenges where I'm the leader
],
}) })
.sort('-official -createdAt') .sort('-official -createdAt')
// see below why we're not using populate // see below why we're not using populate