diff --git a/frontend/controller/actions/chatroom.js b/frontend/controller/actions/chatroom.js index 139b6db56..cec82fba9 100644 --- a/frontend/controller/actions/chatroom.js +++ b/frontend/controller/actions/chatroom.js @@ -21,6 +21,7 @@ sbp('okTurtles.events/on', MESSAGE_RECEIVE_RAW, ({ // If newMessage is undefined, it means that an existing message is being edited newMessage }) => { + const state = sbp('chelonia/contract/state', contractID) const getters = sbp('state/vuex/getters') const mentions = makeMentionFromUserID(getters.ourIdentityContractId) const msgData = newMessage || data @@ -42,10 +43,10 @@ sbp('okTurtles.events/on', MESSAGE_RECEIVE_RAW, ({ messageHash: msgData.hash, height: msgData.height, text: msgData.text, - isDMOrMention: isMentionedMe || getters.chatRoomAttributes.type === CHATROOM_TYPES.DIRECT_MESSAGE, + isDMOrMention: isMentionedMe || state.attributes?.type === CHATROOM_TYPES.DIRECT_MESSAGE, messageType: !newMessage ? MESSAGE_TYPES.TEXT : data.type, memberID: innerSigningContractID, - chatRoomName: getters.chatRoomAttributes.name + chatRoomName: state.attributes?.name }).catch(e => { console.error('[action/chatroom.js] Error on messageReceivePostEffect', e) }) diff --git a/frontend/controller/actions/group.js b/frontend/controller/actions/group.js index 9171803be..beb4e082a 100644 --- a/frontend/controller/actions/group.js +++ b/frontend/controller/actions/group.js @@ -860,8 +860,8 @@ export default (sbp('sbp/selectors/register', { // inside of the exception handler :-( } }, - 'gi.actions/group/notifyProposalStateInGeneralChatRoom': function ({ groupID, proposal }: { groupID: string, proposal: Object }) { - const { generalChatRoomId } = sbp('chelonia/rootState')[groupID] + 'gi.actions/group/notifyProposalStateInGeneralChatRoom': async function ({ groupID, proposal }: { groupID: string, proposal: Object }) { + const { generalChatRoomId } = await sbp('chelonia/contract/state', groupID) return sbp('gi.actions/chatroom/addMessage', { contractID: generalChatRoomId, data: { type: MESSAGE_TYPES.INTERACTIVE, proposal } diff --git a/frontend/controller/actions/identity-kv.js b/frontend/controller/actions/identity-kv.js index 0526701dc..ce17ded01 100644 --- a/frontend/controller/actions/identity-kv.js +++ b/frontend/controller/actions/identity-kv.js @@ -1,7 +1,7 @@ 'use strict' import sbp from '@sbp/sbp' import { KV_KEYS } from '~/frontend/utils/constants.js' -import { KV_QUEUE, ONLINE } from '~/frontend/utils/events.js' +import { KV_QUEUE, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, ONLINE } from '~/frontend/utils/events.js' import { isExpired } from '@model/notifications/utils.js' const initNotificationStatus = (data = {}) => ({ ...data, read: false }) @@ -51,8 +51,7 @@ export default (sbp('sbp/selectors/register', { 'gi.actions/identity/kv/loadChatRoomUnreadMessages': () => { return sbp('okTurtles.eventQueue/queueEvent', KV_QUEUE, async () => { const currentChatRoomUnreadMessages = await sbp('gi.actions/identity/kv/fetchChatRoomUnreadMessages') - // TODO: Can't use state/vuex/commit - sbp('state/vuex/commit', 'setUnreadMessages', currentChatRoomUnreadMessages) + sbp('okTurtles.events/emit', NEW_UNREAD_MESSAGES, currentChatRoomUnreadMessages) }) }, 'gi.actions/identity/kv/initChatRoomUnreadMessages': ({ contractID, messageHash, createdHeight }: { @@ -192,8 +191,7 @@ export default (sbp('sbp/selectors/register', { 'gi.actions/identity/kv/loadPreferences': () => { return sbp('okTurtles.eventQueue/queueEvent', KV_QUEUE, async () => { const preferences = await sbp('gi.actions/identity/kv/fetchPreferences') - // TODO: Can't use state/vuex/commit - sbp('state/vuex/commit', 'setPreferences', preferences) + sbp('okTurtles.events/emit', NEW_PREFERENCES, preferences) }) }, 'gi.actions/identity/kv/updateDistributionBannerVisibility': ({ contractID, hidden }: { contractID: string, hidden: boolean }) => { diff --git a/frontend/controller/app/identity.js b/frontend/controller/app/identity.js index acbbb3500..379008bc2 100644 --- a/frontend/controller/app/identity.js +++ b/frontend/controller/app/identity.js @@ -4,7 +4,7 @@ import { GIErrorUIRuntimeError, L, LError, LTags } from '@common/common.js' import { cloneDeep } from '@model/contracts/shared/giLodash.js' import sbp from '@sbp/sbp' import Vue from 'vue' -import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR } from '~/frontend/utils/events.js' +import { LOGIN, LOGIN_COMPLETE, LOGIN_ERROR, NEW_PREFERENCES, NEW_UNREAD_MESSAGES } from '~/frontend/utils/events.js' import { Secret } from '~/shared/domains/chelonia/Secret.js' import { boxKeyPair, buildRegisterSaltRequest, computeCAndHc, decryptContractSalt, hash, hashPassword, randomNonce } from '~/shared/zkpp.js' // Using relative path to crypto.js instead of ~-path to workaround some esbuild bug @@ -147,6 +147,14 @@ sbp('okTurtles.events/on', LOGIN, async ({ identityContractID, encryptionParams, } }) +sbp('okTurtles.events/on', NEW_PREFERENCES, (currentChatRoomUnreadMessages) => { + sbp('state/vuex/commit', 'setUnreadMessages', currentChatRoomUnreadMessages) +}) + +sbp('okTurtles.events/on', NEW_UNREAD_MESSAGES, (preferences) => { + sbp('state/vuex/commit', 'setPreferences', preferences) +}) + /* Commented out as persistentActions are not being used sbp('okTurtles.events/on', LOGOUT, (a) => { // TODO: [SW] It may make more sense to load persistent actions in diff --git a/frontend/controller/serviceworkers/sw-primary.js b/frontend/controller/serviceworkers/sw-primary.js index f7a03da83..e4f73c0a9 100644 --- a/frontend/controller/serviceworkers/sw-primary.js +++ b/frontend/controller/serviceworkers/sw-primary.js @@ -15,7 +15,7 @@ import { GIMessage } from '~/shared/domains/chelonia/GIMessage.js' import { Secret } from '~/shared/domains/chelonia/Secret.js' import { CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, EVENT_HANDLED } from '~/shared/domains/chelonia/events.js' import { deserializer, serializer } from '~/shared/serdes/index.js' -import { ACCEPTED_GROUP, DELETED_CHATROOM, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, LEFT_CHATROOM, LEFT_GROUP, NAMESPACE_REGISTRATION, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP } from '../../utils/events.js' +import { ACCEPTED_GROUP, DELETED_CHATROOM, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, LEFT_CHATROOM, LEFT_GROUP, NAMESPACE_REGISTRATION, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP } from '../../utils/events.js' deserializer.register(GIMessage) deserializer.register(Secret) @@ -32,7 +32,7 @@ sbp('sbp/filters/global/add', (domain, selector, data) => { console.debug(`[sw] [sbp] ${selector}`, data) }); -[EVENT_HANDLED, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, LOGIN, LOGIN_ERROR, LOGOUT, ACCEPTED_GROUP, DELETED_CHATROOM, LEFT_CHATROOM, LEFT_GROUP, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, NAMESPACE_REGISTRATION, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP, PROPOSAL_ARCHIVED].forEach(et => { +[EVENT_HANDLED, CONTRACTS_MODIFIED, CONTRACT_IS_SYNCING, LOGIN, LOGIN_ERROR, LOGOUT, ACCEPTED_GROUP, DELETED_CHATROOM, LEFT_CHATROOM, LEFT_GROUP, JOINED_CHATROOM, JOINED_GROUP, KV_EVENT, NAMESPACE_REGISTRATION, NEW_PREFERENCES, NEW_UNREAD_MESSAGES, NOTIFICATION_EMITTED, NOTIFICATION_REMOVED, NOTIFICATION_STATUS_LOADED, SWITCH_GROUP, PROPOSAL_ARCHIVED].forEach(et => { sbp('okTurtles.events/on', et, (...args) => { const { data } = serializer(args) const message = { diff --git a/frontend/model/notifications/messageReceivePostEffect.js b/frontend/model/notifications/messageReceivePostEffect.js index 64fd5ecf5..4309a8d30 100644 --- a/frontend/model/notifications/messageReceivePostEffect.js +++ b/frontend/model/notifications/messageReceivePostEffect.js @@ -27,7 +27,10 @@ async function messageReceivePostEffect ({ }): Promise { // TODO: This can't be a root getter when running in a SW const rootGetters = await sbp('state/vuex/getters') - const isGroupDM = rootGetters.isGroupDirectMessage(contractID) + const rootState = await sbp('state/vuex/state') + const identityContractID = rootState.loggedIn?.identityContractID + if (!identityContractID) return + const isDM = rootState[identityContractID].chatRooms[contractID] const shouldAddToUnreadMessages = isDMOrMention || [MESSAGE_TYPES.INTERACTIVE, MESSAGE_TYPES.POLL].includes(messageType) await sbp('chelonia/contract/wait', contractID) @@ -37,10 +40,24 @@ async function messageReceivePostEffect ({ let title = `# ${chatRoomName}` let icon - if (isGroupDM) { + + if (isDM) { // NOTE: partner identity contract could not be synced yet - title = rootGetters.ourGroupDirectMessages[contractID].title - icon = rootGetters.ourGroupDirectMessages[contractID].picture + const members = rootState[contractID].members + const isDMToMyself = members.length === 1 && members[0] === identityContractID + const partners = members + .filter(memberID => memberID !== identityContractID) + .sort((p1, p2) => { + const p1JoinedDate = new Date(identityContractID.members[p1].joinedDate).getTime() + const p2JoinedDate = new Date(identityContractID.members[p2].joinedDate).getTime() + return p1JoinedDate - p2JoinedDate + }) + const lastJoinedPartner = isDMToMyself ? identityContractID : partners[partners.length - 1] + + title = isDMToMyself + ? rootGetters.userDisplayNameFromID(identityContractID) + : partners.map(cID => rootGetters.userDisplayNameFromID(cID)).join(', ') + icon = rootGetters.ourContactProfilesById[lastJoinedPartner]?.picture } const path = `/group-chat/${contractID}` diff --git a/frontend/utils/events.js b/frontend/utils/events.js index d2806488c..d1170f36a 100644 --- a/frontend/utils/events.js +++ b/frontend/utils/events.js @@ -66,3 +66,6 @@ export const CHELONIA_STATE_MODIFIED = 'chelonia-state-modified' export const NOTIFICATION_EMITTED = 'notification-emitted' export const NOTIFICATION_REMOVED = 'notification-removed' export const NOTIFICATION_STATUS_LOADED = 'notification-status-loaded' + +export const NEW_UNREAD_MESSAGES = 'new-unread-messages' +export const NEW_PREFERENCES = 'new-preferences'