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

Notify proposal changes in general chatroom #2165

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fa1dcdf
fix: error in using hook and improved MessageInteractive
Silver-IT Jul 2, 2024
0dcc8c5
feat: notify proposal state changes in general chatroom
Silver-IT Jul 2, 2024
998673e
feat: implemented to notify messages for proposal state changes
Silver-IT Jul 2, 2024
4e477f7
Merge branch 'master' into 2138-notify-all-types-of-proposals-in-gene…
Silver-IT Jul 2, 2024
1255bd2
feat: notify proposal status changes in General chatroom
Silver-IT Jul 3, 2024
167d746
Merge branch 'master' into 2138-notify-all-types-of-proposals-in-gene…
Silver-IT Jul 3, 2024
971bf78
feat: define proposalType inside messageType
Silver-IT Jul 3, 2024
c7fe5d0
fix: cypress function switchChannel
Silver-IT Jul 3, 2024
e6564d7
chore: tiny update and Travis retry
Silver-IT Jul 3, 2024
c9c25a6
chore: tiny update and Travis retry
Silver-IT Jul 3, 2024
243fec8
chore: added comment and Travis retry
Silver-IT Jul 3, 2024
5ff05d4
chore: tiny update and Travis retry :(
Silver-IT Jul 3, 2024
086b385
chore: removed useless code and Travis retry
Silver-IT Jul 3, 2024
61bf071
fix: initchatRoomUnreadMessages as soon as join
Silver-IT Jul 3, 2024
e6e10de
fix: kv/set conflict error
Silver-IT Jul 4, 2024
c2de3ae
Merge branch 'master' into 2138-notify-all-types-of-proposals-in-gene…
Silver-IT Jul 4, 2024
d0cb617
fix: addMessage after the channel is deleted
Silver-IT Jul 4, 2024
224e3d7
feat: add limit of characters to payment method
Silver-IT Jul 4, 2024
7baf7e0
Merge branch 'master' into 2138-notify-all-types-of-proposals-in-gene…
Silver-IT Jul 4, 2024
1a9f2f6
chore: reverted unrelated changes
Silver-IT Jul 5, 2024
682edf9
fix: resolved conflicts
Silver-IT Jul 5, 2024
cb90ee7
Merge branch 'master' into 2138-notify-all-types-of-proposals-in-gene…
Silver-IT Jul 7, 2024
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
13 changes: 5 additions & 8 deletions frontend/controller/actions/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -876,20 +876,17 @@ export default (sbp('sbp/selectors/register', {
const rootState = sbp('state/vuex/state')
const { generalChatRoomId } = rootState[params.contractID]

for (const proposal of proposals) {
for (let i = 0; i < proposals.length; i++) {
const isLastProposal = i === proposals.length - 1
await sbp('gi.actions/chatroom/addMessage', {
...omit(params, ['options', 'contractID', 'data', 'hooks']),
contractID: generalChatRoomId,
data: {
type: MESSAGE_TYPES.INTERACTIVE,
proposal: {
...proposal,
variant: STATUS_EXPIRING
}
proposal: { ...proposals[i], status: STATUS_EXPIRING }
},
hooks: {
prepublish: params.hooks?.prepublish,
postpublish: null
prepublish: null,
postpublish: isLastProposal ? params.hooks?.postpublish : null
}
})
}
Expand Down
32 changes: 20 additions & 12 deletions frontend/controller/actions/identity-kv.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ export default (sbp('sbp/selectors/register', {
if (!ourIdentityContractId) {
throw new Error('Unable to update chatroom unreadMessages without an active session')
}
return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.UNREAD_MESSAGES, data, {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict

return sbp('chelonia/queueInvocation', ourIdentityContractId, () => {
return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.UNREAD_MESSAGES, data, {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict
})
})
},
'gi.actions/identity/kv/loadChatRoomUnreadMessages': () => {
Expand Down Expand Up @@ -163,10 +166,13 @@ export default (sbp('sbp/selectors/register', {
if (!ourIdentityContractId) {
throw new Error('Unable to update preferences without an active session')
}
return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.PREFERENCES, data, {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict

return sbp('chelonia/queueInvocation', ourIdentityContractId, () => {
return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.PREFERENCES, data, {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict
})
})
},
'gi.actions/identity/kv/loadPreferences': () => {
Expand Down Expand Up @@ -220,10 +226,12 @@ export default (sbp('sbp/selectors/register', {
return null
}

return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.NOTIFICATIONS, applyStorageRules(data), {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict: updatedOnConflict
return sbp('chelonia/queueInvocation', ourIdentityContractId, () => {
return sbp('chelonia/kv/set', ourIdentityContractId, KV_KEYS.NOTIFICATIONS, applyStorageRules(data), {
encryptionKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'cek'),
signingKeyId: sbp('chelonia/contract/currentKeyIdByName', ourIdentityContractId, 'csk'),
onconflict: updatedOnConflict
})
})
},
'gi.actions/identity/kv/loadNotificationStatus': () => {
Expand Down
3 changes: 1 addition & 2 deletions frontend/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ async function startApp () {
'chelonia/queueInvocation', 'chelonia/contract/wait',
'chelonia/contract/waitingForKeyShareTo',
'chelonia/contract/successfulKeySharesByContractID',
'gi.actions/chatroom/leave',
'gi.actions/group/removeOurselves', 'gi.actions/group/groupProfileUpdate', 'gi.actions/group/displayMincomeChangedPrompt', 'gi.actions/group/addChatRoom',
'gi.actions/group/join', 'gi.actions/group/joinChatRoom',
'gi.actions/identity/addJoinDirectMessageKey', 'gi.actions/identity/leaveGroup',
Expand All @@ -130,7 +129,7 @@ async function startApp () {
'chelonia/out/keyDel',
'chelonia/contract/disconnect',
'gi.actions/identity/removeFiles',
'gi.actions/chatroom/join',
'gi.actions/chatroom/join', 'gi.actions/chatroom/leave', 'gi.actions/chatroom/addMessage',
'chelonia/contract/hasKeysToPerformOperation',
'gi.actions/identity/kv/initChatRoomUnreadMessages', 'gi.actions/identity/kv/deleteChatRoomUnreadMessages',
'gi.actions/identity/kv/setChatRoomReadUntil',
Expand Down
48 changes: 41 additions & 7 deletions frontend/model/contracts/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
STATUS_OPEN, STATUS_CANCELLED, STATUS_EXPIRED, MAX_ARCHIVED_PROPOSALS, MAX_ARCHIVED_PERIODS,
PROPOSAL_ARCHIVED, PAYMENTS_ARCHIVED, MAX_SAVED_PERIODS, GROUP_PAYMENT_METHOD_MAX_CHAR,
INVITE_INITIAL_CREATOR, PROFILE_STATUS, INVITE_EXPIRES_IN_DAYS,
CHATROOM_GENERAL_NAME, CHATROOM_PRIVACY_LEVEL, CHATROOM_TYPES
CHATROOM_GENERAL_NAME, CHATROOM_PRIVACY_LEVEL, CHATROOM_TYPES, MESSAGE_TYPES
} from './shared/constants.js'
import { paymentStatusType, paymentType, PAYMENT_COMPLETED } from './shared/payments/index.js'
import { createPaymentInfo, paymentHashesFromPaymentPeriod } from './shared/functions.js'
Expand Down Expand Up @@ -913,8 +913,8 @@ sbp('chelonia/defineContract', {
// TODO - verify if type of proposal already exists (SETTING_CHANGE).
}
}),
process ({ data, meta, hash, height, innerSigningContractID }, { state }) {
Vue.set(state.proposals, hash, {
process ({ data, meta, hash, contractID, height, innerSigningContractID }, { state }) {
const proposal = {
data,
meta,
height,
Expand All @@ -923,10 +923,19 @@ sbp('chelonia/defineContract', {
status: STATUS_OPEN,
notifiedBeforeExpire: false,
payload: null // set later by group/proposalVote
})
}
Vue.set(state.proposals, hash, proposal)
// TODO: save all proposals disk so that we only keep open proposals in memory
// TODO: create a global timer to auto-pass/archive expired votes
// make sure to set that proposal's status as STATUS_EXPIRED if it's expired
sbp('gi.contracts/group/pushSideEffect', contractID,
['gi.contracts/group/notifyProposalStateInGeneralChatroom', {
contractID,
innerSigningContractID,
height,
proposal: { ...proposal, proposalId: hash }
}]
)
},
sideEffect ({ contractID, meta, data, height, innerSigningContractID }, { getters }) {
const { loggedIn } = sbp('state/vuex/state')
Expand Down Expand Up @@ -1012,22 +1021,23 @@ sbp('chelonia/defineContract', {
}
Vue.set(proposal, 'status', STATUS_CANCELLED)
Vue.set(proposal, 'dateClosed', meta.createdDate)
notifyAndArchiveProposal({ state, proposalHash: data.proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash: data.proposalHash, proposal, innerSigningContractID, contractID, meta, height })
}
},
'gi.contracts/group/markProposalsExpired': {
validate: actionRequireActiveMember(objectOf({
proposalIds: arrayOf(string)
})),
process ({ data, meta, contractID, height }, { state }) {
process ({ data, meta, contractID, innerSigningContractID, height }, { state }) {
if (data.proposalIds.length) {
for (const proposalId of data.proposalIds) {
const proposal = state.proposals[proposalId]

if (proposal) {
Vue.set(proposal, 'status', STATUS_EXPIRED)
Vue.set(proposal, 'dateClosed', meta.createdDate)
notifyAndArchiveProposal({ state, proposalHash: proposalId, proposal, contractID, meta, height })

notifyAndArchiveProposal({ state, proposalHash: proposalId, proposal, innerSigningContractID, contractID, meta, height })
}
}
}
Expand Down Expand Up @@ -1747,6 +1757,30 @@ sbp('chelonia/defineContract', {
}])
}
},
'gi.contracts/group/notifyProposalStateInGeneralChatroom': async function ({ contractID, innerSigningContractID, height, proposal }) {
const rootState = sbp('state/vuex/state')
const { profiles, generalChatRoomId } = rootState[contractID]
const myProfile = profiles[rootState.loggedIn.identityContractID]
if (rootState.loggedIn.identityContractID === innerSigningContractID) {
if (isActionOlderThanUser(contractID, height, myProfile)) {
await sbp('gi.actions/chatroom/addMessage', {
contractID: generalChatRoomId,
data: {
type: MESSAGE_TYPES.INTERACTIVE,
proposal: {
proposalId: proposal.proposalId,
proposalType: proposal.data.proposalType,
proposalData: proposal.data.proposalData,
expires_date_ms: proposal.data.expires_date_ms,
createdDate: proposal.meta.createdDate,
creatorID: proposal.creatorID,
status: proposal.status
}
}
})
}
}
},
'gi.contracts/group/sendMincomeChangedNotification': async function (contractID, meta, data, height, innerSigningContractID) {
// NOTE: When group's mincome has changed, below actions should be taken.
// - When mincome has increased, send 'MINCOME_CHANGED' notification to both receiving/pledging members.
Expand Down
19 changes: 16 additions & 3 deletions frontend/model/contracts/shared/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import {
MESSAGE_TYPES,
MESSAGE_NOTIFICATIONS,
POLL_TYPES,
STATUS_EXPIRING
STATUS_OPEN,
STATUS_PASSED,
STATUS_FAILED,
STATUS_EXPIRING,
STATUS_EXPIRED,
STATUS_CANCELLED
} from './constants.js'

// group.js related
Expand All @@ -36,13 +41,21 @@ export const chatRoomAttributesType: any = objectOf({
export const messageType: any = objectMaybeOf({
type: unionOf(...Object.values(MESSAGE_TYPES).map(v => literalOf(v))),
text: string,
proposal: objectMaybeOf({
proposal: objectOf({
proposalId: string,
proposalType: string,
proposalData: object,
expires_date_ms: number,
createdDate: string,
creatorID: string,
variant: unionOf([STATUS_EXPIRING].map(v => literalOf(v))) // NOTE: only expiring proposals could be notified at the moment
status: unionOf(...[
STATUS_OPEN,
STATUS_PASSED,
STATUS_FAILED,
STATUS_EXPIRING,
STATUS_EXPIRED,
STATUS_CANCELLED
].map(v => literalOf(v)))
}),
notification: objectMaybeOf({
type: unionOf(...Object.values(MESSAGE_NOTIFICATIONS).map(v => literalOf(v))),
Expand Down
46 changes: 30 additions & 16 deletions frontend/model/contracts/shared/voting/proposals.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,26 @@ import {
// STATUS_CANCELLED
} from '../constants.js'

export function notifyAndArchiveProposal (
{ state, proposalHash, proposal, contractID, meta, height }: {
state: Object, proposalHash: string, proposal: any, contractID: string, meta: Object, height: number
}) {
export function notifyAndArchiveProposal ({ state, proposalHash, innerSigningContractID, proposal, contractID, meta, height }: {
state: Object,
proposalHash: string,
proposal: any,
innerSigningContractID: string,
contractID: string,
meta: Object,
height: number
}) {
Vue.delete(state.proposals, proposalHash)

sbp('gi.contracts/group/pushSideEffect', contractID,
['gi.contracts/group/notifyProposalStateInGeneralChatroom', {
contractID,
innerSigningContractID,
height,
proposal: { ...proposal, proposalId: proposalHash }
}]
)

// NOTE: we can not make notification for the proposal closal
// in the /proposalVote/sideEffect
// because we remove the state.proposals[proposalHash] in the process function
Expand Down Expand Up @@ -79,12 +93,12 @@ export function oneVoteToPass (proposalHash: string): boolean {
return newResult === VOTE_FOR
}

function voteAgainst (state: any, { meta, data, contractID, height }: any) {
function voteAgainst (state: any, { meta, data, contractID, innerSigningContractID, height }: any) {
const { proposalHash } = data
const proposal = state.proposals[proposalHash]
proposal.status = STATUS_FAILED
sbp('okTurtles.events/emit', PROPOSAL_RESULT, state, VOTE_AGAINST, data)
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
}

// NOTE: The code is ready to receive different proposals settings,
Expand All @@ -102,7 +116,7 @@ const proposals: Object = {
[PROPOSAL_INVITE_MEMBER]: {
defaults: proposalDefaults,
[VOTE_FOR]: function (state, message) {
const { data, contractID, meta, height } = message
const { data, innerSigningContractID, contractID, meta, height } = message
const { proposalHash } = data
const proposal = state.proposals[proposalHash]
proposal.payload = data.passPayload
Expand All @@ -112,7 +126,7 @@ const proposals: Object = {
const forMessage = { ...message, data: data.passPayload }
sbp('gi.contracts/group/invite/process', forMessage, state)
sbp('okTurtles.events/emit', PROPOSAL_RESULT, state, VOTE_FOR, data)
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
// TODO: for now, generate the link and send it to the user's inbox
// however, we cannot send GIMessages in any way from here
// because that means each time someone synchronizes this contract
Expand Down Expand Up @@ -149,7 +163,7 @@ const proposals: Object = {
[PROPOSAL_REMOVE_MEMBER]: {
defaults: proposalDefaults,
[VOTE_FOR]: function (state, message) {
const { data, contractID, meta, height } = message
const { data, innerSigningContractID, contractID, meta, height } = message
const { proposalHash, passPayload } = data
const proposal = state.proposals[proposalHash]
proposal.status = STATUS_PASSED
Expand All @@ -160,14 +174,14 @@ const proposals: Object = {
sbp('gi.contracts/group/pushSideEffect', contractID,
['gi.contracts/group/removeMember/sideEffect', forMessage]
)
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
},
[VOTE_AGAINST]: voteAgainst
},
[PROPOSAL_GROUP_SETTING_CHANGE]: {
defaults: proposalDefaults,
[VOTE_FOR]: function (state, message) {
const { data, contractID, meta, height } = message
const { data, innerSigningContractID, contractID, meta, height } = message
const { proposalHash } = data
const proposal = state.proposals[proposalHash]
proposal.status = STATUS_PASSED
Expand All @@ -182,14 +196,14 @@ const proposals: Object = {
sbp('gi.contracts/group/updateSettings/process', forMessage, state)
sbp('gi.contracts/group/pushSideEffect', contractID,
['gi.contracts/group/updateSettings/sideEffect', forMessage])
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
},
[VOTE_AGAINST]: voteAgainst
},
[PROPOSAL_PROPOSAL_SETTING_CHANGE]: {
defaults: proposalDefaults,
[VOTE_FOR]: function (state, message) {
const { data, contractID, meta, height } = message
const { data, innerSigningContractID, contractID, meta, height } = message
const { proposalHash } = data
const proposal = state.proposals[proposalHash]
proposal.status = STATUS_PASSED
Expand All @@ -199,18 +213,18 @@ const proposals: Object = {
proposalHash
}
sbp('gi.contracts/group/updateAllVotingRules/process', forMessage, state)
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
},
[VOTE_AGAINST]: voteAgainst
},
[PROPOSAL_GENERIC]: {
defaults: proposalDefaults,
[VOTE_FOR]: function (state, { data, contractID, meta, height }) {
[VOTE_FOR]: function (state, { data, innerSigningContractID, contractID, meta, height }) {
const { proposalHash } = data
const proposal = state.proposals[proposalHash]
proposal.status = STATUS_PASSED
sbp('okTurtles.events/emit', PROPOSAL_RESULT, state, VOTE_FOR, data)
notifyAndArchiveProposal({ state, proposalHash, proposal, contractID, meta, height })
notifyAndArchiveProposal({ state, proposalHash, proposal, innerSigningContractID, contractID, meta, height })
},
[VOTE_AGAINST]: voteAgainst
}
Expand Down
Loading
Loading