From 78ad1cd8b0b6f7ddf3e7f3b92dfb79b8e14d7f22 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Fri, 2 Feb 2018 11:18:25 -0700 Subject: [PATCH] Added cache for user styles on chat (#9679) * Added cache for user styles on chat * Added loading on new message and other minor checks * Added null checks * Updated chat tests * Added costume preference to chat * Removed single profile cacheing for new chat messages * Remove owned gear from cache * Updated stats to only use buffs --- .../integration/chat/DELETE-chat_id.test.js | 8 ++-- .../api/v3/integration/chat/POST-chat.test.js | 24 ++++++++++ .../client/components/chat/chatMessages.vue | 28 ++++++++---- website/server/models/group.js | 45 +++++++++++++++++++ 4 files changed, 92 insertions(+), 13 deletions(-) diff --git a/test/api/v3/integration/chat/DELETE-chat_id.test.js b/test/api/v3/integration/chat/DELETE-chat_id.test.js index 5d1f73f867..7880d63436 100644 --- a/test/api/v3/integration/chat/DELETE-chat_id.test.js +++ b/test/api/v3/integration/chat/DELETE-chat_id.test.js @@ -71,11 +71,9 @@ describe('DELETE /groups/:groupId/chat/:chatId', () => { }); it('returns the update chat when previous message parameter is passed and the chat is updated', async () => { - await expect(user.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}?previousMsg=${message.id}`)) - .eventually - .is.an('array') - .to.include(message) - .to.be.lengthOf(1); + let deleteResult = await user.del(`/groups/${groupWithChat._id}/chat/${nextMessage.id}?previousMsg=${message.id}`); + + expect(deleteResult[0].id).to.eql(message.id); }); }); }); diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index fd9569bc8c..04afdde26b 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -364,6 +364,30 @@ describe('POST /chat', () => { expect(message.message.id).to.exist; }); + it('creates a chat with user styles', async () => { + const mount = 'test-mount'; + const pet = 'test-pet'; + const style = 'test-style'; + const userWithStyle = await generateUser({ + 'items.currentMount': mount, + 'items.currentPet': pet, + 'preferences.style': style, + }); + await userWithStyle.sync(); + + const message = await userWithStyle.post(`/groups/${groupWithChat._id}/chat`, { message: testMessage}); + + expect(message.message.id).to.exist; + expect(message.message.userStyles.items.currentMount).to.eql(userWithStyle.items.currentMount); + expect(message.message.userStyles.items.currentPet).to.eql(userWithStyle.items.currentPet); + expect(message.message.userStyles.preferences.style).to.eql(userWithStyle.preferences.style); + expect(message.message.userStyles.preferences.hair).to.eql(userWithStyle.preferences.hair); + expect(message.message.userStyles.preferences.skin).to.eql(userWithStyle.preferences.skin); + expect(message.message.userStyles.preferences.shirt).to.eql(userWithStyle.preferences.shirt); + expect(message.message.userStyles.preferences.chair).to.eql(userWithStyle.preferences.chair); + expect(message.message.userStyles.preferences.background).to.eql(userWithStyle.preferences.background); + }); + it('adds backer info to chat', async () => { const backerInfo = { npc: 'Town Crier', diff --git a/website/client/components/chat/chatMessages.vue b/website/client/components/chat/chatMessages.vue index ddd2480197..0b9494755c 100644 --- a/website/client/components/chat/chatMessages.vue +++ b/website/client/components/chat/chatMessages.vue @@ -11,8 +11,8 @@ .row(v-if='user._id !== msg.uuid') div(:class='inbox ? "col-4" : "col-2"') avatar( - v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected', - :member="cachedProfileData[msg.uuid]", + v-if='msg.userStyles || (cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected)', + :member="msg.userStyles || cachedProfileData[msg.uuid]", :avatarOnly="true", :hideClassBadge='true', @click.native="showMemberModal(msg.uuid)", @@ -36,8 +36,8 @@ @show-member-modal='showMemberModal') div(:class='inbox ? "col-4" : "col-2"') avatar( - v-if='cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected', - :member="cachedProfileData[msg.uuid]", + v-if='msg.userStyles || (cachedProfileData[msg.uuid] && !cachedProfileData[msg.uuid].rejected)', + :member="msg.userStyles || cachedProfileData[msg.uuid]", :avatarOnly="true", :hideClassBadge='true', @click.native="showMemberModal(msg.uuid)", @@ -117,12 +117,12 @@ export default { // @TODO: We need a different lazy load mechnism. // But honestly, adding a paging route to chat would solve this messages () { + this.loadProfileCache(); return this.chat; }, }, watch: { - messages (oldValue, newValue) { - if (newValue.length === oldValue.length) return; + messages () { this.loadProfileCache(); }, }, @@ -139,18 +139,28 @@ export default { this._loadProfileCache(screenPosition); }, 1000), async _loadProfileCache (screenPosition) { + if (this.loading) return; + this.loading = true; + let promises = []; + const noProfilesLoaded = Object.keys(this.cachedProfileData).length === 0; // @TODO: write an explination - if (screenPosition && Math.floor(screenPosition) + 1 > this.currentProfileLoadedEnd / 10) { + // @TODO: Remove this after enough messages are cached + if (!noProfilesLoaded && screenPosition && Math.floor(screenPosition) + 1 > this.currentProfileLoadedEnd / 10) { this.currentProfileLoadedEnd = 10 * (Math.floor(screenPosition) + 1); - } else if (screenPosition) { + } else if (!noProfilesLoaded && screenPosition) { return; } let aboutToCache = {}; this.messages.forEach(message => { let uuid = message.uuid; + + if (message.userStyles) { + this.$set(this.cachedProfileData, uuid, message.userStyles); + } + if (Boolean(uuid) && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) { if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return; aboutToCache[uuid] = {}; @@ -176,6 +186,8 @@ export default { this.$set(this.cachedProfileData, uuid, {rejected: true}); } } + + this.loading = false; }, displayDivider (message) { if (this.currentDayDividerDisplay !== moment(message.timestamp).day()) { diff --git a/website/server/models/group.js b/website/server/models/group.js index a01ab512b6..177163ae84 100644 --- a/website/server/models/group.js +++ b/website/server/models/group.js @@ -466,9 +466,54 @@ export function chatDefaults (msg, user) { return message; } +function setUserStyles (newMessage, user) { + let userStyles = {}; + userStyles.items = {gear: {}}; + + let userCopy = user; + if (user.toObject) userCopy = user.toObject(); + + if (userCopy.items) { + userStyles.items.gear = {}; + userStyles.items.gear.costume = Object.assign({}, userCopy.items.gear.costume); + userStyles.items.gear.equipped = Object.assign({}, userCopy.items.gear.equipped); + + userStyles.items.currentMount = userCopy.items.currentMount; + userStyles.items.currentPet = userCopy.items.currentPet; + } + + + if (userCopy.preferences) { + userStyles.preferences = {}; + if (userCopy.preferences.style) userStyles.preferences.style = userCopy.preferences.style; + userStyles.preferences.hair = userCopy.preferences.hair; + userStyles.preferences.skin = userCopy.preferences.skin; + userStyles.preferences.shirt = userCopy.preferences.shirt; + userStyles.preferences.chair = userCopy.preferences.chair; + userStyles.preferences.size = userCopy.preferences.size; + userStyles.preferences.chair = userCopy.preferences.chair; + userStyles.preferences.background = userCopy.preferences.background; + userStyles.preferences.costume = userCopy.preferences.costume; + } + + userStyles.stats = {}; + if (userCopy.stats && userCopy.stats.buffs) { + userStyles.stats.buffs = { + seafoam: userCopy.stats.buffs.seafoam, + shinySeed: userCopy.stats.buffs.shinySeed, + spookySparkles: userCopy.stats.buffs.spookySparkles, + snowball: userCopy.stats.buffs.snowball, + }; + } + + newMessage.userStyles = userStyles; +} + schema.methods.sendChat = function sendChat (message, user, metaData) { let newMessage = chatDefaults(message, user); + if (user) setUserStyles(newMessage, user); + // Optional data stored in the chat message but not returned // to the users that can be stored for debugging purposes if (metaData) {