diff --git a/net/web/src/session/cards/Cards.jsx b/net/web/src/session/cards/Cards.jsx index 508cc586..e2a6a962 100644 --- a/net/web/src/session/cards/Cards.jsx +++ b/net/web/src/session/cards/Cards.jsx @@ -75,7 +75,7 @@ export function Cards({ closeCards, openContact, openChannel, openListing }) { renderItem={item => ( actions.resync(item.cardId)} open={() => openContact(item.guid)} message={() => message(item.cardId)} - call={() => call(item)} display={state.display} /> + call={() => call(item)} display={state.display} canMessage={state.allowUnsealed || (item.seal && state.sealable)} /> )} /> )} { state.cards.length === 0 && ( diff --git a/net/web/src/session/cards/cardItem/CardItem.jsx b/net/web/src/session/cards/cardItem/CardItem.jsx index 1ea4cae2..0f752c5b 100644 --- a/net/web/src/session/cards/cardItem/CardItem.jsx +++ b/net/web/src/session/cards/cardItem/CardItem.jsx @@ -6,7 +6,7 @@ import { Logo } from 'logo/Logo'; import { Tooltip } from 'antd'; import { MessageOutlined, PhoneOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; -export function CardItem({ item, tooltip, enableIce, resync, open, call, message, display }) { +export function CardItem({ item, tooltip, enableIce, resync, open, call, message, display, canMessage }) { const onResync = (e) => { e.stopPropagation(); @@ -45,9 +45,11 @@ export function CardItem({ item, tooltip, enableIce, resync, open, call, message )} { item.status === 'connected' && display === 'small' && ( -
- -
+ { canMessage && ( +
+ +
+ )} { enableIce && (
@@ -57,9 +59,11 @@ export function CardItem({ item, tooltip, enableIce, resync, open, call, message )} { item.status === 'connected' && display !== 'small' && ( - - - + { canMessage && ( + + + + )} { enableIce && ( diff --git a/net/web/src/session/cards/useCards.hook.js b/net/web/src/session/cards/useCards.hook.js index 33ce772c..9088c477 100644 --- a/net/web/src/session/cards/useCards.hook.js +++ b/net/web/src/session/cards/useCards.hook.js @@ -5,6 +5,7 @@ import { StoreContext } from 'context/StoreContext'; import { ChannelContext } from 'context/ChannelContext'; import { AccountContext } from 'context/AccountContext'; import { RingContext } from 'context/RingContext'; +import { encryptChannelSubject } from 'context/sealUtil'; export function useCards() { @@ -15,6 +16,8 @@ export function useCards() { sorted: false, display: 'small', enableIce: false, + sealable: false, + allowUnsealed: false, cards: [], }); @@ -35,8 +38,14 @@ export function useCards() { }, [viewport.state]); useEffect(() => { - const { enableIce } = account.state?.status || {}; - updateState({ enableIce }); + const { seal, sealKey, status } = account.state; + const allowUnsealed = account.state.status?.allowUnsealed; + if (seal?.publicKey && sealKey?.public && sealKey?.private && seal.publicKey === sealKey.public) { + updateState({ sealable: true, allowUnsealed, enableIce: status?.enableIce }); + } + else { + updateState({ sealable: false, allowUnsealed, enableIce: status?.enableIce }); + } }, [account.state]); useEffect(() => { @@ -51,10 +60,11 @@ export function useCards() { const guid = profile?.guid; const name = profile?.name; const node = profile?.node; + const seal = profile?.seal; const token = detail?.token; const handle = profile?.node ? `${profile.handle}@${profile.node}` : profile.handle; const logo = profile?.imageSet ? card.actions.getCardImageUrl(item.id) : null; - return { cardId, guid, updated, offsync, status, name, node, token, handle, logo }; + return { cardId, guid, updated, offsync, status, name, node, token, handle, logo, seal }; }); let latest = 0; @@ -132,15 +142,25 @@ export function useCards() { const cards = entry?.data?.channelDetail?.contacts?.cards || []; const subject = entry?.data?.channelDetail?.data || ''; const type = entry?.data?.channelDetail?.dataType || ''; - if (cards.length === 1 && cards[0] === cardId && type === 'superbasic' && subject === '{"subject":null}') { + + if (cards.length === 1 && cards[0] === cardId && subject === '{"subject":null}') { channelId = entry.id; } }); if (channelId != null) { return channelId; } - const conversation = await channel.actions.addChannel('superbasic', { subject: null }, [ cardId ]); - return conversation.id; + if (state.sealable && !state.allowUnsealed) { + const keys = [ account.state.sealKey.public ]; + keys.push(card.state.cards.get(cardId).data.cardProfile.seal); + const sealed = encryptChannelSubject(state.subject, keys); + const conversation = await channel.actions.addChannel('sealed', sealed, [ cardId ]); + return conversation.id; + } + else { + const conversation = await channel.actions.addChannel('superbasic', { subject: null }, [ cardId ]); + return conversation.id; + } }, call: async (contact) => { const { cardId, node, guid, token } = contact;