more channel view refactor

This commit is contained in:
balzack 2023-01-16 21:25:07 -08:00
parent 658394760b
commit 2f74c8bd3d
5 changed files with 122 additions and 216 deletions

View File

@ -55,7 +55,7 @@ export function Channels({ open, active }) {
{ state.channels.length > 0 && (
<List local={{ emptyText: '' }} itemLayout="horizontal" dataSource={state.channels} gutter="0"
renderItem={item => (
<ChannelItem cardId={itme.cardId} channelId={item.channelId}
<ChannelItem cardId={item.cardId} channelId={item.channelId}
filter={state.filter} openChannel={open} active={active} />
)}
/>

View File

@ -1,38 +1,37 @@
import { Tooltip } from 'antd';
import { ChannelItemWrapper, Markup } from './ChannelItem.styled';
import { Logo } from 'logo/Logo';
import { AppstoreFilled, SolutionOutlined, UnlockOutlined, LockFilled } from '@ant-design/icons';
import { UnlockOutlined, LockFilled } from '@ant-design/icons';
import { useChannelItem } from './useChannelItem.hook';
export function ChannelItem({ cardId, channelId, openChannel, active }) {
export function ChannelItem({ cardId, channelId, filter, openChannel, active }) {
const itemClass = () => {
if (active.set && active.channel === item.channelId && active.card === item.cardId) {
return "active"
}
return "idle"
};
const { state } = useChannelItem(cardId, channelId, filter, active);
return (
<ChannelItemWrapper onClick={() => openChannel(item.channelId, item.cardId)}>
<div class={itemClass()}>
<ChannelItemWrapper style={{ display: (!state.set || !state.visible) ? 'none' : null }}
onClick={() => openChannel(channelId, cardId)}>
<div class={state.active ? 'active' : 'idle'}>
<div class="item">
<div class="avatar">
<Logo url={item.logo} img={item.img} width={32} height={32} radius={8} />
<Logo url={state.logo} img={state.img} width={32} height={32} radius={8} />
</div>
<div class="details">
<div class="subject">
{ item.locked && !item.unlocked && (
{ state.locked && !state.unlocked && (
<LockFilled style={{ paddingRight: 8 }}/>
)}
{ item.locked && item.unlocked && (
{ state.locked && state.unlocked && (
<UnlockOutlined style={{ paddingRight: 8 }}/>
)}
<span>{ item.subject }</span>
<span>{ state.subject }</span>
</div>
<div class="message">{ item.message }</div>
<div class="message">{ state.message }</div>
</div>
{ item.updatedFlag && (
<Markup />
{ state.updatedFlag && (
<Tooltip placement="topRight" title="New Message">
<Markup />
</Tooltip>
)}
</div>
</div>

View File

@ -6,10 +6,19 @@ import { AccountContext } from 'context/AccountContext';
import { ProfileContext } from 'context/ProfileContext';
import { getCardByGuid } from 'context/cardUtil';
export function useChannelItem(cardId, channelId, filter) {
export function useChannelItem(cardId, channelId, filter, active) {
const [state, setState] = useState({
set: false,
visible: false,
active: false,
locked: false,
unlocked: false,
updateFlag: false,
img: null,
logo: null,
subject: null,
message: null,
});
const card = useContext(CardContext);
@ -34,8 +43,31 @@ export function useChannelItem(cardId, channelId, filter) {
useEffect(() => {
sync();
}, [card, channel, store, account, filter]);
// eslint-disable-next-line
}, [card.state, channel.state, store.state, account.state]);
useEffect(() => {
if (cardId === active?.card && channelId === active?.channel) {
updateState({ active: true });
}
else {
updateState({ active: false });
}
}, [cardId, channelId, active]);
useEffect(() => {
if (filter) {
if (state.subject) {
updateState({ visible: state.subject.toUpperCase().includes(filter) });
}
else {
updateState({ visible: false });
}
}
else {
updateState({ visible: true });
}
}, [filter, state.subject]);
const sync = async () => {
if (!syncing.current) {
@ -87,35 +119,34 @@ export function useChannelItem(cardId, channelId, filter) {
const setChannel = (cardId, channelId) => {
if (cardId) {
const cardItem = card.state.cards.get(cardId);
setChannelItem(cardItem?.channels?.get(channelId));
const channelItem = cardItem?.channels?.get(channelId);
setChannelItem(cardItem, channelItem);
setCardRevision.current = cardItem?.revision;
setChannelRevision.current = channelItem?.revision;
}
else {
setChannelItem(channel.state.channels.get(channelId);
const channelItem = channel.state.channels.get(channelId);
setChannelItem(null, channelItem);
setChannelRevision.current = channelItem?.revision;
}
setSealKey.current = account.state.sealKey;
};
const setChannelItem = (item) => {
const setChannelItem = (cardItem, channelItem) => {
if (!item) {
if (!channelItem) {
updateState({ set: false });
return;
}
}
const chan = { set: true };
const chan = {};
chan.cardId = cardId;
chan.channelId = channelId;
chan.revision = value.revision;
chan.updated = value.data?.channelSummary?.lastTopic?.created;
// set updated flag
const key = `${cardId}:${channelId}`
const login = store.state['login:timestamp'];
if (!chan.updated || !login || chan.updated < login) {
chan.updatedFlag = false;
}
else if (store.state[key] && store.state[key] === value.revision) {
else if (store.state[key] && store.state[key] === channelItem.revision) {
chan.updatedFlag = false;
}
else {
@ -127,15 +158,14 @@ export function useChannelItem(cardId, channelId, filter) {
let names = [];
let img = null;
let logo = null;
if (cardId) {
const contact = card.state.cards.get(cardId);
const profile = contact?.data?.cardProfile;
if (cardItem) {
const profile = cardItem?.data?.cardProfile;
if (profile?.name) {
names.push(profile.name);
}
if (profile?.imageSet) {
img = null;
logo = card.actions.getCardImageUrl(contact.id);
logo = card.actions.getCardImageUrl(cardId);
}
else {
img = 'avatar';
@ -143,7 +173,7 @@ export function useChannelItem(cardId, channelId, filter) {
}
memberCount++;
}
for (let guid of value?.data?.channelDetail?.members) {
for (let guid of channelItem?.data?.channelDetail?.members) {
if (guid !== profile.state.identity.guid) {
const contact = getCardByGuid(card.state.cards, guid);
const profile = contact?.data?.cardProfile;
@ -178,7 +208,7 @@ export function useChannelItem(cardId, channelId, filter) {
}
// set subject
const detail = value.data?.channelDetail;
const detail = channelItem.data?.channelDetail;
if (detail?.dataType === 'sealedchannel') {
// handle sealed subject
chan.locked = true;
@ -200,7 +230,7 @@ export function useChannelItem(cardId, channelId, filter) {
}
// set message
const topic = value.data?.channelSummary?.lastTopic;
const topic = channel.data?.channelSummary?.lastTopic;
if (topic?.dataType === 'sealedtopic') {
// handle sealed topic
}
@ -214,12 +244,8 @@ export function useChannelItem(cardId, channelId, filter) {
}
}
return chan;
updateState({ ...chan });
}
useEffect(() => {
// eslint-disable-next-line
}, [account, channel, card, store, filter, state.sealable]);
return { state, actions };
}

View File

@ -1,46 +1,78 @@
import { useContext, useState, useEffect, useRef } from 'react';
import { useContext, useState, useEffect } from 'react';
import { StoreContext } from 'context/StoreContext';
import { ChannelContext } from 'context/ChannelContext';
import { CardContext } from 'context/CardContext';
import { AccountContext } from 'context/AccountContext';
import { ProfileContext } from 'context/ProfileContext';
import { ViewportContext } from 'context/ViewportContext';
import { getCardByGuid } from 'context/cardUtil';
export function useChannels() {
const [state, setState] = useState({
display: null,
channels: [],
showAdd: false,
busy: false,
members: new Set(),
subject: null,
seal: false,
sealable: false,
filter: null,
busy: false,
showAdd: false,
subject: null,
members: new Set(),
seal: false,
});
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const account = useContext(AccountContext);
const store = useContext(StoreContext);
const profile = useContext(ProfileContext);
const viewport = useContext(ViewportContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
const { seal, sealKey } = account.state;
if (seal?.publicKey && sealKey?.public && sealKey?.private && seal.publicKey === sealKey.public) {
updateState({ sealable: true });
updateState({ seal: false, sealable: true });
}
else {
updateState({ seal: false, sealable: false });
}
}, [account]);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {
const merged = [];
card.state.cards.forEach((cardValue, cardId) => {
cardValue.channels.forEach((channelValue, channelId) => {
const updated = channelValue.data?.channelSummary?.lastTopic?.created;
merged.push({ updated, cardId, channelId });
});
});
channel.state.channels.forEach((channelValue, channelId) => {
const updated = channelValue.data?.channelSummary?.lastTopic?.created;
merged.push({ updated, channelId });
});
merged.sort((a, b) => {
const aUpdated = a.updated;
const bUpdated = b.updated;
if (aUpdated === bUpdated) {
return 0;
}
if (!aUpdated || aUpdated < bUpdated) {
return 1;
}
return -1;
});
updateState({ channels: merged });
// eslint-disable-next-line
}, [channel, card]);
useEffect(() => {
updateState({ display: viewport.state.display });
}, [viewport]);
const actions = {
addChannel: async () => {
@ -48,9 +80,9 @@ export function useChannels() {
if (!state.busy) {
try {
updateState({ busy: true });
let cards = Array.from(state.members.values());
const cards = Array.from(state.members.values());
if (state.seal) {
let keys = [ account.state.sealKey.public ];
const keys = [ account.state.sealKey.public ];
cards.forEach(id => {
keys.push(card.state.cards.get(id).data.cardProfile.seal);
});
@ -74,8 +106,8 @@ export function useChannels() {
},
setSeal: (seal) => {
if (seal) {
let cards = Array.from(state.members.values());
let members = new Set(state.members);
const cards = Array.from(state.members.values());
const members = new Set(state.members);
cards.forEach(id => {
if (!(card.state.cards.get(id)?.data?.cardProfile?.seal)) {
members.delete(id);
@ -91,13 +123,13 @@ export function useChannels() {
updateState({ filter: value.toUpperCase() });
},
setShowAdd: () => {
updateState({ showAdd: true, seal: false });
updateState({ showAdd: true, seal: false, members: new Set(), subject: null });
},
clearShowAdd: () => {
updateState({ showAdd: false, members: new Set(), subject: null });
updateState({ showAdd: false });
},
onMember: (string) => {
let members = new Set(state.members);
const members = new Set(state.members);
if (members.has(string)) {
members.delete(string);
}
@ -117,155 +149,5 @@ export function useChannels() {
},
};
// TODO optimize (avoid rebuild object when not needed)
const getChannel = (cardId, channelId, value) => {
const chan = {};
chan.cardId = cardId;
chan.channelId = channelId;
chan.revision = value.revision;
chan.updated = value.data?.channelSummary?.lastTopic?.created;
// set updated flag
const key = `${cardId}:${channelId}`
const login = store.state['login:timestamp'];
if (!chan.updated || !login || chan.updated < login) {
chan.updatedFlag = false;
}
else if (store.state[key] && store.state[key] === value.revision) {
chan.updatedFlag = false;
}
else {
chan.updatedFlag = true;
}
// extract member info
let memberCount = 0;
let names = [];
let img = null;
let logo = null;
if (cardId) {
const contact = card.state.cards.get(cardId);
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++;
}
for (let guid of value?.data?.channelDetail?.members) {
if (guid !== profile.state.identity.guid) {
const contact = getCardByGuid(card.state.cards, guid);
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++;
}
}
// set logo and label
if (memberCount === 0) {
chan.img = 'solution';
chan.label = 'Notes';
}
else if (memberCount === 1) {
chan.logo = logo;
chan.img = img;
chan.label = names.join(',');
}
else {
chan.img = 'appstore';
chan.label = names.join(',');
}
// set subject
const detail = value.data?.channelDetail;
if (detail?.dataType === 'sealedchannel') {
// handle sealed subject
chan.locked = true;
chan.unlocked = false;
}
else if (detail?.dataType === 'superbasic') {
chan.locked = false;
chan.unlocked = true;
try {
const data = JSON.parse(detail.data);
chan.subject = data.subject;
}
catch(err) {
console.log(err);
}
}
if (chan.subject == null) {
chan.subject = chan.label;
}
// set message
const topic = value.data?.channelSummary?.lastTopic;
if (topic?.dataType === 'sealedtopic') {
// handle sealed topic
}
else if (topic?.dataType === 'superbasictopic') {
try {
const data = JSON.parse(topic.data);
chan.message = data.text;
}
catch(err) {
console.log(err);
}
}
return chan;
}
useEffect(() => {
const merged = [];
card.state.cards.forEach((cardValue, cardId) => {
cardValue.channels.forEach((channelValue, channelId) => {
const updated = value.data?.channelSummary?.lastTopic?.created;
merged.push{ updated, cardId, channelId };
});
});
channel.state.channels.forEach((channelValue, channelId) => {
const updated = value.data?.channelSummary?.lastTopic?.created;
merged.push{ updated, channelId };
});
merged.sort((a, b) => {
const aUpdated = a.updated;
const bUpdated = b.updated;
if (aUpdated === bUpdated) {
return 0;
}
if (!aUpdated || aUpdated < bUpdated) {
return 1;
}
return -1;
});
updateState({ channels: merged });
// eslint-disable-next-line
}, [account, channel, card, store, filter, state.sealable]);
useEffect(() => {
updateState({ display: viewport.state.display });
}, [viewport]);
return { state, actions };
}

View File

@ -5,7 +5,6 @@ import { CardContext } from 'context/CardContext';
import { StoreContext } from 'context/StoreContext';
import { ViewportContext } from 'context/ViewportContext';
import { ProfileContext } from 'context/ProfileContext';
import { ChannelContext } from 'context/ChannelContext';
export function useSession() {