Notifications v2 and Bailey API (#9716)

* Added initial bailey api

* wip

* implement new panel header

* Fixed lint

* add ability to mark notification as seen

* add notification count, remove top badge from user and add ability to mark multiple notifications as seen

* add support dismissall and mark all as read

* do not dismiss actionable notif

* mark as seen when menu is opened instead of closed

* implement ordering, list of actionable notifications

* add groups messages and fix badges count

* add notifications for received cards

* send card received notification to target not sender

* rename notificaion field

* fix integration tests

* mark cards notifications as read and update tests

* add mystery items notifications

* add unallocated stats points notifications

* fix linting

* simplify code

* refactoring and fixes

* fix dropdown opening

* start splitting notifications into their own component

* add notifications for inbox messages

* fix unit tests

* fix default buttons styles

* add initial bailey support

* add title and tests to new stuff notification

* add notification if a group task needs more work

* add tests and fixes for marking a task as needing more work

* make sure user._v is updated

* remove console.log

* notification: hover status and margins

* start styling notifications, add separate files and basic functionalities

* fix tests

* start adding mystery items notification

* wip card notification

* fix cards text

* initial implementation inbox messages

* initial implementation group messages

* disable inbox notifications until mobile is ready

* wip group chat messages

* finish mystery and card notifications

* add bailey notification and fix a lot of stuff

* start adding guilds and parties invitations

* misc invitation fixes

* fix lint issues

* remove old code and add key to notifications

* fix tests

* remove unused code

* add link for public guilds invite

* starts to implement needs work notification design and feature

* fixes to needs work, add group task approved notification

* finish needs work feature

* lots of fixes

* implement quest notification

* bailey fixes and static page

* routing fixes

* fixes #      this.$store.dispatch(guilds:join, {groupId: group.id, type: party});

* read notifications on click

* chat notifications

* fix tests for chat notifications

* fix chat notification test

* fix tests

* fix tests (again)

* try awaiting

* remove only

* more sleep

* add bailey tests

* fix icons alignment

* fix issue with multiple points notifications

* remove merge code

* fix rejecting guild invitation

* make remove area bigger

* fix error with notifications and add migration

* fix migration

* fix typos

* add cleanup migration too

* notifications empty state, new counter color, fix marking messages as seen in guilds

* fixes

* add image and install correct packages

* fix mongoose version

* update bailey

* typo

* make sure chat is marked as read after other requests
This commit is contained in:
Matteo Pagliazzi
2018-01-31 11:55:39 +01:00
committed by GitHub
parent a85282763f
commit 33b249d078
98 changed files with 3003 additions and 1026 deletions
+4 -4
View File
@@ -64,7 +64,7 @@
"groupPlansTitle": "Group Plans",
"newGroupTitle": "New Group",
"subscriberItem": "Mystery Item",
"newSubscriberItem": "New Mystery Item",
"newSubscriberItem": "You have new <span class=\"notification-bold-blue\">Mystery Items</span>",
"subscriberItemText": "Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. See the wiki's 'Mystery Item' page for more information.",
"all": "All",
"none": "None",
@@ -173,8 +173,7 @@
"achievementBewilderText": "Helped defeat the Be-Wilder during the 2016 Spring Fling Event!",
"checkOutProgress": "Check out my progress in Habitica!",
"cards": "Cards",
"cardReceived": "Received a card!",
"cardReceivedFrom": "<%= cardType %> from <%= userName %>",
"cardReceived": "You received a <span class=\"notification-bold-blue\"><%= card %></span>",
"greetingCard": "Greeting Card",
"greetingCardExplanation": "You both receive the Cheery Chum achievement!",
"greetingCardNotes": "Send a greeting card to a party member.",
@@ -281,10 +280,11 @@
"spirituality": "Spirituality",
"time_management": "Time-Management + Accountability",
"recovery_support_groups": "Recovery + Support Groups",
"dismissAll": "Dismiss All",
"messages": "Messages",
"emptyMessagesLine1": "You don't have any messages",
"emptyMessagesLine2": "Send a message to start a conversation!",
"userSentMessage": "<span class=\"notification-bold\"><%= user %></span> sent you a message",
"letsgo": "Let's Go!",
"selected": "Selected",
"howManyToBuy": "How many would you like to buy?",
+13 -7
View File
@@ -48,8 +48,9 @@
"userId": "User ID",
"invite": "Invite",
"leave": "Leave",
"invitedTo": "Invited to <%= name %>",
"invitedToNewParty": "You were invited to join a party! Do you want to leave this party, reject all other party invitations and join <%= partyName %>?",
"invitedToParty": "You were invited to join the Party <span class=\"notification-bold\"><%= party %></span>",
"invitedToPrivateGuild": "You were invited to join the private Guild <span class=\"notification-bold\"><%= guild %></span>",
"invitedToPublicGuild": "You were invited to join the Guild <span class=\"notification-bold-blue\"><%= guild %></span>",
"partyInvitationsText": "You have <%= numberInvites %> party invitations! Choose wisely, because you can only be in one party at a time.",
"joinPartyConfirmationText": "Are you sure you want to join the party \"<%= partyName %>\"? You can only be in one party at a time. If you join, all other party invitations will be rejected.",
"invitationAcceptedHeader": "Your Invitation has been Accepted",
@@ -61,7 +62,8 @@
"partyLoading3": "Your party is gathering. Please wait...",
"partyLoading4": "Your party is materializing. Please wait...",
"systemMessage": "System Message",
"newMsg": "New message in \"<%= name %>\"",
"newMsgGuild": "There are new posts in the Guild <span class=\"notification-bold-blue\"><%= name %></span>",
"newMsgParty": "There are new posts in your Party <span class=\"notification-bold-blue\"><%= name %></span>",
"chat": "Chat",
"sendChat": "Send Chat",
"toolTipMsg": "Fetch Recent Messages",
@@ -154,8 +156,6 @@
"copyAsTodo": "Copy as To-Do",
"messageAddedAsToDo": "Message copied as To-Do.",
"messageWroteIn": "<%= user %> wrote in <%= group %>",
"taskFromInbox": "<%= from %> wrote '<%= message %>'",
"taskTextFromInbox": "Message from <%= from %>",
"msgPreviewHeading": "Message Preview",
"leaderOnlyChallenges": "Only group leader can create challenges",
"sendGift": "Send Gift",
@@ -246,6 +246,7 @@
"confirmClaim": "Are you sure you want to claim this task?",
"confirmUnClaim": "Are you sure you want to unclaim this task?",
"confirmApproval": "Are you sure you want to approve this task?",
"confirmNeedsWork": "Are you sure you want to mark this task as needing work?",
"userRequestsApproval": "<%= userName %> requests approval",
"userCountRequestsApproval": "<%= userCount %> request approval",
"youAreRequestingApproval": "You are requesting approval",
@@ -264,10 +265,15 @@
"assignTask": "Assign Task",
"desktopNotificationsText": "We need your permission to enable desktop notifications for new messages in party chat! Follow your browser's instructions to turn them on.<br><br>You'll receive these notifications only while you have Habitica open. If you decide you don't like them, they can be disabled in your browser's settings.<br><br>This box will close automatically when a decision is made.",
"claim": "Claim",
"removeClaim": "Remove Claim",
"onlyGroupLeaderCanManageSubscription": "Only the group leader can manage the group's subscription",
"yourTaskHasBeenApproved": "Your task \"<%= taskText %>\" has been approved",
"userHasRequestedTaskApproval": "<%= user %> has requested task approval for <%= taskName %>",
"yourTaskHasBeenApproved": "Your task <span class=\"notification-green\"><%= taskText %></span> has been approved.",
"taskNeedsWork": "<span class=\"notification-bold\"><%= managerName %></span> marked <span class=\"notification-bold\"><%= taskText %></span> as needing additional work.",
"userHasRequestedTaskApproval": "<span class=\"notification-bold\"><%= user %></span> requests approval for <span class=\"notification-bold\"><%= taskName %></span>",
"approve": "Approve",
"approveTask": "Approve Task",
"needsWork": "Needs Work",
"viewRequests": "View Requests",
"approvalTitle": "<%= userName %> has completed <%= type %>: \"<%= text %>\"",
"confirmTaskApproval": "Do you want to reward <%= username %> for completing this task?",
"groupSubscriptionPrice": "$9 every month + $3 a month for every additional group member",
+2
View File
@@ -66,5 +66,7 @@
"messageNotificationNotFound": "Notification not found.",
"notificationsRequired": "Notification ids are required.",
"unallocatedStatsPoints": "You have <span class=\"notification-bold-blue\"><%= points %> unallocated Stat Points</span>",
"beginningOfConversation": "This is the beginning of your conversation with <%= userName %>. Remember to be kind, respectful, and follow the Community Guidelines!"
}
+3 -2
View File
@@ -102,8 +102,9 @@
"alreadyUnlockedPart": "Full set already partially unlocked.",
"USD": "(USD)",
"newStuff": "New Stuff by [Bailey](https://twitter.com/Mihakuu)",
"cool": "Tell Me Later",
"newStuff": "New Stuff by <a href=\"https://twitter.com/Mihakuu\" target=\"_blank\">Bailey</a>",
"newBaileyUpdate": "New Bailey Update!",
"tellMeLater": "Tell Me Later",
"dismissAlert": "Dismiss This Alert",
"donateText1": "Adds 20 Gems to your account. Gems are used to buy special in-game items, such as shirts and hairstyles.",
"donateText2": "Help support Habitica",
+1
View File
@@ -25,6 +25,7 @@
"questInvitation": "Quest Invitation: ",
"questInvitationTitle": "Quest Invitation",
"questInvitationInfo": "Invitation for the Quest <%= quest %>",
"invitedToQuest": "You were invited to the Quest <span class=\"notification-bold-blue\"><%= quest %></span>",
"askLater": "Ask Later",
"questLater": "Quest Later",
"buyQuest": "Buy Quest",
+1
View File
@@ -172,6 +172,7 @@
"habitCounterDown": "Negative Counter (Resets <%= frequency %>)",
"taskRequiresApproval": "This task must be approved before you can complete it. Approval has already been requested",
"taskApprovalHasBeenRequested": "Approval has been requested",
"taskApprovalWasNotRequested": "Only a task waiting for approval can be marked as needing more work",
"approvals": "Approvals",
"approvalRequired": "Needs Approval",
"repeatZero": "Daily is never due",
+73 -16
View File
@@ -399,9 +399,16 @@ spells.special = {
}
if (!target.items.special.nyeReceived) target.items.special.nyeReceived = [];
target.items.special.nyeReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.nyeReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'nye',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -427,9 +434,16 @@ spells.special = {
}
if (!target.items.special.valentineReceived) target.items.special.valentineReceived = [];
target.items.special.valentineReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.valentineReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'valentine',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -445,6 +459,7 @@ spells.special = {
notes: t('greetingCardNotes'),
cast (user, target) {
if (user === target) {
if (!user.achievements.greeting) user.achievements.greeting = 0;
user.achievements.greeting++;
} else {
each([user, target], (u) => {
@@ -454,9 +469,16 @@ spells.special = {
}
if (!target.items.special.greetingReceived) target.items.special.greetingReceived = [];
target.items.special.greetingReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.greetingReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'greeting',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -482,9 +504,16 @@ spells.special = {
}
if (!target.items.special.thankyouReceived) target.items.special.thankyouReceived = [];
target.items.special.thankyouReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.thankyouReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'thankyou',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -510,9 +539,16 @@ spells.special = {
}
if (!target.items.special.birthdayReceived) target.items.special.birthdayReceived = [];
target.items.special.birthdayReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.birthdayReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'birthday',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -538,9 +574,16 @@ spells.special = {
}
if (!target.items.special.congratsReceived) target.items.special.congratsReceived = [];
target.items.special.congratsReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.congratsReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'congrats',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -566,9 +609,16 @@ spells.special = {
}
if (!target.items.special.getwellReceived) target.items.special.getwellReceived = [];
target.items.special.getwellReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.getwellReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'getwell',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -594,9 +644,16 @@ spells.special = {
}
if (!target.items.special.goodluckReceived) target.items.special.goodluckReceived = [];
target.items.special.goodluckReceived.push(user.profile.name);
const senderName = user.profile.name;
target.items.special.goodluckReceived.push(senderName);
if (!target.flags) target.flags = {};
if (target.addNotification) target.addNotification('CARD_RECEIVED', {
card: 'goodluck',
from: {
id: user._id,
name: senderName,
},
});
target.flags.cardReceived = true;
user.stats.gp -= 10;
@@ -43,7 +43,6 @@ module.exports = function buyMysterySet (user, req = {}, analytics) {
user.purchased.plan.consecutive.trinkets--;
return [
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
i18n.t('hourglassPurchaseSet', req.language),
+12 -1
View File
@@ -5,13 +5,24 @@ import {
} from '../libs/errors';
import cloneDeep from 'lodash/cloneDeep';
function markNotificationAsRead (user) {
const index = user.notifications.findIndex(notification => {
return notification.type === 'NEW_MYSTERY_ITEMS';
});
if (index !== -1) user.notifications.splice(index, 1);
}
module.exports = function openMysteryItem (user, req = {}, analytics) {
let item = user.purchased.plan.mysteryItems.shift();
const mysteryItems = user.purchased.plan.mysteryItems;
let item = mysteryItems.shift();
if (!item) {
throw new BadRequest(i18n.t('mysteryItemIsEmpty', req.language));
}
if (mysteryItems.length === 0) markNotificationAsRead(user);
item = cloneDeep(content.gear.flat[item]);
user.items.gear.owned[item.key] = true;
+15
View File
@@ -7,6 +7,19 @@ import {
} from '../libs/errors';
import content from '../content/index';
// @TODO move in the servercontroller or keep here?
function markNotificationAsRead (user, cardType) {
const indexToRemove = user.notifications.findIndex(notification => {
if (
notification.type === 'CARD_RECEIVED' &&
notification.data.card === cardType
) return true;
});
if (indexToRemove !== -1) user.notifications.splice(indexToRemove, 1);
}
module.exports = function readCard (user, req = {}) {
let cardType = get(req.params, 'cardType');
@@ -21,6 +34,8 @@ module.exports = function readCard (user, req = {}) {
user.items.special[`${cardType}Received`].shift();
user.flags.cardReceived = false;
markNotificationAsRead(user, cardType);
return [
{ specialItems: user.items.special, cardReceived: user.flags.cardReceived },
i18n.t('readCard', {cardType}, req.language),