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 @@