diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index 6a675aab97..3f52127d01 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -388,6 +388,23 @@ describe('POST /chat', () => { expect(groupMessages[0].id).to.exist; }); + it('creates a chat with a max length of 3000 chars', async () => { + const veryLongMessage = ` + 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789. + THIS PART WON'T BE IN THE MESSAGE (over 3000) + `; + + const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: veryLongMessage}); + const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`); + + expect(newMessage.message.id).to.exist; + expect(groupMessages[0].id).to.exist; + + expect(newMessage.message.text.length).to.eql(3000); + expect(newMessage.message.text).to.not.contain('MESSAGE'); + expect(groupMessages[0].text.length).to.eql(3000); + }); + it('creates a chat with user styles', async () => { const mount = 'test-mount'; const pet = 'test-pet'; diff --git a/test/api/v3/integration/dataexport/GET-export_userdata.xml.test.js b/test/api/v3/integration/dataexport/GET-export_userdata.xml.test.js index 5973f439f7..f99b34d869 100644 --- a/test/api/v3/integration/dataexport/GET-export_userdata.xml.test.js +++ b/test/api/v3/integration/dataexport/GET-export_userdata.xml.test.js @@ -23,6 +23,17 @@ describe('GET /export/userdata.xml', () => { ]); + // add pinnedItem + await user.get('/user/toggle-pinned-item/marketGear/gear.flat.shield_rogue_5'); + + // add a private message + let receiver = await generateUser(); + + user.post('/members/send-private-message', { + message: 'Your first message, hi!', + toUserId: receiver._id, + }); + let response = await user.get('/export/userdata.xml'); let {user: res} = await parseStringAsync(response, {explicitArray: false}); diff --git a/webpack/config/index.js b/webpack/config/index.js index 463e6f3028..ca098d0a95 100644 --- a/webpack/config/index.js +++ b/webpack/config/index.js @@ -62,7 +62,11 @@ module.exports = { target: DEV_BASE_URL, changeOrigin: true, }, - '/logout': { + '/logout-server': { + target: DEV_BASE_URL, + changeOrigin: true, + }, + '/export': { target: DEV_BASE_URL, changeOrigin: true, }, diff --git a/website/client/app.vue b/website/client/app.vue index 342de7a146..1133f0823b 100644 --- a/website/client/app.vue +++ b/website/client/app.vue @@ -331,9 +331,33 @@ export default { ]; if (notificationNotFoundMessage.indexOf(errorMessage) !== -1) snackbarTimeout = true; + let errorsToShow = []; + let usernameCheck = false; + let emailCheck = false; + let passwordCheck = false; + // show only the first error for each param + if (errorData.errors) { + for (let e of errorData.errors) { + if (!usernameCheck && e.param === 'username') { + errorsToShow.push(e.message); + usernameCheck = true; + } + if (!emailCheck && e.param === 'email') { + errorsToShow.push(e.message); + emailCheck = true; + } + if (!passwordCheck && e.param === 'password') { + errorsToShow.push(e.message); + passwordCheck = true; + } + } + } else { + errorsToShow.push(errorMessage); + } + // dispatch as one snackbar notification this.$store.dispatch('snackbars:add', { title: 'Habitica', - text: errorMessage, + text: errorsToShow.join(' '), type: 'error', timeout: snackbarTimeout, }); diff --git a/website/client/assets/images/home/track-habits.png b/website/client/assets/images/home/track-habits.png index d84a6b5563..64d999885f 100755 Binary files a/website/client/assets/images/home/track-habits.png and b/website/client/assets/images/home/track-habits.png differ diff --git a/website/client/assets/images/home/track-habits@2x.png b/website/client/assets/images/home/track-habits@2x.png index ae021b5cd4..88bf5efc1e 100755 Binary files a/website/client/assets/images/home/track-habits@2x.png and b/website/client/assets/images/home/track-habits@2x.png differ diff --git a/website/client/assets/images/home/track-habits@3x.png b/website/client/assets/images/home/track-habits@3x.png index bfaa4f3b65..9c4ce6154c 100755 Binary files a/website/client/assets/images/home/track-habits@3x.png and b/website/client/assets/images/home/track-habits@3x.png differ diff --git a/website/client/components/auth/logout.vue b/website/client/components/auth/logout.vue new file mode 100644 index 0000000000..b826794272 --- /dev/null +++ b/website/client/components/auth/logout.vue @@ -0,0 +1,13 @@ + diff --git a/website/client/components/auth/registerLoginReset.vue b/website/client/components/auth/registerLoginReset.vue index e6dec1f27e..77d1c92cce 100644 --- a/website/client/components/auth/registerLoginReset.vue +++ b/website/client/components/auth/registerLoginReset.vue @@ -402,11 +402,6 @@ export default { window.location.href = redirectTo; }, async login () { - if (!this.username) { - alert('Email is required'); - return; - } - await this.$store.dispatch('auth:login', { username: this.username, // email: this.email, diff --git a/website/client/components/challenges/challengeDetail.vue b/website/client/components/challenges/challengeDetail.vue index bf58dc934f..7464cb16bc 100644 --- a/website/client/components/challenges/challengeDetail.vue +++ b/website/client/components/challenges/challengeDetail.vue @@ -3,7 +3,7 @@ challenge-modal(v-on:updatedChallenge='updatedChallenge') leave-challenge-modal(:challengeId='challenge._id') close-challenge-modal(:members='members', :challengeId='challenge._id') - challenge-member-progress-modal(:memberId='progressMemberId', :challengeId='challenge._id') + challenge-member-progress-modal(:challengeId='challenge._id') .col-12.col-md-8.standard-page .row .col-12.col-md-6 @@ -231,7 +231,6 @@ export default { creatingTask: {}, workingTask: {}, taskFormPurpose: 'create', - progressMemberId: '', searchTerm: '', memberResults: [], }; @@ -345,6 +344,7 @@ export default { this.tasksByType[task.type].splice(index, 1); }, showMemberModal () { + // @TODO: Change these to options and add a custom event to members modal this.$store.state.memberModalOptions.challengeId = this.challenge._id; this.$store.state.memberModalOptions.groupId = 'challenge'; // @TODO: change these terrible settings this.$store.state.memberModalOptions.group = this.group; @@ -374,8 +374,9 @@ export default { Object.assign(this.challenge, eventData.challenge); }, openMemberProgressModal (member) { - this.progressMemberId = member._id; - this.$root.$emit('bv::show::modal', 'challenge-member-modal'); + this.$root.$emit('habitica:challenge:member-progress', { + progressMemberId: member._id, + }); }, async exportChallengeCsv () { // let response = await this.$store.dispatch('challenges:exportChallengeCsv', { diff --git a/website/client/components/challenges/challengeMemberProgressModal.vue b/website/client/components/challenges/challengeMemberProgressModal.vue index dca795c473..e446c4f4db 100644 --- a/website/client/components/challenges/challengeMemberProgressModal.vue +++ b/website/client/components/challenges/challengeMemberProgressModal.vue @@ -1,5 +1,8 @@ + + diff --git a/website/client/components/chat/reportFlagModal.vue b/website/client/components/chat/reportFlagModal.vue index 70aefdb40d..5cfc61c0a6 100644 --- a/website/client/components/chat/reportFlagModal.vue +++ b/website/client/components/chat/reportFlagModal.vue @@ -100,6 +100,7 @@ export default { if (!data.message || !data.groupId) return; this.abuseObject = data.message; this.groupId = data.groupId; + this.reportComment = ''; this.$root.$emit('bv::show::modal', 'report-flag'); }); }, diff --git a/website/client/components/groups/chat.vue b/website/client/components/groups/chat.vue index 2ba54afb4e..58e16cac0e 100644 --- a/website/client/components/groups/chat.vue +++ b/website/client/components/groups/chat.vue @@ -9,8 +9,10 @@ :class='{"user-entry": newMessage}', @keydown='updateCarretPosition', @keyup.ctrl.enter='sendMessageShortcut()', - @paste='disableMessageSendShortcut()' + @paste='disableMessageSendShortcut()', + maxlength='3000' ) + span {{ currentLength }} / 3000 autocomplete( :text='newMessage', v-on:select="selectedAutocomplete", @@ -62,6 +64,11 @@ }, }; }, + computed: { + currentLength () { + return this.newMessage.length; + }, + }, methods: { // https://medium.com/@_jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a getCoord (e, text) { diff --git a/website/client/components/groups/membersModal.vue b/website/client/components/groups/membersModal.vue index c9ca4ca013..1ce8bc98af 100644 --- a/website/client/components/groups/membersModal.vue +++ b/website/client/components/groups/membersModal.vue @@ -53,6 +53,9 @@ div span.dropdown-icon-item .svg-icon.inline(v-html="icons.removeIcon") span.text {{$t('removeManager2')}} + b-dropdown-item(@click='viewProgress(member)') + span.dropdown-icon-item + span.text {{ $t('viewProgress') }} .row(v-if='isLoadMoreAvailable') .col-12.text-center button.btn.btn-secondary(@click='loadMoreMembers()') {{ $t('loadMore') }} @@ -475,6 +478,11 @@ export default { groupData.leader = member; this.$root.$emit('updatedGroup', groupData); }, + viewProgress (member) { + this.$root.$emit('habitica:challenge:member-progress', { + progressMemberId: member._id, + }); + }, }, }; diff --git a/website/client/components/memberDetails.vue b/website/client/components/memberDetails.vue index 478b145f92..7ddf3de069 100644 --- a/website/client/components/memberDetails.vue +++ b/website/client/components/memberDetails.vue @@ -48,7 +48,6 @@ .member-stats { padding-left: 12px; padding-right: 24px; - margin-right: 1px; opacity: 1; transition: width 0.15s ease-out; } @@ -148,6 +147,7 @@ right: 100%; height: calc(100% + 18px); margin-top: -9px; + margin-right: 1px; padding-top: 9px; padding-bottom: 24px; padding-right: 16px; diff --git a/website/client/components/snackbars/notification.vue b/website/client/components/snackbars/notification.vue index 256806f6dc..648387cdb1 100644 --- a/website/client/components/snackbars/notification.vue +++ b/website/client/components/snackbars/notification.vue @@ -50,6 +50,10 @@ transition(name="fade") .error { background-color: #f74e52; + border-radius: 60px; + width: 320px !important; + padding: 10px 5px; + margin-left: 0; color: #fff; } diff --git a/website/client/components/static/faq.vue b/website/client/components/static/faq.vue index a26e1260cd..e2ce08aa0c 100644 --- a/website/client/components/static/faq.vue +++ b/website/client/components/static/faq.vue @@ -1,18 +1,36 @@