diff --git a/doc/api.oa3 b/doc/api.oa3 index e7e027cc..de53aa79 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -2472,7 +2472,7 @@ paths: schema: $ref: '#/components/schemas/Subject' - /content/channels/{channelId}/topics/{topicId}: + /content/channels/{channelId}/topics/{topicId}/detail: get: tags: - content @@ -2508,6 +2508,8 @@ paths: description: account disabled '500': description: internal server error + + /content/channels/{channelId}/topics/{topicId}: delete: tags: - content diff --git a/net/server/internal/api_getChannelTopics.go b/net/server/internal/api_getChannelTopics.go index e4c6f94b..ad515978 100644 --- a/net/server/internal/api_getChannelTopics.go +++ b/net/server/internal/api_getChannelTopics.go @@ -25,7 +25,7 @@ func GetChannelTopics(w http.ResponseWriter, r *http.Request) { } } - var response []*Topic + response := []*Topic{} if revisionSet { var slots []store.TopicSlot if err := store.DB.Preload("Topic").Where("channel_id = ? AND revision > ?", channelSlot.Channel.ID, revision).Find(&slots).Error; err != nil { diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index dac14774..a5a5aaf8 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -555,7 +555,7 @@ var routes = Routes{ Route{ "GetChannelTopic", strings.ToUpper("Get"), - "/content/channels/{channelId}/topics/{topicId}", + "/content/channels/{channelId}/topics/{topicId}/detail", GetChannelTopic, }, diff --git a/net/web/src/Api/getChannelTopic.js b/net/web/src/Api/getChannelTopic.js new file mode 100644 index 00000000..550c73b4 --- /dev/null +++ b/net/web/src/Api/getChannelTopic.js @@ -0,0 +1,9 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getChannelTopic(token, channelId, topicId) { + let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics/${topicId}/detail?agent=${token}`, + { method: 'GET' }); + checkResponse(topic) + return await topic.json() +} + diff --git a/net/web/src/Api/getChannelTopics.js b/net/web/src/Api/getChannelTopics.js new file mode 100644 index 00000000..f553b7c4 --- /dev/null +++ b/net/web/src/Api/getChannelTopics.js @@ -0,0 +1,13 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getChannelTopics(token, channelId, revision) { + let rev = '' + if (revision != null) { + rev = `&revision=${revision}` + } + let topics = await fetchWithTimeout(`/content/channels/${channelId}/topics?agent=${token}${rev}`, + { method: 'GET' }); + checkResponse(topics) + return await topics.json() +} + diff --git a/net/web/src/Api/getContactChannelTopic.js b/net/web/src/Api/getContactChannelTopic.js new file mode 100644 index 00000000..2a0696a2 --- /dev/null +++ b/net/web/src/Api/getContactChannelTopic.js @@ -0,0 +1,9 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getContactChannelTopic(token, channelId, topicId) { + let topic = await fetchWithTimeout(`/content/channels/${channelId}/topics/${topicId}/detail?contact=${token}`, + { method: 'GET' }); + checkResponse(topic) + return await topic.json() +} + diff --git a/net/web/src/Api/getContactChannelTopics.js b/net/web/src/Api/getContactChannelTopics.js new file mode 100644 index 00000000..ca7dc9af --- /dev/null +++ b/net/web/src/Api/getContactChannelTopics.js @@ -0,0 +1,13 @@ +import { checkResponse, fetchWithTimeout } from './fetchUtil'; + +export async function getContactChannelTopics(token, channelId, revision) { + let rev = '' + if (revision != null) { + rev = `&revision=${revision}` + } + let topics = await fetchWithTimeout(`/content/channels/${channelId}/topics?contact=${token}${rev}`, + { method: 'GET' }); + checkResponse(topics) + return await topics.json() +} + diff --git a/net/web/src/AppContext/useAppContext.hook.js b/net/web/src/AppContext/useAppContext.hook.js index f18433d7..93f65986 100644 --- a/net/web/src/AppContext/useAppContext.hook.js +++ b/net/web/src/AppContext/useAppContext.hook.js @@ -47,6 +47,8 @@ async function updateChannels(token, revision, channelMap, mergeChannels) { cur.data.detailRevision = slot.data.detailRevision; } } + cur.data.topicRevision = channel.data.topicRevision; + cur.revision = channel.revision; channelMap.set(channel.id, cur); } else { @@ -152,6 +154,8 @@ async function updateContactChannels(token, viewRevision, channelRevision, chann cur.data.detailRevision = slot.data.detailRevision; } } + cur.data.topicRevision = channel.data.topicRevision; + cur.revision = channel.revision; channelMap.set(channel.id, cur); } else { @@ -280,6 +284,7 @@ export function useAppContext() { getCardImageUrl: (cardId, revision) => getCardImageUrl(state.token, cardId, revision), getCardByGuid: getCardByGuid, getCard: (id) => cards.current.get(id), + getChannel: (id) => channels.current.get(id), getConnectedCards: getConnectedCards, } diff --git a/net/web/src/User/Contact/useContact.hook.js b/net/web/src/User/Contact/useContact.hook.js index ca38e253..37bd2eb6 100644 --- a/net/web/src/User/Contact/useContact.hook.js +++ b/net/web/src/User/Contact/useContact.hook.js @@ -156,7 +156,7 @@ export function useContact() { useEffect(() => { if (app?.state?.access === 'user') { - let card = app.actions.getCard(guid); + let card = app.actions.getCardByGuid(guid); if (card) { let profile = card.data.cardProfile; updateState({ cardId: card.id }); diff --git a/net/web/src/User/Conversation/Conversation.jsx b/net/web/src/User/Conversation/Conversation.jsx index 5e977eb7..b298b182 100644 --- a/net/web/src/User/Conversation/Conversation.jsx +++ b/net/web/src/User/Conversation/Conversation.jsx @@ -17,8 +17,8 @@ export function Conversation() { }); useEffect(() => { - setScrollIndex(998); - }) + setScrollIndex(state.topics.length); + }, [state]) const renderRow = ({ index, isScrolling, key, parent, style }) => { @@ -33,7 +33,7 @@ export function Conversation() { {({ measure, registerChild }) => ( // 'style' attribute required to position cell (within parent List)
- TEST MESSAGE!!! + { state.topics[index].data.topicDetail.data }
)} @@ -56,7 +56,7 @@ export function Conversation() { deferredMeasurementCache={cache} rowHeight={cache.rowHeight} rowRenderer={renderRow} - rowCount={999} + rowCount={state.topics.length} overscanRowCount={16} scrollToIndex={scrollIndex} /> diff --git a/net/web/src/User/Conversation/useConversation.hook.js b/net/web/src/User/Conversation/useConversation.hook.js index ce6aa1a5..4726bb50 100644 --- a/net/web/src/User/Conversation/useConversation.hook.js +++ b/net/web/src/User/Conversation/useConversation.hook.js @@ -1,16 +1,21 @@ -import { useContext, useState, useEffect } from 'react'; +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'; export function useConversation() { const [state, setState] = useState({ + topics: [], }); - const data = useLocation(); - const { contact, channel } = useParams(); + const { card, channel } = useParams(); const navigate = useNavigate(); const app = useContext(AppContext); + const topics = useRef(new Map()); const updateState = (value) => { setState((s) => ({ ...s, ...value })); @@ -22,5 +27,89 @@ export function useConversation() { }, }; + const updateConversation = async () => { + if (card) { + if(app?.actions?.getCard) { + let contact = app.actions.getCard(card); + let conversation = contact.channels.get(channel); + 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); + } + 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, channel, 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, + }); + } + } + } + else { + if(app?.actions?.getChannel) { + let conversation = app.actions.getChannel(channel); + if (conversation?.revision != state.revision) { + let slots = await getChannelTopics(app.state.token, channel, 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, channel, 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 + }); + } + } + } + } + + useEffect(() => { + updateConversation(); + }, [app]); + return { state, actions }; }