From c2aaa9b5928492885c589923591c00e8c41cce75 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Fri, 1 Sep 2017 15:26:10 -0600 Subject: [PATCH] Fixes sept 1 (#9016) * Added avatars to inbox * Added ordering of inbox messages * Fixed blurb not converting to string * Added message to member modal * Added quest invites * Moved filters to server --- .../client/components/chat/chatMessages.vue | 13 ++-- .../client/components/groups/discovery.vue | 73 +++++++++++++++++-- website/client/components/groups/group.vue | 35 ++++++--- .../client/components/groups/membersModal.vue | 9 ++- website/client/components/groups/sidebar.vue | 4 +- website/client/components/groups/tavern.vue | 1 + .../client/components/notificationMenu.vue | 14 ++++ website/client/components/userMenu/inbox.vue | 36 ++++++--- .../client/components/userMenu/profile.vue | 3 + website/client/store/actions/guilds.js | 19 +++-- website/server/controllers/api-v3/groups.js | 13 ++++ 11 files changed, 180 insertions(+), 40 deletions(-) diff --git a/website/client/components/chat/chatMessages.vue b/website/client/components/chat/chatMessages.vue index 7f3d8fd812..e4b915ccf6 100644 --- a/website/client/components/chat/chatMessages.vue +++ b/website/client/components/chat/chatMessages.vue @@ -4,15 +4,13 @@ .col-12 copy-as-todo-modal(:copying-message='copyingMessage', :group-name='groupName', :group-id='groupId') report-flag-modal - .row - .hr.col-12 div(v-for="(msg, index) in chat", v-if='chat && (inbox || Object.keys(cachedProfileData).length > 0)') // @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') - .col-2 + .col-4 avatar(v-if='cachedProfileData[msg.uuid]', :member="cachedProfileData[msg.uuid]", :avatarOnly="true", :hideClassBadge='true') @@ -41,7 +39,7 @@ .svg-icon(v-html="icons.liked") | + {{ likeCount(msg) }} .row(v-if='user._id === msg.uuid') - .card.col-8.offset-2 + .card.col-8 .card-block h3.leader(:class='userLevelStyle(cachedProfileData[msg.uuid])') | {{msg.user}} @@ -65,7 +63,7 @@ span.action.float-right(v-if='likeCount(msg) > 0') .svg-icon(v-html="icons.liked") | + {{ likeCount(msg) }} - .col-2 + .col-4 avatar(v-if='cachedProfileData[msg.uuid]', :member="cachedProfileData[msg.uuid]", :avatarOnly="true", :hideClassBadge='true') @@ -288,10 +286,13 @@ 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]) { + if (uuid && !this.cachedProfileData[uuid] && !aboutToCache[uuid]) { if (uuid === 'system' || this.currentProfileLoadedCount === this.currentProfileLoadedEnd) return; + aboutToCache[uuid] = {}; promises.push(axios.get(`/api/v3/members/${uuid}`)); this.currentProfileLoadedCount += 1; } diff --git a/website/client/components/groups/discovery.vue b/website/client/components/groups/discovery.vue index 5d44621c55..21b7df31e7 100644 --- a/website/client/components/groups/discovery.vue +++ b/website/client/components/groups/discovery.vue @@ -82,6 +82,13 @@ export default { }, ], guilds: [], + queryFilters: { + minMemberCount: 0, + maxMemberCount: 0, + leader: false, + member: false, + categories: '', + }, }; }, created () { @@ -100,18 +107,74 @@ export default { }, }, methods: { - updateSearch (eventData) { - this.search = eventData.searchTerm; + async updateSearch (eventData) { + // this.search = eventData.searchTerm; @TODO: Probably don't need this anymore + + // Reset the page when filters are updated but not the other queries + this.lastPageLoaded = 0; + this.queryFilters.page = this.lastPageLoaded; + + this.queryFilters.search = eventData.searchTerm; + + let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters); + this.guilds = guilds; }, - updateFilters (eventData) { - this.filters = eventData; + async updateFilters (eventData) { + // this.filters = eventData; @TODO: Probably don't need this anymore + + // Reset all filters + this.queryFilters = { + minMemberCount: 0, + maxMemberCount: 0, + leader: false, + member: false, + categories: '', + }; + + // Reset the page when filters are updated + this.lastPageLoaded = 0; + this.queryFilters.page = this.lastPageLoaded; + + this.queryFilters.categories = eventData.categories.join(','); + + // Role filters + let filteringRole = eventData.roles && eventData.roles.length > 0; + if (filteringRole && eventData.roles.indexOf('member') !== -1) { + this.queryFilters.member = true; + } + + if (filteringRole && eventData.roles.indexOf('guild_leader') !== -1) { + this.queryFilters.leader = true; + } + + + // Size filters + if (eventData.guildSize && eventData.guildSize.indexOf('gold_tier') !== -1) { + this.queryFilters.minMemberCount = 1000; + this.queryFilters.maxMemberCount = 0; // No max + } + + if (eventData.guildSize && eventData.guildSize.indexOf('silver_tier') !== -1) { + this.queryFilters.minMemberCount = 100; + this.queryFilters.maxMemberCount = 999; + } + + if (eventData.guildSize && eventData.guildSize.indexOf('bronze_tier') !== -1) { + this.queryFilters.minMemberCount = 0; // No Min + this.queryFilters.maxMemberCount = 99; + } + + let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters); + this.guilds = guilds; }, async fetchGuilds () { // We have the data cached if (this.lastPageLoaded === 0 && this.guilds.length > 0) return; this.loading = true; - let guilds = await this.$store.dispatch('guilds:getPublicGuilds', {page: this.lastPageLoaded}); + + this.queryFilters.page = this.lastPageLoaded; + let guilds = await this.$store.dispatch('guilds:getPublicGuilds', this.queryFilters); if (guilds.length === 0) this.hasLoadedAllGuilds = true; this.guilds.push(...guilds); diff --git a/website/client/components/groups/group.vue b/website/client/components/groups/group.vue index c1c1b0ec69..1498850597 100644 --- a/website/client/components/groups/group.vue +++ b/website/client/components/groups/group.vue @@ -27,12 +27,15 @@ .col-12 h3(v-once) {{ $t('chat') }} - textarea(:placeholder="!isParty ? $t('chatPlaceholder') : $t('partyChatPlaceholder')", v-model='newMessage', @keydown='updateCarretPosition') - autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat') - button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }} - button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }} - .col-12 - chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name') + .row.new-message-row + textarea(:placeholder="!isParty ? $t('chatPlaceholder') : $t('partyChatPlaceholder')", v-model='newMessage', @keydown='updateCarretPosition') + autocomplete(:text='newMessage', v-on:select="selectedAutocomplete", :coords='coords', :chat='group.chat') + button.btn.btn-secondary.send-chat.float-right(v-once, @click='sendMessage()') {{ $t('send') }} + button.btn.btn-secondary.float-left(v-once, @click='fetchRecentMessages()') {{ $t('fetchRecentMessages') }} + + .row + .col-12.hr + chat-message(:chat.sync='group.chat', :group-id='group._id', group-name='group.name') .col-4.sidebar .row(:class='{"guild-background": !isParty}') @@ -256,11 +259,15 @@ .chat-row { margin-top: 2em; - .send-chat { - margin-top: -3.5em; - z-index: 10; + .new-message-row { position: relative; - margin-right: 1em; + } + + .send-chat { + z-index: 10; + position: absolute; + right: 1em; + bottom: 3em; } } @@ -370,6 +377,14 @@ height: 15px; } + + .hr { + width: 100%; + height: 20px; + border-bottom: 1px solid $gray-500; + text-align: center; + margin: 2em 0; + } diff --git a/website/client/components/userMenu/inbox.vue b/website/client/components/userMenu/inbox.vue index 9954e0ae91..d338560d0a 100644 --- a/website/client/components/userMenu/inbox.vue +++ b/website/client/components/userMenu/inbox.vue @@ -30,6 +30,10 @@ span.timeago {{conversation.date | timeAgo}} div {{conversation.lastMessageText.substring(0, 30)}} .col-8.messages + .empty-messages.text-center(v-if='activeChat.length === 0') + .svg-icon.envelope(v-html="icons.messageIcon") + h4(v-once) Nothing Here Yet + p(v-once) Select a conversation on the left chat-message.container-fluid.message-scroll(:chat.sync='activeChat', :inbox='true', ref="chatscroll") // @TODO: Implement new message header here when we fix the above @@ -142,7 +146,7 @@ import Vue from 'vue'; import moment from 'moment'; import filter from 'lodash/filter'; -// import sortBy from 'lodash/sortBy'; +import sortBy from 'lodash/sortBy'; import { mapState } from 'client/libs/store'; import styleHelper from 'client/mixins/styleHelper'; @@ -184,11 +188,6 @@ export default { let message = this.user.inbox.messages[messageId]; let userId = message.uuid; - if (!this.selectedConversation) { - this.selectedConversation = userId; - this.selectConversation(userId); - } - if (!conversations[userId]) { conversations[userId] = { name: message.user, @@ -197,10 +196,19 @@ export default { }; } - conversations[userId].messages.push({ + let newMessage = { text: message.text, timestamp: message.timestamp, - }); + user: message.user, + uuid: message.uuid, + }; + + if (message.sent) { + newMessage.user = this.user.profile.name; + newMessage.uuid = this.user._id; + } + + conversations[userId].messages.push(newMessage); conversations[userId].lastMessageText = message.text; conversations[userId].date = message.timestamp; } @@ -225,11 +233,13 @@ export default { selectConversation (key) { this.selectedConversation = key; let activeChat = this.conversations[this.selectedConversation].messages; - // @TODO: I think I did this wrong - // activeChat = sortBy(this.activeChat, [(o) => { - // return o.timestamp; - // }]); + + activeChat = sortBy(activeChat, [(o) => { + return moment(o.timestamp).toDate(); + }]); + this.$set(this, 'activeChat', activeChat); + Vue.nextTick(() => { let chatscroll = this.$refs.chatscroll.$el; chatscroll.scrollTop = chatscroll.scrollHeight; @@ -244,6 +254,8 @@ export default { this.conversations[this.selectedConversation].messages.push({ text: this.newMessage, timestamp: new Date(), + user: this.user.profile.name, + uuid: this.user._id, }); this.activeChat = this.conversations[this.selectedConversation].messages; diff --git a/website/client/components/userMenu/profile.vue b/website/client/components/userMenu/profile.vue index 43d614b63a..7a1f6f42d6 100644 --- a/website/client/components/userMenu/profile.vue +++ b/website/client/components/userMenu/profile.vue @@ -411,6 +411,9 @@ export default { // @TODO: this common code should handle the above this.achievements = achievementsLib.getAchievementsForProfile(user); + // @TODO For some reason markdown doesn't seem to be handling numbers or maybe undefined? + user.profile.blurb = `${user.profile.blurb}`; + return user; }, incentivesProgress () { diff --git a/website/client/store/actions/guilds.js b/website/client/store/actions/guilds.js index 89532f0fdd..9ca680cc18 100644 --- a/website/client/store/actions/guilds.js +++ b/website/client/store/actions/guilds.js @@ -3,12 +3,21 @@ import omit from 'lodash/omit'; import findIndex from 'lodash/findIndex'; export async function getPublicGuilds (store, payload) { + let params = { + type: 'publicGuilds', + paginate: true, + page: payload.page, + }; + + if (payload.categories) params.categories = payload.categories; + if (payload.minMemberCount) params.minMemberCount = payload.minMemberCount; + if (payload.maxMemberCount) params.maxMemberCount = payload.maxMemberCount; + if (payload.leader) params.leader = payload.leader; + if (payload.member) params.member = payload.member; + if (payload.search) params.search = payload.search; + let response = await axios.get('/api/v3/groups', { - params: { - type: 'publicGuilds', - paginate: true, - page: payload.page, - }, + params, }); return response.data.data; diff --git a/website/server/controllers/api-v3/groups.js b/website/server/controllers/api-v3/groups.js index c15e6c0288..fddb784e99 100644 --- a/website/server/controllers/api-v3/groups.js +++ b/website/server/controllers/api-v3/groups.js @@ -326,6 +326,19 @@ api.getGroups = { filters.memberCount.$lte = parseInt(req.query.maxMemberCount, 10); } + // @TODO: Tests for below? + if (req.query.leader) { + filters.leader = user._id; + } + + if (req.query.member) { + filters._id = { $in: user.guilds }; + } + + if (req.query.search) { + filters.$text = { $search: req.query.search }; + } + let results = await Group.getGroups({ user, types, groupFields, sort, paginate, page: req.query.page, filters,