Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2305 Implement in-app notifications for non-monetary contributions #2314

Merged
merged 20 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion frontend/model/contracts/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -1054,9 +1054,14 @@ sbp('chelonia/defineContract', {
})
)
})),
process ({ data, meta, contractID, innerSigningContractID }, { state, getters }) {
process ({ data, meta, contractID, height, innerSigningContractID }, { state, getters }) {
const groupProfile = state.profiles[innerSigningContractID]
const nonMonetary = groupProfile.nonMonetaryContributions
const isUpdatingNonMonetary = Object.keys(data).some(
key => ['nonMonetaryAdd', 'nonMonetaryRemove', 'nonMonetaryEdit', 'nonMonetaryReplace'].includes(key)
)
const prevNonMonetary = nonMonetary.slice() // Capturing the previous non-monetary list. (To be used for in-app notification for non-monetary updates)

for (const key in data) {
const value = data[key]
switch (key) {
Expand All @@ -1077,6 +1082,22 @@ sbp('chelonia/defineContract', {
}
}

if (isUpdatingNonMonetary && (prevNonMonetary.length || groupProfile.nonMonetaryContributions.length)) {
sbp('gi.contracts/group/pushSideEffect', contractID,
['gi.contracts/group/sendNonMonetaryUpdateNotification', {
contractID, // group contractID
innerSigningContractID, // identity contract ID of the group-member being updated
meta,
height,
getters,
updateData: {
prev: prevNonMonetary,
after: groupProfile.nonMonetaryContributions.slice()
}
}]
)
}

if (data.incomeDetailsType) {
// someone updated their income details, create a snapshot of the haveNeeds
groupProfile['incomeDetailsLastUpdatedDate'] = meta.createdDate
Expand Down Expand Up @@ -1786,6 +1807,30 @@ sbp('chelonia/defineContract', {
console.warn(`removeForeignKeys: ${e.name} error thrown:`, e)
})
},
'gi.contracts/group/sendNonMonetaryUpdateNotification': ({
contractID, // group contractID
innerSigningContractID, // identity contractID of the group-member being updated
meta,
height,
updateData,
getters
}) => {
const { loggedIn } = sbp('state/vuex/state')
const isUpdatingMyself = loggedIn.identityContractID === innerSigningContractID

if (!isUpdatingMyself) {
const myProfile = getters.groupProfile(loggedIn.identityContractID)

if (isActionNewerThanUserJoinedDate(height, myProfile)) {
sbp('gi.notifications/emit', 'NONMONETARY_CONTRIBUTION_UPDATE', {
createdDate: meta.createdDate,
groupID: contractID,
creatorID: innerSigningContractID,
updateData
})
}
}
},
...referenceTally('gi.contracts/group/referenceTally')
}
})
48 changes: 48 additions & 0 deletions frontend/model/notifications/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,5 +311,53 @@ export default ({
scope: 'group',
data
}
},
NONMONETARY_CONTRIBUTION_UPDATE (
data: {
creatorID: string,
updateData: { prev: any[], after: any[] }
}
) {
const { prev, after } = data.updateData
const added = after.filter(v => !prev.includes(v))
const removed = prev.filter(v => !after.includes(v))
const updateType = added.length
? removed.length
? 'updated'
: 'added'
: removed.length
? 'removed'
: null

if (!updateType) {
throw new Error('Cannot emit a NONMONETARY_CONTRIBUTION_UPDATE notification for no updates.')
}

const name = `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${data.creatorID}`
const contributionsFormatted = (entries) => {
const first = entries[0]
const len = entries.length
return entries.length > 1
? L('{first} and {rest} more', { first, rest: len - 1 })
: first
}
const bodyContentMap = {
added: () => L('{name} added non-monetary contribution: {strong_}{added}{_strong}', { name, added: contributionsFormatted(added), ...LTags('strong') }),
removed: () => L('{name} removed non-monetary contribution: {strong_}{removed}{_strong}', { name, removed: contributionsFormatted(removed), ...LTags('strong') }),
updated: () => L('{name} updated non-monetary contribution: added {strong_}{added}{_strong} and removed {strong_}{removed}{_strong}', {
name,
added: contributionsFormatted(added),
removed: contributionsFormatted(removed),
...LTags('strong')
})
}
return {
body: bodyContentMap[updateType](),
scope: 'group',
avatarUserID: data.creatorID,
level: 'info',
icon: 'chart-pie',
linkTo: '/contributions'
}
}
}: { [key: string]: ((data: Object) => NotificationTemplate) })
3 changes: 2 additions & 1 deletion test/cypress/integration/group-paying.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ describe('Group Payments', () => {
cy.log('user1 receives a notification for a thank you note')
cy.giSwitchUser(`user1-${userId}`)
openNotificationCard({
notificationsCount: 5,
// no need to check the exact number of notifications but do check if the notification list contains the item for thank-you message.
notificationsCount: 0,
messageToAssert: `user3-${userId} sent you a thank you note for your contribution.`
})

Expand Down