diff --git a/website/client/app.vue b/website/client/app.vue
index d779790057..95e42e0946 100644
--- a/website/client/app.vue
+++ b/website/client/app.vue
@@ -21,6 +21,13 @@
}
+
+
diff --git a/website/client/components/challenges/challengeDetail.vue b/website/client/components/challenges/challengeDetail.vue
index 0c4d4e8af0..91cb66aa32 100644
--- a/website/client/components/challenges/challengeDetail.vue
+++ b/website/client/components/challenges/challengeDetail.vue
@@ -250,13 +250,17 @@ export default {
this.tasksByType[task.type].splice(index, 1, task);
},
showMemberModal () {
- this.$store.state.groupId = 'challenge'; // @TODO: change these terrible settings
- this.$store.state.viewingMembers = this.members;
+ this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings
+ this.$store.state.memberModalOptions.group = this.group;
+ this.$store.state.memberModalOptions.viewingMembers = this.members;
this.$root.$emit('show::modal', 'members-modal');
},
async joinChallenge () {
this.user.challenges.push(this.challengeId);
await this.$store.dispatch('challenges:joinChallenge', {challengeId: this.challengeId});
+ // @TODO: this doesn't work because of asyncresource
+ let tasks = await this.$store.dispatch('tasks:fetchUserTasks');
+ this.$store.state.tasks.data = tasks.data;
},
async leaveChallenge () {
let keepChallenge = confirm('Do you want to keep challenge tasks?');
diff --git a/website/client/components/challenges/challengeItem.vue b/website/client/components/challenges/challengeItem.vue
index a7f6f3a15f..c028c3b79d 100644
--- a/website/client/components/challenges/challengeItem.vue
+++ b/website/client/components/challenges/challengeItem.vue
@@ -24,7 +24,7 @@
div Challenge Prize
.row.description
.col-12
- | {{challenge.description}}
+ | {{challenge.summary}}
.well.row
.col-3
.count-details
@@ -104,7 +104,8 @@
.description {
color: $gray-200;
- margin-bottom: 2em;
+ margin-top: 1em;
+ margin-bottom: 1em;
overflow: hidden;
}
diff --git a/website/client/components/challenges/challengeModal.vue b/website/client/components/challenges/challengeModal.vue
index 3d64fdf761..527245c80a 100644
--- a/website/client/components/challenges/challengeModal.vue
+++ b/website/client/components/challenges/challengeModal.vue
@@ -34,9 +34,12 @@
.form-check(
v-for="group in categoryOptions",
:key="group.key",
+ v-if='group.key !== "habitica_official" || user.contributor.admin'
)
label.custom-control.custom-checkbox
- input.custom-control-input(type="checkbox", :value="group.key" v-model="workingChallenge.categories")
+ input.custom-control-input(type="checkbox",
+ :value='group.key',
+ v-model="workingChallenge.categories")
span.custom-control-indicator
span.custom-control-description(v-once) {{ $t(group.label) }}
button.btn.btn-primary(@click.prevent="toggleCategorySelect") {{$t('close')}}
@@ -47,7 +50,7 @@
.form-group
label
strong(v-once) {{$t('prize')}}
- input(type='number', min='1', :max='maxPrize', v-model="workingChallenge.prize")
+ input(type='number', :min='minPrize', :max='maxPrize', v-model="workingChallenge.prize")
.row.footer-wrap
.col-12.text-center.submit-button-wrapper
.alert.alert-warning(v-if='insufficientGemsForTavernChallenge')
@@ -220,6 +223,13 @@ export default {
if (this.challenge) {
Object.assign(this.workingChallenge, this.challenge);
this.workingChallenge.categories = [];
+
+ if (this.challenge.categories) {
+ this.challenge.categories.forEach(category => {
+ this.workingChallenge.categories.push(category.slug);
+ });
+ }
+
this.creating = false;
}
});
@@ -230,6 +240,7 @@ export default {
this.groups.push({
name: party.name,
_id: party._id,
+ privacy: 'private',
});
}
@@ -256,7 +267,13 @@ export default {
userBalance = userBalance * 4;
let groupBalance = 0;
- let group = find(this.groups, { _id: this.workingChallenge.group });
+ let group;
+ this.groups.forEach(item => {
+ if (item._id === this.workingChallenge.group) {
+ group = item;
+ return;
+ }
+ });
if (group && group.balance && group.leader === this.user._id) {
groupBalance = group.balance * 4;
@@ -264,6 +281,18 @@ export default {
return userBalance + groupBalance;
},
+ minPrize () {
+ let groupFound;
+ this.groups.forEach(group => {
+ if (group._id === this.workingChallenge.group) {
+ groupFound = group;
+ return;
+ }
+ });
+
+ if (groupFound && groupFound.privacy === 'private') return 0;
+ return 1;
+ },
insufficientGemsForTavernChallenge () {
let balance = this.user.balance || 0;
let isForTavern = this.workingChallenge.group === TAVERN_ID;
@@ -320,6 +349,17 @@ export default {
}
},
updateChallenge () {
+ let categoryKeys = this.workingChallenge.categories;
+ let serverCategories = [];
+ categoryKeys.forEach(key => {
+ let catName = this.categoriesHashByKey[key];
+ serverCategories.push({
+ slug: key,
+ name: catName,
+ });
+ });
+ this.workingChallenge.categories = serverCategories;
+
this.$emit('updatedChallenge', {
challenge: this.workingChallenge,
});
diff --git a/website/client/components/chat/chatMessages.vue b/website/client/components/chat/chatMessages.vue
index e833fdfdc9..7fcef2c2c3 100644
--- a/website/client/components/chat/chatMessages.vue
+++ b/website/client/components/chat/chatMessages.vue
@@ -35,7 +35,7 @@
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
- span.action.float-right
+ span.action.float-right(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }}
.row(v-if='user._id === msg.uuid')
@@ -58,7 +58,7 @@
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
- span.action.float-right
+ span.action.float-right(v-if='likeCount(msg) > 0')
.svg-icon(v-html="icons.liked")
| + {{ likeCount(msg) }}
.col-2
@@ -128,6 +128,7 @@ import axios from 'axios';
import moment from 'moment';
import cloneDeep from 'lodash/cloneDeep';
import { mapState } from 'client/libs/store';
+import throttle from 'lodash/throttle';
import markdownDirective from 'client/directives/markdown';
import Avatar from '../avatar';
import styleHelper from 'client/mixins/styleHelper';
@@ -155,6 +156,14 @@ export default {
mounted () {
this.loadProfileCache();
},
+ created () {
+ window.addEventListener('scroll', throttle(() => {
+ this.loadProfileCache(window.scrollY / 1000);
+ }, 1000));
+ },
+ destroyed () {
+ // window.removeEventListener('scroll', this.handleScroll);
+ },
data () {
return {
icons: Object.freeze({
@@ -167,6 +176,8 @@ export default {
copyingMessage: {},
currentDayDividerDisplay: moment().day(),
cachedProfileData: {},
+ currentProfileLoadedCount: 0,
+ currentProfileLoadedEnd: 10,
};
},
filters: {
@@ -191,14 +202,22 @@ export default {
},
},
methods: {
- async loadProfileCache () {
+ async loadProfileCache (screenPosition) {
let promises = [];
+ // @TODO: write an explination
+ if (screenPosition && Math.floor(screenPosition) + 1 > this.currentProfileLoadedEnd / 10) {
+ this.currentProfileLoadedEnd = 10 * (Math.floor(screenPosition) + 1);
+ } else {
+ return;
+ }
+
this.messages.forEach(message => {
let uuid = message.uuid;
if (uuid && !this.cachedProfileData[uuid]) {
- if (uuid === 'system') return;
+ if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return;
promises.push(axios.get(`/api/v3/members/${uuid}`));
+ this.currentProfileLoadedCount += 1;
}
});
diff --git a/website/client/components/creatorIntro.vue b/website/client/components/creatorIntro.vue
index ee57bc524d..8b82264016 100644
--- a/website/client/components/creatorIntro.vue
+++ b/website/client/components/creatorIntro.vue
@@ -53,8 +53,10 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.slim_shirt_pink.option(@click='set({"preferences.shirt":"pink"})', :class='{active: user.preferences.shirt === "pink"}')
.slim_shirt_white.option(@click='set({"preferences.shirt":"white"})', :class='{active: user.preferences.shirt === "white"}')
.slim_shirt_yellow.option(@click='set({"preferences.shirt":"yellow"})', :class='{active: user.preferences.shirt === "yellow"}')
- .col-12.premium-shirts(v-if='editing')
- .broad_shirt_convict.option(@click='set({"preferences.shirt":"convict"})', :class='{active: user.preferences.shirt === "convict"}')
+ .col-12.customize-options
+ .option(v-for='option in ["convict", "cross", "fire", "horizon", "ocean", "purple", "rainbow", "redblue", "thunder", "tropical", "zombie"]',
+ :class='[`broad_shirt_${option}`, {active: user.preferences.shirt === option}]',
+ @click='set({"preferences.shirt": option})')
.section.customize-section(v-if='activeTopPage === "skin"')
.row.sub-menu.col-6.offset-3.text-center
@@ -141,6 +143,13 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
.hair_flower_4.option(@click='set({"preferences.hair.flower":4})', :class='{active: user.preferences.hair.flower === 4}')
.hair_flower_5.option(@click='set({"preferences.hair.flower":5})', :class='{active: user.preferences.hair.flower === 5}')
.hair_flower_6.option(@click='set({"preferences.hair.flower":6})', :class='{active: user.preferences.hair.flower === 6}')
+ .row(v-if='activeSubPage === "flower"')
+ .col-12.customize-options
+ // button.customize-option(ng-repeat='item in ::getGearArray("animal")', class='{{::item.key}}',
+ ng-class="{locked: user.items.gear.owned[item.key] == undefined, selectableInventory: user.preferences.costume ? user.items.gear.costume.headAccessory == item.key : user.items.gear.equipped.headAccessory == item.key}",
+ popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter',
+ popover-placement='right', popover-append-to-body='true',
+ ng-click='user.items.gear.owned[item.key] ? equip(item.key) : purchase(item.type,item)')
#backgrounds.section.container.customize-section(v-if='activeTopPage === "backgrounds"')
.row.sub-menu.col-6.offset-3
@@ -172,6 +181,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
@click.prevent.stop="togglePinned(bg)"
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
+
.col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
.gem-amount
.svg-icon.gem(v-html='icons.gem')
@@ -215,6 +225,11 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
input.custom-control-input(type="checkbox")
span.custom-control-indicator
span.custom-control-description(v-once) {{ $t('creativity') }}
+ div
+ label.custom-control.custom-checkbox
+ input.custom-control-input(type="checkbox")
+ span.custom-control-indicator
+ span.custom-control-description(v-once) {{ $t('self_care') }}
.section.row.justin-message-section(:class='{top: modalPage > 1}')
.col-9
@@ -401,6 +416,10 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
border-radius: 2px;
}
+ .background:hover {
+ cursor: pointer;
+ }
+
.purchase-single {
width: 141px;
margin: 0 auto;
@@ -489,7 +508,6 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
}
}
-
.badge-svg {
left: calc((100% - 18px) / 2);
cursor: pointer;
@@ -559,22 +577,9 @@ export default {
data () {
let backgroundShopSets = getBackgroundShopSets();
- // @TODO: add dates to backgrounds
- let backgroundShopSetsByYear = {
- 2014: [],
- 2015: [],
- 2016: [],
- 2017: [],
- };
- backgroundShopSets.forEach((set) => {
- let year = set.identifier.substr(set.identifier.length - 4);
- if (!backgroundShopSetsByYear[year]) return;
- backgroundShopSetsByYear[year].push(set);
- });
-
return {
backgroundShopSets,
- backgroundShopSetsByYear,
+ backgroundUpdate: new Date(),
icons: Object.freeze({
logoPurple,
bodyIcon,
@@ -610,6 +615,32 @@ export default {
startingPage () {
return this.$store.state.avatarEditorOptions.startingPage;
},
+ backgroundShopSetsByYear () {
+ // @TODO: add dates to backgrounds
+ let backgroundShopSetsByYear = {
+ 2014: [],
+ 2015: [],
+ 2016: [],
+ 2017: [],
+ };
+
+ // Hack to force update for now until we restructure the data
+ let backgroundUpdate = this.backgroundUpdate; // eslint-disable-line
+
+ this.backgroundShopSets.forEach((set) => {
+ let year = set.identifier.substr(set.identifier.length - 4);
+ if (!backgroundShopSetsByYear[year]) return;
+
+ let setOwnedByUser = false;
+ for (let key in set.items) {
+ if (this.user.purchased.background[key]) setOwnedByUser = true;
+ }
+ set.userOwns = setOwnedByUser;
+
+ backgroundShopSetsByYear[year].push(set);
+ });
+ return backgroundShopSetsByYear;
+ },
},
methods: {
prev () {
@@ -654,7 +685,6 @@ export default {
for (let key in set) {
let value = set[key];
if (type === 'background') key = value.key;
-
if (this.user.purchased[type][key]) setOwnedByUser = true;
}
@@ -697,6 +727,7 @@ export default {
path,
},
});
+ this.backgroundUpdate = new Date();
} catch (e) {
alert(e.message);
}
diff --git a/website/client/components/groups/createPartyModal.vue b/website/client/components/groups/createPartyModal.vue
index 38559783f8..8e5570f1fb 100644
--- a/website/client/components/groups/createPartyModal.vue
+++ b/website/client/components/groups/createPartyModal.vue
@@ -172,9 +172,10 @@ export default {
};
group.name = this.$t('possessiveParty', {name: this.user.profile.name});
let party = await this.$store.dispatch('guilds:create', {group});
- this.$store.state.party = party;
+ this.$store.state.party.data = party;
this.user.party._id = party._id;
this.$root.$emit('hide::modal', 'create-party-modal');
+ this.$router.push('/party');
// @TODO: Analytics.updateUser({'partyID': $scope.group ._id, 'partySize': 1});
},
},
diff --git a/website/client/components/groups/discovery.vue b/website/client/components/groups/discovery.vue
index 4d4b118419..5d44621c55 100644
--- a/website/client/components/groups/discovery.vue
+++ b/website/client/components/groups/discovery.vue
@@ -28,7 +28,7 @@