adding staggard topic support

This commit is contained in:
Roland Osborne 2022-07-10 00:33:50 -07:00
parent b069392cd8
commit dcf0699a3b
8 changed files with 210 additions and 144 deletions

View File

@ -132,7 +132,7 @@ func GetChannelTopics(w http.ResponseWriter, r *http.Request) {
for _, slot := range slots {
if slot.Topic != nil {
if countSet {
w.Header().Set("Topic-Marker", strconv.FormatUint(uint64(slot.ID), 10))
w.Header().Set("topic-marker", strconv.FormatUint(uint64(slot.ID), 10))
countSet = false
}
response = append(response, getTopicModel(&slot))
@ -140,7 +140,7 @@ func GetChannelTopics(w http.ResponseWriter, r *http.Request) {
}
}
w.Header().Set("Topic-Revision", strconv.FormatInt(channelSlot.Revision, 10))
w.Header().Set("topic-revision", strconv.FormatInt(channelSlot.Revision, 10))
WriteResponse(w, response)
}

View File

@ -41,14 +41,23 @@ export function useConversation() {
}, [cardId, channelId]);
useEffect(() => {
let topics = Array.from(conversation.state.topics.values()).sort((a, b) => {
if (a?.data?.topicDetail?.created > b?.data?.topicDetail?.created) {
return 1;
}
if (a?.data?.topicDetail?.created < b?.data?.topicDetail?.created) {
return -1;
}
return 0;
});
updateState({
init: conversation.state.init,
subject: conversation.state.subject,
contacts: conversation.state.contacts,
cardId: conversation.state.cardId,
channelId: conversation.state.channelId,
topics: Array.from(conversation.state.topics.values()),
members: conversation.state.members,
topics,
});
}, [conversation]);

View File

@ -239,6 +239,20 @@ export function VirtualList({ id, items, itemRenderer }) {
const setItems = () => {
// align containers in case history was loaded
if (containers.current.length > 0) {
let container = containers.current[0];
for (let j = 0; j < itemView.current.length; j++) {
if (itemView.current[j].id == container.id) {
for(let i = 0; i < containers.current.length; i++) {
containers.current[i].index = i + j;
}
break;
}
}
}
// remove containers following any removed item
for (let i = 0; i < containers.current.length; i++) {
let container = containers.current[i];
if (itemView.current.length <= container.index || itemView.current[container.index].id != container.id) {

View File

@ -1,13 +1,29 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getChannelTopics(token, channelId, revision) {
export async function getChannelTopics(token, channelId, revision, count, begin, end) {
let rev = ''
if (revision != null) {
rev = `&revision=${revision}`
}
let topics = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}${rev}`,
let cnt = ''
if (count != null) {
cnt = `&count=${count}`
}
let bgn = ''
if (begin != null) {
bgn = `&begin=${begin}`
}
let edn = ''
if (end != null) {
edn = `&end=${end}`
}
let topics = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}${rev}${cnt}${bgn}${edn}`,
{ method: 'GET' });
checkResponse(topics)
return await topics.json()
return {
marker: topics.headers.get('topic-marker'),
revision: topics.headers.get('topic-revision'),
topics: await topics.json(),
}
}

View File

@ -1,13 +1,29 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function getContactChannelTopics(server, token, channelId, revision) {
export async function getContactChannelTopics(server, token, channelId, revision, count, begin, end) {
let rev = ''
if (revision != null) {
rev = `&revision=${revision}`
}
let topics = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?contact=${token}${rev}`,
let cnt = ''
if (count != null) {
cnt = `&count=${count}`
}
let bgn = ''
if (begin != null) {
bgn = `&begin=${begin}`
}
let edn = ''
if (end != null) {
edn = `&end=${end}`
}
let topics = await fetchWithTimeout(`https://${server}/content/channels/${channelId}/topics?contact=${token}${rev}${cnt}${bgn}${edn}`,
{ method: 'GET' });
checkResponse(topics)
return await topics.json()
return {
marker: topics.headers.get('topic-marker'),
revision: topics.headers.get('topic-revision'),
topics: await topics.json(),
}
}

View File

@ -261,12 +261,12 @@ export function useCardContext() {
let channel = card.channels.get(channelId);
return channel?.revision;
},
getChannelTopics: async (cardId, channelId, revision) => {
getChannelTopics: async (cardId, channelId, revision, count, begin, end) => {
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);
return await getContactChannelTopics(node, token, channelId, revision, count, begin, end);
},
getChannelTopic: async (cardId, channelId, topicId) => {
let card = cards.current.get(cardId);

View File

@ -127,8 +127,8 @@ export function useChannelContext() {
let channel = channels.current.get(channelId);
return channel?.revision;
},
getChannelTopics: async (channelId, revision) => {
return await getChannelTopics(access.current, channelId, revision);
getChannelTopics: async (channelId, revision, count, begin, end) => {
return await getChannelTopics(access.current, channelId, revision, count, begin, end);
},
getChannelTopic: async (channelId, topicId) => {
return await getChannelTopic(access.current, channelId, topicId);

View File

@ -4,6 +4,7 @@ import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
export function useConversationContext() {
const TOPIC_BATCH = 32;
const [state, setState] = useState({
init: false,
@ -15,15 +16,27 @@ export function useConversationContext() {
topics: new Map(),
});
const EVENT_OPEN = 1;
const EVENT_MORE = 2;
const EVENT_UPDATE = 3;
const events = useRef([]);
const channelView = useRef({
cardId: null,
channelId: null,
batch: 1,
revision: null,
begin: null,
init: false,
error: false,
});
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const profile = useContext(ProfileContext);
const topics = useRef(new Map());
const revision = useRef(null);
const count = useRef(0);
const conversationId = useRef(null);
const view = useRef(0);
const gone = useRef(false);
const serialize = useRef(0);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -68,109 +81,119 @@ export function useConversationContext() {
return members;
}
const setTopics = async () => {
const { cardId, channelId } = conversationId.current;
const curRevision = revision.current;
const curView = view.current;
const getChannel = () => {
const { cardId, channelId } = channelView.current;
if (cardId) {
let deltaRevision = card.actions.getChannelRevision(cardId, channelId);
if (!deltaRevision && !gone.current) {
gone.current = true;
window.alert("This converstaion has been removed");
return;
return card.actions.getChannel(cardId, channelId);
}
return channel.actions.getChannel(channelId);
}
const getTopicDelta = async (revision, count, begin, end) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.getChannelTopics(cardId, channelId, revision, count, begin, end);
}
return await channel.actions.getChannelTopics(channelId, revision, count, begin, end);
}
const getTopic = async (topicId) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.getChannelTopic(cardId, channelId, topicId);
}
return await channel.actions.getChannelTopic(channelId, topicId);
}
const getChannelRevision = async () => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.getChannelRevision(cardId, channelId);
}
return await channel.actions.getChannelRevision(channelId);
}
const setTopicDelta = async (delta, curView) => {
for (let topic of delta) {
if (topic.data == null) {
if (curView == view.current) {
topics.current.delete(topic.id);
}
}
if (curRevision != deltaRevision) {
let conversation = card.actions.getChannel(cardId, channelId);
let subject = getSubject(conversation);
let contacts = getContacts(conversation);
let members = getMembers(conversation);
let delta = await card.actions.getChannelTopics(cardId, channelId, curRevision);
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) {
cur.data.topicDetail = topic.data.topicDetail;
cur.data.detailRevision = topic.data.detailRevision;
}
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) {
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);
let slot = await getTopic(topic.id);
cur.data.topicDetail = slot.data.topicDetail;
cur.data.detailRevision = slot.data.detailRevision;
}
}
if (curView == view.current) {
updateState({
init: true,
subject,
contacts,
members,
topics: topics.current,
});
revision.current = deltaRevision;
}
else {
topics.current = new Map();
cur.revision = topic.revision;
if (curView == view.current) {
topics.current.set(topic.id, cur);
}
}
}
else {
let deltaRevision = channel.actions.getChannelRevision(channelId);
if (curRevision != deltaRevision) {
let conversation = channel.actions.getChannel(channelId);
let subject = getSubject(conversation);
let contacts = getContacts(conversation);
let members = getMembers(conversation);
let delta = await channel.actions.getChannelTopics(channelId, curRevision);
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);
}
}
const setTopics = async (ev) => {
const curView = view.current;
try {
if (ev.type == EVENT_OPEN) {
const { cardId, channelId } = ev.data;
channelView.current.cardId = cardId;
channelView.current.channelId = channelId;
channelView.current.batch = 1;
channelView.current.error = false;
channelView.current.init = true;
let delta = await getTopicDelta(null, TOPIC_BATCH, null, null);
await setTopicDelta(delta.topics, curView);
channelView.current.revision = delta.revision;
channelView.current.begin = delta.marker;
}
else if (ev.type == EVENT_MORE) {
if (channelView.current.init) {
channelView.current.batch += 1;
let delta = await getTopicDelta(null, channelView.current.batch * TOPIC_BATCH, null, channelView.current.begin);
await setTopicDelta(delta.topics, curView);
channelView.current.begin = delta.marker;
}
if (curView == view.current) {
updateState({
init: true,
subject,
contacts,
members,
topics: topics.current,
});
revision.current = deltaRevision;
}
else {
topics.current = new Map();
}
else if (ev.type == EVENT_UPDATE) {
let deltaRevision = getChannelRevision();
if (channelView.current.init && deltaRevision != channelView.current.revision) {
let delta = await getTopicDelta(channelView.current.revision, null, channelView.current.begin, null);
await setTopicDelta(delta.topics, curView);
channelView.current.revision = delta.revision
}
}
let chan = getChannel();
let subject = getSubject(chan);
let contacts = getContacts(chan);
let members = getMembers(chan);
updateState({
init: true,
subject,
contacts,
members,
topics: topics.current,
});
}
catch (err) {
console.log(err);
if (!channelView.current.error) {
window.alert("This converstaion failed to update");
channelView.current.error = true;
}
}
}
@ -180,58 +203,47 @@ export function useConversationContext() {
return;
}
const { cardId } = conversationId.current;
if (cardId && card.state.cards.get(cardId)?.data.cardDetail.status != 'connected') {
window.alert("You are disconnected from the host");
conversationId.current = null;
return;
}
if (serialize.current == 0) {
serialize.current++;
if (count.current == 0) {
count.current += 1;
while(count.current > 0) {
try {
await setTopics();
}
catch (err) {
console.log(err);
}
count.current -= 1;
while (events.current.length > 0) {
await setTopics(events.current[0]);
events.current.shift();
}
}
else {
count.current += 1;
serialize.current--;
}
};
useEffect(() => {
if (conversationId.current != null) {
updateConversation();
}
events.current.push({ type: EVENT_UPDATE });
updateConversation();
}, [card, channel]);
const actions = {
setConversationId: (cardId, channelId) => {
view.current += 1;
conversationId.current = { cardId, channelId };
revision.current = null;
gone.current = false;
topics.current = new Map();
updateState({ init: false, subject: null, cardId, channelId, topics: topics.current });
events.current = [{ type: EVENT_OPEN, data: { cardId, channelId }}];
updateState({ init: false, subject: null, cardId, channelId, topics: new Map() });
updateConversation();
},
addHistory: () => {
events.current.push({ type: EVENT_MORE });
updateConversation();
},
setChannelSubject: async (subject) => {
return await channel.actions.setChannelSubject(conversationId.current.channelId, subject);
return await channel.actions.setChannelSubject(channelView.current.channelId, subject);
},
setChannelCard: async (cardId) => {
return await channel.actions.setChannelCard(conversationId.current.channelId, cardId);
return await channel.actions.setChannelCard(channelView.current.channelId, cardId);
},
clearChannelCard: async (cardId) => {
return await channel.actions.clearChannelCard(conversationId.current.channelId, cardId);
return await channel.actions.clearChannelCard(channelView.current.channelId, cardId);
},
getAssetUrl: (topicId, assetId) => {
const { cardId, channelId } = conversationId.current;
if (conversationId.current.cardId) {
const { cardId, channelId } = channelView.current;
if (channelView.current.cardId) {
return card.actions.getContactChannelTopicAssetUrl(cardId, channelId, topicId, assetId);
}
else {
@ -239,7 +251,7 @@ export function useConversationContext() {
}
},
removeConversation: async () => {
const { cardId, channelId } = conversationId.current;
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.removeChannel(cardId, channelId);
}
@ -248,7 +260,7 @@ export function useConversationContext() {
}
},
removeTopic: async (topicId) => {
const { cardId, channelId } = conversationId.current;
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.removeChannelTopic(cardId, channelId, topicId);
}
@ -257,8 +269,7 @@ export function useConversationContext() {
}
},
setTopicSubject: async (topicId, data) => {
console.log("DATA:", data);
const { cardId, channelId } = conversationId.current;
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.setChannelTopicSubject(cardId, channelId, topicId, data);
}