Client: remove unnecessary API calls + members fixes (#12179)

* wip

* refactor world state

* allow resource to be reloaded when the server is updated

* fix #9242

* fix event listeners

* remove un-needed code

* add tests for  asyncResourceFactory reloadOnAppVersionChange

* fix double cron notifications and party members showing up in the header after a party invitation is accepted

* remove console.log

* do not send vm info to loggly due to circular dependency + fix typo

* fix #12181

* do not load invites multiple times in members modal

* add hover to challenge member count

* groups: load members only on demand

* minor ui fixes

* choose class: fix vue duplicate key warning

* minor ui fixes

* challanges: load members on demand

* add loading spinner

* change loading mechanism

* fix loading gryphon issues

* reduce code duplication
This commit is contained in:
Matteo Pagliazzi
2020-05-25 17:02:29 +02:00
committed by GitHub
parent ca80f4ee33
commit 08f1e2b273
50 changed files with 394 additions and 259 deletions
+43 -28
View File
@@ -385,7 +385,7 @@
import extend from 'lodash/extend';
import groupUtilities from '@/mixins/groupsUtilities';
import styleHelper from '@/mixins/styleHelper';
import { mapState } from '@/libs/store';
import { mapState, mapGetters } from '@/libs/store';
import * as Analytics from '@/libs/analytics';
import startQuestModal from './startQuestModal';
import questDetailsModal from './questDetailsModal';
@@ -447,6 +447,7 @@ export default {
bronzeGuildBadgeIcon,
}),
members: [],
membersLoaded: false,
selectedQuest: {},
chat: {
submitDisable: false,
@@ -455,7 +456,12 @@ export default {
};
},
computed: {
...mapState({ user: 'user.data' }),
...mapState({
user: 'user.data',
}),
...mapGetters({
partyMembers: 'party:members',
}),
partyStore () {
return this.$store.state.party;
},
@@ -487,10 +493,15 @@ export default {
}
},
},
mounted () {
async mounted () {
if (this.isParty) this.searchId = 'party';
if (!this.searchId) this.searchId = this.groupId;
this.load();
await this.fetchGuild();
this.$root.$on('updatedGroup', this.onGroupUpdate);
},
beforeDestroy () {
this.$root.$off('updatedGroup', this.onGroupUpdate);
},
beforeRouteUpdate (to, from, next) {
this.$set(this, 'searchId', to.params.groupId);
@@ -501,19 +512,9 @@ export default {
acceptCommunityGuidelines () {
this.$store.dispatch('user:set', { 'flags.communityGuidelinesAccepted': true });
},
async load () {
if (this.isParty) {
this.searchId = 'party';
// @TODO: Set up from old client. Decide what we need and what we don't
// Check Desktop notifs
// Load invites
}
await this.fetchGuild();
this.$root.$on('updatedGroup', group => {
const updatedGroup = extend(this.group, group);
this.$set(this.group, updatedGroup);
});
onGroupUpdate (group) {
const updatedGroup = extend(this.group, group);
this.$set(this.group, updatedGroup);
},
/**
@@ -531,6 +532,26 @@ export default {
return this.$store.dispatch('members:getGroupMembers', payload);
},
showMemberModal () {
this.$store.state.memberModalOptions.loading = true;
if (this.isParty) {
this.membersLoaded = true;
this.members = this.partyMembers;
this.$store.state.memberModalOptions.loading = false;
} else if (!this.membersLoaded) {
this.membersLoaded = true;
this.loadMembers({
groupId: this.group._id,
includeAllPublicFields: true,
}).then(m => {
this.members.push(...m);
this.$store.state.memberModalOptions.loading = false;
});
} else {
this.$store.state.memberModalOptions.loading = false;
}
this.$root.$emit('habitica:show-member-modal', {
groupId: this.group._id,
group: this.group,
@@ -565,19 +586,13 @@ export default {
const groupId = this.searchId === 'party' ? this.user.party._id : this.searchId;
if (this.hasUnreadMessages(groupId)) {
// Delay by 1sec to make sure it returns after
// other requests that don't have the notification marked as read
setTimeout(() => {
this.$store.dispatch('chat:markChatSeen', { groupId });
this.$delete(this.user.newMessages, groupId);
}, 1000);
const notification = this.user
.notifications.find(n => n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId);
const notificationId = notification && notification.id;
this.$store.dispatch('chat:markChatSeen', { groupId, notificationId });
}
this.members = await this.loadMembers({
groupId: this.group._id,
includeAllPublicFields: true,
});
},
// returns the notification id or false
hasUnreadMessages (groupId) {
if (this.user.newMessages[groupId]) return true;
@@ -99,14 +99,21 @@
</div>
</div>
</div>
<div v-if="selectedPage === 'members'">
<loading-gryphon v-if="loading" />
<div
v-if="selectedPage === 'members' && !loading"
:class="{'mt-1': invites.length === 0}"
>
<div
v-for="(member, index) in sortedMembers"
:key="member._id"
class="row"
>
<div class="col-11 no-padding-left">
<member-details :member="member" />
<member-details
:member="member"
:class-badge-position="'next-to-name'"
/>
</div>
<div class="col-1 actions">
<b-dropdown right="right">
@@ -201,7 +208,7 @@
class="row gradient"
></div>
</div>
<div v-if="selectedPage === 'invites'">
<div v-if="selectedPage === 'invites' && !loading">
<div
v-for="(member, index) in invites"
:key="member._id"
@@ -270,6 +277,8 @@
.modal-body {
padding-left: 0;
padding-right: 0;
padding-bottom: 0;
padding-top: 0;
}
.member-details {
@@ -378,6 +387,7 @@ import isEmpty from 'lodash/isEmpty';
import { mapState } from '@/libs/store';
import removeMemberModal from '@/components/members/removeMemberModal';
import loadingGryphon from '@/components/ui/loadingGryphon';
import MemberDetails from '../memberDetails';
import removeIcon from '@/assets/members/remove.svg';
import messageIcon from '@/assets/members/message.svg';
@@ -388,6 +398,7 @@ export default {
components: {
MemberDetails,
removeMemberModal,
loadingGryphon,
},
props: ['hideBadge'],
data () {
@@ -474,6 +485,9 @@ export default {
challengeId () {
return this.$store.state.memberModalOptions.challengeId;
},
loading () {
return this.$store.state.memberModalOptions.loading;
},
sortedMembers () {
let sortedMembers = this.members.slice(); // shallow clone to avoid infinite loop
@@ -504,16 +518,6 @@ export default {
},
},
watch: {
groupId () {
// @TODO: We might not need this since groupId is computed now
this.getMembers();
},
challengeId () {
this.getMembers();
},
group () {
this.getMembers();
},
// Watches `searchTerm` and if present, performs a `searchMembers` action
// and usual `getMembers` otherwise
searchTerm () {
@@ -537,7 +541,7 @@ export default {
this.getMembers();
});
},
destroyed () {
beforeDestroy () {
this.$root.$off('habitica:show-member-modal');
},
methods: {
@@ -558,8 +562,9 @@ export default {
});
},
async getMembers () {
const { groupId } = this;
this.members = this.$store.state.memberModalOptions.viewingMembers;
const { groupId } = this;
if (groupId && groupId !== 'challenge') {
const invites = await this.$store.dispatch('members:getGroupInvites', {
groupId,
@@ -567,8 +572,6 @@ export default {
});
this.invites = invites;
}
this.members = this.$store.state.memberModalOptions.viewingMembers;
},
async clickMember (uid, forceShow) {
const user = this.$store.state.user.data;
@@ -209,7 +209,7 @@ export default {
this.$root.$on('selectQuest', this.selectQuest);
},
destroyed () {
beforeDestroy () {
this.$root.$off('selectQuest', this.selectQuest);
},
methods: {