mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
refactor of conversation in webapp
This commit is contained in:
parent
7f41ce6566
commit
ba279dfcda
@ -11,9 +11,9 @@ export function getCardByGuid(cards, guid) {
|
||||
export function getProfileByGuid(cards, guid) {
|
||||
const card = getCardByGuid(cards, guid);
|
||||
if (card?.data?.cardProfile) {
|
||||
const { name, handle, imageSet } = card.data.cardProfile;
|
||||
const { name, handle, imageSet, node } = card.data.cardProfile;
|
||||
const cardId = card.id;
|
||||
return { cardId, name, handle, imageSet }
|
||||
return { cardId, name, handle, imageSet, node }
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export function decryptChannelSubject(subject, contentKey) {
|
||||
export function encryptTopicSubject(subject, contentKey) {
|
||||
const iv = CryptoJS.lib.WordArray.random(128 / 8);
|
||||
const key = CryptoJS.enc.Hex.parse(contentKey);
|
||||
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
|
||||
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(subject), key, { iv: iv });
|
||||
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
|
||||
const messageIv = iv.toString();
|
||||
return { messageEncrypted, messageIv };
|
||||
|
@ -185,6 +185,7 @@ export function useCardContext() {
|
||||
else {
|
||||
delta = await getContactChannels(node, token, setNotifiedView, setNotifiedChannel);
|
||||
}
|
||||
|
||||
for (let channel of delta) {
|
||||
if (channel.data) {
|
||||
let cur = card.channels.get(channel.id);
|
||||
@ -299,7 +300,7 @@ export function useCardContext() {
|
||||
const subject = message([]);
|
||||
await addContactChannelTopic(node, token, channelId, type, subject, files);
|
||||
}
|
||||
resyncCard(cardId);
|
||||
//resyncCard(cardId);
|
||||
},
|
||||
removeTopic: async (cardId, channelId, topicId) => {
|
||||
const card = cards.current.get(cardId);
|
||||
|
@ -153,7 +153,7 @@ export function useChannelContext() {
|
||||
const subject = message([]);
|
||||
await addChannelTopic(access.current, channelId, type, subject);
|
||||
}
|
||||
await resync();
|
||||
//await resync();
|
||||
},
|
||||
removeTopic: async (channelId, topicId) => {
|
||||
await removeChannelTopic(access.current, channelId, topicId);
|
||||
|
@ -212,7 +212,7 @@ export function useConversationContext() {
|
||||
delta = await getTopicDelta(cardId, channelId, null, COUNT, null, marker.current);
|
||||
}
|
||||
else {
|
||||
delta = await getTopicDelta(cardId, channelId, topicRevision, null, marker.current, null);
|
||||
delta = await getTopicDelta(cardId, channelId, setTopicRevision.current, null, marker.current, null);
|
||||
}
|
||||
|
||||
for (let topic of delta?.topics) {
|
||||
@ -240,8 +240,9 @@ export function useConversationContext() {
|
||||
}
|
||||
}
|
||||
|
||||
marker.current = delta.marker;
|
||||
marker.current = delta.marker ? delta.marker : marker.current;
|
||||
setTopicRevision.current = topicRevision;
|
||||
|
||||
updateState({ offsync: false, topicRevision: topicRevision, topics: topics.current });
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export function Conversation({ closeConversation, openDetails, cardId, channelId
|
||||
const thread = useRef(null);
|
||||
|
||||
const topicRenderer = (topic) => {
|
||||
return (<TopicItem host={cardId == null} topic={topic} sealed={state.sealed} sealKey={state.sealKey} />)
|
||||
return (<TopicItem host={cardId == null} topic={topic} />)
|
||||
}
|
||||
|
||||
// an unfortunate cludge for the mobile browser
|
||||
@ -107,8 +107,8 @@ export function Conversation({ closeConversation, openDetails, cardId, channelId
|
||||
)}
|
||||
</div>
|
||||
<div class="topic">
|
||||
{ (!state.sealed || state.sealKey) && (
|
||||
<AddTopic cardId={cardId} channelId={channelId} sealed={state.sealed} sealKey={state.sealKey} />
|
||||
{ (!state.sealed || state.contentKey) && (
|
||||
<AddTopic contentKey={state.contentKey} />
|
||||
)}
|
||||
{ state.uploadError && (
|
||||
<div class="upload-error">
|
||||
|
@ -8,10 +8,11 @@ import { AudioFile } from './audioFile/AudioFile';
|
||||
import { VideoFile } from './videoFile/VideoFile';
|
||||
import { Carousel } from 'carousel/Carousel';
|
||||
|
||||
export function AddTopic({ cardId, channelId, sealed, sealKey }) {
|
||||
export function AddTopic({ contentKey }) {
|
||||
|
||||
const { state, actions } = useAddTopic();
|
||||
|
||||
const [modal, modalContext] = Modal.useModal();
|
||||
const { state, actions } = useAddTopic(cardId, channelId);
|
||||
const attachImage = useRef(null);
|
||||
const attachAudio = useRef(null);
|
||||
const attachVideo = useRef(null);
|
||||
@ -27,7 +28,7 @@ export function AddTopic({ cardId, channelId, sealed, sealKey }) {
|
||||
const addTopic = async () => {
|
||||
if (state.messageText || state.assets.length) {
|
||||
try {
|
||||
await actions.addTopic(sealed, sealKey);
|
||||
await actions.addTopic(contentKey);
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
@ -106,22 +107,22 @@ export function AddTopic({ cardId, channelId, sealed, sealKey }) {
|
||||
value={state.messageText} autocapitalize="none" />
|
||||
</div>
|
||||
<div class="buttons">
|
||||
{ !state.sealed && state.enableImage && (
|
||||
{ !contentKey && state.enableImage && (
|
||||
<div class="button space" onClick={() => attachImage.current.click()}>
|
||||
<PictureOutlined />
|
||||
</div>
|
||||
)}
|
||||
{ !state.sealed && state.enableVideo && (
|
||||
{ !contentKey && state.enableVideo && (
|
||||
<div class="button space" onClick={() => attachVideo.current.click()}>
|
||||
<VideoCameraOutlined />
|
||||
</div>
|
||||
)}
|
||||
{ !state.sealed && state.enableAudio && (
|
||||
{ !contentKey && state.enableAudio && (
|
||||
<div class="button space" onClick={() => attachAudio.current.click()}>
|
||||
<SoundOutlined />
|
||||
</div>
|
||||
)}
|
||||
{ !state.sealed && (
|
||||
{ !contentKey && (
|
||||
<div class="bar space" />
|
||||
)}
|
||||
<div class="button space">
|
||||
|
@ -2,14 +2,14 @@ import { useContext, useState, useEffect } from 'react';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
import { ChannelContext } from 'context/ChannelContext';
|
||||
import { ConversationContext } from 'context/ConversationContext';
|
||||
import { encryptTopicSubject } from 'context/sealUtil';
|
||||
|
||||
export function useAddTopic(cardId, channelId) {
|
||||
export function useAddTopic() {
|
||||
|
||||
const [state, setState] = useState({
|
||||
enableImage: null,
|
||||
enableAudio: null,
|
||||
enableVideo: null,
|
||||
sealed: false,
|
||||
assets: [],
|
||||
messageText: null,
|
||||
textColor: '#444444',
|
||||
@ -50,9 +50,9 @@ export function useAddTopic(cardId, channelId) {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const { enableImage, enableAudio, enableVideo, sealed } = conversation.state;
|
||||
updateState({ enableImage, enableAudio, enableVideo, sealed });
|
||||
}, [conversation]);
|
||||
const { enableImage, enableAudio, enableVideo } = conversation.state.channel?.data?.channelDetail || {};
|
||||
updateState({ enableImage, enableAudio, enableVideo });
|
||||
}, [conversation.state.channel?.data?.channelDetail]);
|
||||
|
||||
const actions = {
|
||||
addImage: (image) => {
|
||||
@ -73,7 +73,9 @@ export function useAddTopic(cardId, channelId) {
|
||||
setPosition: (index, position) => {
|
||||
updateAsset(index, { position });
|
||||
},
|
||||
removeAsset: (idx) => { removeAsset(idx) },
|
||||
removeAsset: (idx) => {
|
||||
removeAsset(idx)
|
||||
},
|
||||
setTextColor: (value) => {
|
||||
updateState({ textColorSet: true, textColor: value });
|
||||
},
|
||||
@ -83,31 +85,42 @@ export function useAddTopic(cardId, channelId) {
|
||||
setTextSize: (value) => {
|
||||
updateState({ textSizeSet: true, textSize: value });
|
||||
},
|
||||
addTopic: async (sealed, sealKey) => {
|
||||
addTopic: async (contentKey) => {
|
||||
if (!state.busy) {
|
||||
try {
|
||||
updateState({ busy: true });
|
||||
let message = {
|
||||
text: state.messageText,
|
||||
textColor: state.textColorSet ? state.textColor : null,
|
||||
textSize: state.textSizeSet ? state.textSize : null,
|
||||
const type = contentKey ? 'sealedtopic' : 'superbasictopic';
|
||||
const message = (assets) => {
|
||||
if (contentKey) {
|
||||
if (assets?.length) {
|
||||
console.log('assets not yet supported on sealed channels');
|
||||
}
|
||||
const message = {
|
||||
text: state.messageText,
|
||||
textColor: state.textColorSet ? state.textColor : null,
|
||||
textSize: state.textSizeSet ? state.textSize : null,
|
||||
}
|
||||
return encryptTopicSubject({ message }, contentKey);
|
||||
}
|
||||
else {
|
||||
if (assets?.length) {
|
||||
return {
|
||||
assets,
|
||||
text: state.messageText,
|
||||
textColor: state.textColorSet ? state.textColor : null,
|
||||
textSize: state.textSizeSet ? state.textSize : null,
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
text: state.messageText,
|
||||
textColor: state.textColorSet ? state.textColor : null,
|
||||
textSize: state.textSizeSet ? state.textSize : null,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (cardId) {
|
||||
if (sealed) {
|
||||
await card.actions.addSealedChannelTopic(cardId, channelId, sealKey, message, state.assets);
|
||||
}
|
||||
else {
|
||||
await card.actions.addChannelTopic(cardId, channelId, message, state.assets);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sealed) {
|
||||
await channel.actions.addSealedChannelTopic(channelId, sealKey, message, state.assets);
|
||||
}
|
||||
else {
|
||||
await channel.actions.addChannelTopic(channelId, message, state.assets);
|
||||
}
|
||||
}
|
||||
await conversation.actions.addTopic(type, message, state.assets);
|
||||
updateState({ busy: false, messageText: null, textColor: '#444444', textColorSet: false,
|
||||
textSize: 12, textSizeSet: false, assets: [] });
|
||||
}
|
||||
|
@ -8,141 +8,22 @@ import { Space, Skeleton, Button, Modal, Input } from 'antd';
|
||||
import { ExclamationCircleOutlined, DeleteOutlined, EditOutlined, FireOutlined, PictureOutlined } from '@ant-design/icons';
|
||||
import { Carousel } from 'carousel/Carousel';
|
||||
|
||||
export function TopicItem({ host, topic, sealed, sealKey }) {
|
||||
|
||||
const { state, actions } = useTopicItem(topic, sealed, sealKey);
|
||||
|
||||
let name = state.name ? state.name : state.handle;
|
||||
let nameClass = state.name ? 'set' : 'unset';
|
||||
if (name == null) {
|
||||
name = "unknown contact"
|
||||
nameClass = "unknown"
|
||||
}
|
||||
|
||||
const renderAsset = (asset, idx, topicId) => {
|
||||
if (asset.image) {
|
||||
return <ImageAsset thumbUrl={actions.getAssetUrl(asset.image.thumb, topicId)}
|
||||
fullUrl={actions.getAssetUrl(asset.image.full, topicId)} />
|
||||
}
|
||||
if (asset.video) {
|
||||
return <VideoAsset thumbUrl={actions.getAssetUrl(asset.video.thumb, topicId)}
|
||||
lqUrl={actions.getAssetUrl(asset.video.lq, topicId)} hdUrl={actions.getAssetUrl(asset.video.hd, topicId)} />
|
||||
}
|
||||
if (asset.audio) {
|
||||
return <AudioAsset label={asset.audio.label} audioUrl={actions.getAssetUrl(asset.audio.full, topicId)} />
|
||||
}
|
||||
return <></>
|
||||
}
|
||||
|
||||
const removeTopic = () => {
|
||||
Modal.confirm({
|
||||
title: 'Do you want to delete this message?',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
okText: 'Yes, Delete',
|
||||
cancelText: 'No, Cancel',
|
||||
onOk() { actions.removeTopic() },
|
||||
});
|
||||
}
|
||||
|
||||
const Options = () => {
|
||||
if (state.editing) {
|
||||
return <></>;
|
||||
}
|
||||
if (state.owner) {
|
||||
return (
|
||||
<div class="buttons">
|
||||
<div class="button" onClick={() => actions.setEditing(true)}>
|
||||
<EditOutlined />
|
||||
</div>
|
||||
<div class="button" onClick={() => removeTopic()}>
|
||||
<DeleteOutlined />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (host) {
|
||||
return (
|
||||
<div class="buttons">
|
||||
<div class="button" onClick={() => removeTopic()}>
|
||||
<DeleteOutlined />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const Message = () => {
|
||||
if (state.editing) {
|
||||
return (
|
||||
<div class="editing">
|
||||
<Input.TextArea defaultValue={state.text} placeholder="message"
|
||||
style={{ resize: 'none', color: state.textColor, fontSize: state.textSize }}
|
||||
onChange={(e) => actions.setEdit(e.target.value)} rows={3} bordered={false}/>
|
||||
<div class="controls">
|
||||
<Space>
|
||||
<Button onClick={() => actions.setEditing(false)}>Cancel</Button>
|
||||
<Button type="primary" onClick={() => actions.setMessage()} loading={state.body}>Save</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div style={{ color: state.textColor, fontSize: state.textSize }}>{ state.text }</div>
|
||||
}
|
||||
export function TopicItem({ host, topic }) {
|
||||
|
||||
return (
|
||||
<TopicItemWrapper>
|
||||
{ state.init && (
|
||||
<>
|
||||
<div class="topic-header">
|
||||
<div class="avatar">
|
||||
<Logo width={32} height={32} radius={4} url={state.imageUrl} />
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class={nameClass}>{ name }</div>
|
||||
<div>{ state.created }</div>
|
||||
</div>
|
||||
{ !state.sealed && (
|
||||
<div class="topic-options">
|
||||
<Options />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{ !state.confirmed && (
|
||||
<div class="skeleton">
|
||||
<Skeleton size={'small'} active={true} title={false} />
|
||||
</div>
|
||||
)}
|
||||
{ state.confirmed && (
|
||||
<div>
|
||||
{ state.error && (
|
||||
<div class="asset-placeholder">
|
||||
<FireOutlined style={{ fontSize: 32, color: '#ff8888' }} />
|
||||
</div>
|
||||
)}
|
||||
{ !state.error && !state.ready && (
|
||||
<div class="asset-placeholder">
|
||||
<PictureOutlined style={{ fontSize: 32 }} />
|
||||
</div>
|
||||
)}
|
||||
{ !state.error && state.ready && state.assets.length > 0 && (
|
||||
<div class="topic-assets">
|
||||
<Carousel pad={40} items={state.assets} itemRenderer={renderAsset} />
|
||||
</div>
|
||||
)}
|
||||
<div class="message">
|
||||
{ !state.sealed && (
|
||||
<Message />
|
||||
)}
|
||||
{ state.sealed && (
|
||||
<div class="sealed-message">sealed message</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<div class="topic-header">
|
||||
<div class="avatar">
|
||||
<Logo width={32} height={32} radius={4} url={topic.imageUrl} />
|
||||
</div>
|
||||
<div class="info">
|
||||
<div class={ topic.nameSet ? 'set' : 'unset' }>{ topic.name }</div>
|
||||
<div>{ topic.createdStr }</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message">
|
||||
<div style={{ color: topic.textColor, fontSize: topic.textSize }}>{ topic.text }</div>
|
||||
</div>
|
||||
</TopicItemWrapper>
|
||||
)
|
||||
}
|
||||
|
@ -1,35 +1,42 @@
|
||||
import { useContext, useState, useEffect } from 'react';
|
||||
import { useContext, useRef, useState, useEffect } from 'react';
|
||||
import { ViewportContext } from 'context/ViewportContext';
|
||||
import { AccountContext } from 'context/AccountContext';
|
||||
import { ConversationContext } from 'context/ConversationContext';
|
||||
import { UploadContext } from 'context/UploadContext';
|
||||
import { StoreContext } from 'context/StoreContext';
|
||||
import { CardContext } from 'context/CardContext';
|
||||
import { ProfileContext } from 'context/ProfileContext';
|
||||
import { isUnsealed, getChannelSeals, getContentKey } from 'context/sealUtil';
|
||||
import { JSEncrypt } from 'jsencrypt'
|
||||
|
||||
import { decryptTopicSubject } from 'context/sealUtil';
|
||||
import { getProfileByGuid } from 'context/cardUtil';
|
||||
|
||||
export function useConversation(cardId, channelId) {
|
||||
|
||||
const [state, setState] = useState({
|
||||
display: null,
|
||||
logo: null,
|
||||
subject: null,
|
||||
topics: [],
|
||||
loadingInit: false,
|
||||
loadingMore: false,
|
||||
upload: false,
|
||||
uploadError: false,
|
||||
uploadPercent: 0,
|
||||
error: false,
|
||||
topics: [],
|
||||
loading: false,
|
||||
sealed: false,
|
||||
sealKey: null,
|
||||
delayed: false,
|
||||
contentKey: null,
|
||||
});
|
||||
|
||||
const profile = useContext(ProfileContext);
|
||||
const card = useContext(CardContext);
|
||||
const account = useContext(AccountContext);
|
||||
const viewport = useContext(ViewportContext);
|
||||
const conversation = useContext(ConversationContext);
|
||||
const upload = useContext(UploadContext);
|
||||
const store = useContext(StoreContext);
|
||||
|
||||
const loading = useRef(false);
|
||||
const conversationId = useRef(null);
|
||||
const topics = useRef(new Map());
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
@ -39,19 +46,28 @@ export function useConversation(cardId, channelId) {
|
||||
}, [viewport]);
|
||||
|
||||
useEffect(() => {
|
||||
let sealKey;
|
||||
const seals = conversation.state.seals;
|
||||
if (seals?.length > 0) {
|
||||
seals.forEach(seal => {
|
||||
if (seal.publicKey === account.state.sealKey?.public) {
|
||||
let crypto = new JSEncrypt();
|
||||
crypto.setPrivateKey(account.state.sealKey.private);
|
||||
sealKey = crypto.decrypt(seal.sealedKey);
|
||||
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 contentKey = getContentKey(seals, sealKey);
|
||||
updateState({ sealed: true, contentKey });
|
||||
}
|
||||
});
|
||||
else {
|
||||
updateState({ sealed: true, contentKey: null });
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
updateState({ sealed: true, contentKey: null });
|
||||
}
|
||||
}
|
||||
updateState({ sealed: conversation.state.sealed, sealKey });
|
||||
}, [account.state.sealKey, conversation.state.seals, conversation.state.sealed]);
|
||||
else {
|
||||
updateState({ sealed: false, contentKey: null });
|
||||
}
|
||||
}, [account.state.sealKey, conversation.state.channel?.data?.channelDetail]);
|
||||
|
||||
useEffect(() => {
|
||||
let active = false;
|
||||
@ -83,42 +99,133 @@ export function useConversation(cardId, channelId) {
|
||||
}
|
||||
|
||||
updateState({ upload: active, uploadError, uploadPercent });
|
||||
}, [cardId, channelId, upload]);
|
||||
}, [cardId, channelId, upload.state]);
|
||||
|
||||
const setChannel = async () => {
|
||||
if (!loading.current && conversationId.current) {
|
||||
const { card, channel } = conversationId.current;
|
||||
loading.current = true;
|
||||
conversationId.current = null;
|
||||
updateState({ loading: true });
|
||||
await conversation.setChannel(card, channel);
|
||||
updateState({ loading: false });
|
||||
loading.current = false;
|
||||
await setChannel();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updateState({ delayed: false, topics: [] });
|
||||
setTimeout(() => {
|
||||
updateState({ delayed: true });
|
||||
}, 250);
|
||||
conversation.actions.setChannel(cardId, channelId);
|
||||
// eslint-disable-next-line
|
||||
}, [cardId, channelId]);
|
||||
|
||||
const syncTopic = async (item, value) => {
|
||||
const revision = value.data?.detailRevision;
|
||||
const detail = value.data?.topicDetail || {};
|
||||
const identity = profile.state.identity || {};
|
||||
|
||||
item.create = detail.created;
|
||||
const date = new Date(detail.created * 1000);
|
||||
const now = new Date();
|
||||
const offset = now.getTime() - date.getTime();
|
||||
if(offset < 86400000) {
|
||||
item.createdStr = date.toLocaleTimeString([], {hour: 'numeric', minute:'2-digit'});
|
||||
}
|
||||
else if (offset < 31449600000) {
|
||||
item.createdStr = date.toLocaleDateString("en-US", {day: 'numeric', month:'numeric'});
|
||||
}
|
||||
else {
|
||||
item.createdStr = date.toLocaleDateString("en-US");
|
||||
}
|
||||
|
||||
if (detail.guid === identity.guid) {
|
||||
item.creator = true;
|
||||
item.imageUrl = profile.state.imageUrl;
|
||||
if (identity.name) {
|
||||
item.name = identity.name;
|
||||
item.nameSet = true;
|
||||
}
|
||||
else {
|
||||
item.name = `${identity.handle}@${identity.node}`;
|
||||
item.nameSet = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
item.creator = false;
|
||||
const contact = getProfileByGuid(card.state.cards, detail.guid);
|
||||
if (contact) {
|
||||
item.imageUrl = contact.imageSet ? card.actions.getCardImageUrl(contact.cardId) : null;
|
||||
if (contact.name) {
|
||||
item.name = contact.name;
|
||||
item.nameSet = true;
|
||||
}
|
||||
else {
|
||||
item.name = `${contact.handle}@${contact.node}`;
|
||||
item.nameSet = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
item.imageUrl = null;
|
||||
item.name = 'unknown';
|
||||
item.nameSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.dataType === 'superbasictopic') {
|
||||
if (item.revision !== revision) {
|
||||
try {
|
||||
const message = JSON.parse(detail.data);
|
||||
item.text = message.text;
|
||||
item.textColor = message.textColor ? message.textColor : '#444444';
|
||||
item.textSize = message.textSize ? message.textSize : 14;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (detail.dataType === 'sealedtopic') {
|
||||
if (item.revision !== revision || item.contentKey !== state.contentKey) {
|
||||
item.contentKey = state.contentKey;
|
||||
try {
|
||||
const subject = decryptTopicSubject(detail.data, state.contentKey);
|
||||
item.text = subject.message.text;
|
||||
item.textColor = subject.message.textColor ? subject.message.textColor : '#444444';
|
||||
item.textSize = subject.message.textSize ? subject.message.textSize : 14;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
item.revision = revision;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let topics = Array.from(conversation.state.topics.values()).sort((a, b) => {
|
||||
const aTimestamp = a?.data?.topicDetail?.created;
|
||||
const bTimestamp = b?.data?.topicDetail?.created;
|
||||
if(aTimestamp === bTimestamp) {
|
||||
const messages = new Map();
|
||||
conversation.state.topics.forEach((value, topicId) => {
|
||||
let item = topics.current.get(topicId);
|
||||
if (!item) {
|
||||
item = { topicId };
|
||||
}
|
||||
syncTopic(item, value);
|
||||
messages.set(topicId, item);
|
||||
});
|
||||
topics.current = messages;
|
||||
|
||||
const sorted = Array.from(messages.values()).sort((a, b) => {
|
||||
if(a.created === b.created) {
|
||||
return 0;
|
||||
}
|
||||
if(aTimestamp == null || aTimestamp < bTimestamp) {
|
||||
if(a.created == null || a.created < b.created) {
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
if (topics.length) {
|
||||
updateState({ delayed: false });
|
||||
}
|
||||
else {
|
||||
setTimeout(() => {
|
||||
updateState({ delayed: true });
|
||||
}, 250);
|
||||
}
|
||||
const { error, loadingInit, loadingMore, subject, logoUrl, logoImg } = conversation.state;
|
||||
updateState({ topics, error, loadingInit, loadingMore, subject, logoUrl, logoImg });
|
||||
store.actions.setValue(`${channelId}::${cardId}`, Number(conversation.state.topicRevision));
|
||||
// eslint-disable-next-line
|
||||
}, [conversation]);
|
||||
|
||||
updateState({ topics: sorted });
|
||||
// eslint-disable-next-line
|
||||
}, [conversation.state, profile.state, card.state, state.contentKey]);
|
||||
|
||||
const actions = {
|
||||
more: () => {
|
||||
|
Loading…
Reference in New Issue
Block a user