-
-
Notifications
You must be signed in to change notification settings - Fork 44
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
Improve to manage notifications and proposals #2046
Conversation
Passing run #2489 ↗︎
Details:
Review all test suite changes for PR #2046 ↗︎ |
* PR for release v0.4.2 * feat: save notification body as object format --------- Co-authored-by: Greg Slepak <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for the review!
Worked on two Issues in this PR, instead of working on separate PRs, in order to reduce conflicts mainly in templates.js
file.
validate: actionRequireActiveMember(objectOf({ | ||
proposalIds: arrayOf(string) | ||
})), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make data
meaningful, I've updated the type from array
to object
.
@@ -1948,13 +1976,15 @@ sbp('chelonia/defineContract', { | |||
console.warn(`removeForeignKeys: ${e.name} error thrown:`, e) | |||
}) | |||
}, | |||
'gi.contracts/group/emitNotificationAfterSyncing': async (contractIDs, notificationName, notificationData) => { | |||
'gi.contracts/group/emitNotificationsAfterSyncing': async (contractIDs, notifications) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated function to be able to emit multiple notifications.
@@ -52,7 +52,6 @@ export const CHATROOM_MEMBER_MENTION_SPECIAL_CHAR = '@' | |||
export const CHATROOM_CHANNEL_MENTION_SPECIAL_CHAR = '#' | |||
|
|||
// chatroom events | |||
export const CHATROOM_MESSAGE_ACTION = 'chatroom-message-action' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use this action anymore unless we use the contracts of old versions. I remember this action was removed in the PR #1521 where @corrideat implemented E2E Protocol.
export function swapMentionIDForDisplayname (text: string): string { | ||
export function swapMentionIDForDisplayname ( | ||
text: string, | ||
options: Object = { escaped: true, forChat: true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added options
argument and made this function more dynamic.
escaped: this indicates that the text contains escaped characters
forChat: this indicates that the function is being used for messages inside chatroom
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add some inline comments to the code to explain what these parameters are along with some examples of which situations you would want to use one versus the other.
...template, | ||
avatarUserID: template.avatarUserID || sbp('state/vuex/getters').ourIdentityContractId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated order so that avatarUserID
could be overridden.
@@ -0,0 +1,205 @@ | |||
<template lang='pug'> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saved this file inside history
folder, because I am not sure we could use this file or content later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @Silver-IT!
return text.split(regEx).map(t => { | ||
return possibleMentions.includes(t) | ||
? t[0] === CHATROOM_MEMBER_MENTION_SPECIAL_CHAR | ||
? forChat ? t[0] + usernameFromID(t.slice(1)) : userDisplayNameFromID(t.slice(1)) | ||
: (forChat ? t[0] : '') + getChatroomNameById(t.slice(1)) | ||
: t | ||
}).join('') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is written in a far more confusing manner now. Can you please add some parenthesis around the nested ?:
's to make it clearer?
export function swapMentionIDForDisplayname (text: string): string { | ||
export function swapMentionIDForDisplayname ( | ||
text: string, | ||
options: Object = { escaped: true, forChat: true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add some inline comments to the code to explain what these parameters are along with some examples of which situations you would want to use one versus the other.
|
||
return { | ||
body: L("{errName} during {activity} for '{action}' from {b_}{who}{_b} to '{contract}': '{errMsg}'", { | ||
...LTags('b'), | ||
errName: error.name, | ||
activity, | ||
action: action ?? opType, | ||
who: meta?.username ?? message.signingKeyId(), | ||
contract: contractName(contractID), | ||
errMsg: error.message ?? '?' | ||
}), | ||
body: { | ||
key: "{errName} during {activity} for '{action}' from {b_}{who}{_b} to '{contract}': '{errMsg}'", | ||
args: { | ||
...LTags('b'), | ||
errName: error.name, | ||
activity, | ||
action: action ?? opType, | ||
who: `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${message.signingKeyId()}`, | ||
contract: sbp('state/vuex/state').contracts[contractID]?.type ?? contractID, | ||
errMsg: error.message ?? '?' | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the changes in this file must be reverted because user-facing strings must be wrapped in L
functions or the strings
utility will not find them.
const args = { | ||
name: `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${data.creatorID}`, | ||
...LTags('strong') | ||
} | ||
const bodyTemplateMap = { | ||
ADD_MEMBER: () => L('{name} proposed to add a member to the group. Vote now!', { name }), | ||
CHANGE_MINCOME: () => L('{name} proposed to change the group mincome. Vote now!', { name }), | ||
CHANGE_DISTRIBUTION_DATE: () => L('{name} proposed to change the group distribution date. Vote now!', { name }), | ||
CHANGE_VOTING_RULE: () => L('{name} proposed to change the group voting system. Vote now!', { name }), | ||
REMOVE_MEMBER: () => L('{name} proposed to remove a member from the group. Vote now!', { name }), | ||
GENERIC: () => L('{name} created a proposal. Vote now!', { name }) | ||
ADD_MEMBER: () => ({ key: '{strong_}{name}{_strong} proposed to add a member to the group. Vote now!', args }), | ||
CHANGE_MINCOME: () => ({ key: '{strong_}{name}{_strong} proposed to change the group mincome. Vote now!', args }), | ||
CHANGE_DISTRIBUTION_DATE: () => ({ key: '{strong_}{name}{_strong} proposed to change the group distribution date. Vote now!', args }), | ||
CHANGE_VOTING_RULE: () => ({ key: '{strong_}{name}{_strong} proposed to change the group voting system. Vote now!', args }), | ||
REMOVE_MEMBER: () => ({ key: '{strong_}{name}{_strong} proposed to remove a member from the group. Vote now!', args }), | ||
GENERIC: () => ({ key: '{strong_}{name}{_strong} created a proposal. Vote now!', args }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the changes in this file must be reverted because user-facing strings must be wrapped in L
functions or the strings
utility will not find them.
const makeBodyBasedOnCreator = (key) => { | ||
return isMe | ||
? { key: '{strong_}Your{_strong} ' + key, args } | ||
: { | ||
key: "{strong_}{name}'s{_strong} " + key, | ||
args: { ...args, name: `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${creatorID}` } | ||
} | ||
} | ||
const bodyTemplateMap = { | ||
[PROPOSAL_INVITE_MEMBER]: | ||
(opts) => L('{creator} proposal to add {member} to the group was {closedWith}.', opts), | ||
() => makeBodyBasedOnCreator('proposal to add {member} to the group was {strong_}{closedWith}{_strong}.'), | ||
[PROPOSAL_REMOVE_MEMBER]: | ||
(opts) => L('{creator} proposal to remove {member} from the group was {closedWith}.', opts), | ||
() => makeBodyBasedOnCreator('proposal to remove {member} from the group was {strong_}{closedWith}{_strong}.'), | ||
[PROPOSAL_GROUP_SETTING_CHANGE]: | ||
(opts) => L('{creator} proposal to change group\'s {setting} to {value} was {closedWith}.', opts), | ||
() => makeBodyBasedOnCreator("proposal to change group's {setting} to {value} was {strong_}{closedWith}{_strong}."), | ||
[PROPOSAL_PROPOSAL_SETTING_CHANGE]: | ||
(opts) => L('{creator} proposal to change group\'s {setting} was {closedWith}.', opts), // TODO: define message | ||
() => makeBodyBasedOnCreator("proposal to change group's {setting} was {strong_}{closedWith}{_strong}."), | ||
[PROPOSAL_GENERIC]: | ||
(opts) => L('{creator} proposal "{title}" was {closedWith}.', opts) | ||
} | ||
const statusMap = { | ||
[STATUS_PASSED]: { icon: 'check', level: 'success', closedWith: L('accepted') }, | ||
[STATUS_FAILED]: { icon: 'times', level: 'danger', closedWith: L('rejected') }, | ||
[STATUS_CANCELLED]: { icon: 'times', level: 'danger', closedWith: L('cancelled') }, // TODO: define icon, level | ||
[STATUS_EXPIRED]: { icon: 'times', level: 'danger', closedWith: L('expired') } // TODO: define icon, level | ||
() => makeBodyBasedOnCreator('proposal "{title}" was {strong_}{closedWith}{_strong}.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the changes in this file must be reverted because user-facing strings must be wrapped in L
functions or the strings
utility will not find them.
'pledger': () => ({ | ||
key: 'A new distribution period ({period}) has started. Please check Payment TODOs.', | ||
args | ||
}), | ||
'receiver': () => ({ | ||
key: 'A new distribution period ({period}) has started. Please update your income details if they have changed.', | ||
args | ||
}) | ||
} | ||
|
||
return { | ||
avatarUserID: data.creatorID, | ||
body: bodyTemplate[data.memberType], | ||
body: bodyTemplate[data.memberType](), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of the changes in this file must be reverted because user-facing strings must be wrapped in L
functions or the strings
utility will not find them.
.c-icon( | ||
:class='{"is-warning": isYellowHorn}' | ||
) | ||
.c-icon(:class='{"is-warning": isYellowHorn}') | ||
svg-yellow-horn(v-if='isYellowHorn') | ||
svg-horn(v-else) | ||
template(#header='') | ||
.c-header | ||
span.c-title.is-title-5(:class='interactiveMessage.proposalSeverity') {{interactiveMessage.proposalStatus}} | ||
span.has-text-1 {{ humanDate(datetime, { hour: 'numeric', minute: 'numeric' }) }} | ||
span TODO | ||
template(#body='') | ||
.c-text | ||
| {{interactiveMessage.text}} | ||
i18n.c-link(@click='$router.push({ path: "/dashboard#proposals" })') See proposal | ||
.c-text TODO | ||
</template> | ||
|
||
<script> | ||
import { mapGetters } from 'vuex' | ||
import { L } from '@common/common.js' | ||
import { | ||
PROPOSAL_GROUP_SETTING_CHANGE, | ||
PROPOSAL_INVITE_MEMBER, | ||
PROPOSAL_REMOVE_MEMBER, | ||
PROPOSAL_PROPOSAL_SETTING_CHANGE, | ||
PROPOSAL_GENERIC, | ||
PROPOSAL_VARIANTS | ||
} from '@model/contracts/shared/constants.js' | ||
import { getProposalDetails } from '@model/contracts/shared/functions.js' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not sure about this. I think I made a big mistake here when I said "go for it", not fully realizing the implications of my answer, that it would result in a lot of code getting deleted.
What we had here seems very useful. We might change our mind and decide that it's still important to notify users in the #general
chatroom about proposals expiring, or about other situations in the group, in order to keep the engagement in the group high.
I'm sorry to ask this of you, as it is my fault for giving you a bad answer earlier, but can we keep all of the code that was here before, but update it so that it uses the STATUS_*
variables instead?
I think we should still post these interactive messages to the group chat, so that people who aren't paying attention to the notifications can still be reminded to vote on the proposals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ready for the another review!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great PR @Silver-IT! I tested it and it seems to work fairly well, but I wasn't able to test everything.
For example, I wasn't able to test expiring proposals. Is there an easy way to test that?
I also noticed that chatroom messages for when proposals are created or expiring or closed are not posted to the #general
chatroom. Can they be posted? It seems like something that would be useful.
scope: 'group' | ||
} | ||
}, | ||
MEMBER_LEFT (data: { groupID: string, memberID: string }) { | ||
const name = strong(userDisplayNameFromID(data.memberID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function seemed useful to DRY things, why get rid of it?
return { | ||
body: L("{errName} during {activity} for '{action}' from {b_}{who}{_b} to '{contract}': '{errMsg}'", { | ||
...LTags('b'), | ||
errName: error.name, | ||
activity, | ||
action: action ?? opType, | ||
who: meta?.username ?? message.signingKeyId(), | ||
contract: contractName(contractID), | ||
who: `${CHATROOM_MEMBER_MENTION_SPECIAL_CHAR}${message.signingKeyId()}`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please note: the message.signingKeyId()
is NOT a contractID
. So you cannot use it to get the username and therefore CHATROOM_MEMBER_MENTION_SPECIAL_CHAR
is also inappropriate here.
In fact it seems there's currently no simple way to figure out this information from a GIMessage
, and not all GIMessage
s contain this information.
So I've created #2125 to address this and assigned it to @corrideat, but for now let's just remove this who
variable altogether.
To be honest, I didn't test the expiring proposals for this PR, because they are already tested before and I didn't make any changes related it.
Sure, why not? I will make it work then. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional minor change request here
proposal: objectMaybeOf({ | ||
proposalId: string, | ||
proposalType: string, | ||
expires_date_ms: number, | ||
createdDate: string, | ||
creatorID: string, | ||
variant: unionOf(...Object.values(PROPOSAL_VARIANTS).map(v => literalOf(v))) | ||
variant: unionOf([STATUS_EXPIRING].map(v => literalOf(v))) // NOTE: only expiring proposals could be notified at the moment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Silver-IT Please feel free to add all of the types of proposals here, as we want to be able to notify the #general
chatroom of anything related to proposals (being opened, closed, etc.).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning to add all types while I would be working on Issue #2138 after this PR is merged. Do you want me to do it in this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, we can save it for a separate PR 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved!
Summary of Changes