mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
refactor of conversation detail in webapp
This commit is contained in:
parent
a1549747db
commit
9fbfe85d60
@ -46,6 +46,16 @@ export function encryptChannelSubject(subject, publicKeys) {
|
|||||||
return { subjectEncrypted, subjectIv, seals };
|
return { subjectEncrypted, subjectIv, seals };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateChannelSubject(subject, contentKey) {
|
||||||
|
const key = CryptoJS.enc.Hex.parse(contentKey);
|
||||||
|
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||||
|
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
|
||||||
|
const subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
||||||
|
const subjectIv = iv.toString();
|
||||||
|
|
||||||
|
return { subjectEncrypted, subjectIv };
|
||||||
|
}
|
||||||
|
|
||||||
export function decryptChannelSubject(subject, contentKey) {
|
export function decryptChannelSubject(subject, contentKey) {
|
||||||
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
|
const { subjectEncrypted, subjectIv } = JSON.parse(subject);
|
||||||
const iv = CryptoJS.enc.Hex.parse(subjectIv);
|
const iv = CryptoJS.enc.Hex.parse(subjectIv);
|
||||||
|
@ -4,7 +4,7 @@ import { SelectItem } from './selectItem/SelectItem';
|
|||||||
import { useCardSelect } from './useCardSelect.hook';
|
import { useCardSelect } from './useCardSelect.hook';
|
||||||
import { Logo } from 'logo/Logo';
|
import { Logo } from 'logo/Logo';
|
||||||
|
|
||||||
export function CardSelect({ filter, unknown, select, selected, markup, emptyMessage }) {
|
export function CardSelect({ filter, unknown, select, selected, markup, emptyMessage, setItem, clearItem }) {
|
||||||
|
|
||||||
const { state } = useCardSelect(filter);
|
const { state } = useCardSelect(filter);
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ export function CardSelect({ filter, unknown, select, selected, markup, emptyMes
|
|||||||
{ state.cards?.length > 0 && (
|
{ state.cards?.length > 0 && (
|
||||||
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.cards} gutter="0"
|
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.cards} gutter="0"
|
||||||
renderItem={item => (
|
renderItem={item => (
|
||||||
<SelectItem item={item} select={select} selected={selected} markup={markup} />
|
<SelectItem item={item} select={select} selected={selected} markup={markup} setItem={setItem} clearItem={clearItem} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -3,7 +3,7 @@ import { SelectItemWrapper, Markup } from './SelectItem.styled';
|
|||||||
import { useSelectItem } from './useSelectItem.hook';
|
import { useSelectItem } from './useSelectItem.hook';
|
||||||
import { Logo } from 'logo/Logo';
|
import { Logo } from 'logo/Logo';
|
||||||
|
|
||||||
export function SelectItem({ item, select, selected, markup }) {
|
export function SelectItem({ item, select, selected, markup, setItem, clearItem }) {
|
||||||
|
|
||||||
const { state } = useSelectItem(item, selected, markup);
|
const { state } = useSelectItem(item, selected, markup);
|
||||||
const profile = item?.data?.cardProfile;
|
const profile = item?.data?.cardProfile;
|
||||||
@ -19,6 +19,12 @@ export function SelectItem({ item, select, selected, markup }) {
|
|||||||
if (select) {
|
if (select) {
|
||||||
select(item.id);
|
select(item.id);
|
||||||
}
|
}
|
||||||
|
if (setItem && !state.selected) {
|
||||||
|
setItem(item.id);
|
||||||
|
}
|
||||||
|
if (clearItem && state.selected) {
|
||||||
|
clearItem(item.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -29,7 +35,7 @@ export function SelectItem({ item, select, selected, markup }) {
|
|||||||
<div class="name">{ profile?.name }</div>
|
<div class="name">{ profile?.name }</div>
|
||||||
<div class="handle">{ handle() }</div>
|
<div class="handle">{ handle() }</div>
|
||||||
</div>
|
</div>
|
||||||
{ select && (
|
{ (select || setItem || clearItem) && (
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
<Switch checked={state.selected} onChange={onSelect} size="small" />
|
<Switch checked={state.selected} onChange={onSelect} size="small" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,7 +132,7 @@ export function useChannels() {
|
|||||||
item.sealKey = sealKey;
|
item.sealKey = sealKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.title == null || typeof item.title !== 'string') {
|
if (item.title == null || item.title === '' || typeof item.title !== 'string') {
|
||||||
item.subject = item.label;
|
item.subject = item.label;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -9,10 +9,38 @@ import { EditSubject } from './editSubject/EditSubject';
|
|||||||
import { EditMembers } from './editMembers/EditMembers';
|
import { EditMembers } from './editMembers/EditMembers';
|
||||||
import { UnlockOutlined, LockFilled } from '@ant-design/icons';
|
import { UnlockOutlined, LockFilled } from '@ant-design/icons';
|
||||||
|
|
||||||
export function Details({ cardId, channelId, closeDetails, closeConversation, openContact }) {
|
export function Details({ closeDetails, closeConversation, openContact }) {
|
||||||
|
|
||||||
const [modal, modalContext] = Modal.useModal();
|
const [modal, modalContext] = Modal.useModal();
|
||||||
const { state, actions } = useDetails(cardId, channelId);
|
const { state, actions } = useDetails();
|
||||||
|
|
||||||
|
const setMember = async (id) => {
|
||||||
|
try {
|
||||||
|
await actions.setMember(id);
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
modal.error({
|
||||||
|
title: 'Failed to Set Conversation Member',
|
||||||
|
content: 'Please try again.',
|
||||||
|
bodyStyle: { padding: 16 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearMember = async (id) => {
|
||||||
|
try {
|
||||||
|
await actions.clearMember(id);
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
modal.error({
|
||||||
|
title: 'Failed to Clear Conversation Member',
|
||||||
|
content: 'Please try again.',
|
||||||
|
bodyStyle: { padding: 16 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteChannel = async () => {
|
const deleteChannel = async () => {
|
||||||
modal.confirm({
|
modal.confirm({
|
||||||
@ -30,7 +58,7 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
|
|
||||||
const applyDeleteChannel = async () => {
|
const applyDeleteChannel = async () => {
|
||||||
try {
|
try {
|
||||||
await actions.deleteChannel();
|
await actions.removeChannel();
|
||||||
closeConversation();
|
closeConversation();
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
@ -58,7 +86,7 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
|
|
||||||
const applyLeaveChannel = async () => {
|
const applyLeaveChannel = async () => {
|
||||||
try {
|
try {
|
||||||
await actions.leaveChannel();
|
await actions.removeChannel();
|
||||||
closeConversation();
|
closeConversation();
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
@ -121,19 +149,24 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<Logo width={72} height={72} radius={4} img={state.img} />
|
<Logo src={state.logo} width={72} height={72} radius={4} img={state.img} />
|
||||||
</div>
|
</div>
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
{ state.host && (
|
{ state.host && (
|
||||||
<div class="subject" onClick={actions.setEditSubject}>
|
<div class="subject" onClick={actions.setEditSubject}>
|
||||||
{ state.locked && !state.unlocked && (
|
{ state.sealed && !state.contentKey && (
|
||||||
<LockFilled style={{ paddingRight: 4 }} />
|
<LockFilled style={{ paddingRight: 4 }} />
|
||||||
)}
|
)}
|
||||||
{ state.locked && state.unlocked && (
|
{ state.sealed && state.contentKey && (
|
||||||
<UnlockOutlined style={{ paddingRight: 4 }} />
|
<UnlockOutlined style={{ paddingRight: 4 }} />
|
||||||
)}
|
)}
|
||||||
<span>{ state.subject }</span>
|
{ state.title && (
|
||||||
{ state.editable && (
|
<span>{ state.title }</span>
|
||||||
|
)}
|
||||||
|
{ !state.title && (
|
||||||
|
<span>{ state.label }</span>
|
||||||
|
)}
|
||||||
|
{ (!state.sealed || state.contentKey) && (
|
||||||
<span class="edit" onClick={actions.setEditSubject}>
|
<span class="edit" onClick={actions.setEditSubject}>
|
||||||
<EditOutlined style={{ paddingLeft: 4 }}/>
|
<EditOutlined style={{ paddingLeft: 4 }}/>
|
||||||
</span>
|
</span>
|
||||||
@ -142,13 +175,18 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
)}
|
)}
|
||||||
{ !state.host && (
|
{ !state.host && (
|
||||||
<div class="subject">
|
<div class="subject">
|
||||||
{ state.locked && !state.unlocked && (
|
{ state.sealed && !state.contentKey && (
|
||||||
<LockFilled style={{ paddingRight: 4 }} />
|
<LockFilled style={{ paddingRight: 4 }} />
|
||||||
)}
|
)}
|
||||||
{ state.locked && state.unlocked && (
|
{ state.sealed && state.contentKey && (
|
||||||
<UnlockOutlined style={{ paddingRight: 4 }} />
|
<UnlockOutlined style={{ paddingRight: 4 }} />
|
||||||
)}
|
)}
|
||||||
<span>{ state.subject }</span>
|
{ state.title && (
|
||||||
|
<span>{ state.title }</span>
|
||||||
|
)}
|
||||||
|
{ !state.title && (
|
||||||
|
<span>{ state.label }</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{ state.host && (
|
{ state.host && (
|
||||||
@ -163,7 +201,7 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
{ state.host && (
|
{ state.host && (
|
||||||
<div class="button" onClick={deleteChannel}>Delete Topic</div>
|
<div class="button" onClick={deleteChannel}>Delete Topic</div>
|
||||||
)}
|
)}
|
||||||
{ state.host && !state.locked && (
|
{ state.host && !state.sealed && (
|
||||||
<div class="button" onClick={actions.setEditMembers}>Edit Membership</div>
|
<div class="button" onClick={actions.setEditMembers}>Edit Membership</div>
|
||||||
)}
|
)}
|
||||||
{ !state.host && (
|
{ !state.host && (
|
||||||
@ -172,21 +210,21 @@ export function Details({ cardId, channelId, closeDetails, closeConversation, op
|
|||||||
<div class="label">Members</div>
|
<div class="label">Members</div>
|
||||||
<div class="members">
|
<div class="members">
|
||||||
<CardSelect filter={(item) => {
|
<CardSelect filter={(item) => {
|
||||||
if(state.contacts.includes(item.id)) {
|
if(state.members.includes(item.id)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}} unknown={state.unknown}
|
}} unknown={state.unknown}
|
||||||
markup={cardId} />
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Modal title="Edit Subject" centered visible={state.editSubject} footer={editSubjectFooter}
|
<Modal title="Edit Subject" centered visible={state.showEditSubject} footer={editSubjectFooter}
|
||||||
bodyStyle={{ padding: 16 }} onCancel={actions.clearEditSubject}>
|
bodyStyle={{ padding: 16 }} onCancel={actions.clearEditSubject}>
|
||||||
<EditSubject state={state} actions={actions} />
|
<EditSubject subject={state.editSubject} setSubject={actions.setSubjectUpdate} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal title="Edit Members" centered visible={state.editMembers} footer={editMembersFooter}
|
<Modal title="Edit Members" centered visible={state.showEditMembers} footer={editMembersFooter}
|
||||||
bodyStyle={{ padding: 16 }} onCancel={actions.clearEditMembers}>
|
bodyStyle={{ padding: 16 }} onCancel={actions.clearEditMembers}>
|
||||||
<EditMembers state={state} actions={actions} />
|
<EditMembers members={state.editMembers} setMember={setMember} clearMember={clearMember} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</DetailsWrapper>
|
</DetailsWrapper>
|
||||||
);
|
);
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { EditMembersWrapper } from './EditMembers.styled';
|
import { EditMembersWrapper } from './EditMembers.styled';
|
||||||
import { CardSelect } from '../../cardSelect/CardSelect';
|
import { CardSelect } from '../../cardSelect/CardSelect';
|
||||||
|
|
||||||
export function EditMembers({ state, actions }) {
|
export function EditMembers({ members, setMember, clearMember }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditMembersWrapper>
|
<EditMembersWrapper>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<CardSelect
|
<CardSelect
|
||||||
select={actions.onMember}
|
setItem={setMember}
|
||||||
selected={state.members}
|
clearItem={clearMember}
|
||||||
|
selected={members}
|
||||||
filter={(card) => card?.data?.cardDetail?.status === 'connected'}
|
filter={(card) => card?.data?.cardDetail?.status === 'connected'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import { EditSubjectWrapper } from './EditSubject.styled';
|
import { EditSubjectWrapper } from './EditSubject.styled';
|
||||||
|
|
||||||
export function EditSubject({ state, actions }) {
|
export function EditSubject({ subject, setSubject }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditSubjectWrapper>
|
<EditSubjectWrapper>
|
||||||
<Input placeholder="Subject (optional)" spellCheck="false" autocapitalize="word"
|
<Input placeholder="Subject (optional)" spellCheck="false" autocapitalize="word"
|
||||||
value={state.subjectUpdate} onChange={(e) => actions.setSubjectUpdate(e.target.value)} />
|
value={subject} onChange={(e) => setSubject(e.target.value)} />
|
||||||
</EditSubjectWrapper>
|
</EditSubjectWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,190 +1,236 @@
|
|||||||
import { useContext, useState, useEffect } from 'react';
|
import { useContext, useState, useEffect, useRef } from 'react';
|
||||||
import { CardContext } from 'context/CardContext';
|
import { CardContext } from 'context/CardContext';
|
||||||
import { ChannelContext } from 'context/ChannelContext';
|
import { ConversationContext } from 'context/ConversationContext';
|
||||||
import { AccountContext } from 'context/AccountContext';
|
import { AccountContext } from 'context/AccountContext';
|
||||||
|
import { ProfileContext } from 'context/ProfileContext';
|
||||||
import { ViewportContext } from 'context/ViewportContext';
|
import { ViewportContext } from 'context/ViewportContext';
|
||||||
|
import { getCardByGuid } from 'context/cardUtil';
|
||||||
|
import { decryptChannelSubject, updateChannelSubject, getContentKey, getChannelSeals, isUnsealed } from 'context/sealUtil';
|
||||||
|
|
||||||
export function useDetails(cardId, channelId) {
|
export function useDetails() {
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
logo: null,
|
logo: null,
|
||||||
img: null,
|
img: null,
|
||||||
subject: null,
|
|
||||||
server: null,
|
|
||||||
started: null,
|
started: null,
|
||||||
host: null,
|
host: false,
|
||||||
contacts: [],
|
title: null,
|
||||||
members: new Set(),
|
label: null,
|
||||||
editSubject: false,
|
members: [],
|
||||||
editMembers: false,
|
|
||||||
busy: false,
|
|
||||||
subjectUpdate: null,
|
|
||||||
unknown: 0,
|
unknown: 0,
|
||||||
|
|
||||||
|
showEditMembers: false,
|
||||||
|
editMembers: new Set(),
|
||||||
|
|
||||||
|
showEditSubject: false,
|
||||||
|
editSubject: null,
|
||||||
|
|
||||||
display: null,
|
display: null,
|
||||||
locked: false,
|
sealed: false,
|
||||||
unlocked: false,
|
contentKey: null,
|
||||||
editable: false,
|
seals: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const conversation = useContext(ConversationContext);
|
||||||
const card = useContext(CardContext);
|
const card = useContext(CardContext);
|
||||||
const account = useContext(AccountContext);
|
const account = useContext(AccountContext);
|
||||||
const channel = useContext(ChannelContext);
|
const viewport = useContext(ViewportContext);
|
||||||
const viewport = useContext(ViewportContext);
|
const profile = useContext(ProfileContext);
|
||||||
|
|
||||||
|
const cardId = useRef();
|
||||||
|
const channelId = useRef();
|
||||||
|
const key = useRef();
|
||||||
|
const detailRevision = useRef();
|
||||||
|
|
||||||
const updateState = (value) => {
|
const updateState = (value) => {
|
||||||
setState((s) => ({ ...s, ...value }));
|
setState((s) => ({ ...s, ...value }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const { dataType, data } = conversation.state.channel?.data?.channelDetail || {};
|
||||||
|
if (dataType === 'sealed') {
|
||||||
|
try {
|
||||||
|
const { sealKey } = account.state;
|
||||||
|
const seals = getChannelSeals(data);
|
||||||
|
if (isUnsealed(seals, sealKey)) {
|
||||||
|
const decKey = getContentKey(seals, sealKey);
|
||||||
|
updateState({ sealed: true, contentKey: decKey, seals });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateState({ sealed: true, contentKey: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
updateState({ sealed: true, contentKey: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateState({ sealed: false, contentKey: null });
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [account.state.sealKey, conversation.state.channel?.data?.channelDetail]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateState({ display: viewport.state.display });
|
updateState({ display: viewport.state.display });
|
||||||
}, [viewport]);
|
}, [viewport]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let img, subject, subjectUpdate, host, started, contacts, locked, unlocked, editable;
|
|
||||||
let chan;
|
const cardValue = conversation.state.card;
|
||||||
if (cardId) {
|
const channelValue = conversation.state.channel;
|
||||||
const cardChan = card.state.cards.get(cardId);
|
|
||||||
if (cardChan) {
|
// extract channel created info
|
||||||
chan = cardChan.channels.get(channelId);
|
let started;
|
||||||
}
|
let host;
|
||||||
|
const date = new Date(channelValue?.data?.channelDetail?.created * 1000);
|
||||||
|
const now = new Date();
|
||||||
|
if(now.getTime() - date.getTime() < 86400000) {
|
||||||
|
started = date.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chan = channel.state.channels.get(channelId);
|
started = date.toLocaleDateString("en-US");
|
||||||
}
|
}
|
||||||
|
if (cardValue) {
|
||||||
if (chan) {
|
host = false;
|
||||||
if (chan.contacts?.length === 0) {
|
|
||||||
img = 'solution';
|
|
||||||
subject = 'Notes';
|
|
||||||
}
|
|
||||||
else if (chan.contacts?.length > 1) {
|
|
||||||
img = 'appstore'
|
|
||||||
subject = 'Group';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
img = 'team';
|
|
||||||
subject = 'Conversation'
|
|
||||||
}
|
|
||||||
if (chan.data.channelDetail.dataType === 'sealed') {
|
|
||||||
editable = false;
|
|
||||||
try {
|
|
||||||
const seals = JSON.parse(chan.data.channelDetail.data).seals;
|
|
||||||
seals.forEach(seal => {
|
|
||||||
if (account.state.sealKey?.public === seal.publicKey) {
|
|
||||||
editable = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
locked = true;
|
|
||||||
unlocked = chan.data.unsealedChannel != null;
|
|
||||||
if (chan.data.unsealedChannel?.subject) {
|
|
||||||
subject = chan.data.unsealedChannel.subject;
|
|
||||||
subjectUpdate = subject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
locked = false;
|
|
||||||
editable = (chan.cardId == null);
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(chan.data.channelDetail.data);
|
|
||||||
if (parsed.subject) {
|
|
||||||
subject = parsed.subject;
|
|
||||||
subjectUpdate = subject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const date = new Date(chan.data.channelDetail.created * 1000);
|
|
||||||
const now = new Date();
|
|
||||||
if(now.getTime() - date.getTime() < 86400000) {
|
|
||||||
started = date.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
started = date.toLocaleDateString("en-US");
|
|
||||||
}
|
|
||||||
if (chan.cardId) {
|
|
||||||
host = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
host = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chan?.contacts ) {
|
|
||||||
contacts = chan.contacts.map((contact) => contact?.id);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
contacts = [];
|
host = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let members = new Set(contacts);
|
// extract member info
|
||||||
|
let memberCount = 0;
|
||||||
|
let names = [];
|
||||||
|
let img;
|
||||||
|
let logo;
|
||||||
|
let members = [];
|
||||||
let unknown = 0;
|
let unknown = 0;
|
||||||
contacts.forEach(id => {
|
if (cardValue) {
|
||||||
if (id == null) {
|
members.push(cardValue.id);
|
||||||
unknown++;
|
const profile = cardValue.data?.cardProfile;
|
||||||
|
if (profile?.name) {
|
||||||
|
names.push(profile.name);
|
||||||
}
|
}
|
||||||
});
|
if (profile?.imageSet) {
|
||||||
|
img = null;
|
||||||
|
logo = card.actions.getCardImageUrl(cardValue.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
img = 'avatar';
|
||||||
|
logo = null;
|
||||||
|
}
|
||||||
|
memberCount++;
|
||||||
|
}
|
||||||
|
if (channelValue?.data?.channelDetail?.members) {
|
||||||
|
for (let guid of channelValue.data.channelDetail.members) {
|
||||||
|
if (guid !== profile.state.identity.guid) {
|
||||||
|
const contact = getCardByGuid(card.state.cards, guid);
|
||||||
|
if (contact) {
|
||||||
|
members.push(contact.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unknown++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = contact?.data?.cardProfile;
|
||||||
|
if (profile?.name) {
|
||||||
|
names.push(profile.name);
|
||||||
|
}
|
||||||
|
if (profile?.imageSet) {
|
||||||
|
img = null;
|
||||||
|
logo = card.actions.getCardImageUrl(contact.id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
img = 'avatar';
|
||||||
|
logo = null;
|
||||||
|
}
|
||||||
|
memberCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateState({ img, subject, host, started, contacts, members, unknown, subjectUpdate, locked, unlocked, editable });
|
let label;
|
||||||
}, [cardId, channelId, card, channel, account]);
|
if (memberCount === 0) {
|
||||||
|
img = 'solution';
|
||||||
|
label = 'Notes';
|
||||||
|
}
|
||||||
|
else if (memberCount === 1) {
|
||||||
|
label = names.join(',');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
img = 'appstore';
|
||||||
|
label = names.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardId.current !== cardValue?.id || channelId.current !== channelValue?.id ||
|
||||||
|
detailRevision.current !== channelValue?.data?.detailRevision || key.current !== state.contentKey) {
|
||||||
|
let title;
|
||||||
|
try {
|
||||||
|
const detail = channelValue?.data?.channelDetail;
|
||||||
|
if (detail?.dataType === 'sealed' && state.contentKey) {
|
||||||
|
const unsealed = decryptChannelSubject(detail.data, state.contentKey);
|
||||||
|
title = unsealed.subject;
|
||||||
|
}
|
||||||
|
else if (detail?.dataType === 'superbasic') {
|
||||||
|
const data = JSON.parse(detail.data);
|
||||||
|
title = data.subject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
cardId.current = cardValue?.id;
|
||||||
|
channelId.current = channelValue?.id;
|
||||||
|
detailRevision.current = channelValue?.data?.detailRevision;
|
||||||
|
key.current = state.contentKey;
|
||||||
|
updateState({ started, host, title, label, img, logo, unknown, members,
|
||||||
|
editSubject: title, editMembers: new Set(members) });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateState({ started, host, label, img, logo, unknown, members,
|
||||||
|
editMembers: new Set(members) });
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [conversation.state, card.state, state.contentKey]);
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
setEditSubject: () => {
|
setEditSubject: () => {
|
||||||
updateState({ editSubject: true });
|
updateState({ showEditSubject: true });
|
||||||
},
|
},
|
||||||
clearEditSubject: () => {
|
clearEditSubject: () => {
|
||||||
updateState({ editSubject: false });
|
updateState({ showEditSubject: false });
|
||||||
},
|
},
|
||||||
setSubjectUpdate: (subjectUpdate) => {
|
setSubjectUpdate: (editSubject) => {
|
||||||
updateState({ subjectUpdate });
|
updateState({ editSubject });
|
||||||
},
|
},
|
||||||
setSubject: async () => {
|
setSubject: async () => {
|
||||||
if (!state.busy) {
|
if (state.sealed) {
|
||||||
try {
|
if (state.contentKey) {
|
||||||
updateState({ busy: true });
|
const updated = updateChannelSubject(state.editSubject, state.contentKey);
|
||||||
if (state.locked) {
|
updated.seals = state.seals;
|
||||||
channel.actions.setChannelSealedSubject(channelId, state.subjectUpdate, account.state.sealKey);
|
await conversation.actions.setChannelSubject('sealed', updated);
|
||||||
}
|
|
||||||
else {
|
|
||||||
channel.actions.setChannelSubject(channelId, state.subjectUpdate);
|
|
||||||
}
|
|
||||||
updateState({ busy: false });
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
console.log(err);
|
|
||||||
updateState({ busy: false });
|
|
||||||
throw new Error("set channel subject failed");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new Error('operation in progress');
|
const subject = { subject: state.editSubject };
|
||||||
|
await conversation.actions.setChannelSubject('superbasic', subject);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setEditMembers: () => {
|
setEditMembers: () => {
|
||||||
updateState({ editMembers: true });
|
updateState({ editMembers: new Set(state.members), showEditMembers: true });
|
||||||
},
|
},
|
||||||
clearEditMembers: () => {
|
clearEditMembers: () => {
|
||||||
updateState({ editMembers: false });
|
updateState({ showEditMembers: false });
|
||||||
},
|
},
|
||||||
onMember: async (card) => {
|
setMember: async (id) => {
|
||||||
if (state.members.has(card)) {
|
await conversation.actions.setChannelCard(id);
|
||||||
channel.actions.clearChannelCard(channelId, card);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
channel.actions.setChannelCard(channelId, card);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
deleteChannel: async () => {
|
clearMember: async (id) => {
|
||||||
await channel.actions.removeChannel(channelId);
|
await conversation.actions.clearChannelCard(id);
|
||||||
|
},
|
||||||
|
removeChannel: async () => {
|
||||||
|
await conversation.actions.removeChannel();
|
||||||
},
|
},
|
||||||
leaveChannel: async () => {
|
|
||||||
await card.actions.removeChannel(cardId, channelId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { state, actions };
|
return { state, actions };
|
||||||
|
Loading…
Reference in New Issue
Block a user