Merge branch 'develop' into release

This commit is contained in:
Sabe Jones 2017-09-29 22:08:11 +00:00
commit 569fb11db8
8 changed files with 70 additions and 29 deletions

View file

@ -138,12 +138,21 @@ export default {
// @TODO split up this file, it's too big
// Set up Error interceptors
axios.interceptors.response.use((response) => {
if (this.user) {
if (this.user && response.data && response.data.notifications) {
this.$set(this.user, 'notifications', response.data.notifications);
}
return response;
}, (error) => {
if (error.response.status >= 400) {
// Don't show errors from getting user details. These users have delete their account,
// but their chat message still exists.
let configExists = Boolean(error.response) && Boolean(error.response.config);
if (configExists && error.response.config.method === 'get' && error.response.config.url.indexOf('/api/v3/members/') !== -1) {
// @TODO: We resolve the promise because we need our caching to cache this user as tried
// Chat paging should help this, but maybe we can also find another solution..
return Promise.resolve(error);
}
this.$store.state.notificationStore.push({
title: 'Habitica',
text: error.response.data.message,

View file

@ -55,7 +55,7 @@
li
a(href='/apidoc', target='_blank') {{ $t('APIv3') }}
li
a(href='http://data.habitrpg.com/?uuid=', target='_blank') {{ $t('dataDisplayTool') }}
a(href='https://oldgods.net/habitrpg/habitrpg_user_data_display.html', target='_blank') {{ $t('dataDisplayTool') }}
li
a(href='http://habitica.wikia.com/wiki/Guidance_for_Blacksmiths', target='_blank') {{ $t('guidanceForBlacksmiths') }}
li

View file

@ -5,14 +5,14 @@
copy-as-todo-modal(:copying-message='copyingMessage', :group-name='groupName', :group-id='groupId')
report-flag-modal
div(v-for="(msg, index) in chat", v-if='chat && canViewFlag(msg)')
div(v-for="(msg, index) in messages", v-if='chat && canViewFlag(msg)')
// @TODO: is there a different way to do these conditionals? This creates an infinite loop
//.hr(v-if='displayDivider(msg)')
.hr-middle(v-once) {{ msg.timestamp }}
.row(v-if='user._id !== msg.uuid')
div(:class='inbox ? "col-4" : "col-2"')
avatar(
v-if='cachedProfileData[msg.uuid]',
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
:member="cachedProfileData[msg.uuid]",
:avatarOnly="true",
:hideClassBadge='true',
@ -36,13 +36,15 @@
.svg-icon(v-html="icons.like")
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
span.action( @click='copyAsTodo(msg)')
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
.svg-icon(v-html="icons.copy")
| {{$t('copyAsTodo')}}
span.action(v-if='user.contributor.admin || (msg.uuid !== user._id && user.flags.communityGuidelinesAccepted)', @click='report(msg)')
// @TODO make copyAsTodo work in the inbox
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
.svg-icon(v-html="icons.report")
| {{$t('report')}}
span.action(v-if='msg.uuid === user._id || inbox', @click='remove(msg, index)')
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
span.action(v-if='msg.uuid === user._id || inbox || user.contributor.admin', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
span.action.float-right.liked(v-if='likeCount(msg) > 0')
@ -70,12 +72,15 @@
.svg-icon(v-html="icons.like")
span(v-if='!msg.likes[user._id]') {{ $t('like') }}
span(v-if='msg.likes[user._id]') {{ $t('liked') }}
span.action( @click='copyAsTodo(msg)')
span.action(v-if='!inbox', @click='copyAsTodo(msg)')
.svg-icon(v-html="icons.copy")
| {{$t('copyAsTodo')}}
// @TODO make copyAsTodo work in the inbox
span.action(v-if='user.flags.communityGuidelinesAccepted', @click='report(msg)')
span.action(v-if='!inbox && user.flags.communityGuidelinesAccepted', @click='report(msg)')
.svg-icon(v-html="icons.report")
| {{$t('report')}}
// @TODO make flagging/reporting work in the inbox. NOTE: it must work even if the communityGuidelines are not accepted and it MUST work for messages that you have SENT as well as received. -- Alys
span.action(v-if='msg.uuid === user._id', @click='remove(msg, index)')
.svg-icon(v-html="icons.delete")
| {{$t('delete')}}
@ -84,7 +89,7 @@
| + {{ likeCount(msg) }}
div(:class='inbox ? "col-4" : "col-2"')
avatar(
v-if='cachedProfileData[msg.uuid]',
v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected',
:member="cachedProfileData[msg.uuid]",
:avatarOnly="true",
:hideClassBadge='true',
@ -238,7 +243,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 debounce from 'lodash/debounce';
import markdownDirective from 'client/directives/markdown';
import Avatar from '../avatar';
import styleHelper from 'client/mixins/styleHelper';
@ -277,12 +282,10 @@ export default {
this.loadProfileCache();
},
created () {
window.addEventListener('scroll', throttle(() => {
this.loadProfileCache(window.scrollY / 1000);
}, 1000));
window.addEventListener('scroll', this.handleScroll);
},
destroyed () {
// window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('scroll', this.handleScroll);
},
data () {
return {
@ -308,6 +311,7 @@ export default {
cachedProfileData: {},
currentProfileLoadedCount: 0,
currentProfileLoadedEnd: 10,
loading: false,
};
},
filters: {
@ -321,17 +325,22 @@ export default {
},
computed: {
...mapState({user: 'user.data'}),
// @TODO: We need a different lazy load mechnism.
// But honestly, adding a paging route to chat would solve this
messages () {
return this.chat;
},
},
watch: {
messages () {
// @TODO: MAybe we should watch insert and remove?
messages (oldValue, newValue) {
if (newValue.length === oldValue.length) return;
this.loadProfileCache();
},
},
methods: {
handleScroll () {
this.loadProfileCache(window.scrollY / 1000);
},
isUserMentioned (message) {
let user = this.user;
@ -358,7 +367,10 @@ export default {
if (!message.flagCount || message.flagCount < 2) return true;
return this.user.contributor.admin;
},
async loadProfileCache (screenPosition) {
loadProfileCache: debounce(function loadProfileCache (screenPosition) {
this._loadProfileCache(screenPosition);
}, 1000),
async _loadProfileCache (screenPosition) {
let promises = [];
// @TODO: write an explination
@ -368,11 +380,10 @@ export default {
return;
}
// @TODO: Not sure we need this hash
let aboutToCache = {};
this.messages.forEach(message => {
let uuid = message.uuid;
if (uuid && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
if (Boolean(uuid) && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) {
if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return;
aboutToCache[uuid] = {};
promises.push(axios.get(`/api/v3/members/${uuid}`));
@ -382,9 +393,21 @@ export default {
let results = await Promise.all(promises);
results.forEach(result => {
// We could not load the user. Maybe they were deleted. So, let's cache empty so we don't try again
if (!result || !result.data || result.status >= 400) {
return;
}
let userData = result.data.data;
this.$set(this.cachedProfileData, userData._id, userData);
});
// Merge in any attempts that were rejected so we don't attempt again
for (let uuid in aboutToCache) {
if (!this.cachedProfileData[uuid]) {
this.$set(this.cachedProfileData, uuid, {rejected: true});
}
}
},
displayDivider (message) {
if (this.currentDayDividerDisplay !== moment(message.timestamp).day()) {

View file

@ -431,6 +431,7 @@
</style>
<script>
import debounce from 'lodash/debounce';
import extend from 'lodash/extend';
import groupUtilities from 'client/mixins/groupsUtilities';
import styleHelper from 'client/mixins/styleHelper';
@ -655,7 +656,10 @@ export default {
};
document.body.removeChild(div);
},
updateCarretPosition (eventUpdate) {
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
this._updateCarretPosition(eventUpdate);
}, 250),
_updateCarretPosition (eventUpdate) {
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
this.sendMessage();
return;

View file

@ -350,6 +350,7 @@
</style>
<script>
import debounce from 'lodash/debounce';
import { mapState } from 'client/libs/store';
import { TAVERN_ID } from '../../../common/script/constants';
@ -527,7 +528,10 @@ export default {
};
document.body.removeChild(div);
},
updateCarretPosition (eventUpdate) {
updateCarretPosition: debounce(function updateCarretPosition (eventUpdate) {
this._updateCarretPosition(eventUpdate);
}, 250),
_updateCarretPosition (eventUpdate) {
if (eventUpdate.metaKey && eventUpdate.keyCode === 13) {
this.sendMessage();
return;

View file

@ -36,9 +36,9 @@
h6(v-once) {{ $t('class') + ': ' }}
// @TODO: what is classText
span(v-if='classText') {{ classText }}&nbsp;
button.btn.btn-danger.btn-xs(@click='changeClass(null)', v-once) {{ $t('changeClass') }}
small.cost 3
span.Pet_Currency_Gem1x.inline-gems
button.btn.btn-danger.btn-xs(@click='changeClassForUser(true)', v-once) {{ $t('changeClass') }}
small.cost &nbsp; 3 {{ $t('gems') }}
// @TODO add icon span.Pet_Currency_Gem1x.inline-gems
hr
div
@ -82,7 +82,7 @@
button.btn.btn-primary(@click='showBailey()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('showBaileyPop')") {{ $t('showBailey') }}
button.btn.btn-primary(@click='openRestoreModal()', popover-trigger='mouseenter', popover-placement='right', :popover="$t('fixValPop')") {{ $t('fixVal') }}
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClass({})',
button.btn.btn-primary(v-if='user.preferences.disableClasses == true', @click='changeClassForUser(false)',
popover-trigger='mouseenter', popover-placement='right', :popover="$t('enableClassPop')") {{ $t('enableClass') }}
hr
@ -378,8 +378,8 @@ export default {
this.$router.go('/tasks');
},
async changeClass () {
if (!confirm('Are you sure you want to change your class for 3 gems?')) return;
async changeClassForUser (confirmationNeeded) {
if (confirmationNeeded && !confirm(this.$t('changeClassConfirmCost'))) return;
try {
changeClass(this.user);
await axios.post('/api/v3/user/change-class');

View file

@ -5,7 +5,7 @@ div
.profile-actions
button.btn.btn-secondary(@click='sendMessage()')
.svg-icon.message-icon(v-html="icons.message")
button.btn.btn-secondary(v-if='userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
button.btn.btn-secondary(v-if='user._id !== this.userLoggedIn._id && userLoggedIn.inbox.blocks.indexOf(user._id) === -1', :tooltip="$t('unblock')",
@click="blockUser()", tooltip-placement='right')
span.glyphicon.glyphicon-plus
| {{$t('block')}}

View file

@ -125,6 +125,7 @@
"mystery": "Mystery",
"changeClass": "Change Class, Refund Attribute Points",
"lvl10ChangeClass": "To change class you must be at least level 10.",
"changeClassConfirmCost": "Are you sure you want to change your class for 3 Gems?",
"invalidClass":"Invalid class. Please specify 'warrior', 'rogue', 'wizard', or 'healer'.",
"levelPopover": "Each level earns you one point to assign to an attribute of your choice. You can do so manually, or let the game decide for you using one of the Automatic Allocation options.",
"unallocated": "Unallocated Attribute Points",
@ -158,7 +159,7 @@
"respawn": "Respawn!",
"youDied": "You Died!",
"dieText": "You've lost a Level, all your Gold, and a random piece of Equipment. Arise, Habiteer, and try again! Curb those negative Habits, be vigilant in completion of Dailies, and hold death at arm's length with a Health Potion if you falter!",
"sureReset": "Are you sure? This will reset your character's class and allocated points (you'll get them all back to re-allocate), and costs 3 gems.",
"sureReset": "Are you sure? This will reset your character's class and allocated points (you'll get them all back to re-allocate), and costs 3 Gems.",
"purchaseFor": "Purchase for <%= cost %> Gems?",
"notEnoughMana": "Not enough mana.",
"invalidTarget": "You can't cast a skill on that.",