From 6b85537400fd2e470e1a4332440f3960d4ebea9e Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Sun, 24 Apr 2022 00:27:28 -0700 Subject: [PATCH] separating contexts --- net/web/src/Api/getCardDetail.js | 9 ++ net/web/src/Api/getCardProfile.js | 8 + net/web/src/Api/getCards.js | 12 ++ net/web/src/Api/getContactProfile.js | 8 + net/web/src/Api/setCardProfile.js | 8 + .../src/AppContext/useAccountContext.hook.js | 25 +-- net/web/src/AppContext/useCardContext.hook.js | 143 +++++++++++++++++- .../src/AppContext/useChannelContext.hook.js | 62 +++++++- .../src/AppContext/useGroupContext.hook.js | 50 +++--- .../src/AppContext/useProfileContext.hook.js | 27 ++-- .../User/Conversation/useConversation.hook.js | 3 +- 11 files changed, 297 insertions(+), 58 deletions(-) create mode 100644 net/web/src/Api/getCardDetail.js create mode 100644 net/web/src/Api/getCardProfile.js create mode 100644 net/web/src/Api/getCards.js create mode 100644 net/web/src/Api/getContactProfile.js create mode 100644 net/web/src/Api/setCardProfile.js diff --git a/net/web/src/Api/getCardDetail.js b/net/web/src/Api/getCardDetail.js new file mode 100644 index 00000000..69be56ab --- /dev/null +++ b/net/web/src/Api/getCardDetail.js @@ -0,0 +1,9 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getCardDetail(token, cardId) { + let param = "?agent=" + token + let detail = await fetchWithTimeout(`/contact/cards/${cardId}/detail${param}`, { method: 'GET' }); + checkResponse(detail); + return await detail.json() +} + diff --git a/net/web/src/Api/getCardProfile.js b/net/web/src/Api/getCardProfile.js new file mode 100644 index 00000000..974e6712 --- /dev/null +++ b/net/web/src/Api/getCardProfile.js @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getCardProfile(token, cardId) { + let profile = await fetchWithTimeout(`/contact/cards/${cardId}/profile?agent=${token}`, { method: 'GET' }); + checkResponse(profile); + return await profile.json() +} + diff --git a/net/web/src/Api/getCards.js b/net/web/src/Api/getCards.js new file mode 100644 index 00000000..a05dad31 --- /dev/null +++ b/net/web/src/Api/getCards.js @@ -0,0 +1,12 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getCards(token, revision) { + let param = "agent=" + token + if (revision != null) { + param += '&revision=' + revision + } + let cards = await fetchWithTimeout(`/contact/cards?${param}`, { method: 'GET' }); + checkResponse(cards) + return await cards.json() +} + diff --git a/net/web/src/Api/getContactProfile.js b/net/web/src/Api/getContactProfile.js new file mode 100644 index 00000000..611f42fb --- /dev/null +++ b/net/web/src/Api/getContactProfile.js @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getContactProfile(server, guid, token) { + let profile = await fetchWithTimeout(`https://${server}/profile/message?contact=${guid}.${token}`, { method: 'GET', }); + checkResponse(profile); + return await profile.json() +} + diff --git a/net/web/src/Api/setCardProfile.js b/net/web/src/Api/setCardProfile.js new file mode 100644 index 00000000..7c6a2215 --- /dev/null +++ b/net/web/src/Api/setCardProfile.js @@ -0,0 +1,8 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function setCardProfile(token, cardId, message) { + let profile = await fetchWithTimeout(`/contact/cards/${cardId}/profile?agent=${token}`, { method: 'PUT', body: JSON.stringify(message) }); + checkResponse(profile); + return await profile.json() +} + diff --git a/net/web/src/AppContext/useAccountContext.hook.js b/net/web/src/AppContext/useAccountContext.hook.js index 0d50833b..eb8a5030 100644 --- a/net/web/src/AppContext/useAccountContext.hook.js +++ b/net/web/src/AppContext/useAccountContext.hook.js @@ -4,37 +4,40 @@ import { getAccountStatus } from '../Api/getAccountStatus'; export function useAccountContext() { const [state, setState] = useState({ - token: null, - revision: 0, status: null, }); + const access = useRef(null); + const revision = useRef(null); const next = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - const setStatus = async (revision) => { + const setStatus = async (rev) => { if (next.current == null) { - let status = await getAccountStatus(state.token); - updateState({ revision, status }); + if (revision.current != rev) { + let status = await getAccountStatus(access.current); + updateState({ status }); + revision.current = rev; + } if (next.current != null) { - let rev = next.current; + let r = next.current; next.current = null; - setStatus(rev); + setStatus(r); } } else { - next.current = revision; + next.current = rev; } } const actions = { setToken: async (token) => { - updateState({ token }); + access.current = token; }, - setRevision: async (revision) => { - setStatus(revision); + setRevision: async (rev) => { + setStatus(rev); }, setSearchable: async (flag) => { await setAccountSearchable(state.token, flag); diff --git a/net/web/src/AppContext/useCardContext.hook.js b/net/web/src/AppContext/useCardContext.hook.js index b84b8ae2..31711446 100644 --- a/net/web/src/AppContext/useCardContext.hook.js +++ b/net/web/src/AppContext/useCardContext.hook.js @@ -1,24 +1,157 @@ import { useEffect, useState, useRef } from 'react'; +import { getContactChannels } from '../Api/getContactChannels'; +import { getContactChannel } from '../Api/getContactChannel'; +import { getContactProfile } from '../Api/getContactProfile'; +import { setCardProfile } from '../Api/setCardProfile'; +import { getCards } from '../Api/getCards'; +import { getCardImageUrl } from '../Api/getCardImageUrl'; +import { getCardProfile } from '../Api/getCardProfile'; +import { getCardDetail } from '../Api/getCardDetail'; export function useCardContext() { const [state, setState] = useState({ - token: null, - revision: 0, + cards: new Map(), }); + const access = useRef(null); + const revision = useRef(null); + const next = useRef(null); + const cards = useRef(new Map()); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } + const updateCards = async () => { + let delta = await getCards(access.current, revision.current); + for (let card of delta) { + if (card.data) { + let cur = cards.current.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(access.current, card.id); + } + 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(access.current, 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(access.current, 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; + } + 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; + } + } + cur.revision = card.revision; + cards.current.set(card.id, cur); + } + else { + cards.current.delete(card.id); + } + } + } + + const updateContactChannels = async (token, viewRevision, channelRevision, channelMap) => { + let delta = await getContactChannels(token, viewRevision, channelRevision); + for (let channel of delta) { + 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); + } + } + } + + const updateContactArticles = async (token, viewRevision, articleRevision, articleMap) => { + console.log("update contact articles"); + } + + 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; + setCards(r); + } + } + else { + next.current = rev; + } + } + const actions = { setToken: async (token) => { - updateState({ token }); + access.current = token; }, - setRevision: async (revision) => { - updateState({ revision }); + setRevision: async (rev) => { + setCards(rev); }, } +console.log(state); + return { state, actions } } diff --git a/net/web/src/AppContext/useChannelContext.hook.js b/net/web/src/AppContext/useChannelContext.hook.js index aeaa8deb..748d43ad 100644 --- a/net/web/src/AppContext/useChannelContext.hook.js +++ b/net/web/src/AppContext/useChannelContext.hook.js @@ -1,21 +1,73 @@ import { useEffect, useState, useRef } from 'react'; +import { getChannels } from '../Api/getChannels'; +import { getChannel } from '../Api/getChannel'; export function useChannelContext() { const [state, setState] = useState({ - token: null, - revision: 0, + channels: new Map(), }); + const access = useRef(null); + const revision = useRef(null); + const channels = useRef(new Map()); + const next = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } + const updateChannels = async () => { + let delta = await getChannels(access.current, revision.current); + for (let channel of delta) { + if (channel.data) { + let cur = channels.current.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(state.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; + channels.current.set(channel.id, cur); + } + else { + channels.current.delete(channel.id); + } + } + } + + const setChannels = async (rev) => { + if (next.current == null) { + if (revision.current != rev) { + await updateChannels(); + updateState({ channels: channels.current }); + revision.current = rev; + } + if (next.current != null) { + let r = next.current; + next.current = null; + setChannels(r); + } + } + else { + next.current = rev; + } + } + const actions = { setToken: (token) => { - updateState({ token }); + access.current = token; }, - setRevision: async (revision) => { - updateState({ revision }); + setRevision: async (rev) => { + setChannels(rev); }, } diff --git a/net/web/src/AppContext/useGroupContext.hook.js b/net/web/src/AppContext/useGroupContext.hook.js index 91ed93e9..be5679d8 100644 --- a/net/web/src/AppContext/useGroupContext.hook.js +++ b/net/web/src/AppContext/useGroupContext.hook.js @@ -3,49 +3,53 @@ import { getGroups } from '../Api/getGroups'; export function useGroupContext() { const [state, setState] = useState({ - token: null, - revision: null, groups: new Map(), }); - const next = useRef(null); + const access = useRef(null); + const revision = useRef(null); const groups = useRef(new Map()); - - useEffect(() => { - }, []); + const next = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - const setGroups = async (revision) => { - if (next.current == null) { - let delta = await getGroups(state.token, state.revision); - for (let group of delta) { - if (group.data) { - groups.set(group.id, group); - } - else { - groups.delete(group.id); - } + const updateGroups = async () => { + let delta = await getGroups(access.current, revision.current); + for (let group of delta) { + if (group.data) { + groups.set(group.id, group); + } + else { + groups.delete(group.id); + } + } + } + + const setGroups = async (rev) => { + if (next.current == null) { + if (revision.current != rev) { + await updateGroups(); + updateState({ groups: groups.current }); + revision.current = rev; } - updateState({ revision, groups }); if (next.current != null) { - let rev = next.current; + let r = next.current; next.current = null; - setGroups(rev); + setGroups(r); } } else { - next.current = revision; + next.current = rev; } } const actions = { setToken: async (token) => { - updateState({ token }); + access.current = token; }, - setRevision: async (revision) => { - setGroups(revision); + setRevision: async (rev) => { + setGroups(rev); }, } diff --git a/net/web/src/AppContext/useProfileContext.hook.js b/net/web/src/AppContext/useProfileContext.hook.js index 91415773..23f22abf 100644 --- a/net/web/src/AppContext/useProfileContext.hook.js +++ b/net/web/src/AppContext/useProfileContext.hook.js @@ -6,37 +6,40 @@ import { getProfileImageUrl } from '../Api/getProfileImageUrl'; export function useProfileContext() { const [state, setState] = useState({ - token: null, - revision: 0, profile: {}, }); + const access = useRef(null); + const revision = useRef(null); const next = useRef(null); const updateState = (value) => { setState((s) => ({ ...s, ...value })) } - const setProfile = async (revision) => { + const setProfile = async (rev) => { if (next.current == null) { - let profile = await getProfile(state.token); - updateState({ revision, profile }); + if (revision.current != rev) { + let profile = await getProfile(access.current); + updateState({ profile }); + revision.current = rev; + } if (next.current != null) { - let rev = next.current; + let r = next.current; next.current = null; - setProfile(rev); + setProfile(r); } } else { - next.current = revision; + next.current = rev; } } const actions = { setToken: (token) => { - updateState({ token }); + access.current = token; }, - setRevision: (revision) => { - setProfile(revision); + setRevision: (rev) => { + setProfile(rev); }, setProfileData: async (name, location, description) => { await setProfileData(state.token, name, location, description); @@ -44,7 +47,7 @@ export function useProfileContext() { setProfileImage: async (image) => { await setProfileImage(state.token, image); }, - profileImageUrl: () => getProfileImageUrl(state.token, state.Data?.profile?.revision), + profileImageUrl: () => getProfileImageUrl(access.current, revision.current), } return { state, actions } diff --git a/net/web/src/User/Conversation/useConversation.hook.js b/net/web/src/User/Conversation/useConversation.hook.js index 91c11f71..f78984c4 100644 --- a/net/web/src/User/Conversation/useConversation.hook.js +++ b/net/web/src/User/Conversation/useConversation.hook.js @@ -37,7 +37,6 @@ export function useConversation() { if (conversation?.revision != state.revision) { let token = contact.data.cardProfile.guid + "." + contact.data.cardDetail.token; let slots = await getContactChannelTopics(token, channel, state.revision); - for (let topic of slots) { if (topic.data == null) { topics.current.delete(topic.id); @@ -65,7 +64,7 @@ export function useConversation() { updateState({ topics: Array.from(topics.current.values()), - revision: conversation.Revision, + revision: conversation.revision, }); } }