mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 06:07:21 +01:00
Add more achievements to notifications list (#11332)
* Add more achievements to notifications list * use addNotification * Fix logic to not award stable achievements over and over * Improve achievement display * Fix setting notifications for party inviter * Add tests that notifications are added correctly * Fix tests
This commit is contained in:
committed by
Matteo Pagliazzi
parent
ec7ded042b
commit
360aaa9f0b
@@ -384,6 +384,7 @@ describe('User Model', () => {
|
|||||||
user = await user.save();
|
user = await user.save();
|
||||||
// verify that it's been awarded
|
// verify that it's been awarded
|
||||||
expect(user.achievements.beastMaster).to.equal(true);
|
expect(user.achievements.beastMaster).to.equal(true);
|
||||||
|
expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_BEAST_MASTER')).to.exist;
|
||||||
|
|
||||||
// reset the user
|
// reset the user
|
||||||
user.achievements.beastMasterCount = 0;
|
user.achievements.beastMasterCount = 0;
|
||||||
@@ -414,6 +415,25 @@ describe('User Model', () => {
|
|||||||
expect(user.achievements.beastMaster).to.not.equal(true);
|
expect(user.achievements.beastMaster).to.not.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('adds achievements to notification list', async () => {
|
||||||
|
let user = new User();
|
||||||
|
user = await user.save(); // necessary for user.isSelected to work correctly
|
||||||
|
|
||||||
|
// Create conditions for achievements to be awarded
|
||||||
|
user.achievements.beastMasterCount = 3;
|
||||||
|
user.achievements.mountMasterCount = 3;
|
||||||
|
user.achievements.triadBingoCount = 3;
|
||||||
|
expect(user.achievements.beastMaster).to.not.equal(true); // verify that it was not awarded initially
|
||||||
|
expect(user.achievements.mountMaster).to.not.equal(true); // verify that it was not awarded initially
|
||||||
|
expect(user.achievements.triadBingo).to.not.equal(true); // verify that it was not awarded initially
|
||||||
|
|
||||||
|
user = await user.save();
|
||||||
|
// verify that it's been awarded
|
||||||
|
expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_BEAST_MASTER')).to.exist;
|
||||||
|
expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_MOUNT_MASTER')).to.exist;
|
||||||
|
expect(user.notifications.find(notification => notification.type === 'ACHIEVEMENT_TRIAD_BINGO')).to.exist;
|
||||||
|
});
|
||||||
|
|
||||||
context('manage unallocated stats points notifications', () => {
|
context('manage unallocated stats points notifications', () => {
|
||||||
it('doesn\'t add a notification if there are no points to allocate', async () => {
|
it('doesn\'t add a notification if there are no points to allocate', async () => {
|
||||||
let user = new User();
|
let user = new User();
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ describe('POST /group/:groupId/join', () => {
|
|||||||
name: 'Testing Party',
|
name: 'Testing Party',
|
||||||
type: 'party',
|
type: 'party',
|
||||||
});
|
});
|
||||||
|
|
||||||
await leader.post(`/groups/${party._id}/invite`, {
|
await leader.post(`/groups/${party._id}/invite`, {
|
||||||
uuids: [member._id],
|
uuids: [member._id],
|
||||||
});
|
});
|
||||||
@@ -325,7 +326,9 @@ describe('POST /group/:groupId/join', () => {
|
|||||||
await leader.sync();
|
await leader.sync();
|
||||||
|
|
||||||
expect(member).to.have.nested.property('achievements.partyUp', true);
|
expect(member).to.have.nested.property('achievements.partyUp', true);
|
||||||
|
expect(member.notifications.find(notification => notification.type === 'ACHIEVEMENT_PARTY_UP')).to.exist;
|
||||||
expect(leader).to.have.nested.property('achievements.partyUp', true);
|
expect(leader).to.have.nested.property('achievements.partyUp', true);
|
||||||
|
expect(leader.notifications.find(notification => notification.type === 'ACHIEVEMENT_PARTY_UP')).to.exist;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not award Party On achievement to party of size 2', async () => {
|
it('does not award Party On achievement to party of size 2', async () => {
|
||||||
@@ -349,7 +352,9 @@ describe('POST /group/:groupId/join', () => {
|
|||||||
await leader.sync();
|
await leader.sync();
|
||||||
|
|
||||||
expect(member).to.have.nested.property('achievements.partyOn', true);
|
expect(member).to.have.nested.property('achievements.partyOn', true);
|
||||||
|
expect(member.notifications.find(notification => notification.type === 'ACHIEVEMENT_PARTY_ON')).to.exist;
|
||||||
expect(leader).to.have.nested.property('achievements.partyOn', true);
|
expect(leader).to.have.nested.property('achievements.partyOn', true);
|
||||||
|
expect(leader.notifications.find(notification => notification.type === 'ACHIEVEMENT_PARTY_ON')).to.exist;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -440,7 +440,6 @@ export default {
|
|||||||
if (this.isParty) {
|
if (this.isParty) {
|
||||||
await this.$store.dispatch('party:getParty', true);
|
await this.$store.dispatch('party:getParty', true);
|
||||||
this.group = this.$store.state.party.data;
|
this.group = this.$store.state.party.data;
|
||||||
this.checkForAchievements();
|
|
||||||
} else {
|
} else {
|
||||||
const group = await this.$store.dispatch('guilds:getGroup', {groupId: this.searchId});
|
const group = await this.$store.dispatch('guilds:getGroup', {groupId: this.searchId});
|
||||||
this.$set(this, 'group', group);
|
this.$set(this, 'group', group);
|
||||||
@@ -462,21 +461,6 @@ export default {
|
|||||||
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
|
return n.type === 'NEW_CHAT_MESSAGE' && n.data.group.id === groupId;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
checkForAchievements () {
|
|
||||||
// Checks if user's party has reached 2 players for the first time.
|
|
||||||
if (!this.user.achievements.partyUp && this.group.memberCount >= 2) {
|
|
||||||
// @TODO
|
|
||||||
// User.set({'achievements.partyUp':true});
|
|
||||||
// Achievement.displayAchievement('partyUp');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if user's party has reached 4 players for the first time.
|
|
||||||
if (!this.user.achievements.partyOn && this.group.memberCount >= 4) {
|
|
||||||
// @TODO
|
|
||||||
// User.set({'achievements.partyOn':true});
|
|
||||||
// Achievement.displayAchievement('partyOn');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async join () {
|
async join () {
|
||||||
if (this.group.cancelledPlan && !confirm(this.$t('aboutToJoinCancelledGroupPlan'))) {
|
if (this.group.cancelledPlan && !confirm(this.$t('aboutToJoinCancelledGroupPlan'))) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -175,6 +175,43 @@ const NOTIFICATIONS = {
|
|||||||
label: ($t) => `${$t('achievement')}: ${$t('achievementAridAuthority')}`,
|
label: ($t) => `${$t('achievement')}: ${$t('achievementAridAuthority')}`,
|
||||||
modalId: 'generic-achievement',
|
modalId: 'generic-achievement',
|
||||||
},
|
},
|
||||||
|
ACHIEVEMENT_PARTY_UP: {
|
||||||
|
achievement: true,
|
||||||
|
label: ($t) => `${$t('achievement')}: ${$t('achievementPartyUp')}`,
|
||||||
|
modalId: 'generic-achievement',
|
||||||
|
},
|
||||||
|
ACHIEVEMENT_PARTY_ON: {
|
||||||
|
achievement: true,
|
||||||
|
label: ($t) => `${$t('achievement')}: ${$t('achievementPartyOn')}`,
|
||||||
|
modalId: 'generic-achievement',
|
||||||
|
},
|
||||||
|
ACHIEVEMENT_BEAST_MASTER: {
|
||||||
|
achievement: true,
|
||||||
|
label: ($t) => `${$t('achievement')}: ${$t('beastAchievement')}`,
|
||||||
|
modalId: 'generic-achievement',
|
||||||
|
data: {
|
||||||
|
message: ($t) => $t('achievement'),
|
||||||
|
modalText: ($t) => $t('mountAchievement'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ACHIEVEMENT_MOUNT_MASTER: {
|
||||||
|
achievement: true,
|
||||||
|
label: ($t) => `${$t('achievement')}: ${$t('mountAchievement')}`,
|
||||||
|
modalId: 'generic-achievement',
|
||||||
|
data: {
|
||||||
|
message: ($t) => $t('achievement'),
|
||||||
|
modalText: ($t) => $t('mountAchievement'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ACHIEVEMENT_TRIAD_BINGO: {
|
||||||
|
achievement: true,
|
||||||
|
label: ($t) => `${$t('achievement')}: ${$t('triadBingoAchievement')}`,
|
||||||
|
modalId: 'generic-achievement',
|
||||||
|
data: {
|
||||||
|
message: ($t) => $t('achievement'),
|
||||||
|
modalText: ($t) => $t('triadBingoAchievement'),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -230,7 +267,8 @@ export default {
|
|||||||
'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
|
'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
|
||||||
'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL',
|
'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL',
|
||||||
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS',
|
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS',
|
||||||
'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', 'GENERIC_ACHIEVEMENT',
|
'GENERIC_ACHIEVEMENT', 'ACHIEVEMENT_PARTY_UP', 'ACHIEVEMENT_PARTY_ON', 'ACHIEVEMENT_BEAST_MASTER',
|
||||||
|
'ACHIEVEMENT_MOUNT_MASTER', 'ACHIEVEMENT_TRIAD_BINGO', 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY',
|
||||||
].forEach(type => {
|
].forEach(type => {
|
||||||
handledNotifications[type] = true;
|
handledNotifications[type] = true;
|
||||||
});
|
});
|
||||||
@@ -381,21 +419,30 @@ export default {
|
|||||||
if (!config) {
|
if (!config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.achievement) {
|
if (config.achievement) {
|
||||||
this.playSound('Achievement_Unlocked');
|
this.playSound('Achievement_Unlocked');
|
||||||
} else if (config.sound) {
|
} else if (config.sound) {
|
||||||
this.playSound(config.sound);
|
this.playSound(config.sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let data = {};
|
||||||
if (notification.data) {
|
if (notification.data) {
|
||||||
this.notificationData = notification.data;
|
data = notification.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.modalText && config.data.modalText) {
|
||||||
|
data.modalText = config.data.modalText(this.$t);
|
||||||
|
}
|
||||||
|
if (!data.message && config.data.message) {
|
||||||
|
data.message = config.data.message(this.$t);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notificationData = data;
|
||||||
if (forceToModal) {
|
if (forceToModal) {
|
||||||
this.$root.$emit('bv::show::modal', config.modalId);
|
this.$root.$emit('bv::show::modal', config.modalId);
|
||||||
} else {
|
} else {
|
||||||
this.text(config.label(this.$t), () => {
|
this.text(config.label(this.$t), () => {
|
||||||
|
this.notificationData = data;
|
||||||
this.$root.$emit('bv::show::modal', config.modalId);
|
this.$root.$emit('bv::show::modal', config.modalId);
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
@@ -607,6 +654,11 @@ export default {
|
|||||||
case 'ACHIEVEMENT_BACK_TO_BASICS':
|
case 'ACHIEVEMENT_BACK_TO_BASICS':
|
||||||
case 'ACHIEVEMENT_DUST_DEVIL':
|
case 'ACHIEVEMENT_DUST_DEVIL':
|
||||||
case 'ACHIEVEMENT_ARID_AUTHORITY':
|
case 'ACHIEVEMENT_ARID_AUTHORITY':
|
||||||
|
case 'ACHIEVEMENT_PARTY_UP':
|
||||||
|
case 'ACHIEVEMENT_PARTY_ON':
|
||||||
|
case 'ACHIEVEMENT_BEAST_MASTER':
|
||||||
|
case 'ACHIEVEMENT_MOUNT_MASTER':
|
||||||
|
case 'ACHIEVEMENT_TRIAD_BINGO':
|
||||||
case 'GENERIC_ACHIEVEMENT':
|
case 'GENERIC_ACHIEVEMENT':
|
||||||
this.showNotificationWithModal(notification);
|
this.showNotificationWithModal(notification);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -22,9 +22,11 @@
|
|||||||
"achievementDustDevil": "Dust Devil",
|
"achievementDustDevil": "Dust Devil",
|
||||||
"achievementDustDevilText": "Has collected all Desert Pets.",
|
"achievementDustDevilText": "Has collected all Desert Pets.",
|
||||||
"achievementDustDevilModalText": "You collected all the Desert Pets!",
|
"achievementDustDevilModalText": "You collected all the Desert Pets!",
|
||||||
|
"achievementPartyUp": "You teamed up with a party member!",
|
||||||
"achievementAridAuthority": "Arid Authority",
|
"achievementAridAuthority": "Arid Authority",
|
||||||
"achievementAridAuthorityText": "Has tamed all Desert Mounts.",
|
"achievementAridAuthorityText": "Has tamed all Desert Mounts.",
|
||||||
"achievementAridAuthorityModalText": "You tamed all the Desert Mounts!",
|
"achievementAridAuthorityModalText": "You tamed all the Desert Mounts!",
|
||||||
"achievementKickstarter2019": "Pin Kickstarter Backer",
|
"achievementKickstarter2019": "Pin Kickstarter Backer",
|
||||||
"achievementKickstarter2019Text": "Backed the 2019 Pin Kickstarter Project"
|
"achievementKickstarter2019Text": "Backed the 2019 Pin Kickstarter Project",
|
||||||
|
"achievementPartyOn": "Your party grew to 4 members!"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -606,13 +606,25 @@ api.joinGroup = {
|
|||||||
promises.push(User.update({
|
promises.push(User.update({
|
||||||
$or: [{'party._id': group._id}, {_id: user._id}],
|
$or: [{'party._id': group._id}, {_id: user._id}],
|
||||||
'achievements.partyUp': {$ne: true},
|
'achievements.partyUp': {$ne: true},
|
||||||
}, {$set: {'achievements.partyUp': true}}, {multi: true}).exec());
|
}, {$set: {'achievements.partyUp': true}, $push: {notifications: {type: 'ACHIEVEMENT_PARTY_UP'}}}, {multi: true}).exec());
|
||||||
|
if (inviter) {
|
||||||
|
if (inviter.achievements.partyUp !== true) {
|
||||||
|
// Since the notification list of the inviter is already updated in this save we need to add the notification here
|
||||||
|
inviter.addNotification('ACHIEVEMENT_PARTY_UP');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (group.memberCount > 3) {
|
if (group.memberCount > 3) {
|
||||||
promises.push(User.update({
|
promises.push(User.update({
|
||||||
$or: [{'party._id': group._id}, {_id: user._id}],
|
$or: [{'party._id': group._id}, {_id: user._id}],
|
||||||
'achievements.partyOn': {$ne: true},
|
'achievements.partyOn': {$ne: true},
|
||||||
}, {$set: {'achievements.partyOn': true}}, {multi: true}).exec());
|
}, {$set: {'achievements.partyOn': true}, $push: {notifications: {type: 'ACHIEVEMENT_PARTY_ON'}}}, {multi: true}).exec());
|
||||||
|
if (inviter) {
|
||||||
|
if (inviter.achievements.partyOn !== true) {
|
||||||
|
// Since the notification list of the inviter is already updated in this save we need to add the notification here
|
||||||
|
inviter.addNotification('ACHIEVEMENT_PARTY_ON');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,23 +188,26 @@ schema.pre('save', true, function preSaveUser (next, done) {
|
|||||||
// Determines if Beast Master should be awarded
|
// Determines if Beast Master should be awarded
|
||||||
let beastMasterProgress = common.count.beastMasterProgress(this.items.pets);
|
let beastMasterProgress = common.count.beastMasterProgress(this.items.pets);
|
||||||
|
|
||||||
if (beastMasterProgress >= 90 || this.achievements.beastMasterCount > 0) {
|
if ((beastMasterProgress >= 90 || this.achievements.beastMasterCount > 0) && this.achievements.beastMaster !== true) {
|
||||||
this.achievements.beastMaster = true;
|
this.achievements.beastMaster = true;
|
||||||
|
this.addNotification('ACHIEVEMENT_BEAST_MASTER');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if Mount Master should be awarded
|
// Determines if Mount Master should be awarded
|
||||||
let mountMasterProgress = common.count.mountMasterProgress(this.items.mounts);
|
let mountMasterProgress = common.count.mountMasterProgress(this.items.mounts);
|
||||||
|
|
||||||
if (mountMasterProgress >= 90 || this.achievements.mountMasterCount > 0) {
|
if ((mountMasterProgress >= 90 || this.achievements.mountMasterCount > 0) && this.achievements.mountMaster !== true) {
|
||||||
this.achievements.mountMaster = true;
|
this.achievements.mountMaster = true;
|
||||||
|
this.addNotification('ACHIEVEMENT_MOUNT_MASTER');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determines if Triad Bingo should be awarded
|
// Determines if Triad Bingo should be awarded
|
||||||
let dropPetCount = common.count.dropPetsCurrentlyOwned(this.items.pets);
|
let dropPetCount = common.count.dropPetsCurrentlyOwned(this.items.pets);
|
||||||
let qualifiesForTriad = dropPetCount >= 90 && mountMasterProgress >= 90;
|
let qualifiesForTriad = dropPetCount >= 90 && mountMasterProgress >= 90;
|
||||||
|
|
||||||
if (qualifiesForTriad || this.achievements.triadBingoCount > 0) {
|
if ((qualifiesForTriad || this.achievements.triadBingoCount > 0) && this.achievements.triadBingo !== true) {
|
||||||
this.achievements.triadBingo = true;
|
this.achievements.triadBingo = true;
|
||||||
|
this.addNotification('ACHIEVEMENT_TRIAD_BINGO');
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXAMPLE CODE for allowing all existing and new players to be
|
// EXAMPLE CODE for allowing all existing and new players to be
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ const NOTIFICATION_TYPES = [
|
|||||||
'ACHIEVEMENT_MIND_OVER_MATTER',
|
'ACHIEVEMENT_MIND_OVER_MATTER',
|
||||||
'ACHIEVEMENT_DUST_DEVIL',
|
'ACHIEVEMENT_DUST_DEVIL',
|
||||||
'ACHIEVEMENT_ARID_AUTHORITY',
|
'ACHIEVEMENT_ARID_AUTHORITY',
|
||||||
|
'ACHIEVEMENT_PARTY_UP',
|
||||||
|
'ACHIEVEMENT_PARTY_ON',
|
||||||
|
'ACHIEVEMENT_BEAST_MASTER',
|
||||||
|
'ACHIEVEMENT_MOUNT_MASTER',
|
||||||
|
'ACHIEVEMENT_TRIAD_BINGO',
|
||||||
];
|
];
|
||||||
|
|
||||||
const Schema = mongoose.Schema;
|
const Schema = mongoose.Schema;
|
||||||
|
|||||||
Reference in New Issue
Block a user