mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-10-26 10:42:52 +01:00
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:
@@ -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 () => {
|
||||
let challenges = await user.get('/challenges/user');
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
.social-circle
|
||||
a(href='https://twitter.com/habitica', target='_blank')
|
||||
.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')
|
||||
.social-icon.svg-icon.instagram(v-html='icons.instagram')
|
||||
.social-circle
|
||||
|
||||
@@ -257,11 +257,18 @@ export default {
|
||||
await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId});
|
||||
},
|
||||
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) => {
|
||||
return challengeId === this.challengeId;
|
||||
});
|
||||
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 () {
|
||||
this.$root.$emit('show::modal', 'close-challenge-modal');
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<template lang="pug">
|
||||
.card
|
||||
router-link(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
|
||||
h3 {{challenge.name}}
|
||||
.row
|
||||
router-link.col-12(:to="{ name: 'challenge', params: { challengeId: challenge._id } }")
|
||||
h3 {{challenge.name}}
|
||||
.row
|
||||
.col-6
|
||||
div.details
|
||||
@@ -24,7 +25,6 @@
|
||||
.row.description
|
||||
.col-12
|
||||
| {{challenge.description}}
|
||||
.container.well-wrapper
|
||||
.well.row
|
||||
.col-3
|
||||
.count-details
|
||||
@@ -48,7 +48,7 @@
|
||||
div {{$t('reward')}}
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
<style lang="scss" scoped>
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.card {
|
||||
@@ -104,7 +104,6 @@
|
||||
|
||||
.description {
|
||||
color: $gray-200;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -142,8 +141,8 @@
|
||||
width: 26px;
|
||||
}
|
||||
|
||||
.count-details {
|
||||
padding-left: 1em;
|
||||
.count-details span {
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.count {
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
strong(v-once) {{$t('description')}}*
|
||||
div.description-count.float-right {{charactersRemaining}} {{ $t('charactersRemaining') }}
|
||||
b-form-input.description-textarea(type="text", textarea, :placeholder="$t('challengeDescriptionPlaceHolder')", v-model="workingChallenge.description")
|
||||
// @TODO: Implemenet in V2 .form-group
|
||||
.form-group
|
||||
label
|
||||
strong(v-once) {{$t('guildInformation')}}*
|
||||
strong(v-once) Challenge Information*
|
||||
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')
|
||||
label
|
||||
strong(v-once) {{$t('where')}}
|
||||
@@ -296,6 +297,7 @@ export default {
|
||||
this.$emit('createChallenge', challenge);
|
||||
this.ressetWorkingChallenge();
|
||||
this.$root.$emit('hide::modal', 'challenge-modal');
|
||||
this.$router.push(`/challenges/${challenge._id}`);
|
||||
},
|
||||
updateChallenge () {
|
||||
this.$emit('updatedChallenge', {
|
||||
|
||||
@@ -20,7 +20,7 @@ div
|
||||
.col-12
|
||||
strong(v-once) {{$t('doYouWantedToDeleteChallenge')}}
|
||||
.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")
|
||||
</template>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
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")
|
||||
span(v-once) {{$t('createChallenge')}}
|
||||
.row
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// @TODO: implement sorting span.dropdown-label {{ $t('sortBy') }}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
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")
|
||||
span(v-once) {{$t('createChallenge')}}
|
||||
|
||||
@@ -153,7 +153,9 @@ export default {
|
||||
this.$root.$emit('show::modal', 'challenge-modal');
|
||||
},
|
||||
async loadchallanges () {
|
||||
this.challenges = await this.$store.dispatch('challenges:getUserChallenges');
|
||||
this.challenges = await this.$store.dispatch('challenges:getUserChallenges', {
|
||||
member: true,
|
||||
});
|
||||
},
|
||||
challengeCreated (challenge) {
|
||||
this.challenges.push(challenge);
|
||||
|
||||
@@ -59,7 +59,7 @@ export default {
|
||||
},
|
||||
});
|
||||
localStorage.clear();
|
||||
this.$router.push('/home');
|
||||
window.location.href = '/home';
|
||||
this.$root.$emit('hide::modal', 'reset');
|
||||
},
|
||||
},
|
||||
|
||||
@@ -146,27 +146,27 @@
|
||||
.form(v-if='user.auth.local', name='changeUsername', novalidate)
|
||||
//-.alert.alert-danger(ng-messages='changeUsername.$error && changeUsername.submitted') {{ $t('fillAll') }}
|
||||
.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
|
||||
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') }}
|
||||
|
||||
h5 {{ $t('changeEmail') }}
|
||||
.form(v-if='user.auth.local', name='changeEmail', novalidate)
|
||||
.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
|
||||
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') }}
|
||||
|
||||
h5 {{ $t('changePass') }}
|
||||
.form(v-if='user.auth.local', name='changePassword', novalidate)
|
||||
.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
|
||||
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
|
||||
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') }}
|
||||
|
||||
div
|
||||
|
||||
@@ -186,7 +186,7 @@ const router = new VueRouter({
|
||||
},
|
||||
{
|
||||
name: 'challenge',
|
||||
path: 'challenges/:challengeId',
|
||||
path: ':challengeId',
|
||||
component: ChallengeDetail,
|
||||
props: true,
|
||||
},
|
||||
|
||||
@@ -26,9 +26,10 @@ export async function leaveChallenge (store, payload) {
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
export async function getUserChallenges () {
|
||||
let response = await axios.get('/api/v3/challenges/user');
|
||||
|
||||
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);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -371,13 +371,19 @@ api.getUserChallenges = {
|
||||
middlewares: [authWithHeaders()],
|
||||
async handler (req, res) {
|
||||
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({
|
||||
$or: [
|
||||
{_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
|
||||
],
|
||||
$or: orOptions,
|
||||
})
|
||||
.sort('-official -createdAt')
|
||||
// see below why we're not using populate
|
||||
|
||||
Reference in New Issue
Block a user