mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
Merge branch 'develop' into release
This commit is contained in:
@@ -1,41 +1,41 @@
|
|||||||
.tier1 {
|
.tier1 {
|
||||||
color: #c42870 !important;
|
color: #c42870;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier2 {
|
.tier2 {
|
||||||
color: #b01515 !important;
|
color: #b01515;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier3 {
|
.tier3 {
|
||||||
color: #d70e14 !important;
|
color: #d70e14;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier4 {
|
.tier4 {
|
||||||
color: #c24d00 !important;
|
color: #c24d00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier5 {
|
.tier5 {
|
||||||
color: #9e650f !important;
|
color: #9e650f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier6 {
|
.tier6 {
|
||||||
color: #2b8363 !important;
|
color: #2b8363;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier7 {
|
.tier7 {
|
||||||
color: #167e87 !important;
|
color: #167e87;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier8 {
|
.tier8 {
|
||||||
color: #277eab !important;
|
color: #277eab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tier9 {
|
.tier9 {
|
||||||
color: #6133b4 !important;
|
color: #6133b4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tierNPC, .npc {
|
.tierNPC, .npc {
|
||||||
color: #77f4c7 !important;
|
color: #77f4c7;
|
||||||
fill: #77f4c7;
|
fill: #77f4c7;
|
||||||
stroke: #005737;
|
stroke: #005737;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,13 @@
|
|||||||
.row
|
.row
|
||||||
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
||||||
challenge-item(:challenge='challenge')
|
challenge-item(:challenge='challenge')
|
||||||
|
mugen-scroll(
|
||||||
.row
|
:handler="infiniteScrollTrigger",
|
||||||
h2.col-12.loading(v-if='loading') {{ $t('loading') }}
|
:should-handle="!loading && canLoadMore",
|
||||||
|
:threshold="1",
|
||||||
.row
|
v-show="loading",
|
||||||
.col-12.text-center(v-if='showLoadMore && !loading && filteredChallenges.length > 0')
|
)
|
||||||
button.btn.btn-flat.btn-show-more(@click='loadMore()') {{ $t('loadMore') }}
|
h2.col-12.loading(v-once) {{ $t('loading') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@@ -75,17 +75,22 @@ import challengeUtilities from 'client/mixins/challengeUtilities';
|
|||||||
|
|
||||||
import positiveIcon from 'assets/svg/positive.svg';
|
import positiveIcon from 'assets/svg/positive.svg';
|
||||||
|
|
||||||
|
import MugenScroll from 'vue-mugen-scroll';
|
||||||
|
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [challengeUtilities],
|
mixins: [challengeUtilities],
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
ChallengeItem,
|
ChallengeItem,
|
||||||
challengeModal,
|
challengeModal,
|
||||||
|
MugenScroll,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: true,
|
||||||
showLoadMore: true,
|
canLoadMore: true,
|
||||||
icons: Object.freeze({
|
icons: Object.freeze({
|
||||||
positiveIcon,
|
positiveIcon,
|
||||||
}),
|
}),
|
||||||
@@ -169,17 +174,26 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only show the load more Button if the max count was returned
|
// only show the load more Button if the max count was returned
|
||||||
this.showLoadMore = challenges.length === 10;
|
this.canLoadMore = challenges.length === 10;
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
challengeCreated (challenge) {
|
challengeCreated (challenge) {
|
||||||
this.challenges.push(challenge);
|
this.challenges.push(challenge);
|
||||||
},
|
},
|
||||||
async loadMore () {
|
infiniteScrollTrigger () {
|
||||||
|
// show loading and wait until the loadMore debounced
|
||||||
|
// or else it would trigger on every scrolling-pixel (while not loading)
|
||||||
|
if (this.canLoadMore) {
|
||||||
|
this.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadMore();
|
||||||
|
},
|
||||||
|
loadMore: debounce(function loadMoreDebounce () {
|
||||||
this.page += 1;
|
this.page += 1;
|
||||||
this.loadChallenges();
|
this.loadChallenges();
|
||||||
},
|
}, 1000),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -29,13 +29,13 @@
|
|||||||
.row
|
.row
|
||||||
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
.col-12.col-md-6(v-for='challenge in filteredChallenges')
|
||||||
challenge-item(:challenge='challenge')
|
challenge-item(:challenge='challenge')
|
||||||
|
mugen-scroll(
|
||||||
.row
|
:handler="infiniteScrollTrigger",
|
||||||
h2.col-12.loading(v-if='loading') {{ $t('loading') }}
|
:should-handle="!loading && canLoadMore",
|
||||||
|
:threshold="1",
|
||||||
.row
|
v-show="loading",
|
||||||
.col-12.text-center(v-if='showLoadMore && !loading && filteredChallenges.length > 0')
|
)
|
||||||
button.btn.btn-flat.btn-show-more(@click='loadMore()') {{ $t('loadMore') }}
|
h2.col-12.loading(v-once) {{ $t('loading') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
@@ -88,6 +88,9 @@ import challengeUtilities from 'client/mixins/challengeUtilities';
|
|||||||
|
|
||||||
import challengeIcon from 'assets/svg/challenge.svg';
|
import challengeIcon from 'assets/svg/challenge.svg';
|
||||||
import positiveIcon from 'assets/svg/positive.svg';
|
import positiveIcon from 'assets/svg/positive.svg';
|
||||||
|
import MugenScroll from 'vue-mugen-scroll';
|
||||||
|
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [challengeUtilities],
|
mixins: [challengeUtilities],
|
||||||
@@ -95,6 +98,7 @@ export default {
|
|||||||
Sidebar,
|
Sidebar,
|
||||||
ChallengeItem,
|
ChallengeItem,
|
||||||
challengeModal,
|
challengeModal,
|
||||||
|
MugenScroll,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -103,7 +107,7 @@ export default {
|
|||||||
positiveIcon,
|
positiveIcon,
|
||||||
}),
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
showLoadMore: true,
|
canLoadMore: true,
|
||||||
challenges: [],
|
challenges: [],
|
||||||
sort: 'none',
|
sort: 'none',
|
||||||
sortOptions: [
|
sortOptions: [
|
||||||
@@ -202,17 +206,26 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// only show the load more Button if the max count was returned
|
// only show the load more Button if the max count was returned
|
||||||
this.showLoadMore = challenges.length === 10;
|
this.canLoadMore = challenges.length === 10;
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
challengeCreated (challenge) {
|
challengeCreated (challenge) {
|
||||||
this.challenges.push(challenge);
|
this.challenges.push(challenge);
|
||||||
},
|
},
|
||||||
async loadMore () {
|
infiniteScrollTrigger () {
|
||||||
|
// show loading and wait until the loadMore debounced
|
||||||
|
// or else it would trigger on every scrolling-pixel (while not loading)
|
||||||
|
if (this.canLoadMore) {
|
||||||
|
this.loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadMore();
|
||||||
|
},
|
||||||
|
loadMore: debounce(function loadMoreDebounce () {
|
||||||
this.page += 1;
|
this.page += 1;
|
||||||
this.loadChallenges();
|
this.loadChallenges();
|
||||||
},
|
}, 1000),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '~client/assets/scss/colors.scss';
|
@import '~client/assets/scss/colors.scss';
|
||||||
|
|
||||||
a {
|
a.no-tier {
|
||||||
color: $gray-50;
|
color: $gray-50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ export default {
|
|||||||
userLevelStyleFromLevel (level, npc, style) {
|
userLevelStyleFromLevel (level, npc, style) {
|
||||||
style = style || '';
|
style = style || '';
|
||||||
if (npc) style += ' npc';
|
if (npc) style += ' npc';
|
||||||
if (level) style += ` tier${level}`;
|
style += level ? ` tier${level}` : ' no-tier';
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ api.loginLocal = {
|
|||||||
let user = await User.findOne(login).exec();
|
let user = await User.findOne(login).exec();
|
||||||
|
|
||||||
// if user is using social login, then user will not have a hashed_password stored
|
// if user is using social login, then user will not have a hashed_password stored
|
||||||
if (!user.auth.local.hashed_password) throw new NotAuthorized(res.t('invalidLoginCredentialsLong'));
|
if (!user || !user.auth.local.hashed_password) throw new NotAuthorized(res.t('invalidLoginCredentialsLong'));
|
||||||
|
|
||||||
let isValidPassword;
|
let isValidPassword;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import {
|
|||||||
} from '../models/group';
|
} from '../models/group';
|
||||||
import apiError from '../libs/apiError';
|
import apiError from '../libs/apiError';
|
||||||
|
|
||||||
const partyMembersFields = 'profile.name stats achievements items.special';
|
const partyMembersFields = 'profile.name stats achievements items.special notifications flags';
|
||||||
|
// Excluding notifications and flags from the list of public fields to return.
|
||||||
|
const partyMembersPublicFields = 'profile.name stats achievements items.special';
|
||||||
|
|
||||||
// @TODO: After refactoring individual spells, move quantity to the calculations
|
// @TODO: After refactoring individual spells, move quantity to the calculations
|
||||||
|
|
||||||
@@ -78,8 +80,7 @@ async function castPartySpell (req, party, partyMembers, user, spell, quantity =
|
|||||||
'party._id': party._id,
|
'party._id': party._id,
|
||||||
_id: { $ne: user._id }, // add separately
|
_id: { $ne: user._id }, // add separately
|
||||||
})
|
})
|
||||||
// .select(partyMembersFields) Selecting the entire user because otherwise when saving it'll save
|
.select(partyMembersFields)
|
||||||
// default values for non-selected fields and pre('save') will mess up thinking some values are missing
|
|
||||||
.exec();
|
.exec();
|
||||||
|
|
||||||
partyMembers.unshift(user);
|
partyMembers.unshift(user);
|
||||||
@@ -101,8 +102,7 @@ async function castUserSpell (res, req, party, partyMembers, targetId, user, spe
|
|||||||
if (!party) throw new NotFound(res.t('partyNotFound'));
|
if (!party) throw new NotFound(res.t('partyNotFound'));
|
||||||
partyMembers = await User
|
partyMembers = await User
|
||||||
.findOne({_id: targetId, 'party._id': party._id})
|
.findOne({_id: targetId, 'party._id': party._id})
|
||||||
// .select(partyMembersFields) Selecting the entire user because otherwise when saving it'll save
|
.select(partyMembersFields)
|
||||||
// default values for non-selected fields and pre('save') will mess up thinking some values are missing
|
|
||||||
.exec();
|
.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,9 +183,9 @@ async function castSpell (req, res, {isV3 = false}) {
|
|||||||
let partyMembersRes = Array.isArray(partyMembers) ? partyMembers : [partyMembers];
|
let partyMembersRes = Array.isArray(partyMembers) ? partyMembers : [partyMembers];
|
||||||
|
|
||||||
// Only return some fields.
|
// Only return some fields.
|
||||||
// See comment above on why we can't just select the necessary fields when querying
|
// We can't just return the selected fields because they're private
|
||||||
partyMembersRes = partyMembersRes.map(partyMember => {
|
partyMembersRes = partyMembersRes.map(partyMember => {
|
||||||
return common.pickDeep(partyMember.toJSON(), common.$w(partyMembersFields));
|
return common.pickDeep(partyMember.toJSON(), common.$w(partyMembersPublicFields));
|
||||||
});
|
});
|
||||||
|
|
||||||
let userToJson = user;
|
let userToJson = user;
|
||||||
|
|||||||
Reference in New Issue
Block a user