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 @@
b-modal#challenge-member-modal(title="User Progress", size='lg')
+ .row.award-row
+ .col-12.text-center
+ button.btn.btn-primary(v-once, @click='closeChallenge()') {{ $t('awardWinners') }}
.row
task-column.col-6(
v-for="column in columns",
@@ -8,12 +11,19 @@
:taskListOverride='tasksByType[column]')
+
+
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 @@
- .container-fluid
+ .container-fluid(role="tablist")
.row
.col-12.col-md-6.offset-md-3
- h1 {{ $t('frequentlyAskedQuestions') }}
- .faq-question(v-for='(heading, index) in headings')
- h2.accordion(@click='setActivePage(heading)') {{ $t(`faqQuestion${index}`) }}
- div(v-if='pageState[heading]', v-markdown="$t('webFaqAnswer' + index, replacements)")
+ h1#faq-heading {{ $t('frequentlyAskedQuestions') }}
+ .faq-question(v-for='(heading, index) in headings', :key="index")
+ h2(role="tab", v-b-toggle="heading", @click="handleClick($event)", variant="info") {{ $t(`faqQuestion${index}`) }}
+ b-collapse(:id="heading", :visible="isVisible(heading)", accordion="faq", role="tabpanel")
+ div.card-body(v-markdown="$t('webFaqAnswer' + index, replacements)")
hr
p(v-markdown="$t('webFaqStillNeedHelp')")