more context refactor

This commit is contained in:
Roland Osborne 2022-04-25 14:29:57 -07:00
parent a10050b118
commit cfb827db3a
12 changed files with 138 additions and 334 deletions

View File

@ -1,7 +1,7 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getContactChannelTopic(token, channelId, topicId) {
let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics/${topicId}/detail?contact=${token}`,
export async function getContactChannelTopic(server, token, channelId, topicId) {
let topic = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics/${topicId}/detail?contact=${token}`,
{ method: 'GET' });
checkResponse(topic)
return await topic.json()

View File

@ -1,11 +1,11 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getContactChannelTopics(token, channelId, revision) {
export async function getContactChannelTopics(server, token, channelId, revision) {
let rev = ''
if (revision != null) {
rev = `&revision=${revision}`
}
let topics = await fetchWithTimeout(`/content/channels/${channelId}/topics?contact=${token}${rev}`,
let topics = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?contact=${token}${rev}`,
{ method: 'GET' });
checkResponse(topics)
return await topics.json()

View File

@ -1,10 +1,5 @@
import { useEffect, useState, useRef, useContext } from 'react';
import { getContactProfile, setCardProfile, getCards, getCardImageUrl, getCardProfile, getCardDetail, getListingImageUrl, getListing, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
import { getChannels } from '../Api/getChannels';
import { getChannel } from '../Api/getChannel';
import { getContactChannels } from '../Api/getContactChannels';
import { getContactChannel } from '../Api/getContactChannel';
import { getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
import { AccountContext } from './AccountContext';
import { ProfileContext } from './ProfileContext';
import { ArticleContext } from './ArticleContext';
@ -12,146 +7,6 @@ import { GroupContext } from './GroupContext';
import { CardContext } from './CardContext';
import { ChannelContext } from './ChannelContext';
async function updateChannels(token, revision, channelMap, mergeChannels) {
let channels = await getChannels(token, revision);
for (let channel of channels) {
if (channel.data) {
let cur = channelMap.get(channel.id);
if (cur == null) {
cur = { id: channel.id, data: { } }
}
if (cur.data.detailRevision != channel.data.detailRevision) {
if (channel.data.channelDetail != null) {
cur.data.channelDetail = channel.data.channelDetail;
cur.data.detailRevision = channel.data.detailRevision;
}
else {
let slot = await getChannel(token, channel.id);
cur.data.channelDetail = slot.data.channelDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.data.topicRevision = channel.data.topicRevision;
cur.revision = channel.revision;
channelMap.set(channel.id, cur);
}
else {
channelMap.delete(channel.id);
}
}
mergeChannels();
}
async function updateCards(token, revision, cardMap, updateData, mergeChannels) {
let cards = await getCards(token, revision);
for (let card of cards) {
if (card.data) {
let cur = cardMap.get(card.id);
if (cur == null) {
cur = { id: card.id, data: { articles: new Map() }, channels: new Map() }
}
if (cur.data.detailRevision != card.data.detailRevision) {
if (card.data.cardDetail != null) {
cur.data.cardDetail = card.data.cardDetail;
}
else {
cur.data.cardDetail = await getCardDetail(token, card.id);
}
mergeChannels();
cur.data.detailRevision = card.data.detailRevision;
}
if (cur.data.profileRevision != card.data.profileRevision) {
if (card.data.cardProfile != null) {
cur.data.cardProfile = card.data.cardProfile;
}
else {
cur.data.cardProfile = await getCardProfile(token, card.id);
}
cur.data.profileRevision = card.data.profileRevision;
}
const { cardDetail, cardProfile } = cur.data;
if (cardDetail.status === 'connected') {
if (cur.data.profileRevision != card.data.notifiedProfile) {
let message = await getContactProfile(cardProfile.node, cardProfile.guid, cardDetail.token);
await setCardProfile(token, card.id, message);
// update remote profile
cur.data.notifiedProfile = card.data.notifiedProfile;
}
if (cur.data.notifiedView != card.data.notifiedView) {
// update remote articles and channels
cur.data.articles = new Map();
cur.channels = new Map();
let contactToken = cur.data.cardProfile.guid + "." + cur.data.cardDetail.token
await updateContactChannels(contactToken, cur.data.notifiedView, cur.dataNotifiedChannel, cur.channels);
await updateContactArticles(contactToken, cur.data.notifiedView, cur.dataNotifiedArticle, cur.data.articles);
// update view
cur.data.notifiedArticle = card.data.notifiedArticle;
cur.data.notifiedChannel = card.data.notifiedChannel;
cur.data.notifiedView = card.data.notifiedView;
mergeChannels();
}
if (cur.data.notifiedArticle != card.data.notifiedArticle) {
// update remote articles
let contactToken = cur.data.cardProfile.guid + "." + cur.data.cardDetail.token
await updateContactArticles(contactToken, cur.data.notifiedView, cur.dataNotifiedArticle, cur.data.articles);
cur.data.notifiedArticle = card.data.notifiedArticle;
}
if (cur.data.notifiedChannel != card.data.notifiedChannel) {
// update remote channels
let contactToken = cur.data.cardProfile.guid + "." + cur.data.cardDetail.token
await updateContactChannels(contactToken, cur.data.notifiedView, cur.dataNotifiedChannel, cur.channels);
cur.data.notifiedChannel = card.data.notifiedChannel;
mergeChannels();
}
}
cur.revision = card.revision;
cardMap.set(card.id, cur);
}
else {
cardMap.delete(card.id);
mergeChannels();
}
}
updateData({ cards: Array.from(cardMap.values()) });
}
async function updateContactChannels(token, viewRevision, channelRevision, channelMap) {
let channels = await getContactChannels(token, viewRevision, channelRevision);
for (let channel of channels) {
if (channel.data) {
let cur = channelMap.get(channel.id);
if (cur == null) {
cur = { id: channel.id, data: { } }
}
if (cur.data.detailRevision != channel.data.detailRevision) {
if (channel.data.channelDetail != null) {
cur.data.channelDetail = channel.data.channelDetail;
cur.data.detailRevision = channel.data.detailRevision;
}
else {
let slot = await getContactChannel(token, channel.id);
cur.data.channelDetail = slot.data.channelDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.data.topicRevision = channel.data.topicRevision;
cur.revision = channel.revision;
channelMap.set(channel.id, cur);
}
else {
channelMap.delete(channel.id);
}
}
}
async function updateContactArticles(token, viewRevision, articleRevision, articleMap) {
console.log("update contact attributes");
}
async function appCreate(username, password, updateState, setWebsocket) {
await createAccount(username, password);
let access = await setLogin(username, password)
@ -177,15 +32,10 @@ export function useAppContext() {
const [state, setState] = useState(null);
const [appRevision, setAppRevision] = useState();
const cardRevision = useRef(null);
const channelRevision = useRef(null);
const channels = useRef(new Map());
const cards = useRef(new Map());
const delay = useRef(2);
const ws = useRef(null);
const revision = useRef(null);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }))
}
@ -203,46 +53,8 @@ export function useAppContext() {
const groupContext = useContext(GroupContext);
const articleContext = useContext(ArticleContext);
const mergeChannels = () => {
let merged = [];
cards.current.forEach((value, key, map) => {
if (value?.data?.cardDetail?.status === 'connected') {
value.channels.forEach((slot, key, map) => {
merged.push({ guid: value?.data?.cardProfile?.guid, cardId: value?.id, channel: slot });
});
}
});
channels.current.forEach((value, key, map) => {
merged.push({ channel: value });
});
updateData({ channels: merged });
}
const getCardByGuid = (guid) => {
let card = null;
cards.current.forEach((value, key, map) => {
if(value?.data?.cardProfile?.guid == guid) {
card = value
}
});
return card;
}
const getConnectedCards = () => {
let connected = []
cards.current.forEach((value, key, map) => {
if(value?.data?.cardDetail?.status === 'connected') {
connected.push(value);
}
});
return connected;
}
const resetData = () => {
revision.current = null;
cardRevision.current = null;
channels.current = new Map();
cards.current = new Map();
setState({});
}
@ -251,13 +63,6 @@ export function useAppContext() {
appLogout(updateState, clearWebsocket);
resetData();
},
getRegistry: async (node) => getListing(node),
getRegistryImageUrl: (server, guid, revision) => getListingImageUrl(server, guid, revision),
getCardImageUrl: (cardId, revision) => getCardImageUrl(state.token, cardId, revision),
getCardByGuid: getCardByGuid,
getCard: (id) => cards.current.get(id),
getChannel: (id) => channels.current.get(id),
getConnectedCards: getConnectedCards,
}
const adminActions = {
@ -289,29 +94,6 @@ export function useAppContext() {
}
}, [appRevision]);
const processRevision = async (token) => {
while(revision.current != null) {
let rev = revision.current;
// update card if revision changed
if (rev.card != cardRevision.current) {
await updateCards(token, cardRevision.current, cards.current, updateData, mergeChannels);
cardRevision.current = rev.card
}
// update channel if revision changed
if (rev.channel != channelRevision.current) {
await updateChannels(token, channelRevision.current, channels.current, mergeChannels);
channelRevision.current = rev.channel
}
// check if new revision was received during processing
if (rev == revision.current) {
revision.current = null
}
}
}
const setWebsocket = (token) => {
accountContext.actions.setToken(token);
@ -324,15 +106,8 @@ export function useAppContext() {
ws.current = new WebSocket("wss://" + window.location.host + "/status");
ws.current.onmessage = (ev) => {
try {
if (revision.current != null) {
revision.current = JSON.parse(ev.data);
}
else {
let rev = JSON.parse(ev.data);
revision.current = rev;
processRevision(token);
setAppRevision(rev);
}
let rev = JSON.parse(ev.data);
setAppRevision(rev);
}
catch (err) {
console.log(err);

View File

@ -13,6 +13,8 @@ import { getCardOpenMessage } from '../Api/getCardOpenMessage';
import { setCardOpenMessage } from '../Api/setCardOpenMessage';
import { getCardCloseMessage } from '../Api/getCardCloseMessage';
import { setCardCloseMessage } from '../Api/setCardCloseMessage';
import { getContactChannelTopics } from '../Api/getContactChannelTopics';
import { getContactChannelTopic } from '../Api/getContactChannelTopic';
import { addCard } from '../Api/addCard';
import { removeCard } from '../Api/removeCard';
@ -69,8 +71,8 @@ export function useCardContext() {
cur.data.articles = new Map();
cur.channels = new Map();
await updateContactChannels(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.dataNotifiedChannel, cur.channels);
await updateContactArticles(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.dataNotifiedArticle, cur.data.articles);
await updateContactChannels(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.data.notifiedChannel, cur.channels);
await updateContactArticles(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.data.notifiedArticle, cur.data.articles);
// update view
cur.data.notifiedArticle = card.data.notifiedArticle;
@ -79,16 +81,19 @@ export function useCardContext() {
}
if (cur.data.notifiedArticle != card.data.notifiedArticle) {
// update remote articles
await updateContactArticles(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.dataNotifiedArticle, cur.data.articles);
await updateContactArticles(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.data.notifiedArticle, cur.data.articles);
cur.data.notifiedArticle = card.data.notifiedArticle;
}
if (cur.data.notifiedChannel != card.data.notifiedChannel) {
// update remote channels
await updateContactChannels(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.dataNotifiedChannel, cur.channels);
console.log("UPDATE CHANNEL: ", cur.data.notifiedChannel, card.data.notifiedChannel);
await updateContactChannels(card.id, cur.data.cardProfile.guid, cur.data.cardDetail.token, cur.data.notifiedView, cur.data.notifiedChannel, cur.channels);
cur.data.notifiedChannel = card.data.notifiedChannel;
}
}
cur.revision = card.revision;
console.log("SAVE:", cur.data.notifiedChannel);
cards.current.set(card.id, cur);
}
else {
@ -98,6 +103,8 @@ export function useCardContext() {
}
const updateContactChannels = async (cardId, guid, token, viewRevision, channelRevision, channelMap) => {
console.log("UPDATE CONTACT CHANNELS: ", viewRevision, channelRevision);
let delta = await getContactChannels(guid + "." + token, viewRevision, channelRevision);
for (let channel of delta) {
if (channel.data) {
@ -132,12 +139,15 @@ export function useCardContext() {
const setCards = async (rev) => {
if (next.current == null) {
await updateCards();
updateState({ cards: cards.current });
revision.current = rev;
if (next.current != null) {
let r = next.current;
next.current = null;
next.current = rev;
if (revision.current != rev) {
await updateCards();
updateState({ cards: cards.current });
revision.current = rev;
}
let r = next.current;
next.current = null;
if (revision.current != r) {
setCards(r);
}
}
@ -164,13 +174,35 @@ export function useCardContext() {
setCards(rev);
},
getCardByGuid: getCardByGuid,
getImageUrl: (cardId, rev) => getCardImageUrl(access.current, cardId, rev),
getImageUrl: (cardId) => {
let { data } = cards.current.get(cardId);
return getCardImageUrl(access.current, cardId, data.profileRevision)
},
addChannelTopic: async (cardId, channelId, message, assets) => {
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
let token = cardProfile.guid + '.' + cardDetail.token;
let node = cardProfile.node;
await addContactChannelTopic(node, token, channelId, message, assets);
},
getChannelRevision: (cardId, channelId) => {
let card = cards.current.get(cardId);
let channel = card.channels.get(channelId);
return channel.revision;
},
getChannelTopics: async (cardId, channelId, revision) => {
let card = cards.current.get(cardId);
let node = card.data.cardProfile.node;
let channel = card.channels.get(channelId);
let token = card.data.cardProfile.guid + '.' + card.data.cardDetail.token;
return await getContactChannelTopics(node, token, channelId, revision);
},
getChannelTopic: async (cardId, channelId, topicId) => {
let card = cards.current.get(cardId);
let node = card.data.cardProfile.node;
let channel = card.channels.get(channelId);
let token = card.data.cardProfile.guid + '.' + card.data.cardDetail.token;
return await getContactChannelTopic(node, token, channelId, topicId);
},
addCard: async (message) => {
return await addCard(access.current, message);
},

View File

@ -3,6 +3,8 @@ import { getChannels } from '../Api/getChannels';
import { getChannel } from '../Api/getChannel';
import { addChannel } from '../Api/addChannel';
import { addChannelTopic } from '../Api/addChannelTopic';
import { getChannelTopics } from '../Api/getChannelTopics';
import { getChannelTopic } from '../Api/getChannelTopic';
export function useChannelContext() {
const [state, setState] = useState({
@ -31,7 +33,7 @@ export function useChannelContext() {
cur.data.detailRevision = channel.data.detailRevision;
}
else {
let slot = await getChannel(state.token, channel.id);
let slot = await getChannel(access.current, channel.id);
cur.data.channelDetail = slot.data.channelDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
@ -48,14 +50,15 @@ export function useChannelContext() {
const setChannels = async (rev) => {
if (next.current == null) {
next.current = rev;
if (revision.current != rev) {
await updateChannels();
updateState({ channels: channels.current });
revision.current = rev;
}
if (next.current != null) {
let r = next.current;
next.current = null;
let r = next.current;
next.current = null;
if (revision.current != r) {
setChannels(r);
}
}
@ -77,6 +80,16 @@ export function useChannelContext() {
addChannelTopic: async (channelId, message, assets) => {
await addChannelTopic(access.current, channelId, message, assets);
},
getChannelRevision: (channelId) => {
let channel = channels.current.get(channelId);
return channel.revision;
},
getChannelTopics: async (channelId, revision) => {
return await getChannelTopics(access.current, channelId, revision);
},
getChannelTopic: async (channelId, topicId) => {
return await getChannelTopic(access.current, channelId, topicId);
},
}
return { state, actions }

View File

@ -159,7 +159,7 @@ export function useContact() {
updateState({ location: profile.location });
updateState({ node: profile.node });
if (contact.data.cardProfile.imageSet) {
updateState({ imageUrl: card.actions.getImageUrl(contact.id, contact.revision) });
updateState({ imageUrl: card.actions.getImageUrl(contact.id) });
}
else {
updateState({ imageUrl: '' });

View File

@ -42,8 +42,9 @@ export const AddTopicWrapper = styled.div`
`;
export const BusySpin = styled(Spin)`
display: flex;
position: absolute;
margin-right: 12px;
padding-right: 12px;
x-index: 10;
`;

View File

@ -1,12 +1,10 @@
import { useContext, useState, useEffect, useRef } from 'react';
import { AppContext } from '../../../AppContext/AppContext';
import { ConversationContext } from '../../../ConversationContext/ConversationContext';
export function useTopicItem() {
const [state, setState] = useState({});
const app = useContext(AppContext);
const conversation = useContext(ConversationContext);
const updateState = (value) => {

View File

@ -1,11 +1,8 @@
import { useContext, useState, useEffect, useRef } from 'react';
import { AppContext } from '../../AppContext/AppContext';
import { useNavigate, useLocation, useParams } from "react-router-dom";
import { getChannelTopics } from '../../Api/getChannelTopics';
import { getChannelTopic } from '../../Api/getChannelTopic';
import { getContactChannelTopics } from '../../Api/getContactChannelTopics';
import { getContactChannelTopic } from '../../Api/getContactChannelTopic';
import { ConversationContext } from '../../ConversationContext/ConversationContext';
import { CardContext } from '../../AppContext/CardContext';
import { ChannelContext } from '../../AppContext/ChannelContext';
export function useConversation() {
@ -15,11 +12,11 @@ export function useConversation() {
const { cardId, channelId } = useParams();
const navigate = useNavigate();
const app = useContext(AppContext);
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const conversation = useContext(ConversationContext);
const topics = useRef(new Map());
console.log("PARAMS: ", cardId, channelId);
const revision = useRef(null);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -33,86 +30,79 @@ console.log("PARAMS: ", cardId, channelId);
const updateConversation = async () => {
if (cardId) {
if(app?.actions?.getCard) {
let contact = app.actions.getCard(cardId);
let conversation = contact.channels.get(channelId);
if (conversation?.revision != state.revision) {
let token = contact.data.cardProfile.guid + "." + contact.data.cardDetail.token;
let slots = await getContactChannelTopics(token, channelId, state.revision);
for (let topic of slots) {
if (topic.data == null) {
topics.current.delete(topic.id);
}
else {
let cur = topics.current.get(topic.id);
if (cur == null) {
cur = { id: topic.id, data: {} };
}
if (topic.data.detailRevision != cur.data.detailRevision) {
if(topic.data.topicDetail != null) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
}
else {
let slot = await getContactChannelTopic(token, channelId, topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.revision = topic.revision;
topics.current.set(topic.id, cur);
}
let rev = card.actions.getChannelRevision(cardId, channelId);
if (revision.current != rev) {
let delta = await card.actions.getChannelTopics(cardId, channelId, revision.current);
for (let topic of delta) {
if (topic.data == null) {
topics.current.delete(topic.id);
}
else {
let cur = topics.current.get(topic.id);
if (cur == null) {
cur = { id: topic.id, data: {} };
}
if (topic.data.detailRevision != cur.data.detailRevision) {
if(topic.data.topicDetail != null) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
}
else {
let slot = await card.actions.getChannelTopic(cardId, channelId, topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.revision = topic.revision;
topics.current.set(topic.id, cur);
}
updateState({
topics: Array.from(topics.current.values()),
revision: conversation.revision,
});
}
updateState({
topics: Array.from(topics.current.values()),
});
revision.current = rev;
}
}
else {
if(app?.actions?.getChannel) {
let conversation = app.actions.getChannel(channelId);
if (conversation?.revision != state.revision) {
let slots = await getChannelTopics(app.state.token, channelId, state.revision);
for (let topic of slots) {
if (topic.data == null) {
topics.current.delete(topic.id);
}
else {
let cur = topics.current.get(topic.id);
if (cur == null) {
cur = { id: topic.id, data: {} };
}
if (topic.data.detailRevision != cur.data.detailRevision) {
if(topic.data.topicDetail != null) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
}
else {
let slot = await getChannelTopic(app.state.token, channelId, topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.revision = topic.revision;
topics.current.set(topic.id, cur);
}
let rev = channel.actions.getChannelRevision(channelId);
if (revision.current != rev) {
let delta = await channel.actions.getChannelTopics(channelId, revision.current);
for (let topic of delta) {
if (topic.data == null) {
topics.current.delete(topic.id);
}
else {
let cur = topics.current.get(topic.id);
if (cur == null) {
cur = { id: topic.id, data: {} };
}
if (topic.data.detailRevision != cur.data.detailRevision) {
if(topic.data.topicDetail != null) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
}
else {
let slot = await channel.actions.getChannelTopic(channelId, topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
cur.revision = topic.revision;
topics.current.set(topic.id, cur);
}
updateState({
topics: Array.from(topics.current.values()),
revision: conversation.revision
});
}
updateState({
topics: Array.from(topics.current.values()),
revision: conversation.revision
});
revision.current = rev;
}
}
}
useEffect(() => {
updateConversation();
}, [app]);
}, [card, channel]);
return { state, actions };
}

View File

@ -25,7 +25,7 @@ export function Cards({ showRegistry }) {
const cardImage = (item) => {
if (actions?.getImageUrl) {
return actions.getImageUrl(item.id, item.revision);
return actions.getImageUrl(item.id);
}
return null
}

View File

@ -1,5 +1,4 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from '../../../AppContext/AppContext';
export function useContacts() {
@ -7,13 +6,9 @@ export function useContacts() {
const actions = {};
const app = useContext(AppContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
useEffect(() => {}, [app])
return { state, actions };
}

View File

@ -169,7 +169,7 @@ export function VirtualList({ items, itemRenderer }) {
}
}
else {
let pos = containers.current[0].top + containers.current[0].height;
let pos = containers.current[0].top + containers.current[0].height + 2 * GUTTER;
for (let i = 1; i < containers.current.length; i++) {
if (containers.current[i].top != pos) {
containers.current[i].top = pos;